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.

nickserv.go 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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. "github.com/oragono/oragono/irc/utils"
  9. )
  10. // "enabled" callbacks for specific nickserv commands
  11. func servCmdRequiresAccreg(server *Server) bool {
  12. return server.AccountConfig().Registration.Enabled
  13. }
  14. func servCmdRequiresAuthEnabled(server *Server) bool {
  15. return server.AccountConfig().AuthenticationEnabled
  16. }
  17. const nickservHelp = `NickServ lets you register and login to an account.
  18. To see in-depth help for a specific NickServ command, try:
  19. $b/NS HELP <command>$b
  20. Here are the commands you can use:
  21. %s`
  22. var (
  23. nickservCommands = map[string]*serviceCommand{
  24. "drop": {
  25. handler: nsDropHandler,
  26. help: `Syntax: $bDROP [nickname]$b
  27. DROP de-links the given (or your current) nickname from your user account.`,
  28. helpShort: `$bDROP$b de-links your current (or the given) nickname from your user account.`,
  29. enabled: servCmdRequiresAccreg,
  30. authRequired: true,
  31. },
  32. "ghost": {
  33. handler: nsGhostHandler,
  34. help: `Syntax: $bGHOST <nickname>$b
  35. GHOST disconnects the given user from the network if they're logged in with the
  36. same user account, letting you reclaim your nickname.`,
  37. helpShort: `$bGHOST$b reclaims your nickname.`,
  38. authRequired: true,
  39. },
  40. "group": {
  41. handler: nsGroupHandler,
  42. help: `Syntax: $bGROUP$b
  43. GROUP links your current nickname with your logged-in account, preventing other
  44. users from changing to it (or forcing them to rename).`,
  45. helpShort: `$bGROUP$b links your current nickname to your user account.`,
  46. enabled: servCmdRequiresAccreg,
  47. authRequired: true,
  48. },
  49. "identify": {
  50. handler: nsIdentifyHandler,
  51. help: `Syntax: $bIDENTIFY <username> [password]$b
  52. IDENTIFY lets you login to the given username using either password auth, or
  53. certfp (your client certificate) if a password is not given.`,
  54. helpShort: `$bIDENTIFY$b lets you login to your account.`,
  55. },
  56. "info": {
  57. handler: nsInfoHandler,
  58. help: `Syntax: $bINFO [username]$b
  59. INFO gives you information about the given (or your own) user account.`,
  60. helpShort: `$bINFO$b gives you information on a user account.`,
  61. },
  62. "register": {
  63. handler: nsRegisterHandler,
  64. // TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
  65. // person@example.com, mailto:person@example.com, tel:16505551234.
  66. help: `Syntax: $bREGISTER <username> <email> [password]$b
  67. REGISTER lets you register a user account. If the server allows anonymous
  68. registration, you can send an asterisk (*) as the email address.
  69. If the password is left out, your account will be registered to your TLS client
  70. certificate (and you will need to use that certificate to login in future).`,
  71. helpShort: `$bREGISTER$b lets you register a user account.`,
  72. enabled: servCmdRequiresAccreg,
  73. },
  74. "sadrop": {
  75. handler: nsDropHandler,
  76. help: `Syntax: $bSADROP <nickname>$b
  77. SADROP forcibly de-links the given nickname from the attached user account.`,
  78. helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
  79. capabs: []string{"unregister"},
  80. enabled: servCmdRequiresAccreg,
  81. },
  82. "unregister": {
  83. handler: nsUnregisterHandler,
  84. help: `Syntax: $bUNREGISTER <username> [code]$b
  85. UNREGISTER lets you delete your user account (or someone else's, if you're an
  86. IRC operator with the correct permissions). To prevent accidental
  87. unregistrations, a verification code is required; invoking the command without
  88. a code will display the necessary code.`,
  89. helpShort: `$bUNREGISTER$b lets you delete your user account.`,
  90. },
  91. "verify": {
  92. handler: nsVerifyHandler,
  93. help: `Syntax: $bVERIFY <username> <code>$b
  94. VERIFY lets you complete an account registration, if the server requires email
  95. or other verification.`,
  96. helpShort: `$bVERIFY$b lets you complete account registration.`,
  97. enabled: servCmdRequiresAccreg,
  98. },
  99. }
  100. )
  101. // nsNotice sends the client a notice from NickServ
  102. func nsNotice(rb *ResponseBuffer, text string) {
  103. rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
  104. }
  105. func nsDropHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  106. sadrop := command == "sadrop"
  107. nick, _ := utils.ExtractParam(params)
  108. err := server.accounts.SetNickReserved(client, nick, sadrop, false)
  109. if err == nil {
  110. nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
  111. } else if err == errAccountNotLoggedIn {
  112. nsNotice(rb, client.t("You're not logged into an account"))
  113. } else if err == errAccountCantDropPrimaryNick {
  114. nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
  115. } else if err == errNicknameReserved {
  116. nsNotice(rb, client.t("That nickname is already reserved by someone else"))
  117. } else {
  118. nsNotice(rb, client.t("Could not ungroup nick"))
  119. }
  120. }
  121. func nsGhostHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  122. nick, _ := utils.ExtractParam(params)
  123. ghost := server.clients.Get(nick)
  124. if ghost == nil {
  125. nsNotice(rb, client.t("No such nick"))
  126. return
  127. } else if ghost == client {
  128. nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
  129. return
  130. }
  131. authorized := false
  132. account := client.Account()
  133. if account != "" {
  134. // the user must either own the nick, or the target client
  135. authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
  136. }
  137. if !authorized {
  138. nsNotice(rb, client.t("You don't own that nick"))
  139. return
  140. }
  141. ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()))
  142. ghost.destroy(false)
  143. }
  144. func nsGroupHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  145. nick := client.NickCasefolded()
  146. err := server.accounts.SetNickReserved(client, nick, false, true)
  147. if err == nil {
  148. nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
  149. } else if err == errAccountTooManyNicks {
  150. nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
  151. } else if err == errNicknameReserved {
  152. nsNotice(rb, client.t("That nickname is already reserved by someone else"))
  153. } else {
  154. nsNotice(rb, client.t("Error reserving nickname"))
  155. }
  156. }
  157. func nsIdentifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  158. loginSuccessful := false
  159. username, passphrase := utils.ExtractParam(params)
  160. // try passphrase
  161. if username != "" && passphrase != "" {
  162. err := server.accounts.AuthenticateByPassphrase(client, username, passphrase)
  163. loginSuccessful = (err == nil)
  164. }
  165. // try certfp
  166. if !loginSuccessful && client.certfp != "" {
  167. err := server.accounts.AuthenticateByCertFP(client)
  168. loginSuccessful = (err == nil)
  169. }
  170. if loginSuccessful {
  171. sendSuccessfulSaslAuth(client, rb, true)
  172. } else {
  173. nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
  174. }
  175. }
  176. func nsInfoHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  177. nick, _ := utils.ExtractParam(params)
  178. if nick == "" {
  179. nick = client.Nick()
  180. }
  181. accountName := nick
  182. if server.AccountConfig().NickReservation.Enabled {
  183. accountName = server.accounts.NickToAccount(nick)
  184. if accountName == "" {
  185. nsNotice(rb, client.t("That nickname is not registered"))
  186. return
  187. }
  188. }
  189. account, err := server.accounts.LoadAccount(accountName)
  190. if err != nil || !account.Verified {
  191. nsNotice(rb, client.t("Account does not exist"))
  192. }
  193. nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
  194. registeredAt := account.RegisteredAt.Format("Jan 02, 2006 15:04:05Z")
  195. nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
  196. // TODO nicer formatting for this
  197. for _, nick := range account.AdditionalNicks {
  198. nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
  199. }
  200. }
  201. func nsRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  202. // get params
  203. username, afterUsername := utils.ExtractParam(params)
  204. email, passphrase := utils.ExtractParam(afterUsername)
  205. if !server.AccountConfig().Registration.Enabled {
  206. nsNotice(rb, client.t("Account registration has been disabled"))
  207. return
  208. }
  209. if username == "" {
  210. nsNotice(rb, client.t("No username supplied"))
  211. return
  212. }
  213. certfp := client.certfp
  214. if passphrase == "" && certfp == "" {
  215. nsNotice(rb, client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
  216. return
  217. }
  218. if client.LoggedIntoAccount() {
  219. if server.AccountConfig().Registration.AllowMultiplePerConnection {
  220. server.accounts.Logout(client)
  221. } else {
  222. nsNotice(rb, client.t("You're already logged into an account"))
  223. return
  224. }
  225. }
  226. config := server.AccountConfig()
  227. var callbackNamespace, callbackValue string
  228. noneCallbackAllowed := false
  229. for _, callback := range config.Registration.EnabledCallbacks {
  230. if callback == "*" {
  231. noneCallbackAllowed = true
  232. }
  233. }
  234. // XXX if ACC REGISTER allows registration with the `none` callback, then ignore
  235. // any callback that was passed here (to avoid confusion in the case where the ircd
  236. // has no mail server configured). otherwise, register using the provided callback:
  237. if noneCallbackAllowed {
  238. callbackNamespace = "*"
  239. } else {
  240. callbackNamespace, callbackValue = parseCallback(email, config)
  241. if callbackNamespace == "" {
  242. nsNotice(rb, client.t("Registration requires a valid e-mail address"))
  243. return
  244. }
  245. }
  246. // get and sanitise account name
  247. account := strings.TrimSpace(username)
  248. err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, client.certfp)
  249. if err == nil {
  250. if callbackNamespace == "*" {
  251. err = server.accounts.Verify(client, account, "")
  252. if err == nil {
  253. sendSuccessfulRegResponse(client, rb, true)
  254. }
  255. } else {
  256. messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s:%s")
  257. message := fmt.Sprintf(messageTemplate, callbackNamespace, callbackValue)
  258. nsNotice(rb, message)
  259. }
  260. }
  261. // details could not be stored and relevant numerics have been dispatched, abort
  262. if err != nil {
  263. errMsg := client.t("Could not register")
  264. if err == errCertfpAlreadyExists {
  265. errMsg = client.t("An account already exists for your certificate fingerprint")
  266. } else if err == errAccountAlreadyRegistered {
  267. errMsg = client.t("Account already exists")
  268. } else if err == errAccountBadPassphrase {
  269. errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid")
  270. }
  271. nsNotice(rb, errMsg)
  272. return
  273. }
  274. }
  275. func nsUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  276. username, verificationCode := utils.ExtractParam(params)
  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("You must specify an account"))
  283. return
  284. }
  285. account, err := server.accounts.LoadAccount(username)
  286. if err == errAccountDoesNotExist {
  287. nsNotice(rb, client.t("Invalid account name"))
  288. return
  289. } else if err != nil {
  290. nsNotice(rb, client.t("Internal error"))
  291. return
  292. }
  293. cfname, _ := CasefoldName(username)
  294. if !(cfname == client.Account() || client.HasRoleCapabs("accreg")) {
  295. nsNotice(rb, client.t("Insufficient oper privs"))
  296. return
  297. }
  298. expectedCode := unregisterConfirmationCode(account.Name, account.RegisteredAt)
  299. if expectedCode != verificationCode {
  300. nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
  301. nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %s %s"), cfname, expectedCode))
  302. return
  303. }
  304. if cfname == client.Account() {
  305. client.server.accounts.Logout(client)
  306. }
  307. err = server.accounts.Unregister(cfname)
  308. if err == errAccountDoesNotExist {
  309. nsNotice(rb, client.t(err.Error()))
  310. } else if err != nil {
  311. nsNotice(rb, client.t("Error while unregistering account"))
  312. } else {
  313. nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), cfname))
  314. }
  315. }
  316. func nsVerifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  317. username, code := utils.ExtractParam(params)
  318. err := server.accounts.Verify(client, username, code)
  319. var errorMessage string
  320. if err == errAccountVerificationInvalidCode || err == errAccountAlreadyVerified {
  321. errorMessage = err.Error()
  322. } else if err != nil {
  323. errorMessage = errAccountVerificationFailed.Error()
  324. }
  325. if errorMessage != "" {
  326. nsNotice(rb, client.t(errorMessage))
  327. return
  328. }
  329. sendSuccessfulRegResponse(client, rb, true)
  330. }