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

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