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 ®istrationCallbackError{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 ®istrationCallbackError{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