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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "strings"
  7. "github.com/goshuirc/irc-go/ircfmt"
  8. )
  9. // "enabled" callbacks for specific nickserv commands
  10. func servCmdRequiresAccreg(config *Config) bool {
  11. return config.Accounts.Registration.Enabled
  12. }
  13. func servCmdRequiresAuthEnabled(config *Config) bool {
  14. return config.Accounts.AuthenticationEnabled
  15. }
  16. func nsGroupEnabled(config *Config) bool {
  17. return config.Accounts.AuthenticationEnabled && config.Accounts.NickReservation.Enabled
  18. }
  19. func nsEnforceEnabled(config *Config) bool {
  20. return config.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.AllowCustomEnforcement
  21. }
  22. const nickservHelp = `NickServ lets you register and login to an account.
  23. To see in-depth help for a specific NickServ command, try:
  24. $b/NS HELP <command>$b
  25. Here are the commands you can use:
  26. %s`
  27. var (
  28. nickservCommands = map[string]*serviceCommand{
  29. "drop": {
  30. handler: nsDropHandler,
  31. help: `Syntax: $bDROP [nickname]$b
  32. DROP de-links the given (or your current) nickname from your user account.`,
  33. helpShort: `$bDROP$b de-links your current (or the given) nickname from your user account.`,
  34. enabled: servCmdRequiresAccreg,
  35. authRequired: true,
  36. },
  37. "enforce": {
  38. handler: nsEnforceHandler,
  39. help: `Syntax: $bENFORCE [method]$b
  40. ENFORCE lets you specify a custom enforcement mechanism for your registered
  41. nicknames. Your options are:
  42. 1. 'none' [no enforcement, overriding the server default]
  43. 2. 'timeout' [anyone using the nick must authenticate before a deadline,
  44. or else they will be renamed]
  45. 3. 'strict' [you must already be authenticated to use the nick]
  46. 4. 'default' [use the server default]
  47. With no arguments, queries your current enforcement status.`,
  48. helpShort: `$bENFORCE$b lets you change how your nicknames are reserved.`,
  49. authRequired: true,
  50. enabled: nsEnforceEnabled,
  51. },
  52. "ghost": {
  53. handler: nsGhostHandler,
  54. help: `Syntax: $bGHOST <nickname>$b
  55. GHOST disconnects the given user from the network if they're logged in with the
  56. same user account, letting you reclaim your nickname.`,
  57. helpShort: `$bGHOST$b reclaims your nickname.`,
  58. authRequired: true,
  59. minParams: 1,
  60. },
  61. "group": {
  62. handler: nsGroupHandler,
  63. help: `Syntax: $bGROUP$b
  64. GROUP links your current nickname with your logged-in account, preventing other
  65. users from changing to it (or forcing them to rename).`,
  66. helpShort: `$bGROUP$b links your current nickname to your user account.`,
  67. enabled: nsGroupEnabled,
  68. authRequired: true,
  69. },
  70. "identify": {
  71. handler: nsIdentifyHandler,
  72. help: `Syntax: $bIDENTIFY <username> [password]$b
  73. IDENTIFY lets you login to the given username using either password auth, or
  74. certfp (your client certificate) if a password is not given.`,
  75. helpShort: `$bIDENTIFY$b lets you login to your account.`,
  76. minParams: 1,
  77. },
  78. "info": {
  79. handler: nsInfoHandler,
  80. help: `Syntax: $bINFO [username]$b
  81. INFO gives you information about the given (or your own) user account.`,
  82. helpShort: `$bINFO$b gives you information on a user account.`,
  83. },
  84. "register": {
  85. handler: nsRegisterHandler,
  86. // TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
  87. // person@example.com, mailto:person@example.com, tel:16505551234.
  88. help: `Syntax: $bREGISTER <username> <email> [password]$b
  89. REGISTER lets you register a user account. If the server allows anonymous
  90. registration, you can send an asterisk (*) as the email address.
  91. If the password is left out, your account will be registered to your TLS client
  92. certificate (and you will need to use that certificate to login in future).`,
  93. helpShort: `$bREGISTER$b lets you register a user account.`,
  94. enabled: servCmdRequiresAccreg,
  95. minParams: 2,
  96. },
  97. "sadrop": {
  98. handler: nsDropHandler,
  99. help: `Syntax: $bSADROP <nickname>$b
  100. SADROP forcibly de-links the given nickname from the attached user account.`,
  101. helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
  102. capabs: []string{"accreg"},
  103. enabled: servCmdRequiresAccreg,
  104. minParams: 1,
  105. },
  106. "unregister": {
  107. handler: nsUnregisterHandler,
  108. help: `Syntax: $bUNREGISTER <username> [code]$b
  109. UNREGISTER lets you delete your user account (or someone else's, if you're an
  110. IRC operator with the correct permissions). To prevent accidental
  111. unregistrations, a verification code is required; invoking the command without
  112. a code will display the necessary code.`,
  113. helpShort: `$bUNREGISTER$b lets you delete your user account.`,
  114. enabled: servCmdRequiresAccreg,
  115. minParams: 1,
  116. },
  117. "verify": {
  118. handler: nsVerifyHandler,
  119. help: `Syntax: $bVERIFY <username> <code>$b
  120. VERIFY lets you complete an account registration, if the server requires email
  121. or other verification.`,
  122. helpShort: `$bVERIFY$b lets you complete account registration.`,
  123. enabled: servCmdRequiresAccreg,
  124. minParams: 2,
  125. },
  126. "passwd": {
  127. handler: nsPasswdHandler,
  128. help: `Syntax: $bPASSWD <current> <new> <new_again>$b
  129. Or: $bPASSWD <username> <new>$b
  130. PASSWD lets you change your account password. You must supply your current
  131. password and confirm the new one by typing it twice. If you're an IRC operator
  132. with the correct permissions, you can use PASSWD to reset someone else's
  133. password by supplying their username and then the desired password.`,
  134. helpShort: `$bPASSWD$b lets you change your password.`,
  135. enabled: servCmdRequiresAuthEnabled,
  136. minParams: 2,
  137. },
  138. }
  139. )
  140. // nsNotice sends the client a notice from NickServ
  141. func nsNotice(rb *ResponseBuffer, text string) {
  142. rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
  143. }
  144. func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  145. sadrop := command == "sadrop"
  146. var nick string
  147. if len(params) > 0 {
  148. nick = params[0]
  149. } else {
  150. nick = client.NickCasefolded()
  151. }
  152. err := server.accounts.SetNickReserved(client, nick, sadrop, false)
  153. if err == nil {
  154. nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
  155. } else if err == errAccountNotLoggedIn {
  156. nsNotice(rb, client.t("You're not logged into an account"))
  157. } else if err == errAccountCantDropPrimaryNick {
  158. nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
  159. } else {
  160. nsNotice(rb, client.t("Could not ungroup nick"))
  161. }
  162. }
  163. func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  164. nick := params[0]
  165. ghost := server.clients.Get(nick)
  166. if ghost == nil {
  167. nsNotice(rb, client.t("No such nick"))
  168. return
  169. } else if ghost == client {
  170. nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
  171. return
  172. }
  173. authorized := false
  174. account := client.Account()
  175. if account != "" {
  176. // the user must either own the nick, or the target client
  177. authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
  178. }
  179. if !authorized {
  180. nsNotice(rb, client.t("You don't own that nick"))
  181. return
  182. }
  183. ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()))
  184. ghost.destroy(false)
  185. }
  186. func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  187. nick := client.Nick()
  188. err := server.accounts.SetNickReserved(client, nick, false, true)
  189. if err == nil {
  190. nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
  191. } else if err == errAccountTooManyNicks {
  192. nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
  193. } else if err == errNicknameReserved {
  194. nsNotice(rb, client.t("That nickname is already reserved by someone else"))
  195. } else {
  196. nsNotice(rb, client.t("Error reserving nickname"))
  197. }
  198. }
  199. func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
  200. throttled, remainingTime := client.loginThrottle.Touch()
  201. if throttled {
  202. nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
  203. return false
  204. }
  205. return true
  206. }
  207. func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  208. if client.LoggedIntoAccount() {
  209. nsNotice(rb, client.t("You're already logged into an account"))
  210. return
  211. }
  212. loginSuccessful := false
  213. username := params[0]
  214. var passphrase string
  215. if len(params) > 1 {
  216. passphrase = params[1]
  217. }
  218. // try passphrase
  219. if passphrase != "" {
  220. if !nsLoginThrottleCheck(client, rb) {
  221. return
  222. }
  223. err := server.accounts.AuthenticateByPassphrase(client, username, passphrase)
  224. loginSuccessful = (err == nil)
  225. }
  226. // try certfp
  227. if !loginSuccessful && client.certfp != "" {
  228. err := server.accounts.AuthenticateByCertFP(client)
  229. loginSuccessful = (err == nil)
  230. }
  231. if loginSuccessful {
  232. sendSuccessfulSaslAuth(client, rb, true)
  233. } else {
  234. nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
  235. }
  236. }
  237. func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  238. var accountName string
  239. if len(params) > 0 {
  240. nick := params[0]
  241. if server.AccountConfig().NickReservation.Enabled {
  242. accountName = server.accounts.NickToAccount(nick)
  243. if accountName == "" {
  244. nsNotice(rb, client.t("That nickname is not registered"))
  245. return
  246. }
  247. } else {
  248. accountName = nick
  249. }
  250. } else {
  251. accountName = client.Account()
  252. if accountName == "" {
  253. nsNotice(rb, client.t("You're not logged into an account"))
  254. return
  255. }
  256. }
  257. account, err := server.accounts.LoadAccount(accountName)
  258. if err != nil || !account.Verified {
  259. nsNotice(rb, client.t("Account does not exist"))
  260. return
  261. }
  262. nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
  263. registeredAt := account.RegisteredAt.Format("Jan 02, 2006 15:04:05Z")
  264. nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
  265. // TODO nicer formatting for this
  266. for _, nick := range account.AdditionalNicks {
  267. nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
  268. }
  269. }
  270. func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  271. // get params
  272. username, email := params[0], params[1]
  273. var passphrase string
  274. if len(params) > 2 {
  275. passphrase = params[2]
  276. }
  277. if !server.AccountConfig().Registration.Enabled {
  278. nsNotice(rb, client.t("Account registration has been disabled"))
  279. return
  280. }
  281. if username == "" {
  282. nsNotice(rb, client.t("No username supplied"))
  283. return
  284. }
  285. certfp := client.certfp
  286. if passphrase == "" && certfp == "" {
  287. nsNotice(rb, client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
  288. return
  289. }
  290. if client.LoggedIntoAccount() {
  291. nsNotice(rb, client.t("You're already logged into an account"))
  292. return
  293. }
  294. config := server.AccountConfig()
  295. var callbackNamespace, callbackValue string
  296. noneCallbackAllowed := false
  297. for _, callback := range config.Registration.EnabledCallbacks {
  298. if callback == "*" {
  299. noneCallbackAllowed = true
  300. }
  301. }
  302. // XXX if ACC REGISTER allows registration with the `none` callback, then ignore
  303. // any callback that was passed here (to avoid confusion in the case where the ircd
  304. // has no mail server configured). otherwise, register using the provided callback:
  305. if noneCallbackAllowed {
  306. callbackNamespace = "*"
  307. } else {
  308. callbackNamespace, callbackValue = parseCallback(email, config)
  309. if callbackNamespace == "" {
  310. nsNotice(rb, client.t("Registration requires a valid e-mail address"))
  311. return
  312. }
  313. }
  314. // get and sanitise account name
  315. account := strings.TrimSpace(username)
  316. err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, client.certfp)
  317. if err == nil {
  318. if callbackNamespace == "*" {
  319. err = server.accounts.Verify(client, account, "")
  320. if err == nil {
  321. sendSuccessfulRegResponse(client, rb, true)
  322. }
  323. } else {
  324. messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s:%s")
  325. message := fmt.Sprintf(messageTemplate, callbackNamespace, callbackValue)
  326. nsNotice(rb, message)
  327. }
  328. }
  329. // details could not be stored and relevant numerics have been dispatched, abort
  330. if err != nil {
  331. errMsg := client.t("Could not register")
  332. if err == errCertfpAlreadyExists {
  333. errMsg = client.t("An account already exists for your certificate fingerprint")
  334. } else if err == errAccountAlreadyRegistered {
  335. errMsg = client.t("Account already exists")
  336. } else if err == errAccountBadPassphrase {
  337. errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid")
  338. }
  339. nsNotice(rb, errMsg)
  340. return
  341. }
  342. }
  343. func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  344. username := params[0]
  345. var verificationCode string
  346. if len(params) > 1 {
  347. verificationCode = params[1]
  348. }
  349. if username == "" {
  350. nsNotice(rb, client.t("You must specify an account"))
  351. return
  352. }
  353. account, err := server.accounts.LoadAccount(username)
  354. if err == errAccountDoesNotExist {
  355. nsNotice(rb, client.t("Invalid account name"))
  356. return
  357. } else if err != nil {
  358. nsNotice(rb, client.t("Internal error"))
  359. return
  360. }
  361. cfname, _ := CasefoldName(username)
  362. if !(cfname == client.Account() || client.HasRoleCapabs("accreg")) {
  363. nsNotice(rb, client.t("Insufficient oper privs"))
  364. return
  365. }
  366. expectedCode := unregisterConfirmationCode(account.Name, account.RegisteredAt)
  367. if expectedCode != verificationCode {
  368. nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
  369. nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %s %s"), cfname, expectedCode))
  370. return
  371. }
  372. if cfname == client.Account() {
  373. client.server.accounts.Logout(client)
  374. }
  375. err = server.accounts.Unregister(cfname)
  376. if err == errAccountDoesNotExist {
  377. nsNotice(rb, client.t(err.Error()))
  378. } else if err != nil {
  379. nsNotice(rb, client.t("Error while unregistering account"))
  380. } else {
  381. nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), cfname))
  382. }
  383. }
  384. func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  385. username, code := params[0], params[1]
  386. err := server.accounts.Verify(client, username, code)
  387. var errorMessage string
  388. if err == errAccountVerificationInvalidCode || err == errAccountAlreadyVerified {
  389. errorMessage = err.Error()
  390. } else if err != nil {
  391. errorMessage = errAccountVerificationFailed.Error()
  392. }
  393. if errorMessage != "" {
  394. nsNotice(rb, client.t(errorMessage))
  395. return
  396. }
  397. sendSuccessfulRegResponse(client, rb, true)
  398. }
  399. func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  400. var target string
  401. var newPassword string
  402. var errorMessage string
  403. hasPrivs := client.HasRoleCapabs("accreg")
  404. if !hasPrivs && !nsLoginThrottleCheck(client, rb) {
  405. return
  406. }
  407. switch len(params) {
  408. case 2:
  409. if !hasPrivs {
  410. errorMessage = "Insufficient privileges"
  411. } else {
  412. target, newPassword = params[0], params[1]
  413. }
  414. case 3:
  415. target = client.Account()
  416. if target == "" {
  417. errorMessage = "You're not logged into an account"
  418. } else if params[1] != params[2] {
  419. errorMessage = "Passwords do not match"
  420. } else {
  421. // check that they correctly supplied the preexisting password
  422. _, err := server.accounts.checkPassphrase(target, params[0])
  423. if err != nil {
  424. errorMessage = "Password incorrect"
  425. } else {
  426. newPassword = params[1]
  427. }
  428. }
  429. default:
  430. errorMessage = "Invalid parameters"
  431. }
  432. if errorMessage != "" {
  433. nsNotice(rb, client.t(errorMessage))
  434. return
  435. }
  436. err := server.accounts.setPassword(target, newPassword)
  437. if err == nil {
  438. nsNotice(rb, client.t("Password changed"))
  439. } else {
  440. server.logger.Error("internal", "could not upgrade user password:", err.Error())
  441. nsNotice(rb, client.t("Password could not be changed due to server error"))
  442. }
  443. }
  444. func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  445. if len(params) == 0 {
  446. status := server.accounts.getStoredEnforcementStatus(client.Account())
  447. nsNotice(rb, fmt.Sprintf(client.t("Your current nickname enforcement is: %s"), status))
  448. } else {
  449. method, err := nickReservationFromString(params[0])
  450. if err != nil {
  451. nsNotice(rb, client.t("Invalid parameters"))
  452. return
  453. }
  454. err = server.accounts.SetEnforcementStatus(client.Account(), method)
  455. if err == nil {
  456. nsNotice(rb, client.t("Enforcement method set"))
  457. } else {
  458. server.logger.Error("internal", "couldn't store NS ENFORCE data", err.Error())
  459. nsNotice(rb, client.t("An error occurred"))
  460. }
  461. }
  462. }