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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "bytes"
  6. "encoding/json"
  7. "fmt"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. "sync/atomic"
  12. "time"
  13. "unicode"
  14. "github.com/oragono/oragono/irc/caps"
  15. "github.com/oragono/oragono/irc/connection_limits"
  16. "github.com/oragono/oragono/irc/email"
  17. "github.com/oragono/oragono/irc/ldap"
  18. "github.com/oragono/oragono/irc/passwd"
  19. "github.com/oragono/oragono/irc/utils"
  20. "github.com/tidwall/buntdb"
  21. )
  22. const (
  23. keyAccountExists = "account.exists %s"
  24. keyAccountVerified = "account.verified %s"
  25. keyAccountUnregistered = "account.unregistered %s"
  26. keyAccountCallback = "account.callback %s"
  27. keyAccountVerificationCode = "account.verificationcode %s"
  28. keyAccountName = "account.name %s" // stores the 'preferred name' of the account, not casemapped
  29. keyAccountRegTime = "account.registered.time %s"
  30. keyAccountCredentials = "account.credentials %s"
  31. keyAccountAdditionalNicks = "account.additionalnicks %s"
  32. keyAccountSettings = "account.settings %s"
  33. keyAccountVHost = "account.vhost %s"
  34. keyCertToAccount = "account.creds.certfp %s"
  35. keyAccountChannels = "account.channels %s" // channels registered to the account
  36. keyAccountJoinedChannels = "account.joinedto %s" // channels a persistent client has joined
  37. keyAccountLastSeen = "account.lastseen %s"
  38. keyVHostQueueAcctToId = "vhostQueue %s"
  39. vhostRequestIdx = "vhostQueue"
  40. maxCertfpsPerAccount = 5
  41. )
  42. // everything about accounts is persistent; therefore, the database is the authoritative
  43. // source of truth for all account information. anything on the heap is just a cache
  44. type AccountManager struct {
  45. // XXX these are up here so they can be aligned to a 64-bit boundary, please forgive me
  46. // autoincrementing ID for vhost requests:
  47. vhostRequestID uint64
  48. vhostRequestPendingCount uint64
  49. sync.RWMutex // tier 2
  50. serialCacheUpdateMutex sync.Mutex // tier 3
  51. vHostUpdateMutex sync.Mutex // tier 3
  52. server *Server
  53. // track clients logged in to accounts
  54. accountToClients map[string][]*Client
  55. nickToAccount map[string]string
  56. skeletonToAccount map[string]string
  57. accountToMethod map[string]NickEnforcementMethod
  58. registerThrottle connection_limits.GenericThrottle
  59. }
  60. func (am *AccountManager) Initialize(server *Server) {
  61. am.accountToClients = make(map[string][]*Client)
  62. am.nickToAccount = make(map[string]string)
  63. am.skeletonToAccount = make(map[string]string)
  64. am.accountToMethod = make(map[string]NickEnforcementMethod)
  65. am.server = server
  66. config := server.Config()
  67. am.buildNickToAccountIndex(config)
  68. am.initVHostRequestQueue(config)
  69. am.createAlwaysOnClients(config)
  70. am.resetRegisterThrottle(config)
  71. }
  72. func (am *AccountManager) resetRegisterThrottle(config *Config) {
  73. am.Lock()
  74. defer am.Unlock()
  75. am.registerThrottle = connection_limits.GenericThrottle{
  76. Duration: config.Accounts.Registration.Throttling.Duration,
  77. Limit: config.Accounts.Registration.Throttling.MaxAttempts,
  78. }
  79. }
  80. func (am *AccountManager) touchRegisterThrottle() (throttled bool) {
  81. am.Lock()
  82. defer am.Unlock()
  83. throttled, _ = am.registerThrottle.Touch()
  84. return
  85. }
  86. func (am *AccountManager) createAlwaysOnClients(config *Config) {
  87. if config.Accounts.Multiclient.AlwaysOn == PersistentDisabled {
  88. return
  89. }
  90. verifiedPrefix := fmt.Sprintf(keyAccountVerified, "")
  91. am.serialCacheUpdateMutex.Lock()
  92. defer am.serialCacheUpdateMutex.Unlock()
  93. var accounts []string
  94. am.server.store.View(func(tx *buntdb.Tx) error {
  95. err := tx.AscendGreaterOrEqual("", verifiedPrefix, func(key, value string) bool {
  96. if !strings.HasPrefix(key, verifiedPrefix) {
  97. return false
  98. }
  99. account := strings.TrimPrefix(key, verifiedPrefix)
  100. accounts = append(accounts, account)
  101. return true
  102. })
  103. return err
  104. })
  105. for _, accountName := range accounts {
  106. account, err := am.LoadAccount(accountName)
  107. if err == nil && account.Verified &&
  108. persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, account.Settings.AlwaysOn) {
  109. am.server.AddAlwaysOnClient(account, am.loadChannels(accountName), am.loadLastSeen(accountName))
  110. }
  111. }
  112. }
  113. func (am *AccountManager) buildNickToAccountIndex(config *Config) {
  114. if !config.Accounts.NickReservation.Enabled {
  115. return
  116. }
  117. nickToAccount := make(map[string]string)
  118. skeletonToAccount := make(map[string]string)
  119. accountToMethod := make(map[string]NickEnforcementMethod)
  120. existsPrefix := fmt.Sprintf(keyAccountExists, "")
  121. am.serialCacheUpdateMutex.Lock()
  122. defer am.serialCacheUpdateMutex.Unlock()
  123. err := am.server.store.View(func(tx *buntdb.Tx) error {
  124. err := tx.AscendGreaterOrEqual("", existsPrefix, func(key, value string) bool {
  125. if !strings.HasPrefix(key, existsPrefix) {
  126. return false
  127. }
  128. account := strings.TrimPrefix(key, existsPrefix)
  129. if _, err := tx.Get(fmt.Sprintf(keyAccountVerified, account)); err == nil {
  130. nickToAccount[account] = account
  131. accountName, err := tx.Get(fmt.Sprintf(keyAccountName, account))
  132. if err != nil {
  133. am.server.logger.Error("internal", "missing account name for", account)
  134. } else {
  135. skeleton, _ := Skeleton(accountName)
  136. skeletonToAccount[skeleton] = account
  137. }
  138. }
  139. if rawNicks, err := tx.Get(fmt.Sprintf(keyAccountAdditionalNicks, account)); err == nil {
  140. additionalNicks := unmarshalReservedNicks(rawNicks)
  141. for _, nick := range additionalNicks {
  142. cfnick, _ := CasefoldName(nick)
  143. nickToAccount[cfnick] = account
  144. skeleton, _ := Skeleton(nick)
  145. skeletonToAccount[skeleton] = account
  146. }
  147. }
  148. if rawPrefs, err := tx.Get(fmt.Sprintf(keyAccountSettings, account)); err == nil {
  149. var prefs AccountSettings
  150. err := json.Unmarshal([]byte(rawPrefs), &prefs)
  151. if err == nil && prefs.NickEnforcement != NickEnforcementOptional {
  152. accountToMethod[account] = prefs.NickEnforcement
  153. } else if err != nil {
  154. am.server.logger.Error("internal", "corrupt account creds", account)
  155. }
  156. }
  157. return true
  158. })
  159. return err
  160. })
  161. if config.Accounts.NickReservation.Method == NickEnforcementStrict {
  162. unregisteredPrefix := fmt.Sprintf(keyAccountUnregistered, "")
  163. am.server.store.View(func(tx *buntdb.Tx) error {
  164. tx.AscendGreaterOrEqual("", unregisteredPrefix, func(key, value string) bool {
  165. if !strings.HasPrefix(key, unregisteredPrefix) {
  166. return false
  167. }
  168. account := strings.TrimPrefix(key, unregisteredPrefix)
  169. accountName := value
  170. nickToAccount[account] = account
  171. skeleton, _ := Skeleton(accountName)
  172. skeletonToAccount[skeleton] = account
  173. return true
  174. })
  175. return nil
  176. })
  177. }
  178. if err != nil {
  179. am.server.logger.Error("internal", "couldn't read reserved nicks", err.Error())
  180. } else {
  181. am.Lock()
  182. am.nickToAccount = nickToAccount
  183. am.skeletonToAccount = skeletonToAccount
  184. am.accountToMethod = accountToMethod
  185. am.Unlock()
  186. }
  187. }
  188. func (am *AccountManager) initVHostRequestQueue(config *Config) {
  189. if !config.Accounts.VHosts.Enabled {
  190. return
  191. }
  192. am.vHostUpdateMutex.Lock()
  193. defer am.vHostUpdateMutex.Unlock()
  194. // the db maps the account name to the autoincrementing integer ID of its request
  195. // create an numerically ordered index on ID, so we can list the oldest requests
  196. // finally, collect the integer id of the newest request and the total request count
  197. var total uint64
  198. var lastIDStr string
  199. err := am.server.store.Update(func(tx *buntdb.Tx) error {
  200. err := tx.CreateIndex(vhostRequestIdx, fmt.Sprintf(keyVHostQueueAcctToId, "*"), buntdb.IndexInt)
  201. if err != nil {
  202. return err
  203. }
  204. return tx.Descend(vhostRequestIdx, func(key, value string) bool {
  205. if lastIDStr == "" {
  206. lastIDStr = value
  207. }
  208. total++
  209. return true
  210. })
  211. })
  212. if err != nil {
  213. am.server.logger.Error("internal", "could not create vhost queue index", err.Error())
  214. }
  215. lastID, _ := strconv.ParseUint(lastIDStr, 10, 64)
  216. am.server.logger.Debug("services", fmt.Sprintf("vhost queue length is %d, autoincrementing id is %d", total, lastID))
  217. atomic.StoreUint64(&am.vhostRequestID, lastID)
  218. atomic.StoreUint64(&am.vhostRequestPendingCount, total)
  219. }
  220. func (am *AccountManager) NickToAccount(nick string) string {
  221. cfnick, err := CasefoldName(nick)
  222. if err != nil {
  223. return ""
  224. }
  225. am.RLock()
  226. defer am.RUnlock()
  227. return am.nickToAccount[cfnick]
  228. }
  229. // given an account, combine stored enforcement method with the config settings
  230. // to compute the actual enforcement method
  231. func configuredEnforcementMethod(config *Config, storedMethod NickEnforcementMethod) (result NickEnforcementMethod) {
  232. if !config.Accounts.NickReservation.Enabled {
  233. return NickEnforcementNone
  234. }
  235. result = storedMethod
  236. // if they don't have a custom setting, or customization is disabled, use the default
  237. if result == NickEnforcementOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {
  238. result = config.Accounts.NickReservation.Method
  239. }
  240. if result == NickEnforcementOptional {
  241. // enforcement was explicitly enabled neither in the config or by the user
  242. result = NickEnforcementNone
  243. }
  244. return
  245. }
  246. // Given a nick, looks up the account that owns it and the method (none/timeout/strict)
  247. // used to enforce ownership.
  248. func (am *AccountManager) EnforcementStatus(cfnick, skeleton string) (account string, method NickEnforcementMethod) {
  249. config := am.server.Config()
  250. if !config.Accounts.NickReservation.Enabled {
  251. return "", NickEnforcementNone
  252. }
  253. am.RLock()
  254. defer am.RUnlock()
  255. finalEnforcementMethod := func(account_ string) (result NickEnforcementMethod) {
  256. storedMethod := am.accountToMethod[account_]
  257. return configuredEnforcementMethod(config, storedMethod)
  258. }
  259. nickAccount := am.nickToAccount[cfnick]
  260. skelAccount := am.skeletonToAccount[skeleton]
  261. if nickAccount == "" && skelAccount == "" {
  262. return "", NickEnforcementNone
  263. } else if nickAccount != "" && (skelAccount == nickAccount || skelAccount == "") {
  264. return nickAccount, finalEnforcementMethod(nickAccount)
  265. } else if skelAccount != "" && nickAccount == "" {
  266. return skelAccount, finalEnforcementMethod(skelAccount)
  267. } else {
  268. // nickAccount != skelAccount and both are nonempty:
  269. // two people have competing claims on (this casefolding of) this nick!
  270. nickMethod := finalEnforcementMethod(nickAccount)
  271. skelMethod := finalEnforcementMethod(skelAccount)
  272. switch {
  273. case skelMethod == NickEnforcementNone:
  274. return nickAccount, nickMethod
  275. case nickMethod == NickEnforcementNone:
  276. return skelAccount, skelMethod
  277. default:
  278. // nobody can use this nick
  279. return "!", NickEnforcementStrict
  280. }
  281. }
  282. }
  283. // Sets a custom enforcement method for an account and stores it in the database.
  284. func (am *AccountManager) SetEnforcementStatus(account string, method NickEnforcementMethod) (finalSettings AccountSettings, err error) {
  285. config := am.server.Config()
  286. if !(config.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.AllowCustomEnforcement) {
  287. err = errFeatureDisabled
  288. return
  289. }
  290. setter := func(in AccountSettings) (out AccountSettings, err error) {
  291. out = in
  292. out.NickEnforcement = method
  293. return out, nil
  294. }
  295. _, err = am.ModifyAccountSettings(account, setter)
  296. if err != nil {
  297. return
  298. }
  299. // this update of the data plane is racey, but it's probably fine
  300. am.Lock()
  301. defer am.Unlock()
  302. if method == NickEnforcementOptional {
  303. delete(am.accountToMethod, account)
  304. } else {
  305. am.accountToMethod[account] = method
  306. }
  307. return
  308. }
  309. func (am *AccountManager) AccountToClients(account string) (result []*Client) {
  310. cfaccount, err := CasefoldName(account)
  311. if err != nil {
  312. return
  313. }
  314. am.RLock()
  315. defer am.RUnlock()
  316. return am.accountToClients[cfaccount]
  317. }
  318. func (am *AccountManager) Register(client *Client, account string, callbackNamespace string, callbackValue string, passphrase string, certfp string) error {
  319. casefoldedAccount, err := CasefoldName(account)
  320. skeleton, skerr := Skeleton(account)
  321. if err != nil || skerr != nil || account == "" || account == "*" {
  322. return errAccountCreation
  323. }
  324. if restrictedCasefoldedNicks[casefoldedAccount] || restrictedSkeletons[skeleton] {
  325. return errAccountAlreadyRegistered
  326. }
  327. config := am.server.Config()
  328. // final "is registration allowed" check, probably redundant:
  329. if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") {
  330. return errFeatureDisabled
  331. }
  332. if client != nil && client.Account() != "" {
  333. return errAccountAlreadyLoggedIn
  334. }
  335. if client != nil && am.touchRegisterThrottle() {
  336. am.server.logger.Warning("accounts", "global registration throttle exceeded by client", client.Nick())
  337. return errLimitExceeded
  338. }
  339. // if nick reservation is enabled, you can only register your current nickname
  340. // as an account; this prevents "land-grab" situations where someone else
  341. // registers your nick out from under you and then NS GHOSTs you
  342. // n.b. client is nil during a SAREGISTER
  343. // n.b. if ForceGuestFormat, then there's no concern, because you can't
  344. // register a guest nickname anyway, and the actual registration system
  345. // will prevent any double-register
  346. if client != nil && config.Accounts.NickReservation.Enabled &&
  347. !config.Accounts.NickReservation.ForceGuestFormat &&
  348. client.NickCasefolded() != casefoldedAccount {
  349. return errAccountMustHoldNick
  350. }
  351. // can't register a guest nickname
  352. if config.Accounts.NickReservation.guestRegexpFolded.MatchString(casefoldedAccount) {
  353. return errAccountAlreadyRegistered
  354. }
  355. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  356. unregisteredKey := fmt.Sprintf(keyAccountUnregistered, casefoldedAccount)
  357. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  358. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  359. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  360. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  361. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  362. certFPKey := fmt.Sprintf(keyCertToAccount, certfp)
  363. var creds AccountCredentials
  364. creds.Version = 1
  365. err = creds.SetPassphrase(passphrase, am.server.Config().Accounts.Registration.BcryptCost)
  366. if err != nil {
  367. return err
  368. }
  369. creds.AddCertfp(certfp)
  370. credStr, err := creds.Serialize()
  371. if err != nil {
  372. return err
  373. }
  374. registeredTimeStr := strconv.FormatInt(time.Now().UnixNano(), 10)
  375. callbackSpec := fmt.Sprintf("%s:%s", callbackNamespace, callbackValue)
  376. var setOptions *buntdb.SetOptions
  377. ttl := time.Duration(config.Accounts.Registration.VerifyTimeout)
  378. if ttl != 0 {
  379. setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
  380. }
  381. err = func() error {
  382. am.serialCacheUpdateMutex.Lock()
  383. defer am.serialCacheUpdateMutex.Unlock()
  384. // can't register an account with the same name as a registered nick
  385. if am.NickToAccount(casefoldedAccount) != "" {
  386. return errAccountAlreadyRegistered
  387. }
  388. return am.server.store.Update(func(tx *buntdb.Tx) error {
  389. if _, err := tx.Get(unregisteredKey); err == nil {
  390. return errAccountAlreadyUnregistered
  391. }
  392. _, err = am.loadRawAccount(tx, casefoldedAccount)
  393. if err != errAccountDoesNotExist {
  394. return errAccountAlreadyRegistered
  395. }
  396. if certfp != "" {
  397. // make sure certfp doesn't already exist because that'd be silly
  398. _, err := tx.Get(certFPKey)
  399. if err != buntdb.ErrNotFound {
  400. return errCertfpAlreadyExists
  401. }
  402. }
  403. tx.Set(accountKey, "1", setOptions)
  404. tx.Set(accountNameKey, account, setOptions)
  405. tx.Set(registeredTimeKey, registeredTimeStr, setOptions)
  406. tx.Set(credentialsKey, credStr, setOptions)
  407. tx.Set(callbackKey, callbackSpec, setOptions)
  408. if certfp != "" {
  409. tx.Set(certFPKey, casefoldedAccount, setOptions)
  410. }
  411. return nil
  412. })
  413. }()
  414. if err != nil {
  415. return err
  416. }
  417. code, err := am.dispatchCallback(client, account, callbackNamespace, callbackValue)
  418. if err != nil {
  419. am.Unregister(casefoldedAccount, true)
  420. return errCallbackFailed
  421. } else {
  422. return am.server.store.Update(func(tx *buntdb.Tx) error {
  423. _, _, err = tx.Set(verificationCodeKey, code, setOptions)
  424. return err
  425. })
  426. }
  427. }
  428. // validatePassphrase checks whether a passphrase is allowed by our rules
  429. func validatePassphrase(passphrase string) error {
  430. // sanity check the length
  431. if len(passphrase) == 0 || len(passphrase) > 300 {
  432. return errAccountBadPassphrase
  433. }
  434. // we use * as a placeholder in some places, if it's gotten this far then fail
  435. if passphrase == "*" {
  436. return errAccountBadPassphrase
  437. }
  438. // for now, just enforce that spaces are not allowed
  439. for _, r := range passphrase {
  440. if unicode.IsSpace(r) {
  441. return errAccountBadPassphrase
  442. }
  443. }
  444. return nil
  445. }
  446. // changes the password for an account
  447. func (am *AccountManager) setPassword(account string, password string, hasPrivs bool) (err error) {
  448. cfAccount, err := CasefoldName(account)
  449. if err != nil {
  450. return errAccountDoesNotExist
  451. }
  452. credKey := fmt.Sprintf(keyAccountCredentials, cfAccount)
  453. var credStr string
  454. am.server.store.View(func(tx *buntdb.Tx) error {
  455. // no need to check verification status here or below;
  456. // you either need to be auth'ed to the account or be an oper to do this
  457. credStr, err = tx.Get(credKey)
  458. return nil
  459. })
  460. if err != nil {
  461. return errAccountDoesNotExist
  462. }
  463. var creds AccountCredentials
  464. err = json.Unmarshal([]byte(credStr), &creds)
  465. if err != nil {
  466. return err
  467. }
  468. if !hasPrivs && creds.Empty() {
  469. return errCredsExternallyManaged
  470. }
  471. err = creds.SetPassphrase(password, am.server.Config().Accounts.Registration.BcryptCost)
  472. if err != nil {
  473. return err
  474. }
  475. if creds.Empty() && !hasPrivs {
  476. return errEmptyCredentials
  477. }
  478. newCredStr, err := creds.Serialize()
  479. if err != nil {
  480. return err
  481. }
  482. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  483. curCredStr, err := tx.Get(credKey)
  484. if credStr != curCredStr {
  485. return errCASFailed
  486. }
  487. _, _, err = tx.Set(credKey, newCredStr, nil)
  488. return err
  489. })
  490. return err
  491. }
  492. func (am *AccountManager) saveChannels(account string, channels []string) {
  493. channelsStr := strings.Join(channels, ",")
  494. key := fmt.Sprintf(keyAccountJoinedChannels, account)
  495. am.server.store.Update(func(tx *buntdb.Tx) error {
  496. tx.Set(key, channelsStr, nil)
  497. return nil
  498. })
  499. }
  500. func (am *AccountManager) loadChannels(account string) (channels []string) {
  501. key := fmt.Sprintf(keyAccountJoinedChannels, account)
  502. var channelsStr string
  503. am.server.store.View(func(tx *buntdb.Tx) error {
  504. channelsStr, _ = tx.Get(key)
  505. return nil
  506. })
  507. if channelsStr != "" {
  508. return strings.Split(channelsStr, ",")
  509. }
  510. return
  511. }
  512. func (am *AccountManager) saveLastSeen(account string, lastSeen time.Time) {
  513. key := fmt.Sprintf(keyAccountLastSeen, account)
  514. var val string
  515. if !lastSeen.IsZero() {
  516. val = strconv.FormatInt(lastSeen.UnixNano(), 10)
  517. }
  518. am.server.store.Update(func(tx *buntdb.Tx) error {
  519. if val != "" {
  520. tx.Set(key, val, nil)
  521. } else {
  522. tx.Delete(key)
  523. }
  524. return nil
  525. })
  526. }
  527. func (am *AccountManager) loadLastSeen(account string) (lastSeen time.Time) {
  528. key := fmt.Sprintf(keyAccountLastSeen, account)
  529. var lsText string
  530. am.server.store.Update(func(tx *buntdb.Tx) error {
  531. lsText, _ = tx.Get(key)
  532. // XXX clear this on startup, because it's not clear when it's
  533. // going to be overwritten, and restarting the server twice in a row
  534. // could result in a large amount of duplicated history replay
  535. tx.Delete(key)
  536. return nil
  537. })
  538. lsNum, err := strconv.ParseInt(lsText, 10, 64)
  539. if err == nil {
  540. return time.Unix(0, lsNum).UTC()
  541. }
  542. return
  543. }
  544. func (am *AccountManager) addRemoveCertfp(account, certfp string, add bool, hasPrivs bool) (err error) {
  545. certfp, err = utils.NormalizeCertfp(certfp)
  546. if err != nil {
  547. return err
  548. }
  549. cfAccount, err := CasefoldName(account)
  550. if err != nil {
  551. return errAccountDoesNotExist
  552. }
  553. credKey := fmt.Sprintf(keyAccountCredentials, cfAccount)
  554. var credStr string
  555. am.server.store.View(func(tx *buntdb.Tx) error {
  556. credStr, err = tx.Get(credKey)
  557. return nil
  558. })
  559. if err != nil {
  560. return errAccountDoesNotExist
  561. }
  562. var creds AccountCredentials
  563. err = json.Unmarshal([]byte(credStr), &creds)
  564. if err != nil {
  565. return err
  566. }
  567. if !hasPrivs && creds.Empty() {
  568. return errCredsExternallyManaged
  569. }
  570. if add {
  571. err = creds.AddCertfp(certfp)
  572. } else {
  573. err = creds.RemoveCertfp(certfp)
  574. }
  575. if err != nil {
  576. return err
  577. }
  578. if creds.Empty() && !hasPrivs {
  579. return errEmptyCredentials
  580. }
  581. newCredStr, err := creds.Serialize()
  582. if err != nil {
  583. return err
  584. }
  585. certfpKey := fmt.Sprintf(keyCertToAccount, certfp)
  586. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  587. curCredStr, err := tx.Get(credKey)
  588. if credStr != curCredStr {
  589. return errCASFailed
  590. }
  591. if add {
  592. _, err = tx.Get(certfpKey)
  593. if err != buntdb.ErrNotFound {
  594. return errCertfpAlreadyExists
  595. }
  596. tx.Set(certfpKey, cfAccount, nil)
  597. } else {
  598. tx.Delete(certfpKey)
  599. }
  600. _, _, err = tx.Set(credKey, newCredStr, nil)
  601. return err
  602. })
  603. return err
  604. }
  605. func (am *AccountManager) dispatchCallback(client *Client, account string, callbackNamespace string, callbackValue string) (string, error) {
  606. if callbackNamespace == "*" || callbackNamespace == "none" || callbackNamespace == "admin" {
  607. return "", nil
  608. } else if callbackNamespace == "mailto" {
  609. return am.dispatchMailtoCallback(client, account, callbackValue)
  610. } else {
  611. return "", fmt.Errorf("Callback not implemented: %s", callbackNamespace)
  612. }
  613. }
  614. func (am *AccountManager) dispatchMailtoCallback(client *Client, account string, callbackValue string) (code string, err error) {
  615. config := am.server.Config().Accounts.Registration.Callbacks.Mailto
  616. code = utils.GenerateSecretToken()
  617. subject := config.VerifyMessageSubject
  618. if subject == "" {
  619. subject = fmt.Sprintf(client.t("Verify your account on %s"), am.server.name)
  620. }
  621. var message bytes.Buffer
  622. fmt.Fprintf(&message, "From: %s\r\n", config.Sender)
  623. fmt.Fprintf(&message, "To: %s\r\n", callbackValue)
  624. if config.DKIM.Domain != "" {
  625. fmt.Fprintf(&message, "Message-ID: <%s@%s>\r\n", utils.GenerateSecretKey(), config.DKIM.Domain)
  626. }
  627. fmt.Fprintf(&message, "Date: %s\r\n", time.Now().UTC().Format(time.RFC1123Z))
  628. fmt.Fprintf(&message, "Subject: %s\r\n", subject)
  629. message.WriteString("\r\n") // blank line: end headers, begin message body
  630. fmt.Fprintf(&message, client.t("Account: %s"), account)
  631. message.WriteString("\r\n")
  632. fmt.Fprintf(&message, client.t("Verification code: %s"), code)
  633. message.WriteString("\r\n")
  634. message.WriteString("\r\n")
  635. message.WriteString(client.t("To verify your account, issue the following command:"))
  636. message.WriteString("\r\n")
  637. fmt.Fprintf(&message, "/MSG NickServ VERIFY %s %s\r\n", account, code)
  638. err = email.SendMail(config, callbackValue, message.Bytes())
  639. if err != nil {
  640. am.server.logger.Error("internal", "Failed to dispatch e-mail to", callbackValue, err.Error())
  641. }
  642. return
  643. }
  644. func (am *AccountManager) Verify(client *Client, account string, code string) error {
  645. casefoldedAccount, err := CasefoldName(account)
  646. var skeleton string
  647. if err != nil || account == "" || account == "*" {
  648. return errAccountVerificationFailed
  649. }
  650. if client != nil && client.Account() != "" {
  651. return errAccountAlreadyLoggedIn
  652. }
  653. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  654. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  655. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  656. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  657. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  658. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  659. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  660. var raw rawClientAccount
  661. func() {
  662. am.serialCacheUpdateMutex.Lock()
  663. defer am.serialCacheUpdateMutex.Unlock()
  664. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  665. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  666. if err == errAccountDoesNotExist {
  667. return errAccountDoesNotExist
  668. } else if err != nil {
  669. return errAccountVerificationFailed
  670. } else if raw.Verified {
  671. return errAccountAlreadyVerified
  672. }
  673. // actually verify the code
  674. // a stored code of "" means a none callback / no code required
  675. success := false
  676. storedCode, err := tx.Get(verificationCodeKey)
  677. if err == nil {
  678. // this is probably unnecessary
  679. if storedCode == "" || utils.SecretTokensMatch(storedCode, code) {
  680. success = true
  681. }
  682. }
  683. if !success {
  684. return errAccountVerificationInvalidCode
  685. }
  686. // verify the account
  687. tx.Set(verifiedKey, "1", nil)
  688. // don't need the code anymore
  689. tx.Delete(verificationCodeKey)
  690. // re-set all other keys, removing the TTL
  691. tx.Set(accountKey, "1", nil)
  692. tx.Set(accountNameKey, raw.Name, nil)
  693. tx.Set(registeredTimeKey, raw.RegisteredAt, nil)
  694. tx.Set(callbackKey, raw.Callback, nil)
  695. tx.Set(credentialsKey, raw.Credentials, nil)
  696. var creds AccountCredentials
  697. // XXX we shouldn't do (de)serialization inside the txn,
  698. // but this is like 2 usec on my system
  699. json.Unmarshal([]byte(raw.Credentials), &creds)
  700. for _, cert := range creds.Certfps {
  701. certFPKey := fmt.Sprintf(keyCertToAccount, cert)
  702. tx.Set(certFPKey, casefoldedAccount, nil)
  703. }
  704. return nil
  705. })
  706. if err == nil {
  707. skeleton, _ = Skeleton(raw.Name)
  708. am.Lock()
  709. am.nickToAccount[casefoldedAccount] = casefoldedAccount
  710. am.skeletonToAccount[skeleton] = casefoldedAccount
  711. am.Unlock()
  712. }
  713. }()
  714. if err != nil {
  715. return err
  716. }
  717. nick := "[server admin]"
  718. if client != nil {
  719. nick = client.Nick()
  720. }
  721. am.server.logger.Info("accounts", "client", nick, "registered account", casefoldedAccount)
  722. raw.Verified = true
  723. clientAccount, err := am.deserializeRawAccount(raw, casefoldedAccount)
  724. if err != nil {
  725. return err
  726. }
  727. if client != nil {
  728. am.Login(client, clientAccount)
  729. }
  730. _, method := am.EnforcementStatus(casefoldedAccount, skeleton)
  731. if method != NickEnforcementNone {
  732. currentClient := am.server.clients.Get(casefoldedAccount)
  733. if currentClient == nil || currentClient == client || currentClient.Account() == casefoldedAccount {
  734. return nil
  735. }
  736. if method == NickEnforcementStrict {
  737. am.server.RandomlyRename(currentClient)
  738. } else if method == NickEnforcementWithTimeout {
  739. currentClient.nickTimer.Touch(nil)
  740. }
  741. }
  742. return nil
  743. }
  744. // register and verify an account, for internal use
  745. func (am *AccountManager) SARegister(account, passphrase string) (err error) {
  746. err = am.Register(nil, account, "admin", "", passphrase, "")
  747. if err == nil {
  748. err = am.Verify(nil, account, "")
  749. }
  750. return
  751. }
  752. func marshalReservedNicks(nicks []string) string {
  753. return strings.Join(nicks, ",")
  754. }
  755. func unmarshalReservedNicks(nicks string) (result []string) {
  756. if nicks == "" {
  757. return
  758. }
  759. return strings.Split(nicks, ",")
  760. }
  761. func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreserve bool, reserve bool) error {
  762. cfnick, err := CasefoldName(nick)
  763. skeleton, skerr := Skeleton(nick)
  764. // garbage nick, or garbage options, or disabled
  765. nrconfig := am.server.Config().Accounts.NickReservation
  766. if err != nil || skerr != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
  767. return errAccountNickReservationFailed
  768. }
  769. // the cache is in sync with the DB while we hold serialCacheUpdateMutex
  770. am.serialCacheUpdateMutex.Lock()
  771. defer am.serialCacheUpdateMutex.Unlock()
  772. // find the affected account, which is usually the client's:
  773. account := client.Account()
  774. if saUnreserve {
  775. // unless this is a sadrop:
  776. account = am.NickToAccount(cfnick)
  777. if account == "" {
  778. // nothing to do
  779. return nil
  780. }
  781. }
  782. if account == "" {
  783. return errAccountNotLoggedIn
  784. }
  785. am.Lock()
  786. accountForNick := am.nickToAccount[cfnick]
  787. var accountForSkeleton string
  788. if reserve {
  789. accountForSkeleton = am.skeletonToAccount[skeleton]
  790. }
  791. am.Unlock()
  792. if reserve && (accountForNick != "" || accountForSkeleton != "") {
  793. return errNicknameReserved
  794. } else if !reserve && !saUnreserve && accountForNick != account {
  795. return errNicknameReserved
  796. } else if !reserve && cfnick == account {
  797. return errAccountCantDropPrimaryNick
  798. }
  799. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, account)
  800. unverifiedAccountKey := fmt.Sprintf(keyAccountExists, cfnick)
  801. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  802. if reserve {
  803. // unverified accounts don't show up in NickToAccount yet (which is intentional),
  804. // however you shouldn't be able to reserve a nick out from under them
  805. _, err := tx.Get(unverifiedAccountKey)
  806. if err == nil {
  807. return errNicknameReserved
  808. }
  809. }
  810. rawNicks, err := tx.Get(nicksKey)
  811. if err != nil && err != buntdb.ErrNotFound {
  812. return err
  813. }
  814. nicks := unmarshalReservedNicks(rawNicks)
  815. if reserve {
  816. if len(nicks) >= nrconfig.AdditionalNickLimit {
  817. return errAccountTooManyNicks
  818. }
  819. nicks = append(nicks, nick)
  820. } else {
  821. // compute (original reserved nicks) minus cfnick
  822. var newNicks []string
  823. for _, reservedNick := range nicks {
  824. cfreservednick, _ := CasefoldName(reservedNick)
  825. if cfreservednick != cfnick {
  826. newNicks = append(newNicks, reservedNick)
  827. } else {
  828. // found the original, unfolded version of the nick we're dropping;
  829. // recompute the true skeleton from it
  830. skeleton, _ = Skeleton(reservedNick)
  831. }
  832. }
  833. nicks = newNicks
  834. }
  835. marshaledNicks := marshalReservedNicks(nicks)
  836. _, _, err = tx.Set(nicksKey, string(marshaledNicks), nil)
  837. return err
  838. })
  839. if err == errAccountTooManyNicks || err == errNicknameReserved {
  840. return err
  841. } else if err != nil {
  842. return errAccountNickReservationFailed
  843. }
  844. // success
  845. am.Lock()
  846. defer am.Unlock()
  847. if reserve {
  848. am.nickToAccount[cfnick] = account
  849. am.skeletonToAccount[skeleton] = account
  850. } else {
  851. delete(am.nickToAccount, cfnick)
  852. delete(am.skeletonToAccount, skeleton)
  853. }
  854. return nil
  855. }
  856. func (am *AccountManager) checkPassphrase(accountName, passphrase string) (account ClientAccount, err error) {
  857. account, err = am.LoadAccount(accountName)
  858. if err != nil {
  859. return
  860. }
  861. if !account.Verified {
  862. err = errAccountUnverified
  863. return
  864. }
  865. switch account.Credentials.Version {
  866. case 0:
  867. err = handleLegacyPasswordV0(am.server, accountName, account.Credentials, passphrase)
  868. case 1:
  869. if passwd.CompareHashAndPassword(account.Credentials.PassphraseHash, []byte(passphrase)) != nil {
  870. err = errAccountInvalidCredentials
  871. }
  872. default:
  873. err = errAccountInvalidCredentials
  874. }
  875. return
  876. }
  877. func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) (err error) {
  878. // XXX check this now, so we don't allow a redundant login for an always-on client
  879. // even for a brief period. the other potential source of nick-account conflicts
  880. // is from force-nick-equals-account, but those will be caught later by
  881. // fixupNickEqualsAccount and if there is a conflict, they will be logged out.
  882. if client.registered {
  883. if clientAlready := am.server.clients.Get(accountName); clientAlready != nil && clientAlready.AlwaysOn() {
  884. return errNickAccountMismatch
  885. }
  886. }
  887. var account ClientAccount
  888. defer func() {
  889. if err == nil {
  890. am.Login(client, account)
  891. }
  892. }()
  893. ldapConf := am.server.Config().Accounts.LDAP
  894. if ldapConf.Enabled {
  895. err = ldap.CheckLDAPPassphrase(ldapConf, accountName, passphrase, am.server.logger)
  896. if err == nil {
  897. account, err = am.LoadAccount(accountName)
  898. // autocreate if necessary:
  899. if err == errAccountDoesNotExist && ldapConf.Autocreate {
  900. err = am.SARegister(accountName, "")
  901. if err != nil {
  902. return
  903. }
  904. account, err = am.LoadAccount(accountName)
  905. }
  906. return
  907. }
  908. }
  909. account, err = am.checkPassphrase(accountName, passphrase)
  910. return err
  911. }
  912. func (am *AccountManager) LoadAccount(accountName string) (result ClientAccount, err error) {
  913. casefoldedAccount, err := CasefoldName(accountName)
  914. if err != nil {
  915. err = errAccountDoesNotExist
  916. return
  917. }
  918. var raw rawClientAccount
  919. am.server.store.View(func(tx *buntdb.Tx) error {
  920. raw, err = am.loadRawAccount(tx, casefoldedAccount)
  921. return nil
  922. })
  923. if err != nil {
  924. return
  925. }
  926. result, err = am.deserializeRawAccount(raw, casefoldedAccount)
  927. return
  928. }
  929. func (am *AccountManager) deserializeRawAccount(raw rawClientAccount, cfName string) (result ClientAccount, err error) {
  930. result.Name = raw.Name
  931. result.NameCasefolded = cfName
  932. regTimeInt, _ := strconv.ParseInt(raw.RegisteredAt, 10, 64)
  933. result.RegisteredAt = time.Unix(0, regTimeInt).UTC()
  934. e := json.Unmarshal([]byte(raw.Credentials), &result.Credentials)
  935. if e != nil {
  936. am.server.logger.Error("internal", "could not unmarshal credentials", e.Error())
  937. err = errAccountDoesNotExist
  938. return
  939. }
  940. result.AdditionalNicks = unmarshalReservedNicks(raw.AdditionalNicks)
  941. result.Verified = raw.Verified
  942. if raw.VHost != "" {
  943. e := json.Unmarshal([]byte(raw.VHost), &result.VHost)
  944. if e != nil {
  945. am.server.logger.Warning("internal", "could not unmarshal vhost for account", result.Name, e.Error())
  946. // pretend they have no vhost and move on
  947. }
  948. }
  949. if raw.Settings != "" {
  950. e := json.Unmarshal([]byte(raw.Settings), &result.Settings)
  951. if e != nil {
  952. am.server.logger.Warning("internal", "could not unmarshal settings for account", result.Name, e.Error())
  953. }
  954. }
  955. return
  956. }
  957. func (am *AccountManager) loadRawAccount(tx *buntdb.Tx, casefoldedAccount string) (result rawClientAccount, err error) {
  958. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  959. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  960. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  961. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  962. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  963. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  964. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  965. vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
  966. settingsKey := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
  967. _, e := tx.Get(accountKey)
  968. if e == buntdb.ErrNotFound {
  969. err = errAccountDoesNotExist
  970. return
  971. }
  972. result.Name, _ = tx.Get(accountNameKey)
  973. result.RegisteredAt, _ = tx.Get(registeredTimeKey)
  974. result.Credentials, _ = tx.Get(credentialsKey)
  975. result.Callback, _ = tx.Get(callbackKey)
  976. result.AdditionalNicks, _ = tx.Get(nicksKey)
  977. result.VHost, _ = tx.Get(vhostKey)
  978. result.Settings, _ = tx.Get(settingsKey)
  979. if _, e = tx.Get(verifiedKey); e == nil {
  980. result.Verified = true
  981. }
  982. return
  983. }
  984. func (am *AccountManager) Unregister(account string, erase bool) error {
  985. config := am.server.Config()
  986. casefoldedAccount, err := CasefoldName(account)
  987. if err != nil {
  988. return errAccountDoesNotExist
  989. }
  990. accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
  991. accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
  992. registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
  993. credentialsKey := fmt.Sprintf(keyAccountCredentials, casefoldedAccount)
  994. callbackKey := fmt.Sprintf(keyAccountCallback, casefoldedAccount)
  995. verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
  996. verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
  997. nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
  998. settingsKey := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
  999. vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
  1000. vhostQueueKey := fmt.Sprintf(keyVHostQueueAcctToId, casefoldedAccount)
  1001. channelsKey := fmt.Sprintf(keyAccountChannels, casefoldedAccount)
  1002. joinedChannelsKey := fmt.Sprintf(keyAccountJoinedChannels, casefoldedAccount)
  1003. lastSeenKey := fmt.Sprintf(keyAccountLastSeen, casefoldedAccount)
  1004. unregisteredKey := fmt.Sprintf(keyAccountUnregistered, casefoldedAccount)
  1005. var clients []*Client
  1006. var registeredChannels []string
  1007. // on our way out, unregister all the account's channels and delete them from the db
  1008. defer func() {
  1009. for _, channelName := range registeredChannels {
  1010. err := am.server.channels.SetUnregistered(channelName, casefoldedAccount)
  1011. if err != nil {
  1012. am.server.logger.Error("internal", "couldn't unregister channel", channelName, err.Error())
  1013. }
  1014. }
  1015. }()
  1016. var credText string
  1017. var rawNicks string
  1018. am.serialCacheUpdateMutex.Lock()
  1019. defer am.serialCacheUpdateMutex.Unlock()
  1020. var accountName string
  1021. var channelsStr string
  1022. keepProtections := false
  1023. am.server.store.Update(func(tx *buntdb.Tx) error {
  1024. // get the unfolded account name; for an active account, this is
  1025. // stored under accountNameKey, for an unregistered account under unregisteredKey
  1026. accountName, _ = tx.Get(accountNameKey)
  1027. if accountName == "" {
  1028. accountName, _ = tx.Get(unregisteredKey)
  1029. }
  1030. if erase {
  1031. tx.Delete(unregisteredKey)
  1032. } else {
  1033. if _, err := tx.Get(verifiedKey); err == nil {
  1034. tx.Set(unregisteredKey, accountName, nil)
  1035. keepProtections = true
  1036. }
  1037. }
  1038. tx.Delete(accountKey)
  1039. tx.Delete(accountNameKey)
  1040. tx.Delete(verifiedKey)
  1041. tx.Delete(registeredTimeKey)
  1042. tx.Delete(callbackKey)
  1043. tx.Delete(verificationCodeKey)
  1044. tx.Delete(settingsKey)
  1045. rawNicks, _ = tx.Get(nicksKey)
  1046. tx.Delete(nicksKey)
  1047. credText, err = tx.Get(credentialsKey)
  1048. tx.Delete(credentialsKey)
  1049. tx.Delete(vhostKey)
  1050. channelsStr, _ = tx.Get(channelsKey)
  1051. tx.Delete(channelsKey)
  1052. tx.Delete(joinedChannelsKey)
  1053. tx.Delete(lastSeenKey)
  1054. _, err := tx.Delete(vhostQueueKey)
  1055. am.decrementVHostQueueCount(casefoldedAccount, err)
  1056. return nil
  1057. })
  1058. if err == nil {
  1059. var creds AccountCredentials
  1060. if err := json.Unmarshal([]byte(credText), &creds); err == nil {
  1061. for _, cert := range creds.Certfps {
  1062. certFPKey := fmt.Sprintf(keyCertToAccount, cert)
  1063. am.server.store.Update(func(tx *buntdb.Tx) error {
  1064. if account, err := tx.Get(certFPKey); err == nil && account == casefoldedAccount {
  1065. tx.Delete(certFPKey)
  1066. }
  1067. return nil
  1068. })
  1069. }
  1070. }
  1071. }
  1072. skeleton, _ := Skeleton(accountName)
  1073. additionalNicks := unmarshalReservedNicks(rawNicks)
  1074. registeredChannels = unmarshalRegisteredChannels(channelsStr)
  1075. am.Lock()
  1076. defer am.Unlock()
  1077. clients = am.accountToClients[casefoldedAccount]
  1078. delete(am.accountToClients, casefoldedAccount)
  1079. // protect the account name itself where applicable, but not any grouped nicks
  1080. if !(keepProtections && config.Accounts.NickReservation.Method == NickEnforcementStrict) {
  1081. delete(am.nickToAccount, casefoldedAccount)
  1082. delete(am.skeletonToAccount, skeleton)
  1083. }
  1084. for _, nick := range additionalNicks {
  1085. delete(am.nickToAccount, nick)
  1086. additionalSkel, _ := Skeleton(nick)
  1087. delete(am.skeletonToAccount, additionalSkel)
  1088. }
  1089. for _, client := range clients {
  1090. if config.Accounts.RequireSasl.Enabled {
  1091. client.Logout()
  1092. client.Quit(client.t("You are no longer authorized to be on this server"), nil)
  1093. // destroy acquires a semaphore so we can't call it while holding a lock
  1094. go client.destroy(nil)
  1095. } else {
  1096. am.logoutOfAccount(client)
  1097. }
  1098. }
  1099. if err != nil && !erase {
  1100. return errAccountDoesNotExist
  1101. }
  1102. return nil
  1103. }
  1104. func unmarshalRegisteredChannels(channelsStr string) (result []string) {
  1105. if channelsStr != "" {
  1106. result = strings.Split(channelsStr, ",")
  1107. }
  1108. return
  1109. }
  1110. func (am *AccountManager) ChannelsForAccount(account string) (channels []string) {
  1111. cfaccount, err := CasefoldName(account)
  1112. if err != nil {
  1113. return
  1114. }
  1115. var channelStr string
  1116. key := fmt.Sprintf(keyAccountChannels, cfaccount)
  1117. am.server.store.View(func(tx *buntdb.Tx) error {
  1118. channelStr, _ = tx.Get(key)
  1119. return nil
  1120. })
  1121. return unmarshalRegisteredChannels(channelStr)
  1122. }
  1123. func (am *AccountManager) AuthenticateByCertFP(client *Client, certfp, authzid string) error {
  1124. if certfp == "" {
  1125. return errAccountInvalidCredentials
  1126. }
  1127. var account string
  1128. certFPKey := fmt.Sprintf(keyCertToAccount, certfp)
  1129. err := am.server.store.View(func(tx *buntdb.Tx) error {
  1130. account, _ = tx.Get(certFPKey)
  1131. if account == "" {
  1132. return errAccountInvalidCredentials
  1133. }
  1134. return nil
  1135. })
  1136. if err != nil {
  1137. return err
  1138. }
  1139. if authzid != "" && authzid != account {
  1140. return errAuthzidAuthcidMismatch
  1141. }
  1142. // ok, we found an account corresponding to their certificate
  1143. clientAccount, err := am.LoadAccount(account)
  1144. if err != nil {
  1145. return err
  1146. } else if !clientAccount.Verified {
  1147. return errAccountUnverified
  1148. }
  1149. if client.registered {
  1150. if clientAlready := am.server.clients.Get(clientAccount.Name); clientAlready != nil && clientAlready.AlwaysOn() {
  1151. return errNickAccountMismatch
  1152. }
  1153. }
  1154. am.Login(client, clientAccount)
  1155. return nil
  1156. }
  1157. type settingsMunger func(input AccountSettings) (output AccountSettings, err error)
  1158. func (am *AccountManager) ModifyAccountSettings(account string, munger settingsMunger) (newSettings AccountSettings, err error) {
  1159. casefoldedAccount, err := CasefoldName(account)
  1160. if err != nil {
  1161. return newSettings, errAccountDoesNotExist
  1162. }
  1163. // TODO implement this in general via a compare-and-swap API
  1164. accountData, err := am.LoadAccount(casefoldedAccount)
  1165. if err != nil {
  1166. return
  1167. } else if !accountData.Verified {
  1168. return newSettings, errAccountUnverified
  1169. }
  1170. newSettings, err = munger(accountData.Settings)
  1171. if err != nil {
  1172. return
  1173. }
  1174. text, err := json.Marshal(newSettings)
  1175. if err != nil {
  1176. return
  1177. }
  1178. key := fmt.Sprintf(keyAccountSettings, casefoldedAccount)
  1179. serializedValue := string(text)
  1180. err = am.server.store.Update(func(tx *buntdb.Tx) (err error) {
  1181. _, _, err = tx.Set(key, serializedValue, nil)
  1182. return
  1183. })
  1184. if err != nil {
  1185. err = errAccountUpdateFailed
  1186. return
  1187. }
  1188. // success, push new settings into the client objects
  1189. am.Lock()
  1190. defer am.Unlock()
  1191. for _, client := range am.accountToClients[casefoldedAccount] {
  1192. client.SetAccountSettings(newSettings)
  1193. }
  1194. return
  1195. }
  1196. // represents someone's status in hostserv
  1197. type VHostInfo struct {
  1198. ApprovedVHost string
  1199. Enabled bool
  1200. Forbidden bool
  1201. RequestedVHost string
  1202. RejectedVHost string
  1203. RejectionReason string
  1204. LastRequestTime time.Time
  1205. }
  1206. // pair type, <VHostInfo, accountName>
  1207. type PendingVHostRequest struct {
  1208. VHostInfo
  1209. Account string
  1210. }
  1211. type vhostThrottleExceeded struct {
  1212. timeRemaining time.Duration
  1213. }
  1214. func (vhe *vhostThrottleExceeded) Error() string {
  1215. return fmt.Sprintf("Wait at least %v and try again", vhe.timeRemaining)
  1216. }
  1217. func (vh *VHostInfo) checkThrottle(cooldown time.Duration) (err error) {
  1218. if cooldown == 0 {
  1219. return nil
  1220. }
  1221. now := time.Now().UTC()
  1222. elapsed := now.Sub(vh.LastRequestTime)
  1223. if elapsed > cooldown {
  1224. // success
  1225. vh.LastRequestTime = now
  1226. return nil
  1227. } else {
  1228. return &vhostThrottleExceeded{timeRemaining: cooldown - elapsed}
  1229. }
  1230. }
  1231. // callback type implementing the actual business logic of vhost operations
  1232. type vhostMunger func(input VHostInfo) (output VHostInfo, err error)
  1233. func (am *AccountManager) VHostSet(account string, vhost string) (result VHostInfo, err error) {
  1234. munger := func(input VHostInfo) (output VHostInfo, err error) {
  1235. output = input
  1236. output.Enabled = true
  1237. output.ApprovedVHost = vhost
  1238. return
  1239. }
  1240. return am.performVHostChange(account, munger)
  1241. }
  1242. func (am *AccountManager) VHostRequest(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) {
  1243. munger := func(input VHostInfo) (output VHostInfo, err error) {
  1244. output = input
  1245. if input.Forbidden {
  1246. err = errVhostsForbidden
  1247. return
  1248. }
  1249. // you can update your existing request, but if you were approved or rejected,
  1250. // you can't spam a new request
  1251. if output.RequestedVHost == "" {
  1252. err = output.checkThrottle(cooldown)
  1253. }
  1254. if err != nil {
  1255. return
  1256. }
  1257. output.RequestedVHost = vhost
  1258. output.RejectedVHost = ""
  1259. output.RejectionReason = ""
  1260. output.LastRequestTime = time.Now().UTC()
  1261. return
  1262. }
  1263. return am.performVHostChange(account, munger)
  1264. }
  1265. func (am *AccountManager) VHostTake(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) {
  1266. munger := func(input VHostInfo) (output VHostInfo, err error) {
  1267. output = input
  1268. if input.Forbidden {
  1269. err = errVhostsForbidden
  1270. return
  1271. }
  1272. // if you have a request pending, you can cancel it using take;
  1273. // otherwise, you're subject to the same throttling as if you were making a request
  1274. if output.RequestedVHost == "" {
  1275. err = output.checkThrottle(cooldown)
  1276. }
  1277. if err != nil {
  1278. return
  1279. }
  1280. output.ApprovedVHost = vhost
  1281. output.RequestedVHost = ""
  1282. output.RejectedVHost = ""
  1283. output.RejectionReason = ""
  1284. output.LastRequestTime = time.Now().UTC()
  1285. return
  1286. }
  1287. return am.performVHostChange(account, munger)
  1288. }
  1289. func (am *AccountManager) VHostApprove(account string) (result VHostInfo, err error) {
  1290. munger := func(input VHostInfo) (output VHostInfo, err error) {
  1291. output = input
  1292. output.Enabled = true
  1293. output.ApprovedVHost = input.RequestedVHost
  1294. output.RequestedVHost = ""
  1295. output.RejectionReason = ""
  1296. return
  1297. }
  1298. return am.performVHostChange(account, munger)
  1299. }
  1300. func (am *AccountManager) VHostReject(account string, reason string) (result VHostInfo, err error) {
  1301. munger := func(input VHostInfo) (output VHostInfo, err error) {
  1302. output = input
  1303. output.RejectedVHost = output.RequestedVHost
  1304. output.RequestedVHost = ""
  1305. output.RejectionReason = reason
  1306. return
  1307. }
  1308. return am.performVHostChange(account, munger)
  1309. }
  1310. func (am *AccountManager) VHostSetEnabled(client *Client, enabled bool) (result VHostInfo, err error) {
  1311. munger := func(input VHostInfo) (output VHostInfo, err error) {
  1312. if input.ApprovedVHost == "" {
  1313. err = errNoVhost
  1314. return
  1315. }
  1316. output = input
  1317. output.Enabled = enabled
  1318. return
  1319. }
  1320. return am.performVHostChange(client.Account(), munger)
  1321. }
  1322. func (am *AccountManager) VHostForbid(account string, forbid bool) (result VHostInfo, err error) {
  1323. munger := func(input VHostInfo) (output VHostInfo, err error) {
  1324. output = input
  1325. output.Forbidden = forbid
  1326. return
  1327. }
  1328. return am.performVHostChange(account, munger)
  1329. }
  1330. func (am *AccountManager) performVHostChange(account string, munger vhostMunger) (result VHostInfo, err error) {
  1331. account, err = CasefoldName(account)
  1332. if err != nil || account == "" {
  1333. err = errAccountDoesNotExist
  1334. return
  1335. }
  1336. am.vHostUpdateMutex.Lock()
  1337. defer am.vHostUpdateMutex.Unlock()
  1338. clientAccount, err := am.LoadAccount(account)
  1339. if err != nil {
  1340. err = errAccountDoesNotExist
  1341. return
  1342. } else if !clientAccount.Verified {
  1343. err = errAccountUnverified
  1344. return
  1345. }
  1346. result, err = munger(clientAccount.VHost)
  1347. if err != nil {
  1348. return
  1349. }
  1350. vhtext, err := json.Marshal(result)
  1351. if err != nil {
  1352. err = errAccountUpdateFailed
  1353. return
  1354. }
  1355. vhstr := string(vhtext)
  1356. key := fmt.Sprintf(keyAccountVHost, account)
  1357. queueKey := fmt.Sprintf(keyVHostQueueAcctToId, account)
  1358. err = am.server.store.Update(func(tx *buntdb.Tx) error {
  1359. if _, _, err := tx.Set(key, vhstr, nil); err != nil {
  1360. return err
  1361. }
  1362. // update request queue
  1363. if clientAccount.VHost.RequestedVHost == "" && result.RequestedVHost != "" {
  1364. id := atomic.AddUint64(&am.vhostRequestID, 1)
  1365. if _, _, err = tx.Set(queueKey, strconv.FormatUint(id, 10), nil); err != nil {
  1366. return err
  1367. }
  1368. atomic.AddUint64(&am.vhostRequestPendingCount, 1)
  1369. } else if clientAccount.VHost.RequestedVHost != "" && result.RequestedVHost == "" {
  1370. _, err = tx.Delete(queueKey)
  1371. am.decrementVHostQueueCount(account, err)
  1372. }
  1373. return nil
  1374. })
  1375. if err != nil {
  1376. err = errAccountUpdateFailed
  1377. return
  1378. }
  1379. am.applyVhostToClients(account, result)
  1380. return result, nil
  1381. }
  1382. // XXX annoying helper method for keeping the queue count in sync with the DB
  1383. // `err` is the buntdb error returned from deleting the queue key
  1384. func (am *AccountManager) decrementVHostQueueCount(account string, err error) {
  1385. if err == nil {
  1386. // successfully deleted a queue entry, do a 2's complement decrement:
  1387. atomic.AddUint64(&am.vhostRequestPendingCount, ^uint64(0))
  1388. } else if err != buntdb.ErrNotFound {
  1389. am.server.logger.Error("internal", "buntdb dequeue error", account, err.Error())
  1390. }
  1391. }
  1392. func (am *AccountManager) VHostListRequests(limit int) (requests []PendingVHostRequest, total int) {
  1393. am.vHostUpdateMutex.Lock()
  1394. defer am.vHostUpdateMutex.Unlock()
  1395. total = int(atomic.LoadUint64(&am.vhostRequestPendingCount))
  1396. prefix := fmt.Sprintf(keyVHostQueueAcctToId, "")
  1397. accounts := make([]string, 0, limit)
  1398. err := am.server.store.View(func(tx *buntdb.Tx) error {
  1399. return tx.Ascend(vhostRequestIdx, func(key, value string) bool {
  1400. accounts = append(accounts, strings.TrimPrefix(key, prefix))
  1401. return len(accounts) < limit
  1402. })
  1403. })
  1404. if err != nil {
  1405. am.server.logger.Error("internal", "couldn't traverse vhost queue", err.Error())
  1406. return
  1407. }
  1408. for _, account := range accounts {
  1409. accountInfo, err := am.LoadAccount(account)
  1410. if err == nil {
  1411. requests = append(requests, PendingVHostRequest{
  1412. Account: account,
  1413. VHostInfo: accountInfo.VHost,
  1414. })
  1415. } else {
  1416. am.server.logger.Error("internal", "corrupt account", account, err.Error())
  1417. }
  1418. }
  1419. return
  1420. }
  1421. func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) {
  1422. // if hostserv is disabled in config, then don't grant vhosts
  1423. // that were previously approved while it was enabled
  1424. if !am.server.Config().Accounts.VHosts.Enabled {
  1425. return
  1426. }
  1427. vhost := ""
  1428. if info.Enabled && !info.Forbidden {
  1429. vhost = info.ApprovedVHost
  1430. }
  1431. oldNickmask := client.NickMaskString()
  1432. updated := client.SetVHost(vhost)
  1433. if updated {
  1434. // TODO: doing I/O here is kind of a kludge
  1435. go client.sendChghost(oldNickmask, client.Hostname())
  1436. }
  1437. }
  1438. func (am *AccountManager) applyVhostToClients(account string, result VHostInfo) {
  1439. am.RLock()
  1440. clients := am.accountToClients[account]
  1441. am.RUnlock()
  1442. for _, client := range clients {
  1443. am.applyVHostInfo(client, result)
  1444. }
  1445. }
  1446. func (am *AccountManager) Login(client *Client, account ClientAccount) {
  1447. client.Login(account)
  1448. client.nickTimer.Touch(nil)
  1449. am.applyVHostInfo(client, account.VHost)
  1450. casefoldedAccount := client.Account()
  1451. am.Lock()
  1452. defer am.Unlock()
  1453. am.accountToClients[casefoldedAccount] = append(am.accountToClients[casefoldedAccount], client)
  1454. }
  1455. func (am *AccountManager) Logout(client *Client) {
  1456. am.Lock()
  1457. defer am.Unlock()
  1458. casefoldedAccount := client.Account()
  1459. if casefoldedAccount == "" {
  1460. return
  1461. }
  1462. am.logoutOfAccount(client)
  1463. clients := am.accountToClients[casefoldedAccount]
  1464. if len(clients) <= 1 {
  1465. delete(am.accountToClients, casefoldedAccount)
  1466. return
  1467. }
  1468. remainingClients := make([]*Client, len(clients)-1)
  1469. remainingPos := 0
  1470. for currentPos := 0; currentPos < len(clients); currentPos++ {
  1471. if clients[currentPos] != client {
  1472. remainingClients[remainingPos] = clients[currentPos]
  1473. remainingPos++
  1474. }
  1475. }
  1476. am.accountToClients[casefoldedAccount] = remainingClients
  1477. }
  1478. var (
  1479. // EnabledSaslMechanisms contains the SASL mechanisms that exist and that we support.
  1480. // This can be moved to some other data structure/place if we need to load/unload mechs later.
  1481. EnabledSaslMechanisms = map[string]func(*Server, *Client, string, []byte, *ResponseBuffer) bool{
  1482. "PLAIN": authPlainHandler,
  1483. "EXTERNAL": authExternalHandler,
  1484. }
  1485. )
  1486. // AccountCredentials stores the various methods for verifying accounts.
  1487. type AccountCredentials struct {
  1488. Version uint
  1489. PassphraseSalt []byte // legacy field, not used by v1 and later
  1490. PassphraseHash []byte
  1491. Certfps []string
  1492. }
  1493. func (ac *AccountCredentials) Empty() bool {
  1494. return len(ac.PassphraseHash) == 0 && len(ac.Certfps) == 0
  1495. }
  1496. // helper to assemble the serialized JSON for an account's credentials
  1497. func (ac *AccountCredentials) Serialize() (result string, err error) {
  1498. ac.Version = 1
  1499. credText, err := json.Marshal(*ac)
  1500. if err != nil {
  1501. return "", err
  1502. }
  1503. return string(credText), nil
  1504. }
  1505. func (ac *AccountCredentials) SetPassphrase(passphrase string, bcryptCost uint) (err error) {
  1506. if passphrase == "" {
  1507. ac.PassphraseHash = nil
  1508. return nil
  1509. }
  1510. if validatePassphrase(passphrase) != nil {
  1511. return errAccountBadPassphrase
  1512. }
  1513. ac.PassphraseHash, err = passwd.GenerateFromPassword([]byte(passphrase), int(bcryptCost))
  1514. if err != nil {
  1515. return errAccountBadPassphrase
  1516. }
  1517. return nil
  1518. }
  1519. func (ac *AccountCredentials) AddCertfp(certfp string) (err error) {
  1520. // XXX we require that certfp is already normalized (rather than normalize here
  1521. // and pass back the normalized version as an additional return parameter);
  1522. // this is just a final sanity check:
  1523. if len(certfp) != 64 {
  1524. return utils.ErrInvalidCertfp
  1525. }
  1526. for _, current := range ac.Certfps {
  1527. if certfp == current {
  1528. return errNoop
  1529. }
  1530. }
  1531. if maxCertfpsPerAccount <= len(ac.Certfps) {
  1532. return errLimitExceeded
  1533. }
  1534. ac.Certfps = append(ac.Certfps, certfp)
  1535. return nil
  1536. }
  1537. func (ac *AccountCredentials) RemoveCertfp(certfp string) (err error) {
  1538. found := false
  1539. newList := make([]string, 0, len(ac.Certfps))
  1540. for _, current := range ac.Certfps {
  1541. if current == certfp {
  1542. found = true
  1543. } else {
  1544. newList = append(newList, current)
  1545. }
  1546. }
  1547. if !found {
  1548. // this is important because it prevents you from deleting someone else's
  1549. // fingerprint record
  1550. return errNoop
  1551. }
  1552. ac.Certfps = newList
  1553. return nil
  1554. }
  1555. type MulticlientAllowedSetting int
  1556. const (
  1557. MulticlientAllowedServerDefault MulticlientAllowedSetting = iota
  1558. MulticlientDisallowedByUser
  1559. MulticlientAllowedByUser
  1560. )
  1561. // controls whether/when clients without event-playback support see fake
  1562. // PRIVMSGs for JOINs
  1563. type ReplayJoinsSetting uint
  1564. const (
  1565. ReplayJoinsCommandsOnly = iota // replay in HISTORY or CHATHISTORY output
  1566. ReplayJoinsAlways // replay in HISTORY, CHATHISTORY, or autoreplay
  1567. ReplayJoinsNever // never replay
  1568. )
  1569. func replayJoinsSettingFromString(str string) (result ReplayJoinsSetting, err error) {
  1570. switch strings.ToLower(str) {
  1571. case "commands-only":
  1572. result = ReplayJoinsCommandsOnly
  1573. case "always":
  1574. result = ReplayJoinsAlways
  1575. case "never":
  1576. result = ReplayJoinsNever
  1577. default:
  1578. err = errInvalidParams
  1579. }
  1580. return
  1581. }
  1582. // XXX: AllowBouncer cannot be renamed AllowMulticlient because it is stored in
  1583. // persistent JSON blobs in the database
  1584. type AccountSettings struct {
  1585. AutoreplayLines *int
  1586. NickEnforcement NickEnforcementMethod
  1587. AllowBouncer MulticlientAllowedSetting
  1588. ReplayJoins ReplayJoinsSetting
  1589. AlwaysOn PersistentStatus
  1590. AutoreplayMissed bool
  1591. DMHistory HistoryStatus
  1592. }
  1593. // ClientAccount represents a user account.
  1594. type ClientAccount struct {
  1595. // Name of the account.
  1596. Name string
  1597. NameCasefolded string
  1598. RegisteredAt time.Time
  1599. Credentials AccountCredentials
  1600. Verified bool
  1601. AdditionalNicks []string
  1602. VHost VHostInfo
  1603. Settings AccountSettings
  1604. }
  1605. // convenience for passing around raw serialized account data
  1606. type rawClientAccount struct {
  1607. Name string
  1608. RegisteredAt string
  1609. Credentials string
  1610. Callback string
  1611. Verified bool
  1612. AdditionalNicks string
  1613. VHost string
  1614. Settings string
  1615. }
  1616. // logoutOfAccount logs the client out of their current account.
  1617. func (am *AccountManager) logoutOfAccount(client *Client) {
  1618. if client.Account() == "" {
  1619. // already logged out
  1620. return
  1621. }
  1622. client.Logout()
  1623. go client.nickTimer.Touch(nil)
  1624. // dispatch account-notify
  1625. // TODO: doing the I/O here is kind of a kludge, let's move this somewhere else
  1626. go func() {
  1627. for friend := range client.Friends(caps.AccountNotify) {
  1628. friend.Send(nil, client.NickMaskString(), "ACCOUNT", "*")
  1629. }
  1630. }()
  1631. }