From 7608e0c76c1793222f94dc7e5c0445d6005a2ac0 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 5 Feb 2019 18:22:00 -0500 Subject: [PATCH 1/7] fix #121 --- irc/server.go | 4 ++-- oragono.yaml | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/irc/server.go b/irc/server.go index 57aa5ac7..4bc1d326 100644 --- a/irc/server.go +++ b/irc/server.go @@ -261,7 +261,7 @@ func (server *Server) acceptClient(conn clientConn) { } } - server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr)) + server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr)) // prolly don't need to alert snomasks on this, only on connection reg NewClient(server, conn.Conn, conn.IsTLS) @@ -421,7 +421,7 @@ func (server *Server) tryRegister(c *Client) { server.stats.ChangeTotal(1) // continue registration - server.logger.Debug("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname)) + server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname)) server.snomasks.Send(sno.LocalConnects, fmt.Sprintf("Client connected [%s] [u:%s] [h:%s] [ip:%s] [r:%s]", c.nick, c.username, c.rawHostname, c.IPString(), c.realname)) // "register"; this includes the initial phase of session resumption diff --git a/oragono.yaml b/oragono.yaml index a63e9475..38d222d5 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -98,7 +98,7 @@ server: allow-plaintext-resume: false # maximum length of clients' sendQ in bytes - # this should be big enough to hold /LIST and HELP replies + # this should be big enough to hold bursts of channel/direct messages max-sendq: 16k # maximum number of connections per subnet @@ -354,10 +354,11 @@ logging: # file log to given target filename # stdout log to stdout # stderr log to stderr - method: file stderr + # (you can specify multiple methods, e.g., to log to both stderr and a file) + method: stderr # filename to log to, if file method is selected - filename: ircd.log + # filename: ircd.log # type(s) of logs to keep here. you can use - to exclude those types # @@ -374,15 +375,16 @@ logging: # password password hashing and comparing # userinput raw lines sent by users # useroutput raw lines sent to users - type: "* -userinput -useroutput -localconnect -localconnect-ip" + type: "* -userinput -useroutput" # one of: debug info warn error level: info - - - # avoid logging IP addresses to file - method: stderr - type: localconnect localconnect-ip - level: debug + #- + # # example of a file log that avoids logging IP addresses + # method: file + # filename: ircd.log + # type: "* -userinput -useroutput -localconnect -localconnect-ip" + # level: debug # debug options debug: From 726dbedab6d4be91005ef9a8713c80f46c346922 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 5 Feb 2019 20:14:53 -0500 Subject: [PATCH 2/7] changelog updates --- CHANGELOG.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f1dbc2..3a7ca4b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,12 @@ All notable changes to Oragono will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). For the purposes of versioning, we consider the "public API" to refer to the configuration files, CLI interface and database format. -## Unreleased -New release of Oragono! Up to 057d00b. +## [0.13.0-rc] +This release has a wide range of improvements and new features. Highlights include: +* Support for storing and replaying message history, via various protocol extensions: the `draft/resume-0.2` capability, the `CHATHISTORY` command, and a custom `HISTORY` command +* Confusables prevention for Unicode nicknames and account names +* User-customizable nickname protection schemes +* A SASL-only mode in which all clients must authenticate with SASL ### Config Changes * `allow-custom-enforcement` key added under `accounts`. @@ -15,9 +19,12 @@ New release of Oragono! Up to 057d00b. * `login-throttling` section added under `accounts`. * `method` key now under `accounts` now allows the value `"optional"`. * Logging type `server` has been added, replacing the `startup`, `rehash`, and `shutdown` types. -s* We no longer listen on port `6668` by default (this fixes Docker installs). +* We no longer listen on port `6668` by default (this fixes Docker installs). +* The default logging configuration now logs to stderr only, rather than to both stderr and a file ### Security +* Added a SASL-only mode in which all clients must authenticate with SASL +* Added login throttling as a hardening measure against password guessing ### Added * `oragono genpasswd` now works when piping input in (fixes Docker installs). @@ -31,15 +38,15 @@ s* We no longer listen on port `6668` by default (this fixes Docker installs). * Added new subcommands to `NICKSERV`, including: * `PASSWD` to change account passwords. * `ENFORCE` to set a specific enforcement mechanism on your nick. + * `SAREGISTER` to allow operators to manually create new user accounts * Added Unicode confusable detection and prevention when changing nicknames and registering accounts. ### Changed * `SASL PLAIN` logins now log more correctly. * Database upgrade failures now provide information about the error that occurred. -* Idents are now restricted in the same way as other servers. +* Idents (sometimes called "usernames") are now restricted to ASCII, similar to other servers. * In addition to the founder, now auto-ops (halfop and higher) automatically bypass channel join restrictions. * Log lines now display time down to milliseconds, instead of just seconds. -* Logging-in can now be throttled, and is by default. * Updated all translation files (thanks to our amazing translators!). * Updated proposed IRCv3 capability to version [`draft/resume-0.2`](https://github.com/ircv3/ircv3-specifications/pull/306). * When nick ownership is enabled, users can now select which enforcement mechanism to use with their nickname. @@ -54,19 +61,20 @@ s* We no longer listen on port `6668` by default (this fixes Docker installs). * Channel names with right-to-left characters are now casefolded correctly. * Fixed incorrect rejection of nickmasks with Unicode RTL nicknames. * Fixed nickname sync issue which could cause clients to fail to see each other. -* Fixed some internal socker logic, to prevent race conditions. * Invalid `ISUPPORT` tokens are now explicitly rejected. * Made `server-time` timestamp format more consistent and safer. * Oragono now exits with status (1) if it fails to start. * Prevent logging in multiple times when using `/NS IDENTIFY`. * Prevented the db handler from automagically creating the database without initializing it (thanks @enckse!). We also now automatically create the datastore on `run`. * Updated internal command line parsing (thanks @iNecas!). +* Fixed handling of CIDR width in connection limiting/throttling ### Internal Notes * `DLINE` and `KLINE` refactored, and expired bans are now removed from the database. * Logging system optimised. * Services handlers refactored. * Translations are now sent to/PR'd from CrowdIn automagically as we develop the software. +* Direct responses to client commands are now sent "synchronously", bypassing the sendq ## [0.12.0] - 2018-10-15 From 775ead700ff9948b98f4349cfd4ad2293a23bbfc Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 5 Feb 2019 19:03:42 -0500 Subject: [PATCH 3/7] prevent nick reservation land-grabs --- irc/accounts.go | 10 +++++++++- irc/errors.go | 9 +++++---- irc/handlers.go | 29 ++++++++++++++++------------- irc/nickserv.go | 9 +-------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/irc/accounts.go b/irc/accounts.go index 061caeec..dc08aba6 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -306,8 +306,16 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames return errAccountAlreadyRegistered } - // can't register a guest nickname config := am.server.AccountConfig() + // if nick reservation is enabled, you can only register your current nickname + // as an account; this prevents "land-grab" situations where someone else + // registers your nick out from under you and then NS GHOSTs you + // n.b. client is nil during a SAREGISTER: + if config.NickReservation.Enabled && client != nil && client.Nick() != account { + return errAccountMustHoldNick + } + + // can't register a guest nickname renamePrefix := strings.ToLower(config.NickReservation.RenamePrefix) if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) { return errAccountAlreadyRegistered diff --git a/irc/errors.go b/irc/errors.go index cc22ace8..e9829ec6 100644 --- a/irc/errors.go +++ b/irc/errors.go @@ -9,14 +9,14 @@ import "errors" // Runtime Errors var ( - errAccountAlreadyRegistered = errors.New("Account already exists") - errAccountAlreadyVerified = errors.New("Account is already verified") + errAccountAlreadyRegistered = errors.New(`Account already exists`) + errAccountAlreadyVerified = errors.New(`Account is already verified`) errAccountCantDropPrimaryNick = errors.New("Can't unreserve primary nickname") errAccountCreation = errors.New("Account could not be created") errAccountCredUpdate = errors.New("Could not update password hash to new method") errAccountDoesNotExist = errors.New("Account does not exist") errAccountInvalidCredentials = errors.New("Invalid account credentials") - errAccountBadPassphrase = errors.New("Passphrase contains forbidden characters or is otherwise invalid") + errAccountBadPassphrase = errors.New(`Passphrase contains forbidden characters or is otherwise invalid`) errAccountNickReservationFailed = errors.New("Could not (un)reserve nick") errAccountNotLoggedIn = errors.New("You're not logged into an account") errAccountTooManyNicks = errors.New("Account has too many reserved nicks") @@ -24,8 +24,9 @@ var ( errAccountVerificationFailed = errors.New("Account verification failed") errAccountVerificationInvalidCode = errors.New("Invalid account verification code") errAccountUpdateFailed = errors.New("Error while updating your account information") + errAccountMustHoldNick = errors.New(`You must hold that nickname in order to register it`) errCallbackFailed = errors.New("Account verification could not be sent") - errCertfpAlreadyExists = errors.New("An account already exists with your certificate") + errCertfpAlreadyExists = errors.New(`An account already exists for your certificate fingerprint`) errChannelAlreadyRegistered = errors.New("Channel is already registered") errChannelNameInUse = errors.New("Channel name in use") errInvalidChannelName = errors.New("Invalid channel name") diff --git a/irc/handlers.go b/irc/handlers.go index 45112aad..bbb77afa 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -153,19 +153,7 @@ func accRegisterHandler(server *Server, client *Client, msg ircmsg.IrcMessage, r err = server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, certfp) if err != nil { - msg := "Unknown" - code := ERR_UNKNOWNERROR - if err == errCertfpAlreadyExists { - msg = "An account already exists for your certificate fingerprint" - } else if err == errAccountAlreadyRegistered { - msg = "Account already exists" - code = ERR_ACCOUNT_ALREADY_EXISTS - } else if err == errAccountBadPassphrase { - msg = "Passphrase contains forbidden characters or is otherwise invalid" - } - if err == errAccountAlreadyRegistered || err == errAccountCreation || err == errCertfpAlreadyExists { - msg = err.Error() - } + msg, code := registrationErrorToMessageAndCode(err) rb.Add(nil, server.name, code, nick, "ACC", "REGISTER", client.t(msg)) return false } @@ -186,6 +174,21 @@ func accRegisterHandler(server *Server, client *Client, msg ircmsg.IrcMessage, r return false } +func registrationErrorToMessageAndCode(err error) (message, numeric string) { + // default responses: let's be risk-averse about displaying internal errors + // to the clients, especially for something as sensitive as accounts + message = `Could not register` + numeric = ERR_UNKNOWNERROR + switch err { + case errAccountAlreadyRegistered, errAccountAlreadyVerified: + message = err.Error() + numeric = ERR_ACCOUNT_ALREADY_EXISTS + case errAccountCreation, errAccountMustHoldNick, errAccountBadPassphrase, errCertfpAlreadyExists: + message = err.Error() + } + return +} + // helper function to dispatch messages when a client successfully registers func sendSuccessfulRegResponse(client *Client, rb *ResponseBuffer, forNS bool) { if forNS { diff --git a/irc/nickserv.go b/irc/nickserv.go index 8ebe0b52..0352d458 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -376,14 +376,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params [] // details could not be stored and relevant numerics have been dispatched, abort if err != nil { - errMsg := client.t("Could not register") - if err == errCertfpAlreadyExists { - errMsg = client.t("An account already exists for your certificate fingerprint") - } else if err == errAccountAlreadyRegistered || err == errAccountAlreadyVerified { - errMsg = client.t("Account already exists") - } else if err == errAccountBadPassphrase { - errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid") - } + errMsg, _ := registrationErrorToMessageAndCode(err) nsNotice(rb, errMsg) return } From d321bf63ae62fe4c701ae1aa686a7eeaa1ab6150 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 5 Feb 2019 20:14:57 -0500 Subject: [PATCH 4/7] document nick reservation --- docs/MANUAL.md | 60 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 11c22edf..e2197de8 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -117,17 +117,10 @@ In this section, we'll explain and go through using various features of the Orag In most IRC servers you can use `NickServ` to register an account. You can do the same thing with Oragono, by default, with no other software needed! -However, there are some differences between how Oragono handles accounts and how most other servers do. Some of these differences are that: - -- In Oragono, account names are completely unrelated to nicknames. -- In Oragono, there's no nickname ownership unless you configure a config section. - -With nickname ownership and account names, on most IRC servers your nickname and your account name are one and the same thing. This isn't the case with Oragono. When using Oragono, your nickname and account name are totally unrelated. However, you can enable nickname ownership with the `nick-reservation` section in the config. - These are the two ways you can register an account: /QUOTE ACC REGISTER * passphrase : - /NS REGISTER + /NS REGISTER * This is the way to go if you want to use a regular password. `` and `` are your username and password, respectively (make sure the leave that one `:` before your actual password!). @@ -138,6 +131,57 @@ If you want to use a TLS client certificate to authenticate (`SASL CERTFP`), the Once you've registered, you'll need to setup SASL to login (or use NickServ IDENTIFY). One of the more complete SASL instruction pages is Freenode's page [here](https://freenode.net/kb/answer/sasl). Open up that page, find your IRC client and then setup SASL with your chosen username and password! +## Nickname reservation + +Oragono supports several different modes of operation with respect to accounts and nicknames. + +### Traditional / lenient mode + +This is the mode that matches the typical pre-modern ircd behavior. In this mode, there is no connection between account names and nicknames. Anyone can use any nickname (as long as it's not already in use by another running client). However, accounts are still useful: they can be used to register channels (see below), and some IRCv3-capable clients (with the `account-tag` or `extended-join` capabilities) may be able to take advantage of them. + +To enable this mode, set the following configs (they are the defaults): + +* `accounts.registration.enabled = true` +* `accounts.authentication-enabled = true` +* `accounts.nick-reservation.enabled = false` + +### Nick reservation + +This is the mode corresponding to a typical IRC network with a service system (like Freenode). In this mode, registering an account gives you privileges over the use of that account as a nickname. The server will then help you to enforce control over your nickname(s): + +* You can proactively prevent anyone from using your nickname, unless they're already logged into your account +* Alternately, you can give clients a grace period to log into your account, but if they don't and the grace period expires, the server will change their nickname to something else +* Alternately, you can forego any proactive enforcement --- but if you decide you want to reclaim your nickname from a squatter, you can `/msg Nickserv ghost stolen_nickname` and they'll be disconnected +* You can associate additional nicknames with your account by changing to it and then issuing `/msg nickserv group` + +To enable this mode, set the following configs: + +* `accounts.registration.enabled = true` +* `accounts.authentication-enabled = true` +* `accounts.nick-reservation.enabled = true` + +The following additional configs are recommended: + +* `accounts.nick-reservation.method = timeout` ; setting `strict` here effectively forces people to use SASL, and some popular clients either do not support SASL, or have bugs in their SASL implementations. +* `accounts.nick-reservation.allow-custom-enforcement = true` ; this allows people to opt into strict enforcement, or opt out of enforcement + +### SASL-only mode + +This mode is comparable to Slack, Mattermost, or similar products intended as internal chat servers for an organization or team. In this mode, clients cannot connect to the server unless they log in with SASL as part of the initial handshake. This allows Oragono to be deployed facing the public Internet, with fine-grained control over who can log in. + +In this mode, clients must have a valid account to connect, so they cannot register their own accounts. Accordingly, an operator must do the initial account creation, using the `SAREGISTER` command of NickServ. (For more details, `/msg nickserv help saregister`.) To bootstrap this process, the SASL requirement can be disabled initially so that a first account can be created. Alternately, connections from localhost are exempt (by default) from the SASL requirement. + +To enable this mode, set the following configs: + +* `accounts.registration.enabled = true` +* `accounts.authentication-enabled = true` +* `accounts.require-sasl.enabled = true` +* `accounts.nick-reservation.enabled = true` + +Additionally, the following config is recommended: + +* `accounts.nick-reservation.method = strict` + ## Channel Registration From fba91b908ffab26b1ffba66341c213ee314d550e Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 5 Feb 2019 19:56:46 -0500 Subject: [PATCH 5/7] document chanserv amode, fixes #299 --- docs/MANUAL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index e2197de8..7b637f39 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -193,6 +193,8 @@ To register a channel, make sure you're joined to it and logged into your accoun For example, `/CS REGISTER #channel` will register the channel `#test` to my account. If you have a registered channel, you can use `/CS OP #channel` to regain ops in it. Right now, the options for a registered channel are pretty sparse, but we'll add more as we go along. +If your friends have registered accounts, you can automatically grant them operator permissions when they join the channel. For more details, see `/CS HELP AMODE`. + ## Language From 73f36fba088eaa0cc1c95758269a3408b434b6c7 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 5 Feb 2019 20:09:36 -0500 Subject: [PATCH 6/7] NS REGISTER needs a login throttle check as well --- irc/nickserv.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/irc/nickserv.go b/irc/nickserv.go index 0352d458..2d3113c0 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -339,6 +339,10 @@ func nsRegisterHandler(server *Server, client *Client, command string, params [] return } + if !nsLoginThrottleCheck(client, rb) { + return + } + config := server.AccountConfig() var callbackNamespace, callbackValue string noneCallbackAllowed := false From 54ca7915c97483def7b4f99e9d9b5249ae3c3d2f Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Tue, 5 Feb 2019 21:33:45 -0500 Subject: [PATCH 7/7] reference NS ENFORCE in the manual --- docs/MANUAL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 7b637f39..c94e550f 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -163,7 +163,7 @@ To enable this mode, set the following configs: The following additional configs are recommended: * `accounts.nick-reservation.method = timeout` ; setting `strict` here effectively forces people to use SASL, and some popular clients either do not support SASL, or have bugs in their SASL implementations. -* `accounts.nick-reservation.allow-custom-enforcement = true` ; this allows people to opt into strict enforcement, or opt out of enforcement +* `accounts.nick-reservation.allow-custom-enforcement = true` ; this allows people to opt into strict enforcement, or opt out of enforcement. For details on how to do this, `/msg nickserv help enforce`. ### SASL-only mode