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

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