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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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/sno"
  9. )
  10. const nickservHelp = `NickServ lets you register and log into a user account.
  11. To register an account:
  12. /NS REGISTER username [password]
  13. Leave out [password] if you're registering using your client certificate fingerprint.
  14. To login to an account:
  15. /NS IDENTIFY [username password]
  16. Leave out [username password] to use your client certificate fingerprint. Otherwise,
  17. the given username and password will be used.`
  18. // extractParam extracts a parameter from the given string, returning the param and the rest of the string.
  19. func extractParam(line string) (string, string) {
  20. rawParams := strings.SplitN(strings.TrimSpace(line), " ", 2)
  21. param0 := rawParams[0]
  22. var param1 string
  23. if 1 < len(rawParams) {
  24. param1 = strings.TrimSpace(rawParams[1])
  25. }
  26. return param0, param1
  27. }
  28. // nickservNoticeHandler handles NOTICEs that NickServ receives.
  29. func (server *Server) nickservNoticeHandler(client *Client, message string, rb *ResponseBuffer) {
  30. // do nothing
  31. }
  32. // nickservPrivmsgHandler handles PRIVMSGs that NickServ receives.
  33. func (server *Server) nickservPrivmsgHandler(client *Client, message string, rb *ResponseBuffer) {
  34. command, params := extractParam(message)
  35. command = strings.ToLower(command)
  36. if command == "help" {
  37. for _, line := range strings.Split(nickservHelp, "\n") {
  38. rb.Notice(line)
  39. }
  40. } else if command == "register" {
  41. // get params
  42. username, passphrase := extractParam(params)
  43. // fail out if we need to
  44. if username == "" {
  45. rb.Notice(client.t("No username supplied"))
  46. return
  47. }
  48. server.nickservRegisterHandler(client, username, passphrase, rb)
  49. } else if command == "identify" {
  50. // get params
  51. username, passphrase := extractParam(params)
  52. server.nickservIdentifyHandler(client, username, passphrase, rb)
  53. } else {
  54. rb.Notice(client.t("Command not recognised. To see the available commands, run /NS HELP"))
  55. }
  56. }
  57. func (server *Server) nickservRegisterHandler(client *Client, username, passphrase string, rb *ResponseBuffer) {
  58. certfp := client.certfp
  59. if passphrase == "" && certfp == "" {
  60. rb.Notice(client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
  61. return
  62. }
  63. if !server.AccountConfig().Registration.Enabled {
  64. rb.Notice(client.t("Account registration has been disabled"))
  65. return
  66. }
  67. if client.LoggedIntoAccount() {
  68. if server.AccountConfig().Registration.AllowMultiplePerConnection {
  69. server.accounts.Logout(client)
  70. } else {
  71. rb.Notice(client.t("You're already logged into an account"))
  72. return
  73. }
  74. }
  75. // get and sanitise account name
  76. account := strings.TrimSpace(username)
  77. casefoldedAccount, err := CasefoldName(account)
  78. // probably don't need explicit check for "*" here... but let's do it anyway just to make sure
  79. if err != nil || username == "*" {
  80. rb.Notice(client.t("Account name is not valid"))
  81. return
  82. }
  83. // account could not be created and relevant numerics have been dispatched, abort
  84. if err != nil {
  85. if err != errAccountCreation {
  86. rb.Notice(client.t("Account registration failed"))
  87. }
  88. return
  89. }
  90. err = server.accounts.Register(client, account, "", "", passphrase, client.certfp)
  91. if err == nil {
  92. err = server.accounts.Verify(client, casefoldedAccount, "")
  93. }
  94. // details could not be stored and relevant numerics have been dispatched, abort
  95. if err != nil {
  96. errMsg := "Could not register"
  97. if err == errCertfpAlreadyExists {
  98. errMsg = "An account already exists for your certificate fingerprint"
  99. } else if err == errAccountAlreadyRegistered {
  100. errMsg = "Account already exists"
  101. }
  102. rb.Notice(client.t(errMsg))
  103. return
  104. }
  105. rb.Notice(client.t("Account created"))
  106. rb.Add(nil, server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, casefoldedAccount, fmt.Sprintf(client.t("You are now logged in as %s"), casefoldedAccount))
  107. rb.Add(nil, server.name, RPL_SASLSUCCESS, client.nick, client.t("Authentication successful"))
  108. server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Account registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), casefoldedAccount, client.nickMaskString))
  109. }
  110. func (server *Server) nickservIdentifyHandler(client *Client, username, passphrase string, rb *ResponseBuffer) {
  111. // fail out if we need to
  112. if !server.AccountConfig().AuthenticationEnabled {
  113. rb.Notice(client.t("Login has been disabled"))
  114. return
  115. }
  116. // try passphrase
  117. if username != "" && passphrase != "" {
  118. // keep it the same as in the ACC CREATE stage
  119. accountName, err := CasefoldName(username)
  120. if err != nil {
  121. rb.Notice(client.t("Could not login with your username/password"))
  122. return
  123. }
  124. err = server.accounts.AuthenticateByPassphrase(client, accountName, passphrase)
  125. if err == nil {
  126. rb.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
  127. return
  128. }
  129. }
  130. // try certfp
  131. if client.certfp != "" {
  132. err := server.accounts.AuthenticateByCertFP(client)
  133. if err == nil {
  134. rb.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), client.AccountName()))
  135. // TODO more notices?
  136. return
  137. }
  138. }
  139. rb.Notice(client.t("Could not login with your TLS certificate or supplied username/password"))
  140. }