1// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
2// released under the MIT license
3
4package irc
5
6import (
7	"crypto/rand"
8	"crypto/x509"
9	"encoding/json"
10	"fmt"
11	"sort"
12	"strconv"
13	"strings"
14	"sync"
15	"time"
16	"unicode"
17
18	"github.com/ergochat/irc-go/ircutils"
19	"github.com/xdg-go/scram"
20
21	"github.com/ergochat/ergo/irc/connection_limits"
22	"github.com/ergochat/ergo/irc/email"
23	"github.com/ergochat/ergo/irc/migrations"
24	"github.com/ergochat/ergo/irc/modes"
25	"github.com/ergochat/ergo/irc/passwd"
26	"github.com/ergochat/ergo/irc/utils"
27	"github.com/tidwall/buntdb"
28)
29
30const (
31	keyAccountExists           = "account.exists %s"
32	keyAccountVerified         = "account.verified %s"
33	keyAccountUnregistered     = "account.unregistered %s"
34	keyAccountVerificationCode = "account.verificationcode %s"
35	keyAccountName             = "account.name %s" // stores the 'preferred name' of the account, not casemapped
36	keyAccountRegTime          = "account.registered.time %s"
37	keyAccountCredentials      = "account.credentials %s"
38	keyAccountAdditionalNicks  = "account.additionalnicks %s"
39	keyAccountSettings         = "account.settings %s"
40	keyAccountVHost            = "account.vhost %s"
41	keyCertToAccount           = "account.creds.certfp %s"
42	keyAccountChannels         = "account.channels %s" // channels registered to the account
43	keyAccountLastSeen         = "account.lastseen %s"
44	keyAccountModes            = "account.modes %s"     // user modes for the always-on client as a string
45	keyAccountRealname         = "account.realname %s"  // client realname stored as string
46	keyAccountSuspended        = "account.suspended %s" // client realname stored as string
47	keyAccountPwReset          = "account.pwreset %s"
48	keyAccountEmailChange      = "account.emailchange %s"
49	// for an always-on client, a map of channel names they're in to their current modes
50	// (not to be confused with their amodes, which a non-always-on client can have):
51	keyAccountChannelToModes = "account.channeltomodes %s"
52
53	maxCertfpsPerAccount = 5
54)
55
56// everything about accounts is persistent; therefore, the database is the authoritative
57// source of truth for all account information. anything on the heap is just a cache
58type AccountManager struct {
59	sync.RWMutex                      // tier 2
60	serialCacheUpdateMutex sync.Mutex // tier 3
61
62	server *Server
63	// track clients logged in to accounts
64	accountToClients  map[string][]*Client
65	nickToAccount     map[string]string
66	skeletonToAccount map[string]string
67	accountToMethod   map[string]NickEnforcementMethod
68	registerThrottle  connection_limits.GenericThrottle
69}
70
71func (am *AccountManager) Initialize(server *Server) {
72	am.accountToClients = make(map[string][]*Client)
73	am.nickToAccount = make(map[string]string)
74	am.skeletonToAccount = make(map[string]string)
75	am.accountToMethod = make(map[string]NickEnforcementMethod)
76	am.server = server
77
78	config := server.Config()
79	am.buildNickToAccountIndex(config)
80	am.createAlwaysOnClients(config)
81	am.resetRegisterThrottle(config)
82}
83
84func (am *AccountManager) resetRegisterThrottle(config *Config) {
85	am.Lock()
86	defer am.Unlock()
87
88	am.registerThrottle = connection_limits.GenericThrottle{
89		Duration: config.Accounts.Registration.Throttling.Duration,
90		Limit:    config.Accounts.Registration.Throttling.MaxAttempts,
91	}
92}
93
94func (am *AccountManager) touchRegisterThrottle() (throttled bool) {
95	am.Lock()
96	defer am.Unlock()
97	throttled, _ = am.registerThrottle.Touch()
98	return
99}
100
101func (am *AccountManager) createAlwaysOnClients(config *Config) {
102	if config.Accounts.Multiclient.AlwaysOn == PersistentDisabled {
103		return
104	}
105
106	verifiedPrefix := fmt.Sprintf(keyAccountVerified, "")
107
108	am.serialCacheUpdateMutex.Lock()
109	defer am.serialCacheUpdateMutex.Unlock()
110
111	var accounts []string
112
113	am.server.store.View(func(tx *buntdb.Tx) error {
114		err := tx.AscendGreaterOrEqual("", verifiedPrefix, func(key, value string) bool {
115			if !strings.HasPrefix(key, verifiedPrefix) {
116				return false
117			}
118			account := strings.TrimPrefix(key, verifiedPrefix)
119			accounts = append(accounts, account)
120			return true
121		})
122		return err
123	})
124
125	for _, accountName := range accounts {
126		account, err := am.LoadAccount(accountName)
127		if err == nil && (account.Verified && account.Suspended == nil) &&
128			persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, account.Settings.AlwaysOn) {
129			am.server.AddAlwaysOnClient(
130				account,
131				am.loadChannels(accountName),
132				am.loadLastSeen(accountName),
133				am.loadModes(accountName),
134				am.loadRealname(accountName),
135			)
136		}
137	}
138}
139
140func (am *AccountManager) buildNickToAccountIndex(config *Config) {
141	if !config.Accounts.NickReservation.Enabled {
142		return
143	}
144
145	nickToAccount := make(map[string]string)
146	skeletonToAccount := make(map[string]string)
147	accountToMethod := make(map[string]NickEnforcementMethod)
148	existsPrefix := fmt.Sprintf(keyAccountExists, "")
149
150	am.serialCacheUpdateMutex.Lock()
151	defer am.serialCacheUpdateMutex.Unlock()
152
153	err := am.server.store.View(func(tx *buntdb.Tx) error {
154		err := tx.AscendGreaterOrEqual("", existsPrefix, func(key, value string) bool {
155			if !strings.HasPrefix(key, existsPrefix) {
156				return false
157			}
158
159			account := strings.TrimPrefix(key, existsPrefix)
160			if _, err := tx.Get(fmt.Sprintf(keyAccountVerified, account)); err == nil {
161				nickToAccount[account] = account
162				accountName, err := tx.Get(fmt.Sprintf(keyAccountName, account))
163				if err != nil {
164					am.server.logger.Error("internal", "missing account name for", account)
165				} else {
166					skeleton, _ := Skeleton(accountName)
167					skeletonToAccount[skeleton] = account
168				}
169			}
170			if rawNicks, err := tx.Get(fmt.Sprintf(keyAccountAdditionalNicks, account)); err == nil {
171				additionalNicks := unmarshalReservedNicks(rawNicks)
172				for _, nick := range additionalNicks {
173					cfnick, _ := CasefoldName(nick)
174					nickToAccount[cfnick] = account
175					skeleton, _ := Skeleton(nick)
176					skeletonToAccount[skeleton] = account
177				}
178			}
179
180			if rawPrefs, err := tx.Get(fmt.Sprintf(keyAccountSettings, account)); err == nil && rawPrefs != "" {
181				var prefs AccountSettings
182				err := json.Unmarshal([]byte(rawPrefs), &prefs)
183				if err == nil && prefs.NickEnforcement != NickEnforcementOptional {
184					accountToMethod[account] = prefs.NickEnforcement
185				} else if err != nil {
186					am.server.logger.Error("internal", "corrupt account settings", account, err.Error())
187				}
188			}
189
190			return true
191		})
192		return err
193	})
194
195	if config.Accounts.NickReservation.Method == NickEnforcementStrict {
196		unregisteredPrefix := fmt.Sprintf(keyAccountUnregistered, "")
197		am.server.store.View(func(tx *buntdb.Tx) error {
198			tx.AscendGreaterOrEqual("", unregisteredPrefix, func(key, value string) bool {
199				if !strings.HasPrefix(key, unregisteredPrefix) {
200					return false
201				}
202				account := strings.TrimPrefix(key, unregisteredPrefix)
203				accountName := value
204				nickToAccount[account] = account
205				skeleton, _ := Skeleton(accountName)
206				skeletonToAccount[skeleton] = account
207				return true
208			})
209			return nil
210		})
211	}
212
213	if err != nil {
214		am.server.logger.Error("internal", "couldn't read reserved nicks", err.Error())
215	} else {
216		am.Lock()
217		am.nickToAccount = nickToAccount
218		am.skeletonToAccount = skeletonToAccount
219		am.accountToMethod = accountToMethod
220		am.Unlock()
221	}
222}
223
224func (am *AccountManager) NickToAccount(nick string) string {
225	cfnick, err := CasefoldName(nick)
226	if err != nil {
227		return ""
228	}
229	skel, err := Skeleton(nick)
230	if err != nil {
231		return ""
232	}
233
234	am.RLock()
235	defer am.RUnlock()
236	account := am.nickToAccount[cfnick]
237	if account != "" {
238		return account
239	}
240	return am.skeletonToAccount[skel]
241}
242
243// given an account, combine stored enforcement method with the config settings
244// to compute the actual enforcement method
245func configuredEnforcementMethod(config *Config, storedMethod NickEnforcementMethod) (result NickEnforcementMethod) {
246	if !config.Accounts.NickReservation.Enabled {
247		return NickEnforcementNone
248	}
249	result = storedMethod
250	// if they don't have a custom setting, or customization is disabled, use the default
251	if result == NickEnforcementOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {
252		result = config.Accounts.NickReservation.Method
253	}
254	if result == NickEnforcementOptional {
255		// enforcement was explicitly enabled neither in the config or by the user
256		result = NickEnforcementNone
257	}
258	return
259}
260
261// Given a nick, looks up the account that owns it and the method (none/timeout/strict)
262// used to enforce ownership.
263func (am *AccountManager) EnforcementStatus(cfnick, skeleton string) (account string, method NickEnforcementMethod) {
264	config := am.server.Config()
265	if !config.Accounts.NickReservation.Enabled {
266		return "", NickEnforcementNone
267	}
268
269	am.RLock()
270	defer am.RUnlock()
271
272	finalEnforcementMethod := func(account_ string) (result NickEnforcementMethod) {
273		storedMethod := am.accountToMethod[account_]
274		return configuredEnforcementMethod(config, storedMethod)
275	}
276
277	nickAccount := am.nickToAccount[cfnick]
278	skelAccount := am.skeletonToAccount[skeleton]
279	if nickAccount == "" && skelAccount == "" {
280		return "", NickEnforcementNone
281	} else if nickAccount != "" && (skelAccount == nickAccount || skelAccount == "") {
282		return nickAccount, finalEnforcementMethod(nickAccount)
283	} else if skelAccount != "" && nickAccount == "" {
284		return skelAccount, finalEnforcementMethod(skelAccount)
285	} else {
286		// nickAccount != skelAccount and both are nonempty:
287		// two people have competing claims on (this casefolding of) this nick!
288		nickMethod := finalEnforcementMethod(nickAccount)
289		skelMethod := finalEnforcementMethod(skelAccount)
290		switch {
291		case skelMethod == NickEnforcementNone:
292			return nickAccount, nickMethod
293		case nickMethod == NickEnforcementNone:
294			return skelAccount, skelMethod
295		default:
296			// nobody can use this nick
297			return "!", NickEnforcementStrict
298		}
299	}
300}
301
302// Sets a custom enforcement method for an account and stores it in the database.
303func (am *AccountManager) SetEnforcementStatus(account string, method NickEnforcementMethod) (finalSettings AccountSettings, err error) {
304	config := am.server.Config()
305	if !(config.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.AllowCustomEnforcement) {
306		err = errFeatureDisabled
307		return
308	}
309
310	setter := func(in AccountSettings) (out AccountSettings, err error) {
311		out = in
312		out.NickEnforcement = method
313		return out, nil
314	}
315
316	_, err = am.ModifyAccountSettings(account, setter)
317	if err != nil {
318		return
319	}
320
321	// this update of the data plane is racey, but it's probably fine
322	am.Lock()
323	defer am.Unlock()
324
325	if method == NickEnforcementOptional {
326		delete(am.accountToMethod, account)
327	} else {
328		am.accountToMethod[account] = method
329	}
330
331	return
332}
333
334func (am *AccountManager) AccountToClients(account string) (result []*Client) {
335	cfaccount, err := CasefoldName(account)
336	if err != nil {
337		return
338	}
339
340	am.RLock()
341	defer am.RUnlock()
342	return am.accountToClients[cfaccount]
343}
344
345func (am *AccountManager) Register(client *Client, account string, callbackNamespace string, callbackValue string, passphrase string, certfp string) error {
346	casefoldedAccount, err := CasefoldName(account)
347	skeleton, skerr := Skeleton(account)
348	if err != nil || skerr != nil || account == "" || account == "*" {
349		return errAccountCreation
350	}
351
352	if restrictedCasefoldedNicks.Has(casefoldedAccount) || restrictedSkeletons.Has(skeleton) {
353		return errAccountAlreadyRegistered
354	}
355
356	config := am.server.Config()
357
358	// final "is registration allowed" check:
359	if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") || am.server.Defcon() <= 4 {
360		return errFeatureDisabled
361	}
362
363	if client != nil && client.Account() != "" {
364		return errAccountAlreadyLoggedIn
365	}
366
367	if client != nil && am.touchRegisterThrottle() {
368		am.server.logger.Warning("accounts", "global registration throttle exceeded by client", client.Nick())
369		return errLimitExceeded
370	}
371
372	// if nick reservation is enabled, don't let people reserve nicknames
373	// that they would not be eligible to take, e.g.,
374	// 1. a nickname that someone else is currently holding
375	// 2. a nickname confusable with an existing reserved nickname
376	// this has a lot of weird edge cases because of force-guest-format
377	// and the possibility of registering a nickname on an "unregistered connection"
378	// (i.e., pre-handshake).
379	if client != nil && config.Accounts.NickReservation.Enabled {
380		_, nickAcquireError, _ := am.server.clients.SetNick(client, nil, account, true)
381		if !(nickAcquireError == nil || nickAcquireError == errNoop) {
382			return errAccountMustHoldNick
383		}
384	}
385
386	// can't register a guest nickname
387	if config.Accounts.NickReservation.guestRegexpFolded.MatchString(casefoldedAccount) {
388		return errAccountAlreadyRegistered
389	}
390
391	accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
392	unregisteredKey := fmt.Sprintf(keyAccountUnregistered, casefoldedAccount)
393	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
394	registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
395	credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
396	verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
397	settingsKey := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
398	certFPKey := fmt.Sprintf(keyCertToAccount, certfp)
399
400	var creds AccountCredentials
401	creds.Version = 1
402	err = creds.SetPassphrase(passphrase, am.server.Config().Accounts.Registration.BcryptCost)
403	if err != nil {
404		return err
405	}
406	creds.AddCertfp(certfp)
407	credStr, err := creds.Serialize()
408	if err != nil {
409		return err
410	}
411
412	var settingsStr string
413	if callbackNamespace == "mailto" {
414		settings := AccountSettings{Email: callbackValue}
415		j, err := json.Marshal(settings)
416		if err == nil {
417			settingsStr = string(j)
418		}
419	}
420
421	registeredTimeStr := strconv.FormatInt(time.Now().UnixNano(), 10)
422
423	var setOptions *buntdb.SetOptions
424	ttl := time.Duration(config.Accounts.Registration.VerifyTimeout)
425	if ttl != 0 {
426		setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
427	}
428
429	err = func() error {
430		am.serialCacheUpdateMutex.Lock()
431		defer am.serialCacheUpdateMutex.Unlock()
432
433		// can't register an account with the same name as a registered nick
434		if am.NickToAccount(account) != "" {
435			return errAccountAlreadyRegistered
436		}
437
438		return am.server.store.Update(func(tx *buntdb.Tx) error {
439			if _, err := tx.Get(unregisteredKey); err == nil {
440				return errAccountAlreadyUnregistered
441			}
442
443			_, err = am.loadRawAccount(tx, casefoldedAccount)
444			if err != errAccountDoesNotExist {
445				return errAccountAlreadyRegistered
446			}
447
448			if certfp != "" {
449				// make sure certfp doesn't already exist because that'd be silly
450				_, err := tx.Get(certFPKey)
451				if err != buntdb.ErrNotFound {
452					return errCertfpAlreadyExists
453				}
454			}
455
456			tx.Set(accountKey, "1", setOptions)
457			tx.Set(accountNameKey, account, setOptions)
458			tx.Set(registeredTimeKey, registeredTimeStr, setOptions)
459			tx.Set(credentialsKey, credStr, setOptions)
460			tx.Set(settingsKey, settingsStr, setOptions)
461			if certfp != "" {
462				tx.Set(certFPKey, casefoldedAccount, setOptions)
463			}
464			return nil
465		})
466	}()
467
468	if err != nil {
469		return err
470	}
471
472	code, err := am.dispatchCallback(client, account, callbackNamespace, callbackValue)
473	if err != nil {
474		am.Unregister(casefoldedAccount, true)
475		return &registrationCallbackError{underlying: err}
476	} else {
477		return am.server.store.Update(func(tx *buntdb.Tx) error {
478			_, _, err = tx.Set(verificationCodeKey, code, setOptions)
479			return err
480		})
481	}
482}
483
484type registrationCallbackError struct {
485	underlying error
486}
487
488func (r *registrationCallbackError) Error() string {
489	return `Account verification could not be sent`
490}
491
492func registrationCallbackErrorText(config *Config, client *Client, err error) string {
493	if callbackErr, ok := err.(*registrationCallbackError); ok {
494		// only expose a user-visible error if we are doing direct sending
495		if config.Accounts.Registration.EmailVerification.DirectSendingEnabled() {
496			errorText := ircutils.SanitizeText(callbackErr.underlying.Error(), 350)
497			return fmt.Sprintf(client.t("Could not dispatch registration e-mail: %s"), errorText)
498		} else {
499			return client.t("Could not dispatch registration e-mail")
500		}
501	} else {
502		return ""
503	}
504}
505
506// ValidatePassphrase checks whether a passphrase is allowed by our rules
507func ValidatePassphrase(passphrase string) error {
508	// sanity check the length
509	if len(passphrase) == 0 || len(passphrase) > 300 {
510		return errAccountBadPassphrase
511	}
512	// we use * as a placeholder in some places, if it's gotten this far then fail
513	if passphrase == "*" {
514		return errAccountBadPassphrase
515	}
516	// validate that the passphrase contains no spaces, and furthermore is valid as a
517	// non-final IRC parameter. we already checked that it is nonempty:
518	if passphrase[0] == ':' {
519		return errAccountBadPassphrase
520	}
521	for _, r := range passphrase {
522		if unicode.IsSpace(r) {
523			return errAccountBadPassphrase
524		}
525	}
526	return nil
527}
528
529// changes the password for an account
530func (am *AccountManager) setPassword(accountName string, password string, hasPrivs bool) (err error) {
531	cfAccount, err := CasefoldName(accountName)
532	if err != nil {
533		return errAccountDoesNotExist
534	}
535
536	credKey := fmt.Sprintf(keyAccountCredentials, cfAccount)
537	var credStr string
538	am.server.store.View(func(tx *buntdb.Tx) error {
539		// no need to check verification status here or below;
540		// you either need to be auth'ed to the account or be an oper to do this
541		credStr, err = tx.Get(credKey)
542		return nil
543	})
544
545	if err != nil {
546		return errAccountDoesNotExist
547	}
548
549	var creds AccountCredentials
550	err = json.Unmarshal([]byte(credStr), &creds)
551	if err != nil {
552		return err
553	}
554
555	if !hasPrivs && creds.Empty() {
556		return errCredsExternallyManaged
557	}
558
559	err = creds.SetPassphrase(password, am.server.Config().Accounts.Registration.BcryptCost)
560	if err != nil {
561		return err
562	}
563
564	if creds.Empty() && !hasPrivs {
565		return errEmptyCredentials
566	}
567
568	newCredStr, err := creds.Serialize()
569	if err != nil {
570		return err
571	}
572
573	err = am.server.store.Update(func(tx *buntdb.Tx) error {
574		curCredStr, err := tx.Get(credKey)
575		if credStr != curCredStr {
576			return errCASFailed
577		}
578		_, _, err = tx.Set(credKey, newCredStr, nil)
579		return err
580	})
581
582	return err
583}
584
585type alwaysOnChannelStatus struct {
586	Modes    string
587	JoinTime int64
588}
589
590func (am *AccountManager) saveChannels(account string, channelToModes map[string]alwaysOnChannelStatus) {
591	j, err := json.Marshal(channelToModes)
592	if err != nil {
593		am.server.logger.Error("internal", "couldn't marshal channel-to-modes", account, err.Error())
594		return
595	}
596	jStr := string(j)
597	key := fmt.Sprintf(keyAccountChannelToModes, account)
598	am.server.store.Update(func(tx *buntdb.Tx) error {
599		tx.Set(key, jStr, nil)
600		return nil
601	})
602}
603
604func (am *AccountManager) loadChannels(account string) (channelToModes map[string]alwaysOnChannelStatus) {
605	key := fmt.Sprintf(keyAccountChannelToModes, account)
606	var channelsStr string
607	am.server.store.View(func(tx *buntdb.Tx) error {
608		channelsStr, _ = tx.Get(key)
609		return nil
610	})
611	if channelsStr == "" {
612		return nil
613	}
614	err := json.Unmarshal([]byte(channelsStr), &channelToModes)
615	if err != nil {
616		am.server.logger.Error("internal", "couldn't marshal channel-to-modes", account, err.Error())
617		return nil
618	}
619	return
620}
621
622func (am *AccountManager) saveModes(account string, uModes modes.Modes) {
623	modeStr := uModes.String()
624	key := fmt.Sprintf(keyAccountModes, account)
625	am.server.store.Update(func(tx *buntdb.Tx) error {
626		tx.Set(key, modeStr, nil)
627		return nil
628	})
629}
630
631func (am *AccountManager) loadModes(account string) (uModes modes.Modes) {
632	key := fmt.Sprintf(keyAccountModes, account)
633	var modeStr string
634	am.server.store.View(func(tx *buntdb.Tx) error {
635		modeStr, _ = tx.Get(key)
636		return nil
637	})
638	for _, m := range modeStr {
639		uModes = append(uModes, modes.Mode(m))
640	}
641	return
642}
643
644func (am *AccountManager) saveLastSeen(account string, lastSeen map[string]time.Time) {
645	key := fmt.Sprintf(keyAccountLastSeen, account)
646	var val string
647	if len(lastSeen) != 0 {
648		text, _ := json.Marshal(lastSeen)
649		val = string(text)
650	}
651	err := am.server.store.Update(func(tx *buntdb.Tx) error {
652		if val != "" {
653			tx.Set(key, val, nil)
654		} else {
655			tx.Delete(key)
656		}
657		return nil
658	})
659	if err != nil {
660		am.server.logger.Error("internal", "error persisting lastSeen", account, err.Error())
661	}
662}
663
664func (am *AccountManager) loadLastSeen(account string) (lastSeen map[string]time.Time) {
665	key := fmt.Sprintf(keyAccountLastSeen, account)
666	var lsText string
667	am.server.store.Update(func(tx *buntdb.Tx) error {
668		lsText, _ = tx.Get(key)
669		return nil
670	})
671	if lsText == "" {
672		return nil
673	}
674	err := json.Unmarshal([]byte(lsText), &lastSeen)
675	if err != nil {
676		return nil
677	}
678	return
679}
680
681func (am *AccountManager) saveRealname(account string, realname string) {
682	key := fmt.Sprintf(keyAccountRealname, account)
683	am.server.store.Update(func(tx *buntdb.Tx) error {
684		if realname != "" {
685			tx.Set(key, realname, nil)
686		} else {
687			tx.Delete(key)
688		}
689		return nil
690	})
691}
692
693func (am *AccountManager) loadRealname(account string) (realname string) {
694	key := fmt.Sprintf(keyAccountRealname, account)
695	am.server.store.Update(func(tx *buntdb.Tx) error {
696		realname, _ = tx.Get(key)
697		return nil
698	})
699	return
700}
701
702func (am *AccountManager) addRemoveCertfp(account, certfp string, add bool, hasPrivs bool) (err error) {
703	certfp, err = utils.NormalizeCertfp(certfp)
704	if err != nil {
705		return err
706	}
707
708	cfAccount, err := CasefoldName(account)
709	if err != nil {
710		return errAccountDoesNotExist
711	}
712
713	credKey := fmt.Sprintf(keyAccountCredentials, cfAccount)
714	var credStr string
715	am.server.store.View(func(tx *buntdb.Tx) error {
716		credStr, err = tx.Get(credKey)
717		return nil
718	})
719
720	if err != nil {
721		return errAccountDoesNotExist
722	}
723
724	var creds AccountCredentials
725	err = json.Unmarshal([]byte(credStr), &creds)
726	if err != nil {
727		return err
728	}
729
730	if !hasPrivs && creds.Empty() {
731		return errCredsExternallyManaged
732	}
733
734	if add {
735		err = creds.AddCertfp(certfp)
736	} else {
737		err = creds.RemoveCertfp(certfp)
738	}
739	if err != nil {
740		return err
741	}
742
743	if creds.Empty() && !hasPrivs {
744		return errEmptyCredentials
745	}
746
747	newCredStr, err := creds.Serialize()
748	if err != nil {
749		return err
750	}
751
752	certfpKey := fmt.Sprintf(keyCertToAccount, certfp)
753	err = am.server.store.Update(func(tx *buntdb.Tx) error {
754		curCredStr, err := tx.Get(credKey)
755		if credStr != curCredStr {
756			return errCASFailed
757		}
758		if add {
759			_, err = tx.Get(certfpKey)
760			if err != buntdb.ErrNotFound {
761				return errCertfpAlreadyExists
762			}
763			tx.Set(certfpKey, cfAccount, nil)
764		} else {
765			tx.Delete(certfpKey)
766		}
767		_, _, err = tx.Set(credKey, newCredStr, nil)
768		return err
769	})
770
771	return err
772}
773
774func (am *AccountManager) dispatchCallback(client *Client, account string, callbackNamespace string, callbackValue string) (string, error) {
775	if callbackNamespace == "*" || callbackNamespace == "none" || callbackNamespace == "admin" {
776		return "", nil
777	} else if callbackNamespace == "mailto" {
778		return am.dispatchMailtoCallback(client, account, callbackValue)
779	} else {
780		return "", fmt.Errorf("Callback not implemented: %s", callbackNamespace)
781	}
782}
783
784func (am *AccountManager) dispatchMailtoCallback(client *Client, account string, callbackValue string) (code string, err error) {
785	config := am.server.Config().Accounts.Registration.EmailVerification
786	code = utils.GenerateSecretToken()
787
788	subject := config.VerifyMessageSubject
789	if subject == "" {
790		subject = fmt.Sprintf(client.t("Verify your account on %s"), am.server.name)
791	}
792
793	message := email.ComposeMail(config, callbackValue, subject)
794	fmt.Fprintf(&message, client.t("Account: %s"), account)
795	message.WriteString("\r\n")
796	fmt.Fprintf(&message, client.t("Verification code: %s"), code)
797	message.WriteString("\r\n")
798	message.WriteString("\r\n")
799	message.WriteString(client.t("To verify your account, issue the following command:"))
800	message.WriteString("\r\n")
801	fmt.Fprintf(&message, "/MSG NickServ VERIFY %s %s\r\n", account, code)
802
803	err = email.SendMail(config, callbackValue, message.Bytes())
804	if err != nil {
805		am.server.logger.Error("internal", "Failed to dispatch e-mail to", callbackValue, err.Error())
806	}
807	return
808}
809
810func (am *AccountManager) Verify(client *Client, account string, code string) error {
811	casefoldedAccount, err := CasefoldName(account)
812	var skeleton string
813	if err != nil || account == "" || account == "*" {
814		return errAccountVerificationFailed
815	}
816
817	if client != nil && client.Account() != "" {
818		return errAccountAlreadyLoggedIn
819	}
820
821	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
822	accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
823	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
824	registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
825	verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
826	credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
827	settingsKey := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
828
829	var raw rawClientAccount
830
831	func() {
832		am.serialCacheUpdateMutex.Lock()
833		defer am.serialCacheUpdateMutex.Unlock()
834
835		// do a final check for confusability (in case someone already verified
836		// a confusable identifier):
837		var unfoldedName string
838		err = am.server.store.View(func(tx *buntdb.Tx) error {
839			unfoldedName, err = tx.Get(accountNameKey)
840			return err
841		})
842		if err != nil {
843			err = errAccountDoesNotExist
844			return
845		}
846		skeleton, err = Skeleton(unfoldedName)
847		if err != nil {
848			err = errAccountDoesNotExist
849			return
850		}
851		err = func() error {
852			am.RLock()
853			defer am.RUnlock()
854			if _, ok := am.skeletonToAccount[skeleton]; ok {
855				return errConfusableIdentifier
856			}
857			return nil
858		}()
859		if err != nil {
860			return
861		}
862
863		err = am.server.store.Update(func(tx *buntdb.Tx) error {
864			raw, err = am.loadRawAccount(tx, casefoldedAccount)
865			if err == errAccountDoesNotExist {
866				return errAccountDoesNotExist
867			} else if err != nil {
868				return errAccountVerificationFailed
869			} else if raw.Verified {
870				return errAccountAlreadyVerified
871			}
872
873			// actually verify the code
874			// a stored code of "" means a none callback / no code required
875			success := false
876			storedCode, err := tx.Get(verificationCodeKey)
877			if err == nil {
878				// this is probably unnecessary
879				if storedCode == "" || utils.SecretTokensMatch(storedCode, code) {
880					success = true
881				}
882			}
883			if !success {
884				return errAccountVerificationInvalidCode
885			}
886
887			// verify the account
888			tx.Set(verifiedKey, "1", nil)
889			// don't need the code anymore
890			tx.Delete(verificationCodeKey)
891			// re-set all other keys, removing the TTL
892			tx.Set(accountKey, "1", nil)
893			tx.Set(accountNameKey, raw.Name, nil)
894			tx.Set(registeredTimeKey, raw.RegisteredAt, nil)
895			tx.Set(credentialsKey, raw.Credentials, nil)
896			tx.Set(settingsKey, raw.Settings, nil)
897
898			var creds AccountCredentials
899			// XXX we shouldn't do (de)serialization inside the txn,
900			// but this is like 2 usec on my system
901			json.Unmarshal([]byte(raw.Credentials), &creds)
902			for _, cert := range creds.Certfps {
903				certFPKey := fmt.Sprintf(keyCertToAccount, cert)
904				tx.Set(certFPKey, casefoldedAccount, nil)
905			}
906
907			return nil
908		})
909
910		if err == nil {
911			am.Lock()
912			am.nickToAccount[casefoldedAccount] = casefoldedAccount
913			am.skeletonToAccount[skeleton] = casefoldedAccount
914			am.Unlock()
915		}
916	}()
917
918	if err != nil {
919		return err
920	}
921
922	nick := "[server admin]"
923	if client != nil {
924		nick = client.Nick()
925	}
926	am.server.logger.Info("accounts", "client", nick, "registered account", account)
927	raw.Verified = true
928	clientAccount, err := am.deserializeRawAccount(raw, casefoldedAccount)
929	if err != nil {
930		return err
931	}
932	if client != nil {
933		am.Login(client, clientAccount)
934		if client.AlwaysOn() {
935			client.markDirty(IncludeRealname)
936		}
937	}
938	// we may need to do nick enforcement here:
939	_, method := am.EnforcementStatus(casefoldedAccount, skeleton)
940	if method == NickEnforcementStrict {
941		currentClient := am.server.clients.Get(casefoldedAccount)
942		if currentClient != nil && currentClient != client && currentClient.Account() != casefoldedAccount {
943			am.server.RandomlyRename(currentClient)
944		}
945	}
946	return nil
947}
948
949// register and verify an account, for internal use
950func (am *AccountManager) SARegister(account, passphrase string) (err error) {
951	err = am.Register(nil, account, "admin", "", passphrase, "")
952	if err == nil {
953		err = am.Verify(nil, account, "")
954	}
955	return
956}
957
958type EmailChangeRecord struct {
959	TimeCreated time.Time
960	Code        string
961	Email       string
962}
963
964func (am *AccountManager) NsSetEmail(client *Client, emailAddr string) (err error) {
965	casefoldedAccount := client.Account()
966	if casefoldedAccount == "" {
967		return errAccountNotLoggedIn
968	}
969
970	if am.touchRegisterThrottle() {
971		am.server.logger.Warning("accounts", "global registration throttle exceeded by client changing email", client.Nick())
972		return errLimitExceeded
973	}
974
975	config := am.server.Config()
976	if !config.Accounts.Registration.EmailVerification.Enabled {
977		return errFeatureDisabled // redundant check, just in case
978	}
979	record := EmailChangeRecord{
980		TimeCreated: time.Now().UTC(),
981		Code:        utils.GenerateSecretToken(),
982		Email:       emailAddr,
983	}
984	recordKey := fmt.Sprintf(keyAccountEmailChange, casefoldedAccount)
985	recordBytes, _ := json.Marshal(record)
986	recordVal := string(recordBytes)
987	am.server.store.Update(func(tx *buntdb.Tx) error {
988		tx.Set(recordKey, recordVal, nil)
989		return nil
990	})
991
992	if err != nil {
993		return err
994	}
995
996	message := email.ComposeMail(config.Accounts.Registration.EmailVerification,
997		emailAddr,
998		fmt.Sprintf(client.t("Verify your change of e-mail address on %s"), am.server.name))
999	message.WriteString(fmt.Sprintf(client.t("To confirm your change of e-mail address on %s, issue the following command:"), am.server.name))
1000	message.WriteString("\r\n")
1001	fmt.Fprintf(&message, "/MSG NickServ VERIFYEMAIL %s\r\n", record.Code)
1002
1003	err = email.SendMail(config.Accounts.Registration.EmailVerification, emailAddr, message.Bytes())
1004	if err == nil {
1005		am.server.logger.Info("services",
1006			fmt.Sprintf("email change verification sent for account %s", casefoldedAccount))
1007		return
1008	} else {
1009		am.server.logger.Error("internal", "Failed to dispatch e-mail change verification to", emailAddr, err.Error())
1010		return &registrationCallbackError{err}
1011	}
1012}
1013
1014func (am *AccountManager) NsVerifyEmail(client *Client, code string) (err error) {
1015	casefoldedAccount := client.Account()
1016	if casefoldedAccount == "" {
1017		return errAccountNotLoggedIn
1018	}
1019
1020	var record EmailChangeRecord
1021	success := false
1022	key := fmt.Sprintf(keyAccountEmailChange, casefoldedAccount)
1023	ttl := time.Duration(am.server.Config().Accounts.Registration.VerifyTimeout)
1024	am.server.store.Update(func(tx *buntdb.Tx) error {
1025		rawStr, err := tx.Get(key)
1026		if err == nil && rawStr != "" {
1027			err := json.Unmarshal([]byte(rawStr), &record)
1028			if err == nil {
1029				if (ttl == 0 || time.Since(record.TimeCreated) < ttl) && utils.SecretTokensMatch(record.Code, code) {
1030					success = true
1031					tx.Delete(key)
1032				}
1033			}
1034		}
1035		return nil
1036	})
1037
1038	if !success {
1039		return errAccountVerificationInvalidCode
1040	}
1041
1042	munger := func(in AccountSettings) (out AccountSettings, err error) {
1043		out = in
1044		out.Email = record.Email
1045		return
1046	}
1047
1048	_, err = am.ModifyAccountSettings(casefoldedAccount, munger)
1049	return
1050}
1051
1052func (am *AccountManager) NsSendpass(client *Client, accountName string) (err error) {
1053	config := am.server.Config()
1054	if !(config.Accounts.Registration.EmailVerification.Enabled && config.Accounts.Registration.EmailVerification.PasswordReset.Enabled) {
1055		return errFeatureDisabled
1056	}
1057
1058	account, err := am.LoadAccount(accountName)
1059	if err != nil {
1060		return err
1061	}
1062	if !account.Verified {
1063		return errAccountUnverified
1064	}
1065	if account.Suspended != nil {
1066		return errAccountSuspended
1067	}
1068	if account.Settings.Email == "" {
1069		return errValidEmailRequired
1070	}
1071
1072	record := PasswordResetRecord{
1073		TimeCreated: time.Now().UTC(),
1074		Code:        utils.GenerateSecretToken(),
1075	}
1076	recordKey := fmt.Sprintf(keyAccountPwReset, account.NameCasefolded)
1077	recordBytes, _ := json.Marshal(record)
1078	recordVal := string(recordBytes)
1079
1080	am.server.store.Update(func(tx *buntdb.Tx) error {
1081		recStr, recErr := tx.Get(recordKey)
1082		if recErr == nil && recStr != "" {
1083			var existing PasswordResetRecord
1084			jErr := json.Unmarshal([]byte(recStr), &existing)
1085			cooldown := time.Duration(config.Accounts.Registration.EmailVerification.PasswordReset.Cooldown)
1086			if jErr == nil && time.Since(existing.TimeCreated) < cooldown {
1087				err = errLimitExceeded
1088				return nil
1089			}
1090		}
1091		tx.Set(recordKey, recordVal, &buntdb.SetOptions{
1092			Expires: true,
1093			TTL:     time.Duration(config.Accounts.Registration.EmailVerification.PasswordReset.Timeout),
1094		})
1095		return nil
1096	})
1097
1098	if err != nil {
1099		return
1100	}
1101
1102	subject := fmt.Sprintf(client.t("Reset your password on %s"), am.server.name)
1103	message := email.ComposeMail(config.Accounts.Registration.EmailVerification, account.Settings.Email, subject)
1104	fmt.Fprintf(&message, client.t("We received a request to reset your password on %s for account: %s"), am.server.name, account.Name)
1105	message.WriteString("\r\n")
1106	fmt.Fprintf(&message, client.t("If you did not initiate this request, you can safely ignore this message."))
1107	message.WriteString("\r\n")
1108	message.WriteString("\r\n")
1109	message.WriteString(client.t("Otherwise, to reset your password, issue the following command (replace `new_password` with your desired password):"))
1110	message.WriteString("\r\n")
1111	fmt.Fprintf(&message, "/MSG NickServ RESETPASS %s %s new_password\r\n", account.Name, record.Code)
1112
1113	err = email.SendMail(config.Accounts.Registration.EmailVerification, account.Settings.Email, message.Bytes())
1114	if err == nil {
1115		am.server.logger.Info("services",
1116			fmt.Sprintf("client %s sent a password reset email for account %s", client.Nick(), account.Name))
1117	} else {
1118		am.server.logger.Error("internal", "Failed to dispatch e-mail to", account.Settings.Email, err.Error())
1119	}
1120	return
1121
1122}
1123
1124func (am *AccountManager) NsResetpass(client *Client, accountName, code, password string) (err error) {
1125	if ValidatePassphrase(password) != nil {
1126		return errAccountBadPassphrase
1127	}
1128	account, err := am.LoadAccount(accountName)
1129	if err != nil {
1130		return
1131	}
1132	if !account.Verified {
1133		return errAccountUnverified
1134	}
1135	if account.Suspended != nil {
1136		return errAccountSuspended
1137	}
1138
1139	success := false
1140	key := fmt.Sprintf(keyAccountPwReset, account.NameCasefolded)
1141	am.server.store.Update(func(tx *buntdb.Tx) error {
1142		rawStr, err := tx.Get(key)
1143		if err == nil && rawStr != "" {
1144			var record PasswordResetRecord
1145			err := json.Unmarshal([]byte(rawStr), &record)
1146			if err == nil && utils.SecretTokensMatch(record.Code, code) {
1147				success = true
1148				tx.Delete(key)
1149			}
1150		}
1151		return nil
1152	})
1153
1154	if success {
1155		return am.setPassword(accountName, password, true)
1156	} else {
1157		return errAccountInvalidCredentials
1158	}
1159}
1160
1161type PasswordResetRecord struct {
1162	TimeCreated time.Time
1163	Code        string
1164}
1165
1166func marshalReservedNicks(nicks []string) string {
1167	return strings.Join(nicks, ",")
1168}
1169
1170func unmarshalReservedNicks(nicks string) (result []string) {
1171	if nicks == "" {
1172		return
1173	}
1174	return strings.Split(nicks, ",")
1175}
1176
1177func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreserve bool, reserve bool) error {
1178	cfnick, err := CasefoldName(nick)
1179	skeleton, skerr := Skeleton(nick)
1180	// garbage nick, or garbage options, or disabled
1181	nrconfig := am.server.Config().Accounts.NickReservation
1182	if err != nil || skerr != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
1183		return errAccountNickReservationFailed
1184	}
1185
1186	// the cache is in sync with the DB while we hold serialCacheUpdateMutex
1187	am.serialCacheUpdateMutex.Lock()
1188	defer am.serialCacheUpdateMutex.Unlock()
1189
1190	// find the affected account, which is usually the client's:
1191	account := client.Account()
1192	if saUnreserve {
1193		// unless this is a sadrop:
1194		account := func() string {
1195			am.RLock()
1196			defer am.RUnlock()
1197			return am.nickToAccount[cfnick]
1198		}()
1199		if account == "" {
1200			// nothing to do
1201			return nil
1202		}
1203	}
1204	if account == "" {
1205		return errAccountNotLoggedIn
1206	}
1207
1208	am.Lock()
1209	accountForNick := am.nickToAccount[cfnick]
1210	var accountForSkeleton string
1211	if reserve {
1212		accountForSkeleton = am.skeletonToAccount[skeleton]
1213	}
1214	am.Unlock()
1215
1216	if reserve && (accountForNick != "" || accountForSkeleton != "") {
1217		return errNicknameReserved
1218	} else if !reserve && !saUnreserve && accountForNick != account {
1219		return errNicknameReserved
1220	} else if !reserve && cfnick == account {
1221		return errAccountCantDropPrimaryNick
1222	}
1223
1224	nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, account)
1225	unverifiedAccountKey := fmt.Sprintf(keyAccountExists, cfnick)
1226	err = am.server.store.Update(func(tx *buntdb.Tx) error {
1227		if reserve {
1228			// unverified accounts don't show up in NickToAccount yet (which is intentional),
1229			// however you shouldn't be able to reserve a nick out from under them
1230			_, err := tx.Get(unverifiedAccountKey)
1231			if err == nil {
1232				return errNicknameReserved
1233			}
1234		}
1235
1236		rawNicks, err := tx.Get(nicksKey)
1237		if err != nil && err != buntdb.ErrNotFound {
1238			return err
1239		}
1240
1241		nicks := unmarshalReservedNicks(rawNicks)
1242
1243		if reserve {
1244			if len(nicks) >= nrconfig.AdditionalNickLimit {
1245				return errAccountTooManyNicks
1246			}
1247			nicks = append(nicks, nick)
1248		} else {
1249			// compute (original reserved nicks) minus cfnick
1250			var newNicks []string
1251			for _, reservedNick := range nicks {
1252				cfreservednick, _ := CasefoldName(reservedNick)
1253				if cfreservednick != cfnick {
1254					newNicks = append(newNicks, reservedNick)
1255				} else {
1256					// found the original, unfolded version of the nick we're dropping;
1257					// recompute the true skeleton from it
1258					skeleton, _ = Skeleton(reservedNick)
1259				}
1260			}
1261			nicks = newNicks
1262		}
1263
1264		marshaledNicks := marshalReservedNicks(nicks)
1265		_, _, err = tx.Set(nicksKey, string(marshaledNicks), nil)
1266		return err
1267	})
1268
1269	if err == errAccountTooManyNicks || err == errNicknameReserved {
1270		return err
1271	} else if err != nil {
1272		return errAccountNickReservationFailed
1273	}
1274
1275	// success
1276	am.Lock()
1277	defer am.Unlock()
1278	if reserve {
1279		am.nickToAccount[cfnick] = account
1280		am.skeletonToAccount[skeleton] = account
1281	} else {
1282		delete(am.nickToAccount, cfnick)
1283		delete(am.skeletonToAccount, skeleton)
1284	}
1285	return nil
1286}
1287
1288func (am *AccountManager) checkPassphrase(accountName, passphrase string) (account ClientAccount, err error) {
1289	account, err = am.LoadAccount(accountName)
1290	// #1476: if grouped nicks are allowed, attempt to interpret accountName as a grouped nick
1291	if err == errAccountDoesNotExist && !am.server.Config().Accounts.NickReservation.ForceNickEqualsAccount {
1292		cfnick, cfErr := CasefoldName(accountName)
1293		if cfErr != nil {
1294			return
1295		}
1296		accountName = func() string {
1297			am.RLock()
1298			defer am.RUnlock()
1299			return am.nickToAccount[cfnick]
1300		}()
1301		if accountName != "" {
1302			account, err = am.LoadAccount(accountName)
1303		}
1304	}
1305	if err != nil {
1306		return
1307	}
1308
1309	if !account.Verified {
1310		err = errAccountUnverified
1311		return
1312	} else if account.Suspended != nil {
1313		err = errAccountSuspended
1314		return
1315	}
1316
1317	switch account.Credentials.Version {
1318	case 0:
1319		err = am.checkLegacyPassphrase(migrations.CheckOragonoPassphraseV0, accountName, account.Credentials.PassphraseHash, passphrase)
1320	case 1:
1321		if passwd.CompareHashAndPassword(account.Credentials.PassphraseHash, []byte(passphrase)) != nil {
1322			err = errAccountInvalidCredentials
1323		}
1324		if err == nil && account.Credentials.SCRAMCreds.Iters == 0 {
1325			// XXX: if the account was created prior to 2.8, it doesn't have SCRAM credentials;
1326			// since we temporarily have access to a valid plaintext password, create them:
1327			am.rehashPassword(account.Name, passphrase)
1328		}
1329	case -1:
1330		err = am.checkLegacyPassphrase(migrations.CheckAthemePassphrase, accountName, account.Credentials.PassphraseHash, passphrase)
1331	case -2:
1332		err = am.checkLegacyPassphrase(migrations.CheckAnopePassphrase, accountName, account.Credentials.PassphraseHash, passphrase)
1333	default:
1334		err = errAccountInvalidCredentials
1335	}
1336	return
1337}
1338
1339func (am *AccountManager) checkLegacyPassphrase(check migrations.PassphraseCheck, account string, hash []byte, passphrase string) (err error) {
1340	err = check(hash, []byte(passphrase))
1341	if err != nil {
1342		if err == migrations.ErrHashInvalid {
1343			am.server.logger.Error("internal", "invalid legacy credentials for account", account)
1344		}
1345		return errAccountInvalidCredentials
1346	}
1347	// re-hash the passphrase with the latest algorithm
1348	am.rehashPassword(account, passphrase)
1349	return nil
1350}
1351
1352func (am *AccountManager) rehashPassword(accountName, passphrase string) {
1353	err := am.setPassword(accountName, passphrase, true)
1354	if err != nil {
1355		am.server.logger.Error("internal", "could not upgrade user password", accountName, err.Error())
1356	}
1357}
1358
1359func (am *AccountManager) loadWithAutocreation(accountName string, autocreate bool) (account ClientAccount, err error) {
1360	account, err = am.LoadAccount(accountName)
1361	if err == errAccountDoesNotExist && autocreate {
1362		err = am.SARegister(accountName, "")
1363		if err != nil {
1364			return
1365		}
1366		account, err = am.LoadAccount(accountName)
1367	}
1368	return
1369}
1370
1371func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) (err error) {
1372	// XXX check this now, so we don't allow a redundant login for an always-on client
1373	// even for a brief period. the other potential source of nick-account conflicts
1374	// is from force-nick-equals-account, but those will be caught later by
1375	// fixupNickEqualsAccount and if there is a conflict, they will be logged out.
1376	if client.registered {
1377		if clientAlready := am.server.clients.Get(accountName); clientAlready != nil && clientAlready.AlwaysOn() {
1378			return errNickAccountMismatch
1379		}
1380	}
1381
1382	if throttled, remainingTime := client.checkLoginThrottle(); throttled {
1383		return &ThrottleError{remainingTime}
1384	}
1385
1386	var account ClientAccount
1387
1388	defer func() {
1389		if err == nil {
1390			am.Login(client, account)
1391		}
1392	}()
1393
1394	config := am.server.Config()
1395	if config.Accounts.AuthScript.Enabled {
1396		var output AuthScriptOutput
1397		output, err = CheckAuthScript(am.server.semaphores.AuthScript, config.Accounts.AuthScript.ScriptConfig,
1398			AuthScriptInput{AccountName: accountName, Passphrase: passphrase, IP: client.IP().String()})
1399		if err != nil {
1400			am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
1401		} else if output.Success {
1402			if output.AccountName != "" {
1403				accountName = output.AccountName
1404			}
1405			account, err = am.loadWithAutocreation(accountName, config.Accounts.AuthScript.Autocreate)
1406			return
1407		}
1408	}
1409
1410	account, err = am.checkPassphrase(accountName, passphrase)
1411	return err
1412}
1413
1414// AllNicks returns the uncasefolded nicknames for all accounts, including additional (grouped) nicks.
1415func (am *AccountManager) AllNicks() (result []string) {
1416	accountNamePrefix := fmt.Sprintf(keyAccountName, "")
1417	accountAdditionalNicksPrefix := fmt.Sprintf(keyAccountAdditionalNicks, "")
1418
1419	am.server.store.View(func(tx *buntdb.Tx) error {
1420		// Account names
1421		err := tx.AscendGreaterOrEqual("", accountNamePrefix, func(key, value string) bool {
1422			if !strings.HasPrefix(key, accountNamePrefix) {
1423				return false
1424			}
1425			result = append(result, value)
1426			return true
1427		})
1428		if err != nil {
1429			return err
1430		}
1431
1432		// Additional nicks
1433		return tx.AscendGreaterOrEqual("", accountAdditionalNicksPrefix, func(key, value string) bool {
1434			if !strings.HasPrefix(key, accountAdditionalNicksPrefix) {
1435				return false
1436			}
1437			additionalNicks := unmarshalReservedNicks(value)
1438			for _, additionalNick := range additionalNicks {
1439				result = append(result, additionalNick)
1440			}
1441			return true
1442		})
1443	})
1444
1445	sort.Strings(result)
1446	return
1447}
1448
1449func (am *AccountManager) LoadAccount(accountName string) (result ClientAccount, err error) {
1450	casefoldedAccount, err := CasefoldName(accountName)
1451	if err != nil {
1452		err = errAccountDoesNotExist
1453		return
1454	}
1455
1456	var raw rawClientAccount
1457	am.server.store.View(func(tx *buntdb.Tx) error {
1458		raw, err = am.loadRawAccount(tx, casefoldedAccount)
1459		return nil
1460	})
1461	if err != nil {
1462		return
1463	}
1464
1465	result, err = am.deserializeRawAccount(raw, casefoldedAccount)
1466	return
1467}
1468
1469// look up the unfolded version of an account name, possibly after deletion
1470func (am *AccountManager) AccountToAccountName(account string) (result string) {
1471	casefoldedAccount, err := CasefoldName(account)
1472	if err != nil {
1473		return
1474	}
1475
1476	unregisteredKey := fmt.Sprintf(keyAccountUnregistered, casefoldedAccount)
1477	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
1478
1479	am.server.store.View(func(tx *buntdb.Tx) error {
1480		if name, err := tx.Get(accountNameKey); err == nil {
1481			result = name
1482			return nil
1483		}
1484		if name, err := tx.Get(unregisteredKey); err == nil {
1485			result = name
1486		}
1487		return nil
1488	})
1489
1490	return
1491}
1492
1493func (am *AccountManager) deserializeRawAccount(raw rawClientAccount, cfName string) (result ClientAccount, err error) {
1494	result.Name = raw.Name
1495	result.NameCasefolded = cfName
1496	regTimeInt, _ := strconv.ParseInt(raw.RegisteredAt, 10, 64)
1497	result.RegisteredAt = time.Unix(0, regTimeInt).UTC()
1498	e := json.Unmarshal([]byte(raw.Credentials), &result.Credentials)
1499	if e != nil {
1500		am.server.logger.Error("internal", "could not unmarshal credentials", e.Error())
1501		err = errAccountDoesNotExist
1502		return
1503	}
1504	result.AdditionalNicks = unmarshalReservedNicks(raw.AdditionalNicks)
1505	result.Verified = raw.Verified
1506	if raw.VHost != "" {
1507		e := json.Unmarshal([]byte(raw.VHost), &result.VHost)
1508		if e != nil {
1509			am.server.logger.Warning("internal", "could not unmarshal vhost for account", result.Name, e.Error())
1510			// pretend they have no vhost and move on
1511		}
1512	}
1513	if raw.Settings != "" {
1514		e := json.Unmarshal([]byte(raw.Settings), &result.Settings)
1515		if e != nil {
1516			am.server.logger.Warning("internal", "could not unmarshal settings for account", result.Name, e.Error())
1517		}
1518	}
1519	if raw.Suspended != "" {
1520		sus := new(AccountSuspension)
1521		e := json.Unmarshal([]byte(raw.Suspended), sus)
1522		if e != nil {
1523			am.server.logger.Error("internal", "corrupt suspension data", result.Name, e.Error())
1524		} else {
1525			result.Suspended = sus
1526		}
1527	}
1528	return
1529}
1530
1531func (am *AccountManager) loadRawAccount(tx *buntdb.Tx, casefoldedAccount string) (result rawClientAccount, err error) {
1532	accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
1533	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
1534	registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
1535	credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
1536	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
1537	nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
1538	vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
1539	settingsKey := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
1540	suspendedKey := fmt.Sprintf(keyAccountSuspended, casefoldedAccount)
1541
1542	_, e := tx.Get(accountKey)
1543	if e == buntdb.ErrNotFound {
1544		err = errAccountDoesNotExist
1545		return
1546	}
1547
1548	result.Name, _ = tx.Get(accountNameKey)
1549	result.RegisteredAt, _ = tx.Get(registeredTimeKey)
1550	result.Credentials, _ = tx.Get(credentialsKey)
1551	result.AdditionalNicks, _ = tx.Get(nicksKey)
1552	result.VHost, _ = tx.Get(vhostKey)
1553	result.Settings, _ = tx.Get(settingsKey)
1554	result.Suspended, _ = tx.Get(suspendedKey)
1555
1556	if _, e = tx.Get(verifiedKey); e == nil {
1557		result.Verified = true
1558	}
1559
1560	return
1561}
1562
1563type AccountSuspension struct {
1564	AccountName string `json:"AccountName,omitempty"`
1565	TimeCreated time.Time
1566	Duration    time.Duration
1567	OperName    string
1568	Reason      string
1569}
1570
1571func (am *AccountManager) Suspend(accountName string, duration time.Duration, operName, reason string) (err error) {
1572	account, err := CasefoldName(accountName)
1573	if err != nil {
1574		return errAccountDoesNotExist
1575	}
1576
1577	suspension := AccountSuspension{
1578		TimeCreated: time.Now().UTC(),
1579		Duration:    duration,
1580		OperName:    operName,
1581		Reason:      reason,
1582	}
1583	suspensionStr, err := json.Marshal(suspension)
1584	if err != nil {
1585		am.server.logger.Error("internal", "suspension json unserializable", err.Error())
1586		return errAccountDoesNotExist
1587	}
1588
1589	existsKey := fmt.Sprintf(keyAccountExists, account)
1590	suspensionKey := fmt.Sprintf(keyAccountSuspended, account)
1591	var setOptions *buntdb.SetOptions
1592	if duration != time.Duration(0) {
1593		setOptions = &buntdb.SetOptions{Expires: true, TTL: duration}
1594	}
1595	err = am.server.store.Update(func(tx *buntdb.Tx) error {
1596		_, err := tx.Get(existsKey)
1597		if err != nil {
1598			return errAccountDoesNotExist
1599		}
1600		_, _, err = tx.Set(suspensionKey, string(suspensionStr), setOptions)
1601		return err
1602	})
1603
1604	if err == errAccountDoesNotExist {
1605		return err
1606	} else if err != nil {
1607		am.server.logger.Error("internal", "couldn't persist suspension", account, err.Error())
1608	} // keep going
1609
1610	am.Lock()
1611	clients := am.accountToClients[account]
1612	delete(am.accountToClients, account)
1613	am.Unlock()
1614
1615	// kill clients, sending them the reason
1616	suspension.AccountName = accountName
1617	for _, client := range clients {
1618		client.Logout()
1619		client.Quit(suspensionToString(client, suspension), nil)
1620		client.destroy(nil)
1621	}
1622	return nil
1623}
1624
1625func (am *AccountManager) killClients(clients []*Client) {
1626	for _, client := range clients {
1627		client.Logout()
1628		client.Quit(client.t("You are no longer authorized to be on this server"), nil)
1629		client.destroy(nil)
1630	}
1631}
1632
1633func (am *AccountManager) Unsuspend(accountName string) (err error) {
1634	cfaccount, err := CasefoldName(accountName)
1635	if err != nil {
1636		return errAccountDoesNotExist
1637	}
1638
1639	existsKey := fmt.Sprintf(keyAccountExists, cfaccount)
1640	suspensionKey := fmt.Sprintf(keyAccountSuspended, cfaccount)
1641	err = am.server.store.Update(func(tx *buntdb.Tx) error {
1642		_, err := tx.Get(existsKey)
1643		if err != nil {
1644			return errAccountDoesNotExist
1645		}
1646		_, err = tx.Delete(suspensionKey)
1647		if err != nil {
1648			return errNoop
1649		}
1650		return nil
1651	})
1652
1653	return err
1654}
1655
1656func (am *AccountManager) ListSuspended() (result []AccountSuspension) {
1657	var names []string
1658	var raw []string
1659
1660	prefix := fmt.Sprintf(keyAccountSuspended, "")
1661	am.server.store.View(func(tx *buntdb.Tx) error {
1662		err := tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
1663			if !strings.HasPrefix(key, prefix) {
1664				return false
1665			}
1666			raw = append(raw, value)
1667			cfname := strings.TrimPrefix(key, prefix)
1668			name, _ := tx.Get(fmt.Sprintf(keyAccountName, cfname))
1669			names = append(names, name)
1670			return true
1671		})
1672		return err
1673	})
1674
1675	result = make([]AccountSuspension, 0, len(raw))
1676	for i := 0; i < len(raw); i++ {
1677		var sus AccountSuspension
1678		err := json.Unmarshal([]byte(raw[i]), &sus)
1679		if err != nil {
1680			am.server.logger.Error("internal", "corrupt data for suspension", names[i], err.Error())
1681			continue
1682		}
1683		sus.AccountName = names[i]
1684		result = append(result, sus)
1685	}
1686	return
1687}
1688
1689// renames an account (within very restrictive limits); see #1380
1690func (am *AccountManager) Rename(oldName, newName string) (err error) {
1691	accountData, err := am.LoadAccount(oldName)
1692	if err != nil {
1693		return
1694	}
1695	newCfName, err := CasefoldName(newName)
1696	if err != nil {
1697		return errNicknameInvalid
1698	}
1699	if newCfName != accountData.NameCasefolded {
1700		return errInvalidAccountRename
1701	}
1702	key := fmt.Sprintf(keyAccountName, accountData.NameCasefolded)
1703	err = am.server.store.Update(func(tx *buntdb.Tx) error {
1704		tx.Set(key, newName, nil)
1705		return nil
1706	})
1707	if err != nil {
1708		return err
1709	}
1710
1711	am.RLock()
1712	defer am.RUnlock()
1713	for _, client := range am.accountToClients[accountData.NameCasefolded] {
1714		client.setAccountName(newName)
1715	}
1716	return nil
1717}
1718
1719func (am *AccountManager) Unregister(account string, erase bool) error {
1720	config := am.server.Config()
1721	casefoldedAccount, err := CasefoldName(account)
1722	if err != nil {
1723		return errAccountDoesNotExist
1724	}
1725
1726	accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
1727	accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
1728	registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
1729	credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
1730	verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
1731	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
1732	nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
1733	settingsKey := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
1734	vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
1735	channelsKey := fmt.Sprintf(keyAccountChannels, casefoldedAccount)
1736	joinedChannelsKey := fmt.Sprintf(keyAccountChannelToModes, casefoldedAccount)
1737	lastSeenKey := fmt.Sprintf(keyAccountLastSeen, casefoldedAccount)
1738	unregisteredKey := fmt.Sprintf(keyAccountUnregistered, casefoldedAccount)
1739	modesKey := fmt.Sprintf(keyAccountModes, casefoldedAccount)
1740	realnameKey := fmt.Sprintf(keyAccountRealname, casefoldedAccount)
1741	suspendedKey := fmt.Sprintf(keyAccountSuspended, casefoldedAccount)
1742	pwResetKey := fmt.Sprintf(keyAccountPwReset, casefoldedAccount)
1743	emailChangeKey := fmt.Sprintf(keyAccountEmailChange, casefoldedAccount)
1744
1745	var clients []*Client
1746	defer func() {
1747		am.killClients(clients)
1748	}()
1749
1750	var registeredChannels []string
1751	// on our way out, unregister all the account's channels and delete them from the db
1752	defer func() {
1753		for _, channelName := range registeredChannels {
1754			err := am.server.channels.SetUnregistered(channelName, casefoldedAccount)
1755			if err != nil {
1756				am.server.logger.Error("internal", "couldn't unregister channel", channelName, err.Error())
1757			}
1758		}
1759	}()
1760
1761	var credText string
1762	var rawNicks string
1763
1764	am.serialCacheUpdateMutex.Lock()
1765	defer am.serialCacheUpdateMutex.Unlock()
1766
1767	var accountName string
1768	var channelsStr string
1769	keepProtections := false
1770	am.server.store.Update(func(tx *buntdb.Tx) error {
1771		// get the unfolded account name; for an active account, this is
1772		// stored under accountNameKey, for an unregistered account under unregisteredKey
1773		accountName, _ = tx.Get(accountNameKey)
1774		if accountName == "" {
1775			accountName, _ = tx.Get(unregisteredKey)
1776		}
1777		if erase {
1778			tx.Delete(unregisteredKey)
1779		} else {
1780			if _, err := tx.Get(verifiedKey); err == nil {
1781				tx.Set(unregisteredKey, accountName, nil)
1782				keepProtections = true
1783			}
1784		}
1785		tx.Delete(accountKey)
1786		tx.Delete(accountNameKey)
1787		tx.Delete(verifiedKey)
1788		tx.Delete(registeredTimeKey)
1789		tx.Delete(verificationCodeKey)
1790		tx.Delete(settingsKey)
1791		rawNicks, _ = tx.Get(nicksKey)
1792		tx.Delete(nicksKey)
1793		credText, err = tx.Get(credentialsKey)
1794		tx.Delete(credentialsKey)
1795		tx.Delete(vhostKey)
1796		channelsStr, _ = tx.Get(channelsKey)
1797		tx.Delete(channelsKey)
1798		tx.Delete(joinedChannelsKey)
1799		tx.Delete(lastSeenKey)
1800		tx.Delete(modesKey)
1801		tx.Delete(realnameKey)
1802		tx.Delete(suspendedKey)
1803		tx.Delete(pwResetKey)
1804		tx.Delete(emailChangeKey)
1805
1806		return nil
1807	})
1808
1809	if err == nil {
1810		var creds AccountCredentials
1811		if err := json.Unmarshal([]byte(credText), &creds); err == nil {
1812			for _, cert := range creds.Certfps {
1813				certFPKey := fmt.Sprintf(keyCertToAccount, cert)
1814				am.server.store.Update(func(tx *buntdb.Tx) error {
1815					if account, err := tx.Get(certFPKey); err == nil && account == casefoldedAccount {
1816						tx.Delete(certFPKey)
1817					}
1818					return nil
1819				})
1820			}
1821		}
1822	}
1823
1824	skeleton, _ := Skeleton(accountName)
1825	additionalNicks := unmarshalReservedNicks(rawNicks)
1826	registeredChannels = unmarshalRegisteredChannels(channelsStr)
1827
1828	am.Lock()
1829	defer am.Unlock()
1830
1831	clients = am.accountToClients[casefoldedAccount]
1832	delete(am.accountToClients, casefoldedAccount)
1833	// protect the account name itself where applicable, but not any grouped nicks
1834	if !(keepProtections && config.Accounts.NickReservation.Method == NickEnforcementStrict) {
1835		delete(am.nickToAccount, casefoldedAccount)
1836		delete(am.skeletonToAccount, skeleton)
1837	}
1838	for _, nick := range additionalNicks {
1839		delete(am.nickToAccount, nick)
1840		additionalSkel, _ := Skeleton(nick)
1841		delete(am.skeletonToAccount, additionalSkel)
1842	}
1843
1844	if err != nil && !erase {
1845		return errAccountDoesNotExist
1846	}
1847
1848	return nil
1849}
1850
1851func unmarshalRegisteredChannels(channelsStr string) (result []string) {
1852	if channelsStr != "" {
1853		result = strings.Split(channelsStr, ",")
1854	}
1855	return
1856}
1857
1858func (am *AccountManager) ChannelsForAccount(account string) (channels []string) {
1859	cfaccount, err := CasefoldName(account)
1860	if err != nil {
1861		return
1862	}
1863
1864	var channelStr string
1865	key := fmt.Sprintf(keyAccountChannels, cfaccount)
1866	am.server.store.View(func(tx *buntdb.Tx) error {
1867		channelStr, _ = tx.Get(key)
1868		return nil
1869	})
1870	return unmarshalRegisteredChannels(channelStr)
1871}
1872
1873func (am *AccountManager) AuthenticateByCertificate(client *Client, certfp string, peerCerts []*x509.Certificate, authzid string) (err error) {
1874	if certfp == "" {
1875		return errAccountInvalidCredentials
1876	}
1877
1878	var clientAccount ClientAccount
1879
1880	defer func() {
1881		if err != nil {
1882			return
1883		} else if !clientAccount.Verified {
1884			err = errAccountUnverified
1885			return
1886		} else if clientAccount.Suspended != nil {
1887			err = errAccountSuspended
1888			return
1889		}
1890		// TODO(#1109) clean this check up?
1891		if client.registered {
1892			if clientAlready := am.server.clients.Get(clientAccount.Name); clientAlready != nil && clientAlready.AlwaysOn() {
1893				err = errNickAccountMismatch
1894				return
1895			}
1896		}
1897		am.Login(client, clientAccount)
1898		return
1899	}()
1900
1901	config := am.server.Config()
1902	if config.Accounts.AuthScript.Enabled {
1903		var output AuthScriptOutput
1904		output, err = CheckAuthScript(am.server.semaphores.AuthScript, config.Accounts.AuthScript.ScriptConfig,
1905			AuthScriptInput{Certfp: certfp, IP: client.IP().String(), peerCerts: peerCerts})
1906		if err != nil {
1907			am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
1908		} else if output.Success && output.AccountName != "" {
1909			clientAccount, err = am.loadWithAutocreation(output.AccountName, config.Accounts.AuthScript.Autocreate)
1910			return
1911		}
1912	}
1913
1914	var account string
1915	certFPKey := fmt.Sprintf(keyCertToAccount, certfp)
1916
1917	err = am.server.store.View(func(tx *buntdb.Tx) error {
1918		account, _ = tx.Get(certFPKey)
1919		if account == "" {
1920			return errAccountInvalidCredentials
1921		}
1922		return nil
1923	})
1924
1925	if err != nil {
1926		return err
1927	}
1928
1929	if authzid != "" && authzid != account {
1930		return errAuthzidAuthcidMismatch
1931	}
1932
1933	// ok, we found an account corresponding to their certificate
1934	clientAccount, err = am.LoadAccount(account)
1935	return err
1936}
1937
1938type settingsMunger func(input AccountSettings) (output AccountSettings, err error)
1939
1940func (am *AccountManager) ModifyAccountSettings(account string, munger settingsMunger) (newSettings AccountSettings, err error) {
1941	casefoldedAccount, err := CasefoldName(account)
1942	if err != nil {
1943		return newSettings, errAccountDoesNotExist
1944	}
1945	// TODO implement this in general via a compare-and-swap API
1946	accountData, err := am.LoadAccount(casefoldedAccount)
1947	if err != nil {
1948		return
1949	} else if !accountData.Verified {
1950		return newSettings, errAccountUnverified
1951	}
1952	newSettings, err = munger(accountData.Settings)
1953	if err != nil {
1954		return
1955	}
1956	text, err := json.Marshal(newSettings)
1957	if err != nil {
1958		return
1959	}
1960	key := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
1961	serializedValue := string(text)
1962	err = am.server.store.Update(func(tx *buntdb.Tx) (err error) {
1963		_, _, err = tx.Set(key, serializedValue, nil)
1964		return
1965	})
1966	if err != nil {
1967		err = errAccountUpdateFailed
1968		return
1969	}
1970	// success, push new settings into the client objects
1971	am.Lock()
1972	defer am.Unlock()
1973	for _, client := range am.accountToClients[casefoldedAccount] {
1974		client.SetAccountSettings(newSettings)
1975	}
1976	return
1977}
1978
1979// represents someone's status in hostserv
1980type VHostInfo struct {
1981	ApprovedVHost string
1982	Enabled       bool
1983}
1984
1985// callback type implementing the actual business logic of vhost operations
1986type vhostMunger func(input VHostInfo) (output VHostInfo, err error)
1987
1988func (am *AccountManager) VHostSet(account string, vhost string) (result VHostInfo, err error) {
1989	munger := func(input VHostInfo) (output VHostInfo, err error) {
1990		output = input
1991		output.Enabled = true
1992		output.ApprovedVHost = vhost
1993		return
1994	}
1995
1996	return am.performVHostChange(account, munger)
1997}
1998
1999func (am *AccountManager) VHostSetEnabled(client *Client, enabled bool) (result VHostInfo, err error) {
2000	munger := func(input VHostInfo) (output VHostInfo, err error) {
2001		if input.ApprovedVHost == "" {
2002			err = errNoVhost
2003			return
2004		}
2005		output = input
2006		output.Enabled = enabled
2007		return
2008	}
2009
2010	return am.performVHostChange(client.Account(), munger)
2011}
2012
2013func (am *AccountManager) performVHostChange(account string, munger vhostMunger) (result VHostInfo, err error) {
2014	account, err = CasefoldName(account)
2015	if err != nil || account == "" {
2016		err = errAccountDoesNotExist
2017		return
2018	}
2019
2020	if am.server.Defcon() <= 3 {
2021		err = errFeatureDisabled
2022		return
2023	}
2024
2025	clientAccount, err := am.LoadAccount(account)
2026	if err != nil {
2027		err = errAccountDoesNotExist
2028		return
2029	} else if !clientAccount.Verified {
2030		err = errAccountUnverified
2031		return
2032	}
2033
2034	result, err = munger(clientAccount.VHost)
2035	if err != nil {
2036		return
2037	}
2038
2039	vhtext, err := json.Marshal(result)
2040	if err != nil {
2041		err = errAccountUpdateFailed
2042		return
2043	}
2044	vhstr := string(vhtext)
2045
2046	key := fmt.Sprintf(keyAccountVHost, account)
2047	err = am.server.store.Update(func(tx *buntdb.Tx) error {
2048		_, _, err := tx.Set(key, vhstr, nil)
2049		return err
2050	})
2051
2052	if err != nil {
2053		err = errAccountUpdateFailed
2054		return
2055	}
2056
2057	am.applyVhostToClients(account, result)
2058	return result, nil
2059}
2060
2061func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) {
2062	// if hostserv is disabled in config, then don't grant vhosts
2063	// that were previously approved while it was enabled
2064	if !am.server.Config().Accounts.VHosts.Enabled {
2065		return
2066	}
2067
2068	vhost := ""
2069	if info.Enabled {
2070		vhost = info.ApprovedVHost
2071	}
2072	oldNickmask := client.NickMaskString()
2073	updated := client.SetVHost(vhost)
2074	if updated && client.Registered() {
2075		// TODO: doing I/O here is kind of a kludge
2076		client.sendChghost(oldNickmask, client.Hostname())
2077	}
2078}
2079
2080func (am *AccountManager) applyVhostToClients(account string, result VHostInfo) {
2081	am.RLock()
2082	clients := am.accountToClients[account]
2083	am.RUnlock()
2084
2085	for _, client := range clients {
2086		am.applyVHostInfo(client, result)
2087	}
2088}
2089
2090func (am *AccountManager) Login(client *Client, account ClientAccount) {
2091	client.Login(account)
2092
2093	am.applyVHostInfo(client, account.VHost)
2094
2095	casefoldedAccount := client.Account()
2096	am.Lock()
2097	defer am.Unlock()
2098	am.accountToClients[casefoldedAccount] = append(am.accountToClients[casefoldedAccount], client)
2099}
2100
2101func (am *AccountManager) Logout(client *Client) {
2102	am.Lock()
2103	defer am.Unlock()
2104
2105	casefoldedAccount := client.Account()
2106	if casefoldedAccount == "" {
2107		return
2108	}
2109
2110	client.Logout()
2111
2112	clients := am.accountToClients[casefoldedAccount]
2113	if len(clients) <= 1 {
2114		delete(am.accountToClients, casefoldedAccount)
2115		return
2116	}
2117	remainingClients := make([]*Client, len(clients)-1)
2118	remainingPos := 0
2119	for currentPos := 0; currentPos < len(clients); currentPos++ {
2120		if clients[currentPos] != client {
2121			remainingClients[remainingPos] = clients[currentPos]
2122			remainingPos++
2123		}
2124	}
2125	am.accountToClients[casefoldedAccount] = remainingClients
2126}
2127
2128var (
2129	// EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support.
2130	// This can be moved to some other data structure/place if we need to load/unload mechs later.
2131	EnabledSaslMechanisms = map[string]func(*Server, *Client, *Session, []byte, *ResponseBuffer) bool{
2132		"PLAIN":         authPlainHandler,
2133		"EXTERNAL":      authExternalHandler,
2134		"SCRAM-SHA-256": authScramHandler,
2135	}
2136)
2137
2138type CredentialsVersion int
2139
2140const (
2141	CredentialsLegacy     CredentialsVersion = 0
2142	CredentialsSHA3Bcrypt CredentialsVersion = 1
2143	// negative numbers for migration
2144	CredentialsAtheme = -1
2145	CredentialsAnope  = -2
2146)
2147
2148type SCRAMCreds struct {
2149	Salt      []byte
2150	Iters     int
2151	StoredKey []byte
2152	ServerKey []byte
2153}
2154
2155// AccountCredentials stores the various methods for verifying accounts.
2156type AccountCredentials struct {
2157	Version        CredentialsVersion
2158	PassphraseHash []byte
2159	Certfps        []string
2160	SCRAMCreds
2161}
2162
2163func (ac *AccountCredentials) Empty() bool {
2164	return len(ac.PassphraseHash) == 0 && len(ac.Certfps) == 0
2165}
2166
2167// helper to assemble the serialized JSON for an account's credentials
2168func (ac *AccountCredentials) Serialize() (result string, err error) {
2169	ac.Version = 1
2170	credText, err := json.Marshal(*ac)
2171	if err != nil {
2172		return "", err
2173	}
2174	return string(credText), nil
2175}
2176
2177func (ac *AccountCredentials) SetPassphrase(passphrase string, bcryptCost uint) (err error) {
2178	if passphrase == "" {
2179		ac.PassphraseHash = nil
2180		ac.SCRAMCreds = SCRAMCreds{}
2181		return nil
2182	}
2183
2184	if ValidatePassphrase(passphrase) != nil {
2185		return errAccountBadPassphrase
2186	}
2187
2188	ac.PassphraseHash, err = passwd.GenerateFromPassword([]byte(passphrase), int(bcryptCost))
2189	if err != nil {
2190		return errAccountBadPassphrase
2191	}
2192
2193	// we can pass an empty account name because it won't actually be incorporated
2194	// into the credentials; it's just a quirk of the xdg-go/scram API that the way
2195	// to produce server credentials is to call NewClient* and then GetStoredCredentials
2196	scramClient, err := scram.SHA256.NewClientUnprepped("", passphrase, "")
2197	if err != nil {
2198		return errAccountBadPassphrase
2199	}
2200	salt := make([]byte, 16)
2201	rand.Read(salt)
2202	// xdg-go/scram says: "Clients have a default minimum PBKDF2 iteration count of 4096."
2203	minIters := 4096
2204	scramCreds := scramClient.GetStoredCredentials(scram.KeyFactors{Salt: string(salt), Iters: minIters})
2205	ac.SCRAMCreds = SCRAMCreds{
2206		Salt:      salt,
2207		Iters:     minIters,
2208		StoredKey: scramCreds.StoredKey,
2209		ServerKey: scramCreds.ServerKey,
2210	}
2211
2212	return nil
2213}
2214
2215func (am *AccountManager) NewScramConversation() *scram.ServerConversation {
2216	server, _ := scram.SHA256.NewServer(am.lookupSCRAMCreds)
2217	return server.NewConversation()
2218}
2219
2220func (am *AccountManager) lookupSCRAMCreds(accountName string) (creds scram.StoredCredentials, err error) {
2221	// strip client ID if present:
2222	if strudelIndex := strings.IndexByte(accountName, '@'); strudelIndex != -1 {
2223		accountName = accountName[:strudelIndex]
2224	}
2225
2226	acct, err := am.LoadAccount(accountName)
2227	if err != nil {
2228		return
2229	}
2230	if acct.Credentials.SCRAMCreds.Iters == 0 {
2231		err = errNoSCRAMCredentials
2232		return
2233	}
2234	creds.Salt = string(acct.Credentials.SCRAMCreds.Salt)
2235	creds.Iters = acct.Credentials.SCRAMCreds.Iters
2236	creds.StoredKey = acct.Credentials.SCRAMCreds.StoredKey
2237	creds.ServerKey = acct.Credentials.SCRAMCreds.ServerKey
2238	return
2239}
2240
2241func (ac *AccountCredentials) AddCertfp(certfp string) (err error) {
2242	// XXX we require that certfp is already normalized (rather than normalize here
2243	// and pass back the normalized version as an additional return parameter);
2244	// this is just a final sanity check:
2245	if len(certfp) != 64 {
2246		return utils.ErrInvalidCertfp
2247	}
2248
2249	for _, current := range ac.Certfps {
2250		if certfp == current {
2251			return errNoop
2252		}
2253	}
2254
2255	if maxCertfpsPerAccount <= len(ac.Certfps) {
2256		return errLimitExceeded
2257	}
2258
2259	ac.Certfps = append(ac.Certfps, certfp)
2260	return nil
2261}
2262
2263func (ac *AccountCredentials) RemoveCertfp(certfp string) (err error) {
2264	found := false
2265	newList := make([]string, 0, len(ac.Certfps))
2266	for _, current := range ac.Certfps {
2267		if current == certfp {
2268			found = true
2269		} else {
2270			newList = append(newList, current)
2271		}
2272	}
2273	if !found {
2274		// this is important because it prevents you from deleting someone else's
2275		// fingerprint record
2276		return errNoop
2277	}
2278	ac.Certfps = newList
2279	return nil
2280}
2281
2282type MulticlientAllowedSetting int
2283
2284const (
2285	MulticlientAllowedServerDefault MulticlientAllowedSetting = iota
2286	MulticlientDisallowedByUser
2287	MulticlientAllowedByUser
2288)
2289
2290// controls whether/when clients without event-playback support see fake
2291// PRIVMSGs for JOINs
2292type ReplayJoinsSetting uint
2293
2294const (
2295	ReplayJoinsCommandsOnly = iota // replay in HISTORY or CHATHISTORY output
2296	ReplayJoinsAlways              // replay in HISTORY, CHATHISTORY, or autoreplay
2297)
2298
2299func replayJoinsSettingFromString(str string) (result ReplayJoinsSetting, err error) {
2300	switch strings.ToLower(str) {
2301	case "commands-only":
2302		result = ReplayJoinsCommandsOnly
2303	case "always":
2304		result = ReplayJoinsAlways
2305	default:
2306		err = errInvalidParams
2307	}
2308	return
2309}
2310
2311// XXX: AllowBouncer cannot be renamed AllowMulticlient because it is stored in
2312// persistent JSON blobs in the database
2313type AccountSettings struct {
2314	AutoreplayLines  *int
2315	NickEnforcement  NickEnforcementMethod
2316	AllowBouncer     MulticlientAllowedSetting
2317	ReplayJoins      ReplayJoinsSetting
2318	AlwaysOn         PersistentStatus
2319	AutoreplayMissed bool
2320	DMHistory        HistoryStatus
2321	AutoAway         PersistentStatus
2322	Email            string
2323}
2324
2325// ClientAccount represents a user account.
2326type ClientAccount struct {
2327	// Name of the account.
2328	Name            string
2329	NameCasefolded  string
2330	RegisteredAt    time.Time
2331	Credentials     AccountCredentials
2332	Verified        bool
2333	Suspended       *AccountSuspension
2334	AdditionalNicks []string
2335	VHost           VHostInfo
2336	Settings        AccountSettings
2337}
2338
2339// convenience for passing around raw serialized account data
2340type rawClientAccount struct {
2341	Name            string
2342	RegisteredAt    string
2343	Credentials     string
2344	Verified        bool
2345	AdditionalNicks string
2346	VHost           string
2347	Settings        string
2348	Suspended       string
2349}
2350