Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

nickserv.go 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "github.com/goshuirc/irc-go/ircfmt"
  10. "github.com/oragono/oragono/irc/modes"
  11. "github.com/oragono/oragono/irc/passwd"
  12. "github.com/oragono/oragono/irc/sno"
  13. "github.com/oragono/oragono/irc/utils"
  14. )
  15. // "enabled" callbacks for specific nickserv commands
  16. func servCmdRequiresAccreg(config *Config) bool {
  17. return config.Accounts.Registration.Enabled
  18. }
  19. func servCmdRequiresAuthEnabled(config *Config) bool {
  20. return config.Accounts.AuthenticationEnabled
  21. }
  22. func servCmdRequiresNickRes(config *Config) bool {
  23. return config.Accounts.AuthenticationEnabled && config.Accounts.NickReservation.Enabled
  24. }
  25. func servCmdRequiresBouncerEnabled(config *Config) bool {
  26. return config.Accounts.Bouncer.Enabled
  27. }
  28. const (
  29. nsPrefix = "NickServ!NickServ@localhost"
  30. // ZNC's nickserv module will not detect this unless it is:
  31. // 1. sent with prefix `nickserv`
  32. // 2. contains the string "identify"
  33. // 3. contains at least one of several other magic strings ("msg" works)
  34. nsTimeoutNotice = `This nickname is reserved. Please login within %v (using $b/msg NickServ IDENTIFY <password>$b or SASL), or switch to a different nickname.`
  35. )
  36. const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
  37. var (
  38. nickservCommands = map[string]*serviceCommand{
  39. "drop": {
  40. handler: nsDropHandler,
  41. help: `Syntax: $bDROP [nickname]$b
  42. DROP de-links the given (or your current) nickname from your user account.`,
  43. helpShort: `$bDROP$b de-links your current (or the given) nickname from your user account.`,
  44. enabled: servCmdRequiresNickRes,
  45. authRequired: true,
  46. },
  47. "enforce": {
  48. hidden: true,
  49. handler: nsEnforceHandler,
  50. help: `Syntax: $bENFORCE [method]$b
  51. ENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help
  52. entry for $bSET$b for more information.`,
  53. authRequired: true,
  54. enabled: servCmdRequiresAccreg,
  55. },
  56. "ghost": {
  57. handler: nsGhostHandler,
  58. help: `Syntax: $bGHOST <nickname>$b
  59. GHOST disconnects the given user from the network if they're logged in with the
  60. same user account, letting you reclaim your nickname.`,
  61. helpShort: `$bGHOST$b reclaims your nickname.`,
  62. enabled: servCmdRequiresNickRes,
  63. authRequired: true,
  64. minParams: 1,
  65. },
  66. "group": {
  67. handler: nsGroupHandler,
  68. help: `Syntax: $bGROUP$b
  69. GROUP links your current nickname with your logged-in account, so other people
  70. will not be able to use it.`,
  71. helpShort: `$bGROUP$b links your current nickname to your user account.`,
  72. enabled: servCmdRequiresNickRes,
  73. authRequired: true,
  74. },
  75. "identify": {
  76. handler: nsIdentifyHandler,
  77. help: `Syntax: $bIDENTIFY <username> [password]$b
  78. IDENTIFY lets you login to the given username using either password auth, or
  79. certfp (your client certificate) if a password is not given.`,
  80. helpShort: `$bIDENTIFY$b lets you login to your account.`,
  81. enabled: servCmdRequiresAuthEnabled,
  82. minParams: 1,
  83. },
  84. "info": {
  85. handler: nsInfoHandler,
  86. help: `Syntax: $bINFO [username]$b
  87. INFO gives you information about the given (or your own) user account.`,
  88. helpShort: `$bINFO$b gives you information on a user account.`,
  89. },
  90. "register": {
  91. handler: nsRegisterHandler,
  92. // TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
  93. // person@example.com, mailto:person@example.com, tel:16505551234.
  94. help: `Syntax: $bREGISTER <password> [email]$b
  95. REGISTER lets you register your current nickname as a user account. If the
  96. server allows anonymous registration, you can omit the e-mail address.
  97. If you are currently logged in with a TLS client certificate and wish to use
  98. it instead of a password to log in, send * as the password.`,
  99. helpShort: `$bREGISTER$b lets you register a user account.`,
  100. enabled: servCmdRequiresAccreg,
  101. minParams: 1,
  102. maxParams: 2,
  103. },
  104. "sadrop": {
  105. handler: nsDropHandler,
  106. help: `Syntax: $bSADROP <nickname>$b
  107. SADROP forcibly de-links the given nickname from the attached user account.`,
  108. helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
  109. capabs: []string{"accreg"},
  110. enabled: servCmdRequiresNickRes,
  111. minParams: 1,
  112. },
  113. "saregister": {
  114. handler: nsSaregisterHandler,
  115. help: `Syntax: $bSAREGISTER <username> [password]$b
  116. SAREGISTER registers an account on someone else's behalf.
  117. This is for use in configurations that require SASL for all connections;
  118. an administrator can set use this command to set up user accounts.`,
  119. helpShort: `$bSAREGISTER$b registers an account on someone else's behalf.`,
  120. enabled: servCmdRequiresAuthEnabled,
  121. capabs: []string{"accreg"},
  122. minParams: 1,
  123. },
  124. "sessions": {
  125. handler: nsSessionsHandler,
  126. help: `Syntax: $bSESSIONS [nickname]$b
  127. SESSIONS lists information about the sessions currently attached, via
  128. the server's bouncer functionality, to your nickname. An administrator
  129. can use this command to list another user's sessions.`,
  130. helpShort: `$bSESSIONS$b lists the sessions attached to a nickname.`,
  131. enabled: servCmdRequiresBouncerEnabled,
  132. },
  133. "unregister": {
  134. handler: nsUnregisterHandler,
  135. help: `Syntax: $bUNREGISTER <username> [code]$b
  136. UNREGISTER lets you delete your user account (or someone else's, if you're an
  137. IRC operator with the correct permissions). To prevent accidental
  138. unregistrations, a verification code is required; invoking the command without
  139. a code will display the necessary code.`,
  140. helpShort: `$bUNREGISTER$b lets you delete your user account.`,
  141. enabled: servCmdRequiresAuthEnabled,
  142. minParams: 1,
  143. },
  144. "verify": {
  145. handler: nsVerifyHandler,
  146. help: `Syntax: $bVERIFY <username> <code>$b
  147. VERIFY lets you complete an account registration, if the server requires email
  148. or other verification.`,
  149. helpShort: `$bVERIFY$b lets you complete account registration.`,
  150. enabled: servCmdRequiresAccreg,
  151. minParams: 2,
  152. },
  153. "passwd": {
  154. handler: nsPasswdHandler,
  155. help: `Syntax: $bPASSWD <current> <new> <new_again>$b
  156. Or: $bPASSWD <username> <new>$b
  157. PASSWD lets you change your account password. You must supply your current
  158. password and confirm the new one by typing it twice. If you're an IRC operator
  159. with the correct permissions, you can use PASSWD to reset someone else's
  160. password by supplying their username and then the desired password. To
  161. indicate an empty password, use * instead.`,
  162. helpShort: `$bPASSWD$b lets you change your password.`,
  163. enabled: servCmdRequiresAuthEnabled,
  164. minParams: 2,
  165. },
  166. "get": {
  167. handler: nsGetHandler,
  168. help: `Syntax: $bGET <setting>$b
  169. GET queries the current values of your account settings. For more information
  170. on the settings and their possible values, see HELP SET.`,
  171. helpShort: `$bGET$b queries the current values of your account settings`,
  172. authRequired: true,
  173. enabled: servCmdRequiresAccreg,
  174. minParams: 1,
  175. },
  176. "saget": {
  177. handler: nsGetHandler,
  178. help: `Syntax: $bSAGET <account> <setting>$b
  179. SAGET queries the values of someone else's account settings. For more
  180. information on the settings and their possible values, see HELP SET.`,
  181. helpShort: `$bSAGET$b queries the current values of another user's account settings`,
  182. enabled: servCmdRequiresAccreg,
  183. minParams: 2,
  184. capabs: []string{"accreg"},
  185. },
  186. "set": {
  187. handler: nsSetHandler,
  188. helpShort: `$bSET$b modifies your account settings`,
  189. // these are broken out as separate strings so they can be translated separately
  190. helpStrings: []string{
  191. `Syntax $bSET <setting> <value>$b
  192. Set modifies your account settings. The following settings are available:`,
  193. `$bENFORCE$b
  194. 'enforce' lets you specify a custom enforcement mechanism for your registered
  195. nicknames. Your options are:
  196. 1. 'none' [no enforcement, overriding the server default]
  197. 2. 'timeout' [anyone using the nick must authenticate before a deadline,
  198. or else they will be renamed]
  199. 3. 'strict' [you must already be authenticated to use the nick]
  200. 4. 'default' [use the server default]`,
  201. `$bBOUNCER$b
  202. If 'bouncer' is enabled and you are already logged in and using a nick, a
  203. second client of yours that authenticates with SASL and requests the same nick
  204. is allowed to attach to the nick as well (this is comparable to the behavior
  205. of IRC "bouncers" like ZNC). Your options are 'on' (allow this behavior),
  206. 'off' (disallow it), and 'default' (use the server default value).`,
  207. `$bAUTOREPLAY-LINES$b
  208. 'autoreplay-lines' controls the number of lines of channel history that will
  209. be replayed to you automatically when joining a channel. Your options are any
  210. positive number, 0 to disable the feature, and 'default' to use the server
  211. default.`,
  212. `$bREPLAY-JOINS$b
  213. 'replay-joins' controls whether replayed channel history will include
  214. lines for join and part. This provides more information about the context of
  215. messages, but may be spammy. Your options are 'always', 'never', and the default
  216. of 'commands-only' (the messages will be replayed in /HISTORY output, but not
  217. during autoreplay).`,
  218. },
  219. authRequired: true,
  220. enabled: servCmdRequiresAccreg,
  221. minParams: 2,
  222. },
  223. "saset": {
  224. handler: nsSetHandler,
  225. help: `Syntax: $bSASET <account> <setting> <value>$b
  226. SASET modifies the values of someone else's account settings. For more
  227. information on the settings and their possible values, see HELP SET.`,
  228. helpShort: `$bSASET$b modifies another user's account settings`,
  229. enabled: servCmdRequiresAccreg,
  230. minParams: 3,
  231. capabs: []string{"accreg"},
  232. },
  233. "cert": {
  234. handler: nsCertHandler,
  235. help: `Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b
  236. CERT examines or modifies the TLS certificate fingerprints that can be used to
  237. log into an account. Specifically, $bCERT LIST$b lists the authorized
  238. fingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and
  239. $bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator
  240. with the correct permissions, you can act on another user's account, for
  241. example with $bCERT ADD <account> <fingerprint>$b.`,
  242. helpShort: `$bCERT$b controls a user account's certificate fingerprints`,
  243. enabled: servCmdRequiresAuthEnabled,
  244. minParams: 1,
  245. },
  246. }
  247. )
  248. // nsNotice sends the client a notice from NickServ
  249. func nsNotice(rb *ResponseBuffer, text string) {
  250. // XXX i can't figure out how to use OragonoServices[servicename].prefix here
  251. // without creating a compile-time initialization loop
  252. rb.Add(nil, nsPrefix, "NOTICE", rb.target.Nick(), text)
  253. }
  254. func nsGetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  255. var account string
  256. if command == "saget" {
  257. account = params[0]
  258. params = params[1:]
  259. } else {
  260. account = client.Account()
  261. }
  262. accountData, err := server.accounts.LoadAccount(account)
  263. if err == errAccountDoesNotExist {
  264. nsNotice(rb, client.t("No such account"))
  265. return
  266. } else if err != nil {
  267. nsNotice(rb, client.t("Error loading account data"))
  268. return
  269. }
  270. displaySetting(params[0], accountData.Settings, client, rb)
  271. }
  272. func displaySetting(settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) {
  273. config := client.server.Config()
  274. switch strings.ToLower(settingName) {
  275. case "enforce":
  276. storedValue := settings.NickEnforcement
  277. serializedStoredValue := nickReservationToString(storedValue)
  278. nsNotice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue))
  279. serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue))
  280. nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue))
  281. case "autoreplay-lines":
  282. if settings.AutoreplayLines == nil {
  283. nsNotice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin))
  284. } else {
  285. nsNotice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines))
  286. }
  287. case "replay-joins":
  288. switch settings.ReplayJoins {
  289. case ReplayJoinsCommandsOnly:
  290. nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output, but not in autoreplay"))
  291. case ReplayJoinsAlways:
  292. nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output and in autoreplay"))
  293. case ReplayJoinsNever:
  294. nsNotice(rb, client.t("You will not see JOINs and PARTs in /HISTORY output or in autoreplay"))
  295. }
  296. case "bouncer":
  297. if !config.Accounts.Bouncer.Enabled {
  298. nsNotice(rb, client.t("This feature has been disabled by the server administrators"))
  299. } else {
  300. switch settings.AllowBouncer {
  301. case BouncerAllowedServerDefault:
  302. if config.Accounts.Bouncer.AllowedByDefault {
  303. nsNotice(rb, client.t("Bouncer functionality is currently enabled for your account, but you can opt out"))
  304. } else {
  305. nsNotice(rb, client.t("Bouncer functionality is currently disabled for your account, but you can opt in"))
  306. }
  307. case BouncerDisallowedByUser:
  308. nsNotice(rb, client.t("Bouncer functionality is currently disabled for your account"))
  309. case BouncerAllowedByUser:
  310. nsNotice(rb, client.t("Bouncer functionality is currently enabled for your account"))
  311. }
  312. }
  313. default:
  314. nsNotice(rb, client.t("No such setting"))
  315. }
  316. }
  317. func nsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  318. var account string
  319. if command == "saset" {
  320. account = params[0]
  321. params = params[1:]
  322. } else {
  323. account = client.Account()
  324. }
  325. var munger settingsMunger
  326. var finalSettings AccountSettings
  327. var err error
  328. switch strings.ToLower(params[0]) {
  329. case "pass":
  330. nsNotice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
  331. return
  332. case "enforce":
  333. var method NickEnforcementMethod
  334. method, err = nickReservationFromString(params[1])
  335. if err != nil {
  336. err = errInvalidParams
  337. break
  338. }
  339. // updating enforcement settings is special-cased, because it requires
  340. // an update to server.accounts.accountToMethod
  341. finalSettings, err = server.accounts.SetEnforcementStatus(account, method)
  342. if err == nil {
  343. finalSettings.NickEnforcement = method // success
  344. }
  345. case "autoreplay-lines":
  346. var newValue *int
  347. if strings.ToLower(params[1]) != "default" {
  348. val, err_ := strconv.Atoi(params[1])
  349. if err_ != nil || val < 0 {
  350. err = errInvalidParams
  351. break
  352. }
  353. newValue = new(int)
  354. *newValue = val
  355. }
  356. munger = func(in AccountSettings) (out AccountSettings, err error) {
  357. out = in
  358. out.AutoreplayLines = newValue
  359. return
  360. }
  361. case "bouncer":
  362. var newValue BouncerAllowedSetting
  363. if strings.ToLower(params[1]) == "default" {
  364. newValue = BouncerAllowedServerDefault
  365. } else {
  366. var enabled bool
  367. enabled, err = utils.StringToBool(params[1])
  368. if enabled {
  369. newValue = BouncerAllowedByUser
  370. } else {
  371. newValue = BouncerDisallowedByUser
  372. }
  373. }
  374. if err == nil {
  375. munger = func(in AccountSettings) (out AccountSettings, err error) {
  376. out = in
  377. out.AllowBouncer = newValue
  378. return
  379. }
  380. }
  381. case "replay-joins":
  382. var newValue ReplayJoinsSetting
  383. newValue, err = replayJoinsSettingFromString(params[1])
  384. if err == nil {
  385. munger = func(in AccountSettings) (out AccountSettings, err error) {
  386. out = in
  387. out.ReplayJoins = newValue
  388. return
  389. }
  390. }
  391. default:
  392. err = errInvalidParams
  393. }
  394. if munger != nil {
  395. finalSettings, err = server.accounts.ModifyAccountSettings(account, munger)
  396. }
  397. switch err {
  398. case nil:
  399. nsNotice(rb, client.t("Successfully changed your account settings"))
  400. displaySetting(params[0], finalSettings, client, rb)
  401. case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
  402. nsNotice(rb, client.t(err.Error()))
  403. default:
  404. // unknown error
  405. nsNotice(rb, client.t("An error occurred"))
  406. }
  407. }
  408. func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  409. sadrop := command == "sadrop"
  410. var nick string
  411. if len(params) > 0 {
  412. nick = params[0]
  413. } else {
  414. nick = client.NickCasefolded()
  415. }
  416. err := server.accounts.SetNickReserved(client, nick, sadrop, false)
  417. if err == nil {
  418. nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
  419. } else if err == errAccountNotLoggedIn {
  420. nsNotice(rb, client.t("You're not logged into an account"))
  421. } else if err == errAccountCantDropPrimaryNick {
  422. nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
  423. } else {
  424. nsNotice(rb, client.t("Could not ungroup nick"))
  425. }
  426. }
  427. func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  428. nick := params[0]
  429. ghost := server.clients.Get(nick)
  430. if ghost == nil {
  431. nsNotice(rb, client.t("No such nick"))
  432. return
  433. } else if ghost == client {
  434. nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
  435. return
  436. }
  437. authorized := false
  438. account := client.Account()
  439. if account != "" {
  440. // the user must either own the nick, or the target client
  441. authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
  442. }
  443. if !authorized {
  444. nsNotice(rb, client.t("You don't own that nick"))
  445. return
  446. }
  447. ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()), nil)
  448. ghost.destroy(nil)
  449. }
  450. func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  451. nick := client.Nick()
  452. err := server.accounts.SetNickReserved(client, nick, false, true)
  453. if err == nil {
  454. nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
  455. } else if err == errAccountTooManyNicks {
  456. nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
  457. } else if err == errNicknameReserved {
  458. nsNotice(rb, client.t("That nickname is already reserved by someone else"))
  459. } else {
  460. nsNotice(rb, client.t("Error reserving nickname"))
  461. }
  462. }
  463. func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
  464. throttled, remainingTime := client.loginThrottle.Touch()
  465. if throttled {
  466. nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
  467. return false
  468. }
  469. return true
  470. }
  471. func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  472. if client.LoggedIntoAccount() {
  473. nsNotice(rb, client.t("You're already logged into an account"))
  474. return
  475. }
  476. loginSuccessful := false
  477. var username, passphrase string
  478. if len(params) == 1 {
  479. if client.certfp != "" {
  480. username = params[0]
  481. } else {
  482. // XXX undocumented compatibility mode with other nickservs, allowing
  483. // /msg NickServ identify passphrase
  484. username = client.NickCasefolded()
  485. passphrase = params[0]
  486. }
  487. } else {
  488. username = params[0]
  489. passphrase = params[1]
  490. }
  491. // try passphrase
  492. if passphrase != "" {
  493. if !nsLoginThrottleCheck(client, rb) {
  494. return
  495. }
  496. err := server.accounts.AuthenticateByPassphrase(client, username, passphrase)
  497. loginSuccessful = (err == nil)
  498. }
  499. // try certfp
  500. if !loginSuccessful && client.certfp != "" {
  501. err := server.accounts.AuthenticateByCertFP(client, "")
  502. loginSuccessful = (err == nil)
  503. }
  504. if loginSuccessful {
  505. sendSuccessfulAccountAuth(client, rb, true, true)
  506. } else {
  507. nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
  508. }
  509. }
  510. func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  511. if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") {
  512. nsNotice(rb, client.t("This command has been disabled by the server administrators"))
  513. return
  514. }
  515. var accountName string
  516. if len(params) > 0 {
  517. nick := params[0]
  518. if server.AccountConfig().NickReservation.Enabled {
  519. accountName = server.accounts.NickToAccount(nick)
  520. if accountName == "" {
  521. nsNotice(rb, client.t("That nickname is not registered"))
  522. return
  523. }
  524. } else {
  525. accountName = nick
  526. }
  527. } else {
  528. accountName = client.Account()
  529. if accountName == "" {
  530. nsNotice(rb, client.t("You're not logged into an account"))
  531. return
  532. }
  533. }
  534. account, err := server.accounts.LoadAccount(accountName)
  535. if err != nil || !account.Verified {
  536. nsNotice(rb, client.t("Account does not exist"))
  537. return
  538. }
  539. nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
  540. registeredAt := account.RegisteredAt.Format(time.RFC1123)
  541. nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
  542. // TODO nicer formatting for this
  543. for _, nick := range account.AdditionalNicks {
  544. nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
  545. }
  546. for _, channel := range server.accounts.ChannelsForAccount(accountName) {
  547. nsNotice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
  548. }
  549. }
  550. func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  551. details := client.Details()
  552. account := details.nick
  553. passphrase := params[0]
  554. var email string
  555. if 1 < len(params) {
  556. email = params[1]
  557. }
  558. certfp := client.certfp
  559. if passphrase == "*" {
  560. if certfp == "" {
  561. nsNotice(rb, client.t("You must be connected with TLS and a client certificate to do this"))
  562. return
  563. } else {
  564. passphrase = ""
  565. }
  566. }
  567. if details.account != "" {
  568. nsNotice(rb, client.t("You're already logged into an account"))
  569. return
  570. }
  571. if !nsLoginThrottleCheck(client, rb) {
  572. return
  573. }
  574. config := server.AccountConfig()
  575. var callbackNamespace, callbackValue string
  576. noneCallbackAllowed := false
  577. for _, callback := range config.Registration.EnabledCallbacks {
  578. if callback == "*" {
  579. noneCallbackAllowed = true
  580. }
  581. }
  582. // XXX if ACC REGISTER allows registration with the `none` callback, then ignore
  583. // any callback that was passed here (to avoid confusion in the case where the ircd
  584. // has no mail server configured). otherwise, register using the provided callback:
  585. if noneCallbackAllowed {
  586. callbackNamespace = "*"
  587. } else {
  588. callbackNamespace, callbackValue = parseCallback(email, config)
  589. if callbackNamespace == "" || callbackValue == "" {
  590. nsNotice(rb, client.t("Registration requires a valid e-mail address"))
  591. return
  592. }
  593. }
  594. err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, client.certfp)
  595. if err == nil {
  596. if callbackNamespace == "*" {
  597. err = server.accounts.Verify(client, account, "")
  598. if err == nil {
  599. sendSuccessfulRegResponse(client, rb, true)
  600. }
  601. } else {
  602. messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
  603. message := fmt.Sprintf(messageTemplate, fmt.Sprintf("%s:%s", callbackNamespace, callbackValue))
  604. nsNotice(rb, message)
  605. }
  606. }
  607. // details could not be stored and relevant numerics have been dispatched, abort
  608. message, _ := registrationErrorToMessageAndCode(err)
  609. if err != nil {
  610. nsNotice(rb, client.t(message))
  611. return
  612. }
  613. }
  614. func nsSaregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  615. var account, passphrase string
  616. account = params[0]
  617. if 1 < len(params) && params[1] != "*" {
  618. passphrase = params[1]
  619. }
  620. err := server.accounts.SARegister(account, passphrase)
  621. if err != nil {
  622. var errMsg string
  623. if err == errAccountAlreadyRegistered || err == errAccountAlreadyVerified {
  624. errMsg = client.t("Account already exists")
  625. } else if err == errAccountBadPassphrase {
  626. errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid")
  627. } else {
  628. server.logger.Error("services", "unknown error from saregister", err.Error())
  629. errMsg = client.t("Could not register")
  630. }
  631. nsNotice(rb, errMsg)
  632. } else {
  633. nsNotice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account))
  634. server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Operator $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] with SAREGISTER"), client.Oper().Name, account))
  635. }
  636. }
  637. func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  638. username := params[0]
  639. var verificationCode string
  640. if len(params) > 1 {
  641. verificationCode = params[1]
  642. }
  643. if username == "" {
  644. nsNotice(rb, client.t("You must specify an account"))
  645. return
  646. }
  647. account, err := server.accounts.LoadAccount(username)
  648. if err == errAccountDoesNotExist {
  649. nsNotice(rb, client.t("Invalid account name"))
  650. return
  651. } else if err != nil {
  652. nsNotice(rb, client.t("Internal error"))
  653. return
  654. }
  655. cfname, _ := CasefoldName(username)
  656. if !(cfname == client.Account() || client.HasRoleCapabs("accreg")) {
  657. nsNotice(rb, client.t("Insufficient oper privs"))
  658. return
  659. }
  660. expectedCode := unregisterConfirmationCode(account.Name, account.RegisteredAt)
  661. if expectedCode != verificationCode {
  662. nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
  663. nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %[1]s %[2]s"), cfname, expectedCode))
  664. return
  665. }
  666. err = server.accounts.Unregister(cfname)
  667. if err == errAccountDoesNotExist {
  668. nsNotice(rb, client.t(err.Error()))
  669. } else if err != nil {
  670. nsNotice(rb, client.t("Error while unregistering account"))
  671. } else {
  672. nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), cfname))
  673. server.logger.Info("accounts", "client", client.Nick(), "unregistered account", cfname)
  674. }
  675. client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] unregistered account $c[grey][$r%s$c[grey]]"), client.NickMaskString(), account.Name))
  676. }
  677. func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  678. username, code := params[0], params[1]
  679. err := server.accounts.Verify(client, username, code)
  680. var errorMessage string
  681. if err == errAccountVerificationInvalidCode || err == errAccountAlreadyVerified {
  682. errorMessage = err.Error()
  683. } else if err != nil {
  684. errorMessage = errAccountVerificationFailed.Error()
  685. }
  686. if errorMessage != "" {
  687. nsNotice(rb, client.t(errorMessage))
  688. return
  689. }
  690. sendSuccessfulRegResponse(client, rb, true)
  691. }
  692. func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  693. var target string
  694. var newPassword string
  695. var errorMessage string
  696. hasPrivs := client.HasRoleCapabs("accreg")
  697. switch len(params) {
  698. case 2:
  699. if !hasPrivs {
  700. errorMessage = `Insufficient privileges`
  701. } else {
  702. target, newPassword = params[0], params[1]
  703. if newPassword == "*" {
  704. newPassword = ""
  705. }
  706. }
  707. case 3:
  708. target = client.Account()
  709. if target == "" {
  710. errorMessage = `You're not logged into an account`
  711. } else if params[1] != params[2] {
  712. errorMessage = `Passwords do not match`
  713. } else {
  714. if !nsLoginThrottleCheck(client, rb) {
  715. return
  716. }
  717. accountData, err := server.accounts.LoadAccount(target)
  718. if err != nil {
  719. errorMessage = `You're not logged into an account`
  720. } else {
  721. hash := accountData.Credentials.PassphraseHash
  722. if hash != nil && passwd.CompareHashAndPassword(hash, []byte(params[0])) != nil {
  723. errorMessage = `Password incorrect`
  724. } else {
  725. newPassword = params[1]
  726. if newPassword == "*" {
  727. newPassword = ""
  728. }
  729. }
  730. }
  731. }
  732. default:
  733. errorMessage = `Invalid parameters`
  734. }
  735. if errorMessage != "" {
  736. nsNotice(rb, client.t(errorMessage))
  737. return
  738. }
  739. err := server.accounts.setPassword(target, newPassword, hasPrivs)
  740. switch err {
  741. case nil:
  742. nsNotice(rb, client.t("Password changed"))
  743. case errEmptyCredentials:
  744. nsNotice(rb, client.t("You can't delete your password unless you add a certificate fingerprint"))
  745. case errCredsExternallyManaged:
  746. nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
  747. case errCASFailed:
  748. nsNotice(rb, client.t("Try again later"))
  749. default:
  750. server.logger.Error("internal", "could not upgrade user password:", err.Error())
  751. nsNotice(rb, client.t("Password could not be changed due to server error"))
  752. }
  753. }
  754. func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  755. newParams := []string{"enforce"}
  756. if len(params) == 0 {
  757. nsGetHandler(server, client, "get", newParams, rb)
  758. } else {
  759. newParams = append(newParams, params[0])
  760. nsSetHandler(server, client, "set", newParams, rb)
  761. }
  762. }
  763. func nsSessionsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  764. target := client
  765. if 0 < len(params) {
  766. target = server.clients.Get(params[0])
  767. if target == nil {
  768. nsNotice(rb, client.t("No such nick"))
  769. return
  770. }
  771. // same permissions check as RPL_WHOISACTUALLY for now:
  772. if target != client && !client.HasMode(modes.Operator) {
  773. nsNotice(rb, client.t("Command restricted"))
  774. return
  775. }
  776. }
  777. sessionData, currentIndex := target.AllSessionData(rb.session)
  778. nsNotice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached session(s)"), target.Nick(), len(sessionData)))
  779. for i, session := range sessionData {
  780. if currentIndex == i {
  781. nsNotice(rb, fmt.Sprintf(client.t("Session %d (currently attached session):"), i+1))
  782. } else {
  783. nsNotice(rb, fmt.Sprintf(client.t("Session %d:"), i+1))
  784. }
  785. nsNotice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
  786. nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
  787. nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123)))
  788. nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
  789. }
  790. }
  791. func nsCertHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  792. verb := strings.ToLower(params[0])
  793. params = params[1:]
  794. var target, certfp string
  795. switch verb {
  796. case "list":
  797. if 1 <= len(params) {
  798. target = params[0]
  799. }
  800. case "add", "del":
  801. if 2 <= len(params) {
  802. target, certfp = params[0], params[1]
  803. } else if len(params) == 1 {
  804. certfp = params[0]
  805. } else {
  806. nsNotice(rb, client.t("Invalid parameters"))
  807. return
  808. }
  809. default:
  810. nsNotice(rb, client.t("Invalid parameters"))
  811. return
  812. }
  813. hasPrivs := client.HasRoleCapabs("accreg")
  814. if target != "" && !hasPrivs {
  815. nsNotice(rb, client.t("Insufficient privileges"))
  816. return
  817. } else if target == "" {
  818. target = client.Account()
  819. if target == "" {
  820. nsNotice(rb, client.t("You're not logged into an account"))
  821. return
  822. }
  823. }
  824. var err error
  825. switch verb {
  826. case "list":
  827. accountData, err := server.accounts.LoadAccount(target)
  828. if err == errAccountDoesNotExist {
  829. nsNotice(rb, client.t("Account does not exist"))
  830. return
  831. } else if err != nil {
  832. nsNotice(rb, client.t("An error occurred"))
  833. return
  834. }
  835. certfps := accountData.Credentials.Certfps
  836. nsNotice(rb, fmt.Sprintf(client.t("There are %[1]d certificate fingerprint(s) authorized for account %[2]s."), len(certfps), accountData.Name))
  837. for i, certfp := range certfps {
  838. nsNotice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
  839. }
  840. return
  841. case "add":
  842. err = server.accounts.addRemoveCertfp(target, certfp, true, hasPrivs)
  843. case "del":
  844. err = server.accounts.addRemoveCertfp(target, certfp, false, hasPrivs)
  845. }
  846. switch err {
  847. case nil:
  848. if verb == "add" {
  849. nsNotice(rb, client.t("Certificate fingerprint successfully added"))
  850. } else {
  851. nsNotice(rb, client.t("Certificate fingerprint successfully removed"))
  852. }
  853. case errNoop:
  854. if verb == "add" {
  855. nsNotice(rb, client.t("That certificate fingerprint was already authorized"))
  856. } else {
  857. nsNotice(rb, client.t("Certificate fingerprint not found"))
  858. }
  859. case errAccountDoesNotExist:
  860. nsNotice(rb, client.t("Account does not exist"))
  861. case errLimitExceeded:
  862. nsNotice(rb, client.t("You already have too many certificate fingerprints"))
  863. case utils.ErrInvalidCertfp:
  864. nsNotice(rb, client.t("Invalid certificate fingerprint"))
  865. case errCertfpAlreadyExists:
  866. nsNotice(rb, client.t("That certificate fingerprint is already associated with another account"))
  867. case errEmptyCredentials:
  868. nsNotice(rb, client.t("You can't remove all your certificate fingerprints unless you add a password"))
  869. case errCredsExternallyManaged:
  870. nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
  871. case errCASFailed:
  872. nsNotice(rb, client.t("Try again later"))
  873. default:
  874. server.logger.Error("internal", "could not modify certificates:", err.Error())
  875. nsNotice(rb, client.t("An error occurred"))
  876. }
  877. }