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.

hostserv.go 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright (c) 2018 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "errors"
  6. "fmt"
  7. "regexp"
  8. "github.com/ergochat/irc-go/ircfmt"
  9. "github.com/ergochat/ergo/irc/sno"
  10. "github.com/ergochat/ergo/irc/utils"
  11. )
  12. const (
  13. hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
  14. in place of your client's hostname/IP).`
  15. )
  16. var (
  17. errVHostBadCharacters = errors.New("Vhost contains prohibited characters")
  18. errVHostTooLong = errors.New("Vhost is too long")
  19. // ascii only for now
  20. defaultValidVhostRegex = regexp.MustCompile(`^[0-9A-Za-z.\-_/]+$`)
  21. )
  22. func hostservEnabled(config *Config) bool {
  23. return config.Accounts.VHosts.Enabled
  24. }
  25. var (
  26. hostservCommands = map[string]*serviceCommand{
  27. "on": {
  28. handler: hsOnOffHandler,
  29. help: `Syntax: $bON$b
  30. ON enables your vhost, if you have one approved.`,
  31. helpShort: `$bON$b enables your vhost, if you have one approved.`,
  32. authRequired: true,
  33. enabled: hostservEnabled,
  34. },
  35. "off": {
  36. handler: hsOnOffHandler,
  37. help: `Syntax: $bOFF$b
  38. OFF disables your vhost, if you have one approved.`,
  39. helpShort: `$bOFF$b disables your vhost, if you have one approved.`,
  40. authRequired: true,
  41. enabled: hostservEnabled,
  42. },
  43. "status": {
  44. handler: hsStatusHandler,
  45. help: `Syntax: $bSTATUS [user]$b
  46. STATUS displays your current vhost, if any, and whether it is enabled or
  47. disabled. A server operator can view someone else's status.`,
  48. helpShort: `$bSTATUS$b shows your vhost status.`,
  49. enabled: hostservEnabled,
  50. },
  51. "set": {
  52. handler: hsSetHandler,
  53. help: `Syntax: $bSET <user> <vhost>$b
  54. SET sets a user's vhost.`,
  55. helpShort: `$bSET$b sets a user's vhost.`,
  56. capabs: []string{"vhosts"},
  57. enabled: hostservEnabled,
  58. minParams: 2,
  59. },
  60. "del": {
  61. handler: hsSetHandler,
  62. help: `Syntax: $bDEL <user>$b
  63. DEL deletes a user's vhost.`,
  64. helpShort: `$bDEL$b deletes a user's vhost.`,
  65. capabs: []string{"vhosts"},
  66. enabled: hostservEnabled,
  67. minParams: 1,
  68. },
  69. "setcloaksecret": {
  70. handler: hsSetCloakSecretHandler,
  71. help: `Syntax: $bSETCLOAKSECRET$b <secret> [code]
  72. SETCLOAKSECRET can be used to set or rotate the cloak secret. You should use
  73. a cryptographically strong secret. To prevent accidental modification, a
  74. verification code is required; invoking the command without a code will
  75. display the necessary code.`,
  76. helpShort: `$bSETCLOAKSECRET$b modifies the IP cloaking secret.`,
  77. capabs: []string{"vhosts", "rehash"},
  78. minParams: 1,
  79. maxParams: 2,
  80. },
  81. }
  82. )
  83. func hsOnOffHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  84. enable := false
  85. if command == "on" {
  86. enable = true
  87. }
  88. _, err := server.accounts.VHostSetEnabled(client, enable)
  89. if err == errNoVhost {
  90. service.Notice(rb, client.t(err.Error()))
  91. } else if err != nil {
  92. service.Notice(rb, client.t("An error occurred"))
  93. } else if enable {
  94. service.Notice(rb, client.t("Successfully enabled your vhost"))
  95. } else {
  96. service.Notice(rb, client.t("Successfully disabled your vhost"))
  97. }
  98. }
  99. func hsStatusHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  100. var accountName string
  101. if len(params) > 0 {
  102. if !client.HasRoleCapabs("vhosts") {
  103. service.Notice(rb, client.t("Command restricted"))
  104. return
  105. }
  106. accountName = params[0]
  107. } else {
  108. accountName = client.Account()
  109. if accountName == "" {
  110. service.Notice(rb, client.t("You're not logged into an account"))
  111. return
  112. }
  113. }
  114. account, err := server.accounts.LoadAccount(accountName)
  115. if err != nil {
  116. if err != errAccountDoesNotExist {
  117. server.logger.Warning("internal", "error loading account info", accountName, err.Error())
  118. }
  119. service.Notice(rb, client.t("No such account"))
  120. return
  121. }
  122. if account.VHost.ApprovedVHost != "" {
  123. service.Notice(rb, fmt.Sprintf(client.t("Account %[1]s has vhost: %[2]s"), accountName, account.VHost.ApprovedVHost))
  124. if !account.VHost.Enabled {
  125. service.Notice(rb, client.t("This vhost is currently disabled, but can be enabled with /HS ON"))
  126. }
  127. } else {
  128. service.Notice(rb, fmt.Sprintf(client.t("Account %s has no vhost"), accountName))
  129. }
  130. }
  131. func validateVhost(server *Server, vhost string, oper bool) error {
  132. config := server.Config()
  133. if len(vhost) > config.Accounts.VHosts.MaxLength {
  134. return errVHostTooLong
  135. }
  136. if !config.Accounts.VHosts.validRegexp.MatchString(vhost) {
  137. return errVHostBadCharacters
  138. }
  139. return nil
  140. }
  141. func hsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  142. oper := client.Oper()
  143. user := params[0]
  144. var vhost string
  145. if command == "set" {
  146. vhost = params[1]
  147. if validateVhost(server, vhost, true) != nil {
  148. service.Notice(rb, client.t("Invalid vhost"))
  149. return
  150. }
  151. }
  152. // else: command == "del", vhost == ""
  153. _, err := server.accounts.VHostSet(user, vhost)
  154. if err != nil {
  155. service.Notice(rb, client.t("An error occurred"))
  156. } else if vhost != "" {
  157. service.Notice(rb, client.t("Successfully set vhost"))
  158. server.snomasks.Send(sno.LocalVhosts, fmt.Sprintf("Operator %[1]s set vhost %[2]s on account %[3]s", oper.Name, vhost, user))
  159. } else {
  160. service.Notice(rb, client.t("Successfully cleared vhost"))
  161. server.snomasks.Send(sno.LocalVhosts, fmt.Sprintf("Operator %[1]s cleared vhost on account %[2]s", oper.Name, user))
  162. }
  163. }
  164. func hsSetCloakSecretHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  165. secret := params[0]
  166. expectedCode := utils.ConfirmationCode(secret, server.ctime)
  167. if len(params) == 1 || params[1] != expectedCode {
  168. service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b")))
  169. service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode)))
  170. return
  171. }
  172. StoreCloakSecret(server.dstore, secret)
  173. service.Notice(rb, client.t("Rotated the cloak secret; you must rehash or restart the server for it to take effect"))
  174. }