mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-23 03:02:48 +01:00
commit
0d311cd939
@ -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 ForceGuestFormat, 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.ForceGuestFormat &&
|
||||||
|
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
|
||||||
}
|
}
|
||||||
@ -939,6 +943,10 @@ func (am *AccountManager) checkPassphrase(accountName, passphrase string) (accou
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) (err error) {
|
func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) (err error) {
|
||||||
|
// XXX check this now, so we don't allow a redundant login for an always-on client
|
||||||
|
// even for a brief period. the other potential source of nick-account conflicts
|
||||||
|
// is from force-nick-equals-account, but those will be caught later by
|
||||||
|
// fixupNickEqualsAccount and if there is a conflict, they will be logged out.
|
||||||
if client.registered {
|
if client.registered {
|
||||||
if clientAlready := am.server.clients.Get(accountName); clientAlready != nil && clientAlready.AlwaysOn() {
|
if clientAlready := am.server.clients.Get(accountName); clientAlready != nil && clientAlready.AlwaysOn() {
|
||||||
return errNickAccountMismatch
|
return errNickAccountMismatch
|
||||||
@ -1536,7 +1544,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.ForceNickEqualsAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
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.ForceGuestFormat {
|
||||||
|
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
|
||||||
|
@ -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
|
||||||
|
ForceGuestFormat bool `yaml:"force-guest-format"`
|
||||||
|
ForceNickEqualsAccount bool `yaml:"force-nick-equals-account"`
|
||||||
|
} `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
|
||||||
|
}
|
||||||
|
@ -41,8 +41,7 @@ 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`)
|
errNickAccountMismatch = errors.New(`Your nickname must match your account name; try logging out and logging back in with SASL`)
|
||||||
errNickAccountMismatch = errors.New(`Your nickname doesn't 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`)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
@ -237,6 +237,8 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
|
|||||||
msg := authErrorToMessage(server, err)
|
msg := authErrorToMessage(server, err)
|
||||||
rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
|
rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
|
||||||
return false
|
return false
|
||||||
|
} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
sendSuccessfulAccountAuth(client, rb, false, true)
|
sendSuccessfulAccountAuth(client, rb, false, true)
|
||||||
@ -245,7 +247,7 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
|
|||||||
|
|
||||||
func authErrorToMessage(server *Server, err error) (msg string) {
|
func authErrorToMessage(server *Server, err error) (msg string) {
|
||||||
switch err {
|
switch err {
|
||||||
case errAccountDoesNotExist, errAccountUnverified, errAccountInvalidCredentials, errAuthzidAuthcidMismatch:
|
case errAccountDoesNotExist, errAccountUnverified, errAccountInvalidCredentials, errAuthzidAuthcidMismatch, errNickAccountMismatch:
|
||||||
return err.Error()
|
return err.Error()
|
||||||
default:
|
default:
|
||||||
// don't expose arbitrary error messages to the user
|
// don't expose arbitrary error messages to the user
|
||||||
@ -280,6 +282,8 @@ func authExternalHandler(server *Server, client *Client, mechanism string, value
|
|||||||
msg := authErrorToMessage(server, err)
|
msg := authErrorToMessage(server, err)
|
||||||
rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
|
rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
|
||||||
return false
|
return false
|
||||||
|
} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
sendSuccessfulAccountAuth(client, rb, false, true)
|
sendSuccessfulAccountAuth(client, rb, false, true)
|
||||||
|
@ -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
|
||||||
|
@ -6,7 +6,6 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -27,31 +26,28 @@ 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, utils.SafeErrorParam(nickname), client.t("Nickname is already in use"))
|
||||||
} else if err == errNicknameReserved {
|
} else if err == errNicknameReserved {
|
||||||
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, utils.SafeErrorParam(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
|
||||||
@ -115,3 +108,23 @@ func (server *Server) RandomlyRename(client *Client) {
|
|||||||
// technically performNickChange can fail to change the nick,
|
// technically performNickChange can fail to change the nick,
|
||||||
// but if they're still delinquent, the timer will get them later
|
// but if they're still delinquent, the timer will get them later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if force-nick-equals-account 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 fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config) (success bool) {
|
||||||
|
if !config.Accounts.NickReservation.ForceNickEqualsAccount {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !client.registered {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
@ -649,12 +649,19 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
|
|||||||
loginSuccessful = (err == nil)
|
loginSuccessful = (err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nickFixupFailed := false
|
||||||
|
if loginSuccessful {
|
||||||
|
if !fixupNickEqualsAccount(client, rb, server.Config()) {
|
||||||
|
loginSuccessful = false
|
||||||
|
// fixupNickEqualsAccount sends its own error message, don't send another
|
||||||
|
nickFixupFailed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if loginSuccessful {
|
if loginSuccessful {
|
||||||
sendSuccessfulAccountAuth(client, rb, true, true)
|
sendSuccessfulAccountAuth(client, rb, true, true)
|
||||||
} else if err == errNickAccountMismatch {
|
} else if !nickFixupFailed {
|
||||||
nsNotice(rb, client.t("That account is set to always-on; try logging out and logging back in with SASL"))
|
nsNotice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err)))
|
||||||
} else {
|
|
||||||
nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,7 +674,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 +711,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 +736,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.ForceGuestFormat {
|
||||||
|
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 +760,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 +771,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 && fixupNickEqualsAccount(client, rb, config) {
|
||||||
sendSuccessfulRegResponse(client, rb, true)
|
sendSuccessfulRegResponse(client, rb, true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -861,8 +877,10 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fixupNickEqualsAccount(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
|
||||||
|
@ -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:
|
||||||
|
22
oragono.yaml
22
oragono.yaml
@ -343,8 +343,26 @@ 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. a caveat: this may prevent
|
||||||
|
# users from choosing nicknames in scripts different from the guest
|
||||||
|
# nickname format.
|
||||||
|
force-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.
|
||||||
|
force-nick-equals-account: 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
|
||||||
|
Loading…
Reference in New Issue
Block a user