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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  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("/MSG NickServ VERIFY %s %s", casefoldedAccount, code) + "\r\n",
  212. }
  213. var message []byte
  214. for i := 0; i < len(messageStrings); i++ {
  215. message = append(message, []byte(messageStrings[i])...)
  216. }
  217. addr := fmt.Sprintf("%s:%d", config.Server, config.Port)
  218. var auth smtp.Auth
  219. if config.Username != "" && config.Password != "" {
  220. auth = smtp.PlainAuth("", config.Username, config.Password, config.Server)
  221. }
  222. // TODO: this will never send the password in plaintext over a nonlocal link,
  223. // but it might send the email in plaintext, regardless of the value of
  224. // config.TLS.InsecureSkipVerify
  225. err = smtp.SendMail(addr, auth, config.Sender, []string{callbackValue}, message)
  226. if err != nil {
  227. am.server.logger.Error("internal", fmt.Sprintf("Failed to dispatch e-mail: %v", err))
  228. }
  229. return
  230. }
  231. func (am *AccountManager) Verify(client *Client, account string, code string) error {
  232. casefoldedAccount, err := CasefoldName(account)
  233. if err != nil || account == "" || account == "*" {
  234. return errAccountVerificationFailed
  235. }
  236. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  237. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  238. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  239. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  240. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  241. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  242. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  243. var raw rawClientAccount
  244. func() {
  245. am.serialCacheUpdateMutex.Lock()
  246. defer am.serialCacheUpdateMutex.Unlock()
  247. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  248. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  249. if err == errAccountDoesNotExist {
  250. return errAccountDoesNotExist
  251. } else if err != nil {
  252. return errAccountVerificationFailed
  253. } else if raw.Verified {
  254. return errAccountAlreadyVerified
  255. }
  256. // actually verify the code
  257. // a stored code of "" means a none callback / no code required
  258. success := false
  259. storedCode, err := tx.Get(verificationCodeKey)
  260. if err == nil {
  261. // this is probably unnecessary
  262. if storedCode == "" || subtle.ConstantTimeCompare([]byte(code), []byte(storedCode)) == 1 {
  263. success = true
  264. }
  265. }
  266. if !success {
  267. return errAccountVerificationInvalidCode
  268. }
  269. // verify the account
  270. tx.Set(verifiedKey, "1", nil)
  271. // don't need the code anymore
  272. tx.Delete(verificationCodeKey)
  273. // re-set all other keys, removing the TTL
  274. tx.Set(accountKey, "1", nil)
  275. tx.Set(accountNameKey, raw.Name, nil)
  276. tx.Set(registeredTimeKey, raw.RegisteredAt, nil)
  277. tx.Set(callbackKey, raw.Callback, nil)
  278. tx.Set(credentialsKey, raw.Credentials, nil)
  279. var creds AccountCredentials
  280. // XXX we shouldn't do (de)serialization inside the txn,
  281. // but this is like 2 usec on my system
  282. json.Unmarshal([]byte(raw.Credentials), &creds)
  283. if creds.Certificate != "" {
  284. certFPKey := fmt.Sprintf(keyCertToAccount, creds.Certificate)
  285. tx.Set(certFPKey, casefoldedAccount, nil)
  286. }
  287. return nil
  288. })
  289. if err == nil {
  290. am.Lock()
  291. am.nickToAccount[casefoldedAccount] = casefoldedAccount
  292. am.Unlock()
  293. }
  294. }()
  295. if err != nil {
  296. return err
  297. }
  298. am.Login(client, raw.Name)
  299. return nil
  300. }
  301. func marshalReservedNicks(nicks []string) string {
  302. return strings.Join(nicks, ",")
  303. }
  304. func unmarshalReservedNicks(nicks string) (result []string) {
  305. if nicks == "" {
  306. return
  307. }
  308. return strings.Split(nicks, ",")
  309. }
  310. func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreserve bool, reserve bool) error {
  311. cfnick, err := CasefoldName(nick)
  312. // garbage nick, or garbage options, or disabled
  313. nrconfig := am.server.AccountConfig().NickReservation
  314. if err != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
  315. return errAccountNickReservationFailed
  316. }
  317. // the cache is in sync with the DB while we hold serialCacheUpdateMutex
  318. am.serialCacheUpdateMutex.Lock()
  319. defer am.serialCacheUpdateMutex.Unlock()
  320. // find the affected account, which is usually the client's:
  321. account := client.Account()
  322. if saUnreserve {
  323. // unless this is a sadrop:
  324. account = am.NickToAccount(cfnick)
  325. if account == "" {
  326. // nothing to do
  327. return nil
  328. }
  329. }
  330. if account == "" {
  331. return errAccountNotLoggedIn
  332. }
  333. accountForNick := am.NickToAccount(cfnick)
  334. if reserve && accountForNick != "" {
  335. return errNicknameReserved
  336. } else if !reserve && !saUnreserve && accountForNick != account {
  337. return errNicknameReserved
  338. } else if !reserve && cfnick == account {
  339. return errAccountCantDropPrimaryNick
  340. }
  341. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, account)
  342. unverifiedAccountKey := fmt.Sprintf(keyAccountExists, cfnick)
  343. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  344. if reserve {
  345. // unverified accounts don't show up in NickToAccount yet (which is intentional),
  346. // however you shouldn't be able to reserve a nick out from under them
  347. _, err := tx.Get(unverifiedAccountKey)
  348. if err == nil {
  349. return errNicknameReserved
  350. }
  351. }
  352. rawNicks, err := tx.Get(nicksKey)
  353. if err != nil && err != buntdb.ErrNotFound {
  354. return err
  355. }
  356. nicks := unmarshalReservedNicks(rawNicks)
  357. if reserve {
  358. if len(nicks) >= nrconfig.AdditionalNickLimit {
  359. return errAccountTooManyNicks
  360. }
  361. nicks = append(nicks, cfnick)
  362. } else {
  363. var newNicks []string
  364. for _, reservedNick := range nicks {
  365. if reservedNick != cfnick {
  366. newNicks = append(newNicks, reservedNick)
  367. }
  368. }
  369. nicks = newNicks
  370. }
  371. marshaledNicks := marshalReservedNicks(nicks)
  372. _, _, err = tx.Set(nicksKey, string(marshaledNicks), nil)
  373. return err
  374. })
  375. if err == errAccountTooManyNicks || err == errNicknameReserved {
  376. return err
  377. } else if err != nil {
  378. return errAccountNickReservationFailed
  379. }
  380. // success
  381. am.Lock()
  382. defer am.Unlock()
  383. if reserve {
  384. am.nickToAccount[cfnick] = account
  385. } else {
  386. delete(am.nickToAccount, cfnick)
  387. }
  388. return nil
  389. }
  390. func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) error {
  391. account, err := am.LoadAccount(accountName)
  392. if err != nil {
  393. return err
  394. }
  395. if !account.Verified {
  396. return errAccountUnverified
  397. }
  398. err = am.server.passwords.CompareHashAndPassword(
  399. account.Credentials.PassphraseHash, account.Credentials.PassphraseSalt, passphrase)
  400. if err != nil {
  401. return errAccountInvalidCredentials
  402. }
  403. am.Login(client, account.Name)
  404. return nil
  405. }
  406. func (am *AccountManager) LoadAccount(accountName string) (result ClientAccount, err error) {
  407. casefoldedAccount, err := CasefoldName(accountName)
  408. if err != nil {
  409. err = errAccountDoesNotExist
  410. return
  411. }
  412. var raw rawClientAccount
  413. am.server.store.View(func(tx *buntdb.Tx) error {
  414. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  415. return nil
  416. })
  417. if err != nil {
  418. return
  419. }
  420. result.Name = raw.Name
  421. regTimeInt, _ := strconv.ParseInt(raw.RegisteredAt, 10, 64)
  422. result.RegisteredAt = time.Unix(regTimeInt, 0)
  423. e := json.Unmarshal([]byte(raw.Credentials), &result.Credentials)
  424. if e != nil {
  425. am.server.logger.Error("internal", fmt.Sprintf("could not unmarshal credentials: %v", e))
  426. err = errAccountDoesNotExist
  427. return
  428. }
  429. result.AdditionalNicks = unmarshalReservedNicks(raw.AdditionalNicks)
  430. result.Verified = raw.Verified
  431. return
  432. }
  433. func (am *AccountManager) loadRawAccount(tx *buntdb.Tx, casefoldedAccount string) (result rawClientAccount, err error) {
  434. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  435. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  436. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  437. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  438. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  439. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  440. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  441. _, e := tx.Get(accountKey)
  442. if e == buntdb.ErrNotFound {
  443. err = errAccountDoesNotExist
  444. return
  445. }
  446. result.Name, _ = tx.Get(accountNameKey)
  447. result.RegisteredAt, _ = tx.Get(registeredTimeKey)
  448. result.Credentials, _ = tx.Get(credentialsKey)
  449. result.Callback, _ = tx.Get(callbackKey)
  450. result.AdditionalNicks, _ = tx.Get(nicksKey)
  451. if _, e = tx.Get(verifiedKey); e == nil {
  452. result.Verified = true
  453. }
  454. return
  455. }
  456. func (am *AccountManager) Unregister(account string) error {
  457. casefoldedAccount, err := CasefoldName(account)
  458. if err != nil {
  459. return errAccountDoesNotExist
  460. }
  461. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  462. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  463. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  464. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  465. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  466. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  467. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  468. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  469. var clients []*Client
  470. var credText string
  471. var rawNicks string
  472. am.serialCacheUpdateMutex.Lock()
  473. defer am.serialCacheUpdateMutex.Unlock()
  474. am.server.store.Update(func(tx *buntdb.Tx) error {
  475. tx.Delete(accountKey)
  476. tx.Delete(accountNameKey)
  477. tx.Delete(verifiedKey)
  478. tx.Delete(registeredTimeKey)
  479. tx.Delete(callbackKey)
  480. tx.Delete(verificationCodeKey)
  481. rawNicks, _ = tx.Get(nicksKey)
  482. tx.Delete(nicksKey)
  483. credText, err = tx.Get(credentialsKey)
  484. tx.Delete(credentialsKey)
  485. return nil
  486. })
  487. if err == nil {
  488. var creds AccountCredentials
  489. if err = json.Unmarshal([]byte(credText), &creds); err == nil && creds.Certificate != "" {
  490. certFPKey := fmt.Sprintf(keyCertToAccount, creds.Certificate)
  491. am.server.store.Update(func(tx *buntdb.Tx) error {
  492. if account, err := tx.Get(certFPKey); err == nil && account == casefoldedAccount {
  493. tx.Delete(certFPKey)
  494. }
  495. return nil
  496. })
  497. }
  498. }
  499. additionalNicks := unmarshalReservedNicks(rawNicks)
  500. am.Lock()
  501. defer am.Unlock()
  502. clients = am.accountToClients[casefoldedAccount]
  503. delete(am.accountToClients, casefoldedAccount)
  504. delete(am.nickToAccount, casefoldedAccount)
  505. for _, nick := range additionalNicks {
  506. delete(am.nickToAccount, nick)
  507. }
  508. for _, client := range clients {
  509. am.logoutOfAccount(client)
  510. }
  511. if err != nil {
  512. return errAccountDoesNotExist
  513. }
  514. return nil
  515. }
  516. func (am *AccountManager) AuthenticateByCertFP(client *Client) error {
  517. if client.certfp == "" {
  518. return errAccountInvalidCredentials
  519. }
  520. var account string
  521. var rawAccount rawClientAccount
  522. certFPKey := fmt.Sprintf(keyCertToAccount, client.certfp)
  523. err := am.server.store.Update(func(tx *buntdb.Tx) error {
  524. var err error
  525. account, _ = tx.Get(certFPKey)
  526. if account == "" {
  527. return errAccountInvalidCredentials
  528. }
  529. rawAccount, err = am.loadRawAccount(tx, account)
  530. if err != nil || !rawAccount.Verified {
  531. return errAccountUnverified
  532. }
  533. return nil
  534. })
  535. if err != nil {
  536. return err
  537. }
  538. // ok, we found an account corresponding to their certificate
  539. am.Login(client, rawAccount.Name)
  540. return nil
  541. }
  542. func (am *AccountManager) Login(client *Client, account string) {
  543. am.Lock()
  544. defer am.Unlock()
  545. am.loginToAccount(client, account)
  546. casefoldedAccount := client.Account()
  547. am.accountToClients[casefoldedAccount] = append(am.accountToClients[casefoldedAccount], client)
  548. }
  549. func (am *AccountManager) Logout(client *Client) {
  550. am.Lock()
  551. defer am.Unlock()
  552. casefoldedAccount := client.Account()
  553. if casefoldedAccount == "" {
  554. return
  555. }
  556. am.logoutOfAccount(client)
  557. clients := am.accountToClients[casefoldedAccount]
  558. if len(clients) <= 1 {
  559. delete(am.accountToClients, casefoldedAccount)
  560. return
  561. }
  562. remainingClients := make([]*Client, len(clients)-1)
  563. remainingPos := 0
  564. for currentPos := 0; currentPos < len(clients); currentPos++ {
  565. if clients[currentPos] != client {
  566. remainingClients[remainingPos] = clients[currentPos]
  567. remainingPos++
  568. }
  569. }
  570. am.accountToClients[casefoldedAccount] = remainingClients
  571. return
  572. }
  573. var (
  574. // EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support.
  575. // This can be moved to some other data structure/place if we need to load/unload mechs later.
  576. EnabledSaslMechanisms = map[string]func(*Server, *Client, string, []byte, *ResponseBuffer) bool{
  577. "PLAIN": authPlainHandler,
  578. "EXTERNAL": authExternalHandler,
  579. }
  580. )
  581. // AccountCredentials stores the various methods for verifying accounts.
  582. type AccountCredentials struct {
  583. PassphraseSalt []byte
  584. PassphraseHash []byte
  585. Certificate string // fingerprint
  586. }
  587. // ClientAccount represents a user account.
  588. type ClientAccount struct {
  589. // Name of the account.
  590. Name string
  591. // RegisteredAt represents the time that the account was registered.
  592. RegisteredAt time.Time
  593. Credentials AccountCredentials
  594. Verified bool
  595. AdditionalNicks []string
  596. }
  597. // convenience for passing around raw serialized account data
  598. type rawClientAccount struct {
  599. Name string
  600. RegisteredAt string
  601. Credentials string
  602. Callback string
  603. Verified bool
  604. AdditionalNicks string
  605. }
  606. // loginToAccount logs the client into the given account.
  607. func (am *AccountManager) loginToAccount(client *Client, account string) {
  608. changed := client.SetAccountName(account)
  609. if changed {
  610. go client.nickTimer.Touch()
  611. }
  612. }
  613. // logoutOfAccount logs the client out of their current account.
  614. func (am *AccountManager) logoutOfAccount(client *Client) {
  615. if client.Account() == "" {
  616. // already logged out
  617. return
  618. }
  619. client.SetAccountName("")
  620. go client.nickTimer.Touch()
  621. // dispatch account-notify
  622. // TODO: doing the I/O here is kind of a kludge, let's move this somewhere else
  623. go func() {
  624. for friend := range client.Friends(caps.AccountNotify) {
  625. friend.Send(nil, client.NickMaskString(), "ACCOUNT", "*")
  626. }
  627. }()
  628. }