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.

authscript.go 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // Copyright (c) 2020 Shivaram Lingamneni
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "crypto/x509"
  6. "encoding/json"
  7. "encoding/pem"
  8. "fmt"
  9. "net"
  10. "github.com/ergochat/ergo/irc/oauth2"
  11. "github.com/ergochat/ergo/irc/utils"
  12. )
  13. // JSON-serializable input and output types for the script
  14. type AuthScriptInput struct {
  15. AccountName string `json:"accountName,omitempty"`
  16. Passphrase string `json:"passphrase,omitempty"`
  17. Certfp string `json:"certfp,omitempty"`
  18. PeerCerts []string `json:"peerCerts,omitempty"`
  19. peerCerts []*x509.Certificate
  20. IP string `json:"ip,omitempty"`
  21. OAuthBearer *oauth2.OAuthBearerOptions `json:"oauth2,omitempty"`
  22. }
  23. type AuthScriptOutput struct {
  24. AccountName string `json:"accountName"`
  25. Success bool `json:"success"`
  26. Error string `json:"error"`
  27. }
  28. func CheckAuthScript(sem utils.Semaphore, config ScriptConfig, input AuthScriptInput) (output AuthScriptOutput, err error) {
  29. if sem != nil {
  30. sem.Acquire()
  31. defer sem.Release()
  32. }
  33. // PEM-encode the peer certificates before applying JSON
  34. if len(input.peerCerts) != 0 {
  35. input.PeerCerts = make([]string, len(input.peerCerts))
  36. for i, cert := range input.peerCerts {
  37. input.PeerCerts[i] = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
  38. }
  39. }
  40. inputBytes, err := json.Marshal(input)
  41. if err != nil {
  42. return
  43. }
  44. outBytes, err := RunScript(config.Command, config.Args, inputBytes, config.Timeout, config.KillTimeout)
  45. if err != nil {
  46. return
  47. }
  48. err = json.Unmarshal(outBytes, &output)
  49. if err != nil {
  50. return
  51. }
  52. if output.Error != "" {
  53. err = fmt.Errorf("Authentication process reported error: %s", output.Error)
  54. }
  55. return
  56. }
  57. type IPScriptResult uint
  58. const (
  59. IPNotChecked IPScriptResult = 0
  60. IPAccepted IPScriptResult = 1
  61. IPBanned IPScriptResult = 2
  62. IPRequireSASL IPScriptResult = 3
  63. )
  64. type IPScriptInput struct {
  65. IP string `json:"ip"`
  66. }
  67. type IPScriptOutput struct {
  68. Result IPScriptResult `json:"result"`
  69. BanMessage string `json:"banMessage"`
  70. // for caching: the network to which this result is applicable, and a TTL in seconds:
  71. CacheNet string `json:"cacheNet"`
  72. CacheSeconds int `json:"cacheSeconds"`
  73. Error string `json:"error"`
  74. }
  75. func CheckIPBan(sem utils.Semaphore, config IPCheckScriptConfig, addr net.IP) (output IPScriptOutput, err error) {
  76. if sem != nil {
  77. sem.Acquire()
  78. defer sem.Release()
  79. }
  80. inputBytes, err := json.Marshal(IPScriptInput{IP: addr.String()})
  81. if err != nil {
  82. return
  83. }
  84. outBytes, err := RunScript(config.Command, config.Args, inputBytes, config.Timeout, config.KillTimeout)
  85. if err != nil {
  86. return
  87. }
  88. err = json.Unmarshal(outBytes, &output)
  89. if err != nil {
  90. return
  91. }
  92. if output.Error != "" {
  93. err = fmt.Errorf("IP ban process reported error: %s", output.Error)
  94. } else if !(IPAccepted <= output.Result && output.Result <= IPRequireSASL) {
  95. err = fmt.Errorf("Invalid result from IP checking script: %d", output.Result)
  96. }
  97. return
  98. }