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.8KB

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