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 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "net/smtp"
  9. "strconv"
  10. "strings"
  11. "sync"
  12. "sync/atomic"
  13. "time"
  14. "unicode"
  15. "github.com/oragono/oragono/irc/caps"
  16. "github.com/oragono/oragono/irc/passwd"
  17. "github.com/oragono/oragono/irc/utils"
  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. keyAccountVHost = "account.vhost %s"
  30. keyCertToAccount = "account.creds.certfp %s"
  31. keyVHostQueueAcctToId = "vhostQueue %s"
  32. vhostRequestIdx = "vhostQueue"
  33. )
  34. // everything about accounts is persistent; therefore, the database is the authoritative
  35. // source of truth for all account information. anything on the heap is just a cache
  36. type AccountManager struct {
  37. // XXX these are up here so they can be aligned to a 64-bit boundary, please forgive me
  38. // autoincrementing ID for vhost requests:
  39. vhostRequestID uint64
  40. vhostRequestPendingCount uint64
  41. sync.RWMutex // tier 2
  42. serialCacheUpdateMutex sync.Mutex // tier 3
  43. vHostUpdateMutex sync.Mutex // tier 3
  44. server *Server
  45. // track clients logged in to accounts
  46. accountToClients map[string][]*Client
  47. nickToAccount map[string]string
  48. }
  49. func NewAccountManager(server *Server) *AccountManager {
  50. am := AccountManager{
  51. accountToClients: make(map[string][]*Client),
  52. nickToAccount: make(map[string]string),
  53. server: server,
  54. }
  55. am.buildNickToAccountIndex()
  56. am.initVHostRequestQueue()
  57. return &am
  58. }
  59. func (am *AccountManager) buildNickToAccountIndex() {
  60. if !am.server.AccountConfig().NickReservation.Enabled {
  61. return
  62. }
  63. result := make(map[string]string)
  64. existsPrefix := fmt.Sprintf(keyAccountExists, "")
  65. am.serialCacheUpdateMutex.Lock()
  66. defer am.serialCacheUpdateMutex.Unlock()
  67. err := am.server.store.View(func(tx *buntdb.Tx) error {
  68. err := tx.AscendGreaterOrEqual("", existsPrefix, func(key, value string) bool {
  69. if !strings.HasPrefix(key, existsPrefix) {
  70. return false
  71. }
  72. accountName := strings.TrimPrefix(key, existsPrefix)
  73. if _, err := tx.Get(fmt.Sprintf(keyAccountVerified, accountName)); err == nil {
  74. result[accountName] = accountName
  75. }
  76. if rawNicks, err := tx.Get(fmt.Sprintf(keyAccountAdditionalNicks, accountName)); err == nil {
  77. additionalNicks := unmarshalReservedNicks(rawNicks)
  78. for _, nick := range additionalNicks {
  79. result[nick] = accountName
  80. }
  81. }
  82. return true
  83. })
  84. return err
  85. })
  86. if err != nil {
  87. am.server.logger.Error("internal", fmt.Sprintf("couldn't read reserved nicks: %v", err))
  88. } else {
  89. am.Lock()
  90. am.nickToAccount = result
  91. am.Unlock()
  92. }
  93. }
  94. func (am *AccountManager) initVHostRequestQueue() {
  95. if !am.server.AccountConfig().VHosts.Enabled {
  96. return
  97. }
  98. am.vHostUpdateMutex.Lock()
  99. defer am.vHostUpdateMutex.Unlock()
  100. // the db maps the account name to the autoincrementing integer ID of its request
  101. // create an numerically ordered index on ID, so we can list the oldest requests
  102. // finally, collect the integer id of the newest request and the total request count
  103. var total uint64
  104. var lastIDStr string
  105. err := am.server.store.Update(func(tx *buntdb.Tx) error {
  106. err := tx.CreateIndex(vhostRequestIdx, fmt.Sprintf(keyVHostQueueAcctToId, "*"), buntdb.IndexInt)
  107. if err != nil {
  108. return err
  109. }
  110. return tx.Descend(vhostRequestIdx, func(key, value string) bool {
  111. if lastIDStr == "" {
  112. lastIDStr = value
  113. }
  114. total++
  115. return true
  116. })
  117. })
  118. if err != nil {
  119. am.server.logger.Error("internal", "could not create vhost queue index", err.Error())
  120. }
  121. lastID, _ := strconv.ParseUint(lastIDStr, 10, 64)
  122. am.server.logger.Debug("services", fmt.Sprintf("vhost queue length is %d, autoincrementing id is %d", total, lastID))
  123. atomic.StoreUint64(&am.vhostRequestID, lastID)
  124. atomic.StoreUint64(&am.vhostRequestPendingCount, total)
  125. }
  126. func (am *AccountManager) NickToAccount(nick string) string {
  127. cfnick, err := CasefoldName(nick)
  128. if err != nil {
  129. return ""
  130. }
  131. am.RLock()
  132. defer am.RUnlock()
  133. return am.nickToAccount[cfnick]
  134. }
  135. func (am *AccountManager) AccountToClients(account string) (result []*Client) {
  136. cfaccount, err := CasefoldName(account)
  137. if err != nil {
  138. return
  139. }
  140. am.RLock()
  141. defer am.RUnlock()
  142. return am.accountToClients[cfaccount]
  143. }
  144. func (am *AccountManager) Register(client *Client, account string, callbackNamespace string, callbackValue string, passphrase string, certfp string) error {
  145. casefoldedAccount, err := CasefoldName(account)
  146. if err != nil || account == "" || account == "*" {
  147. return errAccountCreation
  148. }
  149. // can't register a guest nickname
  150. config := am.server.AccountConfig()
  151. renamePrefix := strings.ToLower(config.NickReservation.RenamePrefix)
  152. if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) {
  153. return errAccountAlreadyRegistered
  154. }
  155. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  156. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  157. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  158. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  159. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  160. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  161. certFPKey := fmt.Sprintf(keyCertToAccount, certfp)
  162. credStr, err := am.serializeCredentials(passphrase, certfp)
  163. if err != nil {
  164. return err
  165. }
  166. registeredTimeStr := strconv.FormatInt(time.Now().Unix(), 10)
  167. callbackSpec := fmt.Sprintf("%s:%s", callbackNamespace, callbackValue)
  168. var setOptions *buntdb.SetOptions
  169. ttl := config.Registration.VerifyTimeout
  170. if ttl != 0 {
  171. setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
  172. }
  173. err = func() error {
  174. am.serialCacheUpdateMutex.Lock()
  175. defer am.serialCacheUpdateMutex.Unlock()
  176. // can't register an account with the same name as a registered nick
  177. if am.NickToAccount(casefoldedAccount) != "" {
  178. return errAccountAlreadyRegistered
  179. }
  180. return am.server.store.Update(func(tx *buntdb.Tx) error {
  181. _, err := am.loadRawAccount(tx, casefoldedAccount)
  182. if err != errAccountDoesNotExist {
  183. return errAccountAlreadyRegistered
  184. }
  185. if certfp != "" {
  186. // make sure certfp doesn't already exist because that'd be silly
  187. _, err := tx.Get(certFPKey)
  188. if err != buntdb.ErrNotFound {
  189. return errCertfpAlreadyExists
  190. }
  191. }
  192. tx.Set(accountKey, "1", setOptions)
  193. tx.Set(accountNameKey, account, setOptions)
  194. tx.Set(registeredTimeKey, registeredTimeStr, setOptions)
  195. tx.Set(credentialsKey, credStr, setOptions)
  196. tx.Set(callbackKey, callbackSpec, setOptions)
  197. if certfp != "" {
  198. tx.Set(certFPKey, casefoldedAccount, setOptions)
  199. }
  200. return nil
  201. })
  202. }()
  203. if err != nil {
  204. return err
  205. }
  206. code, err := am.dispatchCallback(client, casefoldedAccount, callbackNamespace, callbackValue)
  207. if err != nil {
  208. am.Unregister(casefoldedAccount)
  209. return errCallbackFailed
  210. } else {
  211. return am.server.store.Update(func(tx *buntdb.Tx) error {
  212. _, _, err = tx.Set(verificationCodeKey, code, setOptions)
  213. return err
  214. })
  215. }
  216. }
  217. // validatePassphrase checks whether a passphrase is allowed by our rules
  218. func validatePassphrase(passphrase string) error {
  219. // sanity check the length
  220. if len(passphrase) == 0 || len(passphrase) > 600 {
  221. return errAccountBadPassphrase
  222. }
  223. // for now, just enforce that spaces are not allowed
  224. for _, r := range passphrase {
  225. if unicode.IsSpace(r) {
  226. return errAccountBadPassphrase
  227. }
  228. }
  229. return nil
  230. }
  231. // helper to assemble the serialized JSON for an account's credentials
  232. func (am *AccountManager) serializeCredentials(passphrase string, certfp string) (result string, err error) {
  233. var creds AccountCredentials
  234. creds.Version = 1
  235. // we need at least one of passphrase and certfp:
  236. if passphrase == "" && certfp == "" {
  237. return "", errAccountBadPassphrase
  238. }
  239. // but if we have one, it's fine if the other is missing, it just means no
  240. // credential of that type will be accepted.
  241. creds.Certificate = certfp
  242. if passphrase != "" {
  243. if validatePassphrase(passphrase) != nil {
  244. return "", errAccountBadPassphrase
  245. }
  246. bcryptCost := int(am.server.Config().Accounts.Registration.BcryptCost)
  247. creds.PassphraseHash, err = passwd.GenerateFromPassword([]byte(passphrase), bcryptCost)
  248. if err != nil {
  249. am.server.logger.Error("internal", fmt.Sprintf("could not hash password: %v", err))
  250. return "", errAccountCreation
  251. }
  252. }
  253. credText, err := json.Marshal(creds)
  254. if err != nil {
  255. am.server.logger.Error("internal", fmt.Sprintf("could not marshal credentials: %v", err))
  256. return "", errAccountCreation
  257. }
  258. return string(credText), nil
  259. }
  260. // changes the password for an account
  261. func (am *AccountManager) setPassword(account string, password string) (err error) {
  262. casefoldedAccount, err := CasefoldName(account)
  263. if err != nil {
  264. return err
  265. }
  266. act, err := am.LoadAccount(casefoldedAccount)
  267. if err != nil {
  268. return err
  269. }
  270. credStr, err := am.serializeCredentials(password, act.Credentials.Certificate)
  271. if err != nil {
  272. return err
  273. }
  274. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  275. return am.server.store.Update(func(tx *buntdb.Tx) error {
  276. _, _, err := tx.Set(credentialsKey, credStr, nil)
  277. return err
  278. })
  279. }
  280. func (am *AccountManager) dispatchCallback(client *Client, casefoldedAccount string, callbackNamespace string, callbackValue string) (string, error) {
  281. if callbackNamespace == "*" || callbackNamespace == "none" {
  282. return "", nil
  283. } else if callbackNamespace == "mailto" {
  284. return am.dispatchMailtoCallback(client, casefoldedAccount, callbackValue)
  285. } else {
  286. return "", errors.New(fmt.Sprintf("Callback not implemented: %s", callbackNamespace))
  287. }
  288. }
  289. func (am *AccountManager) dispatchMailtoCallback(client *Client, casefoldedAccount string, callbackValue string) (code string, err error) {
  290. config := am.server.AccountConfig().Registration.Callbacks.Mailto
  291. code = utils.GenerateSecretToken()
  292. subject := config.VerifyMessageSubject
  293. if subject == "" {
  294. subject = fmt.Sprintf(client.t("Verify your account on %s"), am.server.name)
  295. }
  296. messageStrings := []string{
  297. fmt.Sprintf("From: %s\r\n", config.Sender),
  298. fmt.Sprintf("To: %s\r\n", callbackValue),
  299. fmt.Sprintf("Subject: %s\r\n", subject),
  300. "\r\n", // end headers, begin message body
  301. fmt.Sprintf(client.t("Account: %s"), casefoldedAccount) + "\r\n",
  302. fmt.Sprintf(client.t("Verification code: %s"), code) + "\r\n",
  303. "\r\n",
  304. client.t("To verify your account, issue one of these commands:") + "\r\n",
  305. fmt.Sprintf("/MSG NickServ VERIFY %s %s", casefoldedAccount, code) + "\r\n",
  306. }
  307. var message []byte
  308. for i := 0; i < len(messageStrings); i++ {
  309. message = append(message, []byte(messageStrings[i])...)
  310. }
  311. addr := fmt.Sprintf("%s:%d", config.Server, config.Port)
  312. var auth smtp.Auth
  313. if config.Username != "" && config.Password != "" {
  314. auth = smtp.PlainAuth("", config.Username, config.Password, config.Server)
  315. }
  316. // TODO: this will never send the password in plaintext over a nonlocal link,
  317. // but it might send the email in plaintext, regardless of the value of
  318. // config.TLS.InsecureSkipVerify
  319. err = smtp.SendMail(addr, auth, config.Sender, []string{callbackValue}, message)
  320. if err != nil {
  321. am.server.logger.Error("internal", fmt.Sprintf("Failed to dispatch e-mail: %v", err))
  322. }
  323. return
  324. }
  325. func (am *AccountManager) Verify(client *Client, account string, code string) error {
  326. casefoldedAccount, err := CasefoldName(account)
  327. if err != nil || account == "" || account == "*" {
  328. return errAccountVerificationFailed
  329. }
  330. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  331. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  332. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  333. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  334. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  335. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  336. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  337. var raw rawClientAccount
  338. func() {
  339. am.serialCacheUpdateMutex.Lock()
  340. defer am.serialCacheUpdateMutex.Unlock()
  341. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  342. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  343. if err == errAccountDoesNotExist {
  344. return errAccountDoesNotExist
  345. } else if err != nil {
  346. return errAccountVerificationFailed
  347. } else if raw.Verified {
  348. return errAccountAlreadyVerified
  349. }
  350. // actually verify the code
  351. // a stored code of "" means a none callback / no code required
  352. success := false
  353. storedCode, err := tx.Get(verificationCodeKey)
  354. if err == nil {
  355. // this is probably unnecessary
  356. if storedCode == "" || utils.SecretTokensMatch(storedCode, code) {
  357. success = true
  358. }
  359. }
  360. if !success {
  361. return errAccountVerificationInvalidCode
  362. }
  363. // verify the account
  364. tx.Set(verifiedKey, "1", nil)
  365. // don't need the code anymore
  366. tx.Delete(verificationCodeKey)
  367. // re-set all other keys, removing the TTL
  368. tx.Set(accountKey, "1", nil)
  369. tx.Set(accountNameKey, raw.Name, nil)
  370. tx.Set(registeredTimeKey, raw.RegisteredAt, nil)
  371. tx.Set(callbackKey, raw.Callback, nil)
  372. tx.Set(credentialsKey, raw.Credentials, nil)
  373. var creds AccountCredentials
  374. // XXX we shouldn't do (de)serialization inside the txn,
  375. // but this is like 2 usec on my system
  376. json.Unmarshal([]byte(raw.Credentials), &creds)
  377. if creds.Certificate != "" {
  378. certFPKey := fmt.Sprintf(keyCertToAccount, creds.Certificate)
  379. tx.Set(certFPKey, casefoldedAccount, nil)
  380. }
  381. return nil
  382. })
  383. if err == nil {
  384. am.Lock()
  385. am.nickToAccount[casefoldedAccount] = casefoldedAccount
  386. am.Unlock()
  387. }
  388. }()
  389. if err != nil {
  390. return err
  391. }
  392. raw.Verified = true
  393. clientAccount, err := am.deserializeRawAccount(raw)
  394. if err != nil {
  395. return err
  396. }
  397. am.Login(client, clientAccount)
  398. return nil
  399. }
  400. func marshalReservedNicks(nicks []string) string {
  401. return strings.Join(nicks, ",")
  402. }
  403. func unmarshalReservedNicks(nicks string) (result []string) {
  404. if nicks == "" {
  405. return
  406. }
  407. return strings.Split(nicks, ",")
  408. }
  409. func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreserve bool, reserve bool) error {
  410. cfnick, err := CasefoldName(nick)
  411. // garbage nick, or garbage options, or disabled
  412. nrconfig := am.server.AccountConfig().NickReservation
  413. if err != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
  414. return errAccountNickReservationFailed
  415. }
  416. // the cache is in sync with the DB while we hold serialCacheUpdateMutex
  417. am.serialCacheUpdateMutex.Lock()
  418. defer am.serialCacheUpdateMutex.Unlock()
  419. // find the affected account, which is usually the client's:
  420. account := client.Account()
  421. if saUnreserve {
  422. // unless this is a sadrop:
  423. account = am.NickToAccount(cfnick)
  424. if account == "" {
  425. // nothing to do
  426. return nil
  427. }
  428. }
  429. if account == "" {
  430. return errAccountNotLoggedIn
  431. }
  432. accountForNick := am.NickToAccount(cfnick)
  433. if reserve && accountForNick != "" {
  434. return errNicknameReserved
  435. } else if !reserve && !saUnreserve && accountForNick != account {
  436. return errNicknameReserved
  437. } else if !reserve && cfnick == account {
  438. return errAccountCantDropPrimaryNick
  439. }
  440. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, account)
  441. unverifiedAccountKey := fmt.Sprintf(keyAccountExists, cfnick)
  442. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  443. if reserve {
  444. // unverified accounts don't show up in NickToAccount yet (which is intentional),
  445. // however you shouldn't be able to reserve a nick out from under them
  446. _, err := tx.Get(unverifiedAccountKey)
  447. if err == nil {
  448. return errNicknameReserved
  449. }
  450. }
  451. rawNicks, err := tx.Get(nicksKey)
  452. if err != nil && err != buntdb.ErrNotFound {
  453. return err
  454. }
  455. nicks := unmarshalReservedNicks(rawNicks)
  456. if reserve {
  457. if len(nicks) >= nrconfig.AdditionalNickLimit {
  458. return errAccountTooManyNicks
  459. }
  460. nicks = append(nicks, cfnick)
  461. } else {
  462. var newNicks []string
  463. for _, reservedNick := range nicks {
  464. if reservedNick != cfnick {
  465. newNicks = append(newNicks, reservedNick)
  466. }
  467. }
  468. nicks = newNicks
  469. }
  470. marshaledNicks := marshalReservedNicks(nicks)
  471. _, _, err = tx.Set(nicksKey, string(marshaledNicks), nil)
  472. return err
  473. })
  474. if err == errAccountTooManyNicks || err == errNicknameReserved {
  475. return err
  476. } else if err != nil {
  477. return errAccountNickReservationFailed
  478. }
  479. // success
  480. am.Lock()
  481. defer am.Unlock()
  482. if reserve {
  483. am.nickToAccount[cfnick] = account
  484. } else {
  485. delete(am.nickToAccount, cfnick)
  486. }
  487. return nil
  488. }
  489. func (am *AccountManager) checkPassphrase(accountName, passphrase string) (account ClientAccount, err error) {
  490. account, err = am.LoadAccount(accountName)
  491. if err != nil {
  492. return
  493. }
  494. if !account.Verified {
  495. err = errAccountUnverified
  496. return
  497. }
  498. switch account.Credentials.Version {
  499. case 0:
  500. err = handleLegacyPasswordV0(am.server, accountName, account.Credentials, passphrase)
  501. case 1:
  502. err = passwd.CompareHashAndPassword(account.Credentials.PassphraseHash, []byte(passphrase))
  503. default:
  504. err = errAccountInvalidCredentials
  505. }
  506. return
  507. }
  508. func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) error {
  509. account, err := am.checkPassphrase(accountName, passphrase)
  510. if err != nil {
  511. return err
  512. }
  513. am.Login(client, account)
  514. return nil
  515. }
  516. func (am *AccountManager) LoadAccount(accountName string) (result ClientAccount, err error) {
  517. casefoldedAccount, err := CasefoldName(accountName)
  518. if err != nil {
  519. err = errAccountDoesNotExist
  520. return
  521. }
  522. var raw rawClientAccount
  523. am.server.store.View(func(tx *buntdb.Tx) error {
  524. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  525. return nil
  526. })
  527. if err != nil {
  528. return
  529. }
  530. result, err = am.deserializeRawAccount(raw)
  531. return
  532. }
  533. func (am *AccountManager) deserializeRawAccount(raw rawClientAccount) (result ClientAccount, err error) {
  534. result.Name = raw.Name
  535. regTimeInt, _ := strconv.ParseInt(raw.RegisteredAt, 10, 64)
  536. result.RegisteredAt = time.Unix(regTimeInt, 0)
  537. e := json.Unmarshal([]byte(raw.Credentials), &result.Credentials)
  538. if e != nil {
  539. am.server.logger.Error("internal", fmt.Sprintf("could not unmarshal credentials: %v", e))
  540. err = errAccountDoesNotExist
  541. return
  542. }
  543. result.AdditionalNicks = unmarshalReservedNicks(raw.AdditionalNicks)
  544. result.Verified = raw.Verified
  545. if raw.VHost != "" {
  546. e := json.Unmarshal([]byte(raw.VHost), &result.VHost)
  547. if e != nil {
  548. am.server.logger.Warning("internal", fmt.Sprintf("could not unmarshal vhost for account %s: %v", result.Name, e))
  549. // pretend they have no vhost and move on
  550. }
  551. }
  552. return
  553. }
  554. func (am *AccountManager) loadRawAccount(tx *buntdb.Tx, casefoldedAccount string) (result rawClientAccount, err error) {
  555. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  556. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  557. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  558. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  559. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  560. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  561. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  562. vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
  563. _, e := tx.Get(accountKey)
  564. if e == buntdb.ErrNotFound {
  565. err = errAccountDoesNotExist
  566. return
  567. }
  568. result.Name, _ = tx.Get(accountNameKey)
  569. result.RegisteredAt, _ = tx.Get(registeredTimeKey)
  570. result.Credentials, _ = tx.Get(credentialsKey)
  571. result.Callback, _ = tx.Get(callbackKey)
  572. result.AdditionalNicks, _ = tx.Get(nicksKey)
  573. result.VHost, _ = tx.Get(vhostKey)
  574. if _, e = tx.Get(verifiedKey); e == nil {
  575. result.Verified = true
  576. }
  577. return
  578. }
  579. func (am *AccountManager) Unregister(account string) error {
  580. casefoldedAccount, err := CasefoldName(account)
  581. if err != nil {
  582. return errAccountDoesNotExist
  583. }
  584. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  585. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  586. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  587. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  588. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  589. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  590. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  591. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  592. vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
  593. vhostQueueKey := fmt.Sprintf(keyVHostQueueAcctToId, casefoldedAccount)
  594. var clients []*Client
  595. var credText string
  596. var rawNicks string
  597. am.serialCacheUpdateMutex.Lock()
  598. defer am.serialCacheUpdateMutex.Unlock()
  599. am.server.store.Update(func(tx *buntdb.Tx) error {
  600. tx.Delete(accountKey)
  601. tx.Delete(accountNameKey)
  602. tx.Delete(verifiedKey)
  603. tx.Delete(registeredTimeKey)
  604. tx.Delete(callbackKey)
  605. tx.Delete(verificationCodeKey)
  606. rawNicks, _ = tx.Get(nicksKey)
  607. tx.Delete(nicksKey)
  608. credText, err = tx.Get(credentialsKey)
  609. tx.Delete(credentialsKey)
  610. tx.Delete(vhostKey)
  611. _, err := tx.Delete(vhostQueueKey)
  612. am.decrementVHostQueueCount(casefoldedAccount, err)
  613. return nil
  614. })
  615. if err == nil {
  616. var creds AccountCredentials
  617. if err = json.Unmarshal([]byte(credText), &creds); err == nil && creds.Certificate != "" {
  618. certFPKey := fmt.Sprintf(keyCertToAccount, creds.Certificate)
  619. am.server.store.Update(func(tx *buntdb.Tx) error {
  620. if account, err := tx.Get(certFPKey); err == nil && account == casefoldedAccount {
  621. tx.Delete(certFPKey)
  622. }
  623. return nil
  624. })
  625. }
  626. }
  627. additionalNicks := unmarshalReservedNicks(rawNicks)
  628. am.Lock()
  629. defer am.Unlock()
  630. clients = am.accountToClients[casefoldedAccount]
  631. delete(am.accountToClients, casefoldedAccount)
  632. delete(am.nickToAccount, casefoldedAccount)
  633. for _, nick := range additionalNicks {
  634. delete(am.nickToAccount, nick)
  635. }
  636. for _, client := range clients {
  637. am.logoutOfAccount(client)
  638. }
  639. if err != nil {
  640. return errAccountDoesNotExist
  641. }
  642. return nil
  643. }
  644. func (am *AccountManager) AuthenticateByCertFP(client *Client) error {
  645. if client.certfp == "" {
  646. return errAccountInvalidCredentials
  647. }
  648. var account string
  649. var rawAccount rawClientAccount
  650. certFPKey := fmt.Sprintf(keyCertToAccount, client.certfp)
  651. err := am.server.store.Update(func(tx *buntdb.Tx) error {
  652. var err error
  653. account, _ = tx.Get(certFPKey)
  654. if account == "" {
  655. return errAccountInvalidCredentials
  656. }
  657. rawAccount, err = am.loadRawAccount(tx, account)
  658. if err != nil || !rawAccount.Verified {
  659. return errAccountUnverified
  660. }
  661. return nil
  662. })
  663. if err != nil {
  664. return err
  665. }
  666. // ok, we found an account corresponding to their certificate
  667. clientAccount, err := am.deserializeRawAccount(rawAccount)
  668. if err != nil {
  669. return err
  670. }
  671. am.Login(client, clientAccount)
  672. return nil
  673. }
  674. // represents someone's status in hostserv
  675. type VHostInfo struct {
  676. ApprovedVHost string
  677. Enabled bool
  678. RequestedVHost string
  679. RejectedVHost string
  680. RejectionReason string
  681. LastRequestTime time.Time
  682. }
  683. // pair type, <VHostInfo, accountName>
  684. type PendingVHostRequest struct {
  685. VHostInfo
  686. Account string
  687. }
  688. // callback type implementing the actual business logic of vhost operations
  689. type vhostMunger func(input VHostInfo) (output VHostInfo, err error)
  690. func (am *AccountManager) VHostSet(account string, vhost string) (result VHostInfo, err error) {
  691. munger := func(input VHostInfo) (output VHostInfo, err error) {
  692. output = input
  693. output.Enabled = true
  694. output.ApprovedVHost = vhost
  695. return
  696. }
  697. return am.performVHostChange(account, munger)
  698. }
  699. func (am *AccountManager) VHostRequest(account string, vhost string) (result VHostInfo, err error) {
  700. munger := func(input VHostInfo) (output VHostInfo, err error) {
  701. output = input
  702. output.RequestedVHost = vhost
  703. output.RejectedVHost = ""
  704. output.RejectionReason = ""
  705. output.LastRequestTime = time.Now().UTC()
  706. return
  707. }
  708. return am.performVHostChange(account, munger)
  709. }
  710. func (am *AccountManager) VHostApprove(account string) (result VHostInfo, err error) {
  711. munger := func(input VHostInfo) (output VHostInfo, err error) {
  712. output = input
  713. output.Enabled = true
  714. output.ApprovedVHost = input.RequestedVHost
  715. output.RequestedVHost = ""
  716. output.RejectionReason = ""
  717. return
  718. }
  719. return am.performVHostChange(account, munger)
  720. }
  721. func (am *AccountManager) VHostReject(account string, reason string) (result VHostInfo, err error) {
  722. munger := func(input VHostInfo) (output VHostInfo, err error) {
  723. output = input
  724. output.RejectedVHost = output.RequestedVHost
  725. output.RequestedVHost = ""
  726. output.RejectionReason = reason
  727. return
  728. }
  729. return am.performVHostChange(account, munger)
  730. }
  731. func (am *AccountManager) VHostSetEnabled(client *Client, enabled bool) (result VHostInfo, err error) {
  732. munger := func(input VHostInfo) (output VHostInfo, err error) {
  733. output = input
  734. output.Enabled = enabled
  735. return
  736. }
  737. return am.performVHostChange(client.Account(), munger)
  738. }
  739. func (am *AccountManager) performVHostChange(account string, munger vhostMunger) (result VHostInfo, err error) {
  740. account, err = CasefoldName(account)
  741. if err != nil || account == "" {
  742. err = errAccountDoesNotExist
  743. return
  744. }
  745. am.vHostUpdateMutex.Lock()
  746. defer am.vHostUpdateMutex.Unlock()
  747. clientAccount, err := am.LoadAccount(account)
  748. if err != nil {
  749. err = errAccountDoesNotExist
  750. return
  751. } else if !clientAccount.Verified {
  752. err = errAccountUnverified
  753. return
  754. }
  755. result, err = munger(clientAccount.VHost)
  756. if err != nil {
  757. return
  758. }
  759. vhtext, err := json.Marshal(result)
  760. if err != nil {
  761. err = errAccountUpdateFailed
  762. return
  763. }
  764. vhstr := string(vhtext)
  765. key := fmt.Sprintf(keyAccountVHost, account)
  766. queueKey := fmt.Sprintf(keyVHostQueueAcctToId, account)
  767. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  768. if _, _, err := tx.Set(key, vhstr, nil); err != nil {
  769. return err
  770. }
  771. // update request queue
  772. if clientAccount.VHost.RequestedVHost == "" && result.RequestedVHost != "" {
  773. id := atomic.AddUint64(&am.vhostRequestID, 1)
  774. if _, _, err = tx.Set(queueKey, strconv.FormatUint(id, 10), nil); err != nil {
  775. return err
  776. }
  777. atomic.AddUint64(&am.vhostRequestPendingCount, 1)
  778. } else if clientAccount.VHost.RequestedVHost != "" && result.RequestedVHost == "" {
  779. _, err = tx.Delete(queueKey)
  780. am.decrementVHostQueueCount(account, err)
  781. }
  782. return nil
  783. })
  784. if err != nil {
  785. err = errAccountUpdateFailed
  786. return
  787. }
  788. am.applyVhostToClients(account, result)
  789. return result, nil
  790. }
  791. // XXX annoying helper method for keeping the queue count in sync with the DB
  792. // `err` is the buntdb error returned from deleting the queue key
  793. func (am *AccountManager) decrementVHostQueueCount(account string, err error) {
  794. if err == nil {
  795. // successfully deleted a queue entry, do a 2's complement decrement:
  796. atomic.AddUint64(&am.vhostRequestPendingCount, ^uint64(0))
  797. } else if err != buntdb.ErrNotFound {
  798. am.server.logger.Error("internal", "buntdb dequeue error", account, err.Error())
  799. }
  800. }
  801. func (am *AccountManager) VHostListRequests(limit int) (requests []PendingVHostRequest, total int) {
  802. am.vHostUpdateMutex.Lock()
  803. defer am.vHostUpdateMutex.Unlock()
  804. total = int(atomic.LoadUint64(&am.vhostRequestPendingCount))
  805. prefix := fmt.Sprintf(keyVHostQueueAcctToId, "")
  806. accounts := make([]string, 0, limit)
  807. err := am.server.store.View(func(tx *buntdb.Tx) error {
  808. return tx.Ascend(vhostRequestIdx, func(key, value string) bool {
  809. accounts = append(accounts, strings.TrimPrefix(key, prefix))
  810. return len(accounts) < limit
  811. })
  812. })
  813. if err != nil {
  814. am.server.logger.Error("internal", "couldn't traverse vhost queue", err.Error())
  815. return
  816. }
  817. for _, account := range accounts {
  818. accountInfo, err := am.LoadAccount(account)
  819. if err == nil {
  820. requests = append(requests, PendingVHostRequest{
  821. Account: account,
  822. VHostInfo: accountInfo.VHost,
  823. })
  824. } else {
  825. am.server.logger.Error("internal", "corrupt account", account, err.Error())
  826. }
  827. }
  828. return
  829. }
  830. func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) {
  831. // if hostserv is disabled in config, then don't grant vhosts
  832. // that were previously approved while it was enabled
  833. if !am.server.AccountConfig().VHosts.Enabled {
  834. return
  835. }
  836. vhost := ""
  837. if info.Enabled {
  838. vhost = info.ApprovedVHost
  839. }
  840. oldNickmask := client.NickMaskString()
  841. updated := client.SetVHost(vhost)
  842. if updated {
  843. // TODO: doing I/O here is kind of a kludge
  844. go client.sendChghost(oldNickmask, vhost)
  845. }
  846. }
  847. func (am *AccountManager) applyVhostToClients(account string, result VHostInfo) {
  848. am.RLock()
  849. clients := am.accountToClients[account]
  850. am.RUnlock()
  851. for _, client := range clients {
  852. am.applyVHostInfo(client, result)
  853. }
  854. }
  855. func (am *AccountManager) Login(client *Client, account ClientAccount) {
  856. changed := client.SetAccountName(account.Name)
  857. if changed {
  858. go client.nickTimer.Touch()
  859. }
  860. am.applyVHostInfo(client, account.VHost)
  861. casefoldedAccount := client.Account()
  862. am.Lock()
  863. defer am.Unlock()
  864. am.accountToClients[casefoldedAccount] = append(am.accountToClients[casefoldedAccount], client)
  865. }
  866. func (am *AccountManager) Logout(client *Client) {
  867. am.Lock()
  868. defer am.Unlock()
  869. casefoldedAccount := client.Account()
  870. if casefoldedAccount == "" {
  871. return
  872. }
  873. am.logoutOfAccount(client)
  874. clients := am.accountToClients[casefoldedAccount]
  875. if len(clients) <= 1 {
  876. delete(am.accountToClients, casefoldedAccount)
  877. return
  878. }
  879. remainingClients := make([]*Client, len(clients)-1)
  880. remainingPos := 0
  881. for currentPos := 0; currentPos < len(clients); currentPos++ {
  882. if clients[currentPos] != client {
  883. remainingClients[remainingPos] = clients[currentPos]
  884. remainingPos++
  885. }
  886. }
  887. am.accountToClients[casefoldedAccount] = remainingClients
  888. return
  889. }
  890. var (
  891. // EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support.
  892. // This can be moved to some other data structure/place if we need to load/unload mechs later.
  893. EnabledSaslMechanisms = map[string]func(*Server, *Client, string, []byte, *ResponseBuffer) bool{
  894. "PLAIN": authPlainHandler,
  895. "EXTERNAL": authExternalHandler,
  896. }
  897. )
  898. // AccountCredentials stores the various methods for verifying accounts.
  899. type AccountCredentials struct {
  900. Version uint
  901. PassphraseSalt []byte // legacy field, not used by v1 and later
  902. PassphraseHash []byte
  903. Certificate string // fingerprint
  904. }
  905. // ClientAccount represents a user account.
  906. type ClientAccount struct {
  907. // Name of the account.
  908. Name string
  909. // RegisteredAt represents the time that the account was registered.
  910. RegisteredAt time.Time
  911. Credentials AccountCredentials
  912. Verified bool
  913. AdditionalNicks []string
  914. VHost VHostInfo
  915. }
  916. // convenience for passing around raw serialized account data
  917. type rawClientAccount struct {
  918. Name string
  919. RegisteredAt string
  920. Credentials string
  921. Callback string
  922. Verified bool
  923. AdditionalNicks string
  924. VHost string
  925. }
  926. // logoutOfAccount logs the client out of their current account.
  927. func (am *AccountManager) logoutOfAccount(client *Client) {
  928. if client.Account() == "" {
  929. // already logged out
  930. return
  931. }
  932. client.SetAccountName("")
  933. go client.nickTimer.Touch()
  934. // dispatch account-notify
  935. // TODO: doing the I/O here is kind of a kludge, let's move this somewhere else
  936. go func() {
  937. for friend := range client.Friends(caps.AccountNotify) {
  938. friend.Send(nil, client.NickMaskString(), "ACCOUNT", "*")
  939. }
  940. }()
  941. }