You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

accounts.go 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "crypto/rand"
  6. "crypto/subtle"
  7. "encoding/hex"
  8. "encoding/json"
  9. "errors"
  10. "fmt"
  11. "net/smtp"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "time"
  16. "github.com/oragono/oragono/irc/caps"
  17. "github.com/oragono/oragono/irc/passwd"
  18. "github.com/tidwall/buntdb"
  19. )
  20. const (
  21. keyAccountExists = "account.exists %s"
  22. keyAccountVerified = "account.verified %s"
  23. keyAccountCallback = "account.callback %s"
  24. keyAccountVerificationCode = "account.verificationcode %s"
  25. keyAccountName = "account.name %s" // stores the 'preferred name' of the account, not casemapped
  26. keyAccountRegTime = "account.registered.time %s"
  27. keyAccountCredentials = "account.credentials %s"
  28. keyAccountAdditionalNicks = "account.additionalnicks %s"
  29. keyCertToAccount = "account.creds.certfp %s"
  30. )
  31. // everything about accounts is persistent; therefore, the database is the authoritative
  32. // source of truth for all account information. anything on the heap is just a cache
  33. type AccountManager struct {
  34. sync.RWMutex // tier 2
  35. serialCacheUpdateMutex sync.Mutex // tier 3
  36. server *Server
  37. // track clients logged in to accounts
  38. accountToClients map[string][]*Client
  39. nickToAccount map[string]string
  40. }
  41. func NewAccountManager(server *Server) *AccountManager {
  42. am := AccountManager{
  43. accountToClients: make(map[string][]*Client),
  44. nickToAccount: make(map[string]string),
  45. server: server,
  46. }
  47. am.buildNickToAccountIndex()
  48. return &am
  49. }
  50. func (am *AccountManager) buildNickToAccountIndex() {
  51. if !am.server.AccountConfig().NickReservation.Enabled {
  52. return
  53. }
  54. result := make(map[string]string)
  55. existsPrefix := fmt.Sprintf(keyAccountExists, "")
  56. am.serialCacheUpdateMutex.Lock()
  57. defer am.serialCacheUpdateMutex.Unlock()
  58. err := am.server.store.View(func(tx *buntdb.Tx) error {
  59. err := tx.AscendGreaterOrEqual("", existsPrefix, func(key, value string) bool {
  60. if !strings.HasPrefix(key, existsPrefix) {
  61. return false
  62. }
  63. accountName := strings.TrimPrefix(key, existsPrefix)
  64. if _, err := tx.Get(fmt.Sprintf(keyAccountVerified, accountName)); err == nil {
  65. result[accountName] = accountName
  66. }
  67. if rawNicks, err := tx.Get(fmt.Sprintf(keyAccountAdditionalNicks, accountName)); err == nil {
  68. additionalNicks := unmarshalReservedNicks(rawNicks)
  69. for _, nick := range additionalNicks {
  70. result[nick] = accountName
  71. }
  72. }
  73. return true
  74. })
  75. return err
  76. })
  77. if err != nil {
  78. am.server.logger.Error("internal", fmt.Sprintf("couldn't read reserved nicks: %v", err))
  79. } else {
  80. am.Lock()
  81. am.nickToAccount = result
  82. am.Unlock()
  83. }
  84. return
  85. }
  86. func (am *AccountManager) NickToAccount(nick string) string {
  87. cfnick, err := CasefoldName(nick)
  88. if err != nil {
  89. return ""
  90. }
  91. am.RLock()
  92. defer am.RUnlock()
  93. return am.nickToAccount[cfnick]
  94. }
  95. func (am *AccountManager) Register(client *Client, account string, callbackNamespace string, callbackValue string, passphrase string, certfp string) error {
  96. casefoldedAccount, err := CasefoldName(account)
  97. if err != nil || account == "" || account == "*" {
  98. return errAccountCreation
  99. }
  100. // can't register a guest nickname
  101. renamePrefix := strings.ToLower(am.server.AccountConfig().NickReservation.RenamePrefix)
  102. if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) {
  103. return errAccountAlreadyRegistered
  104. }
  105. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  106. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  107. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  108. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  109. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  110. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  111. certFPKey := fmt.Sprintf(keyCertToAccount, certfp)
  112. var creds AccountCredentials
  113. // always set passphrase salt
  114. creds.PassphraseSalt, err = passwd.NewSalt()
  115. if err != nil {
  116. return errAccountCreation
  117. }
  118. // it's fine if this is empty, that just means no certificate is authorized
  119. creds.Certificate = certfp
  120. if passphrase != "" {
  121. creds.PassphraseHash, err = am.server.passwords.GenerateFromPassword(creds.PassphraseSalt, passphrase)
  122. if err != nil {
  123. am.server.logger.Error("internal", fmt.Sprintf("could not hash password: %v", err))
  124. return errAccountCreation
  125. }
  126. }
  127. credText, err := json.Marshal(creds)
  128. if err != nil {
  129. am.server.logger.Error("internal", fmt.Sprintf("could not marshal credentials: %v", err))
  130. return errAccountCreation
  131. }
  132. credStr := string(credText)
  133. registeredTimeStr := strconv.FormatInt(time.Now().Unix(), 10)
  134. callbackSpec := fmt.Sprintf("%s:%s", callbackNamespace, callbackValue)
  135. var setOptions *buntdb.SetOptions
  136. ttl := am.server.AccountConfig().Registration.VerifyTimeout
  137. if ttl != 0 {
  138. setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
  139. }
  140. err = func() error {
  141. am.serialCacheUpdateMutex.Lock()
  142. defer am.serialCacheUpdateMutex.Unlock()
  143. // can't register an account with the same name as a registered nick
  144. if am.NickToAccount(casefoldedAccount) != "" {
  145. return errAccountAlreadyRegistered
  146. }
  147. return am.server.store.Update(func(tx *buntdb.Tx) error {
  148. _, err := am.loadRawAccount(tx, casefoldedAccount)
  149. if err != errAccountDoesNotExist {
  150. return errAccountAlreadyRegistered
  151. }
  152. if certfp != "" {
  153. // make sure certfp doesn't already exist because that'd be silly
  154. _, err := tx.Get(certFPKey)
  155. if err != buntdb.ErrNotFound {
  156. return errCertfpAlreadyExists
  157. }
  158. }
  159. tx.Set(accountKey, "1", setOptions)
  160. tx.Set(accountNameKey, account, setOptions)
  161. tx.Set(registeredTimeKey, registeredTimeStr, setOptions)
  162. tx.Set(credentialsKey, credStr, setOptions)
  163. tx.Set(callbackKey, callbackSpec, setOptions)
  164. if certfp != "" {
  165. tx.Set(certFPKey, casefoldedAccount, setOptions)
  166. }
  167. return nil
  168. })
  169. }()
  170. if err != nil {
  171. return err
  172. }
  173. code, err := am.dispatchCallback(client, casefoldedAccount, callbackNamespace, callbackValue)
  174. if err != nil {
  175. am.Unregister(casefoldedAccount)
  176. return errCallbackFailed
  177. } else {
  178. return am.server.store.Update(func(tx *buntdb.Tx) error {
  179. _, _, err = tx.Set(verificationCodeKey, code, setOptions)
  180. return err
  181. })
  182. }
  183. }
  184. func (am *AccountManager) dispatchCallback(client *Client, casefoldedAccount string, callbackNamespace string, callbackValue string) (string, error) {
  185. if callbackNamespace == "*" || callbackNamespace == "none" {
  186. return "", nil
  187. } else if callbackNamespace == "mailto" {
  188. return am.dispatchMailtoCallback(client, casefoldedAccount, callbackValue)
  189. } else {
  190. return "", errors.New(fmt.Sprintf("Callback not implemented: %s", callbackNamespace))
  191. }
  192. }
  193. func (am *AccountManager) dispatchMailtoCallback(client *Client, casefoldedAccount string, callbackValue string) (code string, err error) {
  194. config := am.server.AccountConfig().Registration.Callbacks.Mailto
  195. buf := make([]byte, 16)
  196. rand.Read(buf)
  197. code = hex.EncodeToString(buf)
  198. subject := config.VerifyMessageSubject
  199. if subject == "" {
  200. subject = fmt.Sprintf(client.t("Verify your account on %s"), am.server.name)
  201. }
  202. messageStrings := []string{
  203. fmt.Sprintf("From: %s\r\n", config.Sender),
  204. fmt.Sprintf("To: %s\r\n", callbackValue),
  205. fmt.Sprintf("Subject: %s\r\n", subject),
  206. "\r\n", // end headers, begin message body
  207. fmt.Sprintf(client.t("Account: %s"), casefoldedAccount) + "\r\n",
  208. fmt.Sprintf(client.t("Verification code: %s"), code) + "\r\n",
  209. "\r\n",
  210. client.t("To verify your account, issue one of these commands:") + "\r\n",
  211. fmt.Sprintf("/ACC VERIFY %s %s", casefoldedAccount, code) + "\r\n",
  212. fmt.Sprintf("/MSG NickServ VERIFY %s %s", casefoldedAccount, code) + "\r\n",
  213. }
  214. var message []byte
  215. for i := 0; i < len(messageStrings); i++ {
  216. message = append(message, []byte(messageStrings[i])...)
  217. }
  218. addr := fmt.Sprintf("%s:%d", config.Server, config.Port)
  219. var auth smtp.Auth
  220. if config.Username != "" && config.Password != "" {
  221. auth = smtp.PlainAuth("", config.Username, config.Password, config.Server)
  222. }
  223. // TODO: this will never send the password in plaintext over a nonlocal link,
  224. // but it might send the email in plaintext, regardless of the value of
  225. // config.TLS.InsecureSkipVerify
  226. err = smtp.SendMail(addr, auth, config.Sender, []string{callbackValue}, message)
  227. if err != nil {
  228. am.server.logger.Error("internal", fmt.Sprintf("Failed to dispatch e-mail: %v", err))
  229. }
  230. return
  231. }
  232. func (am *AccountManager) Verify(client *Client, account string, code string) error {
  233. casefoldedAccount, err := CasefoldName(account)
  234. if err != nil || account == "" || account == "*" {
  235. return errAccountVerificationFailed
  236. }
  237. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  238. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  239. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  240. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  241. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  242. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  243. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  244. var raw rawClientAccount
  245. func() {
  246. am.serialCacheUpdateMutex.Lock()
  247. defer am.serialCacheUpdateMutex.Unlock()
  248. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  249. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  250. if err == errAccountDoesNotExist {
  251. return errAccountDoesNotExist
  252. } else if err != nil {
  253. return errAccountVerificationFailed
  254. } else if raw.Verified {
  255. return errAccountAlreadyVerified
  256. }
  257. // actually verify the code
  258. // a stored code of "" means a none callback / no code required
  259. success := false
  260. storedCode, err := tx.Get(verificationCodeKey)
  261. if err == nil {
  262. // this is probably unnecessary
  263. if storedCode == "" || subtle.ConstantTimeCompare([]byte(code), []byte(storedCode)) == 1 {
  264. success = true
  265. }
  266. }
  267. if !success {
  268. return errAccountVerificationInvalidCode
  269. }
  270. // verify the account
  271. tx.Set(verifiedKey, "1", nil)
  272. // don't need the code anymore
  273. tx.Delete(verificationCodeKey)
  274. // re-set all other keys, removing the TTL
  275. tx.Set(accountKey, "1", nil)
  276. tx.Set(accountNameKey, raw.Name, nil)
  277. tx.Set(registeredTimeKey, raw.RegisteredAt, nil)
  278. tx.Set(callbackKey, raw.Callback, nil)
  279. tx.Set(credentialsKey, raw.Credentials, nil)
  280. var creds AccountCredentials
  281. // XXX we shouldn't do (de)serialization inside the txn,
  282. // but this is like 2 usec on my system
  283. json.Unmarshal([]byte(raw.Credentials), &creds)
  284. if creds.Certificate != "" {
  285. certFPKey := fmt.Sprintf(keyCertToAccount, creds.Certificate)
  286. tx.Set(certFPKey, casefoldedAccount, nil)
  287. }
  288. return nil
  289. })
  290. if err == nil {
  291. am.Lock()
  292. am.nickToAccount[casefoldedAccount] = casefoldedAccount
  293. am.Unlock()
  294. }
  295. }()
  296. if err != nil {
  297. return err
  298. }
  299. am.Login(client, raw.Name)
  300. return nil
  301. }
  302. func marshalReservedNicks(nicks []string) string {
  303. return strings.Join(nicks, ",")
  304. }
  305. func unmarshalReservedNicks(nicks string) (result []string) {
  306. if nicks == "" {
  307. return
  308. }
  309. return strings.Split(nicks, ",")
  310. }
  311. func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreserve bool, reserve bool) error {
  312. cfnick, err := CasefoldName(nick)
  313. // garbage nick, or garbage options, or disabled
  314. nrconfig := am.server.AccountConfig().NickReservation
  315. if err != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
  316. return errAccountNickReservationFailed
  317. }
  318. // the cache is in sync with the DB while we hold serialCacheUpdateMutex
  319. am.serialCacheUpdateMutex.Lock()
  320. defer am.serialCacheUpdateMutex.Unlock()
  321. // find the affected account, which is usually the client's:
  322. account := client.Account()
  323. if saUnreserve {
  324. // unless this is a sadrop:
  325. account = am.NickToAccount(cfnick)
  326. if account == "" {
  327. // nothing to do
  328. return nil
  329. }
  330. }
  331. if account == "" {
  332. return errAccountNotLoggedIn
  333. }
  334. accountForNick := am.NickToAccount(cfnick)
  335. if reserve && accountForNick != "" {
  336. return errNicknameReserved
  337. } else if !reserve && !saUnreserve && accountForNick != account {
  338. return errNicknameReserved
  339. } else if !reserve && cfnick == account {
  340. return errAccountCantDropPrimaryNick
  341. }
  342. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, account)
  343. unverifiedAccountKey := fmt.Sprintf(keyAccountExists, cfnick)
  344. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  345. if reserve {
  346. // unverified accounts don't show up in NickToAccount yet (which is intentional),
  347. // however you shouldn't be able to reserve a nick out from under them
  348. _, err := tx.Get(unverifiedAccountKey)
  349. if err == nil {
  350. return errNicknameReserved
  351. }
  352. }
  353. rawNicks, err := tx.Get(nicksKey)
  354. if err != nil && err != buntdb.ErrNotFound {
  355. return err
  356. }
  357. nicks := unmarshalReservedNicks(rawNicks)
  358. if reserve {
  359. if len(nicks) >= nrconfig.AdditionalNickLimit {
  360. return errAccountTooManyNicks
  361. }
  362. nicks = append(nicks, cfnick)
  363. } else {
  364. var newNicks []string
  365. for _, reservedNick := range nicks {
  366. if reservedNick != cfnick {
  367. newNicks = append(newNicks, reservedNick)
  368. }
  369. }
  370. nicks = newNicks
  371. }
  372. marshaledNicks := marshalReservedNicks(nicks)
  373. _, _, err = tx.Set(nicksKey, string(marshaledNicks), nil)
  374. return err
  375. })
  376. if err == errAccountTooManyNicks || err == errNicknameReserved {
  377. return err
  378. } else if err != nil {
  379. return errAccountNickReservationFailed
  380. }
  381. // success
  382. am.Lock()
  383. defer am.Unlock()
  384. if reserve {
  385. am.nickToAccount[cfnick] = account
  386. } else {
  387. delete(am.nickToAccount, cfnick)
  388. }
  389. return nil
  390. }
  391. func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) error {
  392. account, err := am.LoadAccount(accountName)
  393. if err != nil {
  394. return err
  395. }
  396. if !account.Verified {
  397. return errAccountUnverified
  398. }
  399. err = am.server.passwords.CompareHashAndPassword(
  400. account.Credentials.PassphraseHash, account.Credentials.PassphraseSalt, passphrase)
  401. if err != nil {
  402. return errAccountInvalidCredentials
  403. }
  404. am.Login(client, account.Name)
  405. return nil
  406. }
  407. func (am *AccountManager) LoadAccount(accountName string) (result ClientAccount, err error) {
  408. casefoldedAccount, err := CasefoldName(accountName)
  409. if err != nil {
  410. err = errAccountDoesNotExist
  411. return
  412. }
  413. var raw rawClientAccount
  414. am.server.store.View(func(tx *buntdb.Tx) error {
  415. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  416. return nil
  417. })
  418. if err != nil {
  419. return
  420. }
  421. result.Name = raw.Name
  422. regTimeInt, _ := strconv.ParseInt(raw.RegisteredAt, 10, 64)
  423. result.RegisteredAt = time.Unix(regTimeInt, 0)
  424. e := json.Unmarshal([]byte(raw.Credentials), &result.Credentials)
  425. if e != nil {
  426. am.server.logger.Error("internal", fmt.Sprintf("could not unmarshal credentials: %v", e))
  427. err = errAccountDoesNotExist
  428. return
  429. }
  430. result.AdditionalNicks = unmarshalReservedNicks(raw.AdditionalNicks)
  431. result.Verified = raw.Verified
  432. return
  433. }
  434. func (am *AccountManager) loadRawAccount(tx *buntdb.Tx, casefoldedAccount string) (result rawClientAccount, err error) {
  435. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  436. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  437. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  438. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  439. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  440. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  441. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  442. _, e := tx.Get(accountKey)
  443. if e == buntdb.ErrNotFound {
  444. err = errAccountDoesNotExist
  445. return
  446. }
  447. result.Name, _ = tx.Get(accountNameKey)
  448. result.RegisteredAt, _ = tx.Get(registeredTimeKey)
  449. result.Credentials, _ = tx.Get(credentialsKey)
  450. result.Callback, _ = tx.Get(callbackKey)
  451. result.AdditionalNicks, _ = tx.Get(nicksKey)
  452. if _, e = tx.Get(verifiedKey); e == nil {
  453. result.Verified = true
  454. }
  455. return
  456. }
  457. func (am *AccountManager) Unregister(account string) error {
  458. casefoldedAccount, err := CasefoldName(account)
  459. if err != nil {
  460. return errAccountDoesNotExist
  461. }
  462. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  463. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  464. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  465. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  466. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  467. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  468. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  469. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  470. var clients []*Client
  471. var credText string
  472. var rawNicks string
  473. am.serialCacheUpdateMutex.Lock()
  474. defer am.serialCacheUpdateMutex.Unlock()
  475. am.server.store.Update(func(tx *buntdb.Tx) error {
  476. tx.Delete(accountKey)
  477. tx.Delete(accountNameKey)
  478. tx.Delete(verifiedKey)
  479. tx.Delete(registeredTimeKey)
  480. tx.Delete(callbackKey)
  481. tx.Delete(verificationCodeKey)
  482. rawNicks, _ = tx.Get(nicksKey)
  483. tx.Delete(nicksKey)
  484. credText, err = tx.Get(credentialsKey)
  485. tx.Delete(credentialsKey)
  486. return nil
  487. })
  488. if err == nil {
  489. var creds AccountCredentials
  490. if err = json.Unmarshal([]byte(credText), &creds); err == nil && creds.Certificate != "" {
  491. certFPKey := fmt.Sprintf(keyCertToAccount, creds.Certificate)
  492. am.server.store.Update(func(tx *buntdb.Tx) error {
  493. if account, err := tx.Get(certFPKey); err == nil && account == casefoldedAccount {
  494. tx.Delete(certFPKey)
  495. }
  496. return nil
  497. })
  498. }
  499. }
  500. additionalNicks := unmarshalReservedNicks(rawNicks)
  501. am.Lock()
  502. defer am.Unlock()
  503. clients = am.accountToClients[casefoldedAccount]
  504. delete(am.accountToClients, casefoldedAccount)
  505. delete(am.nickToAccount, casefoldedAccount)
  506. for _, nick := range additionalNicks {
  507. delete(am.nickToAccount, nick)
  508. }
  509. for _, client := range clients {
  510. am.logoutOfAccount(client)
  511. }
  512. if err != nil {
  513. return errAccountDoesNotExist
  514. }
  515. return nil
  516. }
  517. func (am *AccountManager) AuthenticateByCertFP(client *Client) error {
  518. if client.certfp == "" {
  519. return errAccountInvalidCredentials
  520. }
  521. var account string
  522. var rawAccount rawClientAccount
  523. certFPKey := fmt.Sprintf(keyCertToAccount, client.certfp)
  524. err := am.server.store.Update(func(tx *buntdb.Tx) error {
  525. var err error
  526. account, _ = tx.Get(certFPKey)
  527. if account == "" {
  528. return errAccountInvalidCredentials
  529. }
  530. rawAccount, err = am.loadRawAccount(tx, account)
  531. if err != nil || !rawAccount.Verified {
  532. return errAccountUnverified
  533. }
  534. return nil
  535. })
  536. if err != nil {
  537. return err
  538. }
  539. // ok, we found an account corresponding to their certificate
  540. am.Login(client, rawAccount.Name)
  541. return nil
  542. }
  543. func (am *AccountManager) Login(client *Client, account string) {
  544. am.Lock()
  545. defer am.Unlock()
  546. am.loginToAccount(client, account)
  547. casefoldedAccount := client.Account()
  548. am.accountToClients[casefoldedAccount] = append(am.accountToClients[casefoldedAccount], client)
  549. }
  550. func (am *AccountManager) Logout(client *Client) {
  551. am.Lock()
  552. defer am.Unlock()
  553. casefoldedAccount := client.Account()
  554. if casefoldedAccount == "" {
  555. return
  556. }
  557. am.logoutOfAccount(client)
  558. clients := am.accountToClients[casefoldedAccount]
  559. if len(clients) <= 1 {
  560. delete(am.accountToClients, casefoldedAccount)
  561. return
  562. }
  563. remainingClients := make([]*Client, len(clients)-1)
  564. remainingPos := 0
  565. for currentPos := 0; currentPos < len(clients); currentPos++ {
  566. if clients[currentPos] != client {
  567. remainingClients[remainingPos] = clients[currentPos]
  568. remainingPos++
  569. }
  570. }
  571. am.accountToClients[casefoldedAccount] = remainingClients
  572. return
  573. }
  574. var (
  575. // EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support.
  576. // This can be moved to some other data structure/place if we need to load/unload mechs later.
  577. EnabledSaslMechanisms = map[string]func(*Server, *Client, string, []byte, *ResponseBuffer) bool{
  578. "PLAIN": authPlainHandler,
  579. "EXTERNAL": authExternalHandler,
  580. }
  581. )
  582. // AccountCredentials stores the various methods for verifying accounts.
  583. type AccountCredentials struct {
  584. PassphraseSalt []byte
  585. PassphraseHash []byte
  586. Certificate string // fingerprint
  587. }
  588. // ClientAccount represents a user account.
  589. type ClientAccount struct {
  590. // Name of the account.
  591. Name string
  592. // RegisteredAt represents the time that the account was registered.
  593. RegisteredAt time.Time
  594. Credentials AccountCredentials
  595. Verified bool
  596. AdditionalNicks []string
  597. }
  598. // convenience for passing around raw serialized account data
  599. type rawClientAccount struct {
  600. Name string
  601. RegisteredAt string
  602. Credentials string
  603. Callback string
  604. Verified bool
  605. AdditionalNicks string
  606. }
  607. // loginToAccount logs the client into the given account.
  608. func (am *AccountManager) loginToAccount(client *Client, account string) {
  609. changed := client.SetAccountName(account)
  610. if changed {
  611. go client.nickTimer.Touch()
  612. }
  613. }
  614. // logoutOfAccount logs the client out of their current account.
  615. func (am *AccountManager) logoutOfAccount(client *Client) {
  616. if client.Account() == "" {
  617. // already logged out
  618. return
  619. }
  620. client.SetAccountName("")
  621. go client.nickTimer.Touch()
  622. // dispatch account-notify
  623. // TODO: doing the I/O here is kind of a kludge, let's move this somewhere else
  624. go func() {
  625. for friend := range client.Friends(caps.AccountNotify) {
  626. friend.Send(nil, client.NickMaskString(), "ACCOUNT", "*")
  627. }
  628. }()
  629. }