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

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