This commit is contained in:
Shivaram Lingamneni 2020-03-16 07:54:50 -04:00
parent ee63cd135b
commit 26fd3e69a8
11 changed files with 208 additions and 95 deletions

View File

@ -337,24 +337,28 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
return errAccountAlreadyRegistered return errAccountAlreadyRegistered
} }
config := am.server.AccountConfig() config := am.server.Config()
// final "is registration allowed" check, probably redundant: // final "is registration allowed" check, probably redundant:
if !(config.Registration.Enabled || callbackNamespace == "admin") { if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") {
return errFeatureDisabled return errFeatureDisabled
} }
// if nick reservation is enabled, you can only register your current nickname // if nick reservation is enabled, you can only register your current nickname
// as an account; this prevents "land-grab" situations where someone else // as an account; this prevents "land-grab" situations where someone else
// registers your nick out from under you and then NS GHOSTs you // registers your nick out from under you and then NS GHOSTs you
// n.b. client is nil during a SAREGISTER: // n.b. client is nil during a SAREGISTER
if config.NickReservation.Enabled && client != nil && client.NickCasefolded() != casefoldedAccount { // n.b. if EnforceGuestFormat, then there's no concern, because you can't
// register a guest nickname anyway, and the actual registration system
// will prevent any double-register
if client != nil && config.Accounts.NickReservation.Enabled &&
!config.Accounts.NickReservation.EnforceGuestFormat &&
client.NickCasefolded() != casefoldedAccount {
return errAccountMustHoldNick return errAccountMustHoldNick
} }
// can't register a guest nickname // can't register a guest nickname
renamePrefix := strings.ToLower(config.NickReservation.RenamePrefix) if config.Accounts.NickReservation.guestRegexpFolded.MatchString(casefoldedAccount) {
if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) {
return errAccountAlreadyRegistered return errAccountAlreadyRegistered
} }
@ -382,7 +386,7 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
callbackSpec := fmt.Sprintf("%s:%s", callbackNamespace, callbackValue) callbackSpec := fmt.Sprintf("%s:%s", callbackNamespace, callbackValue)
var setOptions *buntdb.SetOptions var setOptions *buntdb.SetOptions
ttl := time.Duration(config.Registration.VerifyTimeout) ttl := time.Duration(config.Accounts.Registration.VerifyTimeout)
if ttl != 0 { if ttl != 0 {
setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl} setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
} }
@ -652,7 +656,7 @@ func (am *AccountManager) dispatchCallback(client *Client, casefoldedAccount str
} }
func (am *AccountManager) dispatchMailtoCallback(client *Client, casefoldedAccount string, callbackValue string) (code string, err error) { func (am *AccountManager) dispatchMailtoCallback(client *Client, casefoldedAccount string, callbackValue string) (code string, err error) {
config := am.server.AccountConfig().Registration.Callbacks.Mailto config := am.server.Config().Accounts.Registration.Callbacks.Mailto
code = utils.GenerateSecretToken() code = utils.GenerateSecretToken()
subject := config.VerifyMessageSubject subject := config.VerifyMessageSubject
@ -811,7 +815,7 @@ func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreser
cfnick, err := CasefoldName(nick) cfnick, err := CasefoldName(nick)
skeleton, skerr := Skeleton(nick) skeleton, skerr := Skeleton(nick)
// garbage nick, or garbage options, or disabled // garbage nick, or garbage options, or disabled
nrconfig := am.server.AccountConfig().NickReservation nrconfig := am.server.Config().Accounts.NickReservation
if err != nil || skerr != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled { if err != nil || skerr != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
return errAccountNickReservationFailed return errAccountNickReservationFailed
} }
@ -1536,7 +1540,7 @@ func (am *AccountManager) VHostListRequests(limit int) (requests []PendingVHostR
func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) { func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) {
// if hostserv is disabled in config, then don't grant vhosts // if hostserv is disabled in config, then don't grant vhosts
// that were previously approved while it was enabled // that were previously approved while it was enabled
if !am.server.AccountConfig().VHosts.Enabled { if !am.server.Config().Accounts.VHosts.Enabled {
return return
} }

View File

@ -116,14 +116,52 @@ func (clients *ClientManager) Resume(oldClient *Client, session *Session) (err e
// SetNick sets a client's nickname, validating it against nicknames in use // SetNick sets a client's nickname, validating it against nicknames in use
func (clients *ClientManager) SetNick(client *Client, session *Session, newNick string) (setNick string, err error) { func (clients *ClientManager) SetNick(client *Client, session *Session, newNick string) (setNick string, err error) {
config := client.server.Config() config := client.server.Config()
newcfnick, err := CasefoldName(newNick)
var newcfnick, newSkeleton string
client.stateMutex.RLock()
account := client.account
accountName := client.accountName
settings := client.accountSettings
registered := client.registered
realname := client.realname
client.stateMutex.RUnlock()
// recompute always-on status, because client.alwaysOn is not set for unregistered clients
var alwaysOn, useAccountName bool
if account != "" {
alwaysOn = persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
useAccountName = alwaysOn || config.Accounts.NickReservation.EnforceAccountName
}
if useAccountName {
if registered && newNick != accountName && newNick != "" {
return "", errNickAccountMismatch
}
newNick = accountName
newcfnick = account
newSkeleton, err = Skeleton(newNick)
if err != nil {
return "", errNicknameInvalid
}
} else {
newNick = strings.TrimSpace(newNick)
if len(newNick) == 0 {
return "", errNickMissing
}
if account == "" && config.Accounts.NickReservation.EnforceGuestFormat {
newNick = strings.Replace(config.Accounts.NickReservation.GuestFormat, "*", newNick, 1)
}
newcfnick, err = CasefoldName(newNick)
if err != nil { if err != nil {
return "", errNicknameInvalid return "", errNicknameInvalid
} }
if len(newNick) > config.Limits.NickLen || len(newcfnick) > config.Limits.NickLen { if len(newNick) > config.Limits.NickLen || len(newcfnick) > config.Limits.NickLen {
return "", errNicknameInvalid return "", errNicknameInvalid
} }
newSkeleton, err := Skeleton(newNick) newSkeleton, err = Skeleton(newNick)
if err != nil { if err != nil {
return "", errNicknameInvalid return "", errNicknameInvalid
} }
@ -133,27 +171,14 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
} }
reservedAccount, method := client.server.accounts.EnforcementStatus(newcfnick, newSkeleton) reservedAccount, method := client.server.accounts.EnforcementStatus(newcfnick, newSkeleton)
client.stateMutex.RLock() if method == NickEnforcementStrict && reservedAccount != "" && reservedAccount != account {
account := client.account return "", errNicknameReserved
accountName := client.accountName }
settings := client.accountSettings
registered := client.registered
realname := client.realname
client.stateMutex.RUnlock()
// recompute this (client.alwaysOn is not set for unregistered clients):
alwaysOn := account != "" && persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
if alwaysOn && registered {
return "", errCantChangeNick
} }
var bouncerAllowed bool var bouncerAllowed bool
if config.Accounts.Multiclient.Enabled { if config.Accounts.Multiclient.Enabled {
if alwaysOn { if useAccountName {
// ignore the pre-reg nick, force a reattach
newNick = accountName
newcfnick = account
bouncerAllowed = true bouncerAllowed = true
} else { } else {
if config.Accounts.Multiclient.AllowedByDefault && settings.AllowBouncer != MulticlientDisallowedByUser { if config.Accounts.Multiclient.AllowedByDefault && settings.AllowBouncer != MulticlientDisallowedByUser {
@ -198,9 +223,7 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
if skeletonHolder != nil && skeletonHolder != client { if skeletonHolder != nil && skeletonHolder != client {
return "", errNicknameInUse return "", errNicknameInUse
} }
if method == NickEnforcementStrict && reservedAccount != "" && reservedAccount != account {
return "", errNicknameReserved
}
clients.removeInternal(client) clients.removeInternal(client)
clients.byNick[newcfnick] = client clients.byNick[newcfnick] = client
clients.bySkeleton[newSkeleton] = client clients.bySkeleton[newSkeleton] = client

View File

@ -7,6 +7,7 @@ package irc
import ( import (
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
@ -243,7 +244,20 @@ type AccountConfig struct {
MaxAttempts int `yaml:"max-attempts"` MaxAttempts int `yaml:"max-attempts"`
} `yaml:"login-throttling"` } `yaml:"login-throttling"`
SkipServerPassword bool `yaml:"skip-server-password"` SkipServerPassword bool `yaml:"skip-server-password"`
NickReservation NickReservationConfig `yaml:"nick-reservation"` NickReservation struct {
Enabled bool
AdditionalNickLimit int `yaml:"additional-nick-limit"`
Method NickEnforcementMethod
AllowCustomEnforcement bool `yaml:"allow-custom-enforcement"`
RenameTimeout time.Duration `yaml:"rename-timeout"`
// RenamePrefix is the legacy field, GuestFormat is the new version
RenamePrefix string `yaml:"rename-prefix"`
GuestFormat string `yaml:"guest-nickname-format"`
guestRegexp *regexp.Regexp
guestRegexpFolded *regexp.Regexp
EnforceGuestFormat bool `yaml:"enforce-guest-format"`
EnforceAccountName bool `yaml:"enforce-account-name"`
} `yaml:"nick-reservation"`
Multiclient MulticlientConfig Multiclient MulticlientConfig
Bouncer *MulticlientConfig // # handle old name for 'multiclient' Bouncer *MulticlientConfig // # handle old name for 'multiclient'
VHosts VHostConfig VHosts VHostConfig
@ -371,15 +385,6 @@ func (cm *Casemapping) UnmarshalYAML(unmarshal func(interface{}) error) (err err
return nil return nil
} }
type NickReservationConfig struct {
Enabled bool
AdditionalNickLimit int `yaml:"additional-nick-limit"`
Method NickEnforcementMethod
AllowCustomEnforcement bool `yaml:"allow-custom-enforcement"`
RenameTimeout time.Duration `yaml:"rename-timeout"`
RenamePrefix string `yaml:"rename-prefix"`
}
// ChannelRegistrationConfig controls channel registration. // ChannelRegistrationConfig controls channel registration.
type ChannelRegistrationConfig struct { type ChannelRegistrationConfig struct {
Enabled bool Enabled bool
@ -883,6 +888,19 @@ func LoadConfig(filename string) (config *Config, err error) {
config.Accounts.Multiclient.AllowedByDefault = true config.Accounts.Multiclient.AllowedByDefault = true
} }
// handle guest format, including the legacy key rename-prefix
if config.Accounts.NickReservation.GuestFormat == "" {
renamePrefix := config.Accounts.NickReservation.RenamePrefix
if renamePrefix == "" {
renamePrefix = "Guest-"
}
config.Accounts.NickReservation.GuestFormat = renamePrefix + "*"
}
config.Accounts.NickReservation.guestRegexp, config.Accounts.NickReservation.guestRegexpFolded, err = compileGuestRegexp(config.Accounts.NickReservation.GuestFormat, config.Server.Casemapping)
if err != nil {
return nil, err
}
var newLogConfigs []logger.LoggingConfig var newLogConfigs []logger.LoggingConfig
for _, logConfig := range config.Logging { for _, logConfig := range config.Logging {
// methods // methods
@ -1163,3 +1181,29 @@ func (config *Config) Diff(oldConfig *Config) (addedCaps, removedCaps *caps.Set)
return return
} }
func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard, folded *regexp.Regexp, err error) {
starIndex := strings.IndexByte(guestFormat, '*')
if starIndex == -1 {
return nil, nil, errors.New("guest format must contain exactly one *")
}
initial := guestFormat[:starIndex]
final := guestFormat[starIndex+1:]
if strings.IndexByte(final, '*') != -1 {
return nil, nil, errors.New("guest format must contain exactly one *")
}
standard, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initial, final))
if err != nil {
return
}
initialFolded, err := casefoldWithSetting(initial, casemapping)
if err != nil {
return
}
finalFolded, err := casefoldWithSetting(final, casemapping)
if err != nil {
return
}
folded, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initialFolded, finalFolded))
return
}

View File

@ -41,8 +41,8 @@ var (
errNicknameInvalid = errors.New("invalid nickname") errNicknameInvalid = errors.New("invalid nickname")
errNicknameInUse = errors.New("nickname in use") errNicknameInUse = errors.New("nickname in use")
errNicknameReserved = errors.New("nickname is reserved") errNicknameReserved = errors.New("nickname is reserved")
errCantChangeNick = errors.New(`Always-on clients can't change nicknames`) errCantChangeNick = errors.New(`You must use your account name as your nickname`)
errNickAccountMismatch = errors.New(`Your nickname doesn't match your account name`) errNickAccountMismatch = errors.New(`Your nickname must match your account name`)
errNoExistingBan = errors.New("Ban does not exist") errNoExistingBan = errors.New("Ban does not exist")
errNoSuchChannel = errors.New(`No such channel`) errNoSuchChannel = errors.New(`No such channel`)
errChannelPurged = errors.New(`This channel was purged by the server operators and cannot be used`) errChannelPurged = errors.New(`This channel was purged by the server operators and cannot be used`)

View File

@ -25,10 +25,6 @@ func (server *Server) ChannelRegistrationEnabled() bool {
return server.Config().Channels.Registration.Enabled return server.Config().Channels.Registration.Enabled
} }
func (server *Server) AccountConfig() *AccountConfig {
return &server.Config().Accounts
}
func (server *Server) GetOperator(name string) (oper *Oper) { func (server *Server) GetOperator(name string) (oper *Oper) {
name, err := CasefoldName(name) name, err := CasefoldName(name)
if err != nil { if err != nil {

View File

@ -32,7 +32,7 @@ import (
) )
// helper function to parse ACC callbacks, e.g., mailto:person@example.com, tel:16505551234 // helper function to parse ACC callbacks, e.g., mailto:person@example.com, tel:16505551234
func parseCallback(spec string, config *AccountConfig) (callbackNamespace string, callbackValue string) { func parseCallback(spec string, config AccountConfig) (callbackNamespace string, callbackValue string) {
callback := strings.ToLower(spec) callback := strings.ToLower(spec)
if callback == "*" { if callback == "*" {
callbackNamespace = "*" callbackNamespace = "*"
@ -127,7 +127,7 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage,
} }
// sasl abort // sasl abort
if !server.AccountConfig().AuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" { if !config.Accounts.AuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" {
rb.Add(nil, server.name, ERR_SASLABORTED, details.nick, client.t("SASL authentication aborted")) rb.Add(nil, server.name, ERR_SASLABORTED, details.nick, client.t("SASL authentication aborted"))
session.sasl.Clear() session.sasl.Clear()
return false return false

View File

@ -178,7 +178,7 @@ func hsNotice(rb *ResponseBuffer, text string) {
// hsNotifyChannel notifies the designated channel of new vhost activity // hsNotifyChannel notifies the designated channel of new vhost activity
func hsNotifyChannel(server *Server, message string) { func hsNotifyChannel(server *Server, message string) {
chname := server.AccountConfig().VHosts.UserRequests.Channel chname := server.Config().Accounts.VHosts.UserRequests.Channel
channel := server.channels.Get(chname) channel := server.channels.Get(chname)
if channel == nil { if channel == nil {
return return
@ -280,11 +280,11 @@ func hsStatusHandler(server *Server, client *Client, command string, params []st
} }
func validateVhost(server *Server, vhost string, oper bool) error { func validateVhost(server *Server, vhost string, oper bool) error {
ac := server.AccountConfig() config := server.Config()
if len(vhost) > ac.VHosts.MaxLength { if len(vhost) > config.Accounts.VHosts.MaxLength {
return errVHostTooLong return errVHostTooLong
} }
if !ac.VHosts.ValidRegexp.MatchString(vhost) { if !config.Accounts.VHosts.ValidRegexp.MatchString(vhost) {
return errVHostBadCharacters return errVHostBadCharacters
} }
return nil return nil

View File

@ -6,7 +6,6 @@ package irc
import ( import (
"crypto/rand" "crypto/rand"
"encoding/hex"
"fmt" "fmt"
"strings" "strings"
@ -27,22 +26,15 @@ var (
) )
// returns whether the change succeeded or failed // returns whether the change succeeded or failed
func performNickChange(server *Server, client *Client, target *Client, session *Session, newnick string, rb *ResponseBuffer) bool { func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) bool {
nickname := strings.TrimSpace(newnick)
currentNick := client.Nick() currentNick := client.Nick()
details := target.Details()
if len(nickname) < 1 { if details.nick == nickname {
rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
return false
}
if target.Nick() == nickname {
return true return true
} }
hadNick := details.nick != "*"
origNickMask := details.nickMask
hadNick := target.HasNick()
origNickMask := target.NickMaskString()
details := target.Details()
assignedNickname, err := client.server.clients.SetNick(target, session, nickname) assignedNickname, err := client.server.clients.SetNick(target, session, nickname)
if err == errNicknameInUse { if err == errNicknameInUse {
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is already in use")) rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is already in use"))
@ -50,8 +42,12 @@ func performNickChange(server *Server, client *Client, target *Client, session *
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is reserved by a different account")) rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is reserved by a different account"))
} else if err == errNicknameInvalid { } else if err == errNicknameInvalid {
rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, currentNick, utils.SafeErrorParam(nickname), client.t("Erroneous nickname")) rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, currentNick, utils.SafeErrorParam(nickname), client.t("Erroneous nickname"))
} else if err == errCantChangeNick { } else if err == errNickAccountMismatch {
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t(err.Error())) // this used to use ERR_NICKNAMEINUSE, but it displayed poorly in some clients;
// ERR_UNKNOWNERROR at least has a better chance of displaying our error text
rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", client.t(err.Error()))
} else if err == errNickMissing {
rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
} else if err != nil { } else if err != nil {
rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error())) rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
} }
@ -96,13 +92,10 @@ func performNickChange(server *Server, client *Client, target *Client, session *
} }
func (server *Server) RandomlyRename(client *Client) { func (server *Server) RandomlyRename(client *Client) {
prefix := server.AccountConfig().NickReservation.RenamePrefix format := server.Config().Accounts.NickReservation.GuestFormat
if prefix == "" {
prefix = "Guest-"
}
buf := make([]byte, 8) buf := make([]byte, 8)
rand.Read(buf) rand.Read(buf)
nick := fmt.Sprintf("%s%s", prefix, hex.EncodeToString(buf)) nick := strings.Replace(format, "*", utils.B32Encoder.EncodeToString(buf), -1)
sessions := client.Sessions() sessions := client.Sessions()
if len(sessions) == 0 { if len(sessions) == 0 {
return return

View File

@ -610,6 +610,23 @@ func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
return true return true
} }
// if enforce-account-name is set, account name and nickname must be equal,
// so we need to re-NICK automatically on every login event (IDENTIFY,
// VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
// then we log them out (they will be able to reattach with SASL)
func nsFixNickname(client *Client, rb *ResponseBuffer, config *Config) (success bool) {
if !config.Accounts.NickReservation.EnforceAccountName {
return true
}
// don't need to supply a nickname, SetNick will use the account name
if !performNickChange(client.server, client, client, rb.session, "", rb) {
client.server.accounts.Logout(client)
nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
return false
}
return true
}
func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if client.LoggedIntoAccount() { if client.LoggedIntoAccount() {
nsNotice(rb, client.t("You're already logged into an account")) nsNotice(rb, client.t("You're already logged into an account"))
@ -649,11 +666,16 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
loginSuccessful = (err == nil) loginSuccessful = (err == nil)
} }
if loginSuccessful {
if !nsFixNickname(client, rb, server.Config()) {
loginSuccessful = false
err = errNickAccountMismatch
}
}
if loginSuccessful { if loginSuccessful {
sendSuccessfulAccountAuth(client, rb, true, true) sendSuccessfulAccountAuth(client, rb, true, true)
} else if err == errNickAccountMismatch { } else if err != errNickAccountMismatch {
nsNotice(rb, client.t("That account is set to always-on; try logging out and logging back in with SASL"))
} else {
nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password")) nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
} }
} }
@ -667,7 +689,7 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
var accountName string var accountName string
if len(params) > 0 { if len(params) > 0 {
nick := params[0] nick := params[0]
if server.AccountConfig().NickReservation.Enabled { if server.Config().Accounts.NickReservation.Enabled {
accountName = server.accounts.NickToAccount(nick) accountName = server.accounts.NickToAccount(nick)
if accountName == "" { if accountName == "" {
nsNotice(rb, client.t("That nickname is not registered")) nsNotice(rb, client.t("That nickname is not registered"))
@ -704,7 +726,6 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
details := client.Details() details := client.Details()
account := details.nick
passphrase := params[0] passphrase := params[0]
var email string var email string
if 1 < len(params) { if 1 < len(params) {
@ -730,10 +751,20 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
return return
} }
config := server.AccountConfig() config := server.Config()
account := details.nick
if config.Accounts.NickReservation.EnforceGuestFormat {
matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
if matches == nil || len(matches) < 2 {
nsNotice(rb, client.t("Erroneous nickname"))
return
}
account = matches[1]
}
var callbackNamespace, callbackValue string var callbackNamespace, callbackValue string
noneCallbackAllowed := false noneCallbackAllowed := false
for _, callback := range config.Registration.EnabledCallbacks { for _, callback := range config.Accounts.Registration.EnabledCallbacks {
if callback == "*" { if callback == "*" {
noneCallbackAllowed = true noneCallbackAllowed = true
} }
@ -744,7 +775,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if noneCallbackAllowed { if noneCallbackAllowed {
callbackNamespace = "*" callbackNamespace = "*"
} else { } else {
callbackNamespace, callbackValue = parseCallback(email, config) callbackNamespace, callbackValue = parseCallback(email, config.Accounts)
if callbackNamespace == "" || callbackValue == "" { if callbackNamespace == "" || callbackValue == "" {
nsNotice(rb, client.t("Registration requires a valid e-mail address")) nsNotice(rb, client.t("Registration requires a valid e-mail address"))
return return
@ -755,7 +786,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
if err == nil { if err == nil {
if callbackNamespace == "*" { if callbackNamespace == "*" {
err = server.accounts.Verify(client, account, "") err = server.accounts.Verify(client, account, "")
if err == nil { if err == nil && nsFixNickname(client, rb, config) {
sendSuccessfulRegResponse(client, rb, true) sendSuccessfulRegResponse(client, rb, true)
} }
} else { } else {
@ -861,8 +892,10 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
return return
} }
if nsFixNickname(client, rb, server.Config()) {
sendSuccessfulRegResponse(client, rb, true) sendSuccessfulRegResponse(client, rb, true)
} }
}
func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var target string var target string

View File

@ -76,7 +76,11 @@ func iterateFolding(profile *precis.Profile, oldStr string) (str string, err err
// Casefold returns a casefolded string, without doing any name or channel character checks. // Casefold returns a casefolded string, without doing any name or channel character checks.
func Casefold(str string) (string, error) { func Casefold(str string) (string, error) {
switch globalCasemappingSetting { return casefoldWithSetting(str, globalCasemappingSetting)
}
func casefoldWithSetting(str string, setting Casemapping) (string, error) {
switch setting {
default: default:
return iterateFolding(precis.UsernameCaseMapped, str) return iterateFolding(precis.UsernameCaseMapped, str)
case CasemappingASCII: case CasemappingASCII:

View File

@ -343,8 +343,24 @@ accounts:
# rename-timeout - this is how long users have 'til they're renamed # rename-timeout - this is how long users have 'til they're renamed
rename-timeout: 30s rename-timeout: 30s
# rename-prefix - this is the prefix to use when renaming clients (e.g. Guest-AB54U31) # format for guest nicknames:
rename-prefix: Guest- # 1. these nicknames cannot be registered or reserved
# 2. if a client is automatically renamed by the server,
# this is the template that will be used (e.g., Guest-nccj6rgmt97cg)
# 3. if enforce-guest-format (see below) is enabled, clients without
# a registered account will have this template applied to their
# nicknames (e.g., 'katie' will become 'Guest-katie')
guest-nickname-format: "Guest-*"
# when enabled, forces users not logged into an account to use
# a nickname matching the guest template:
enforce-guest-format: false
# when enabled, forces users logged into an account to use the
# account name as their nickname. when combined with strict nickname
# enforcement, this lets users treat nicknames and account names
# as equivalent for the purpose of ban/invite/exception lists.
enforce-account-name: false
# multiclient controls whether oragono allows multiple connections to # multiclient controls whether oragono allows multiple connections to
# attach to the same client/nickname identity; this is part of the # attach to the same client/nickname identity; this is part of the