ergo/irc/authscript.go

114 lines
2.8 KiB
Go
Raw Normal View History

2020-06-04 07:18:24 +02:00
// Copyright (c) 2020 Shivaram Lingamneni
// released under the MIT license
package irc
import (
2020-09-23 08:23:35 +02:00
"crypto/x509"
2020-06-04 07:18:24 +02:00
"encoding/json"
2020-09-23 08:23:35 +02:00
"encoding/pem"
2020-06-04 07:18:24 +02:00
"fmt"
"net"
2021-05-25 06:34:38 +02:00
"github.com/ergochat/ergo/irc/utils"
2020-06-04 07:18:24 +02:00
)
// JSON-serializable input and output types for the script
type AuthScriptInput struct {
2020-09-23 08:23:35 +02:00
AccountName string `json:"accountName,omitempty"`
Passphrase string `json:"passphrase,omitempty"`
Certfp string `json:"certfp,omitempty"`
PeerCerts []string `json:"peerCerts,omitempty"`
peerCerts []*x509.Certificate
2020-06-04 08:03:15 +02:00
IP string `json:"ip,omitempty"`
2020-06-04 07:18:24 +02:00
}
type AuthScriptOutput struct {
2020-06-04 08:03:15 +02:00
AccountName string `json:"accountName"`
Success bool `json:"success"`
Error string `json:"error"`
2020-06-04 07:18:24 +02:00
}
func CheckAuthScript(sem utils.Semaphore, config ScriptConfig, input AuthScriptInput) (output AuthScriptOutput, err error) {
if sem != nil {
sem.Acquire()
defer sem.Release()
}
2020-06-04 07:18:24 +02:00
2020-09-23 08:23:35 +02:00
// PEM-encode the peer certificates before applying JSON
if len(input.peerCerts) != 0 {
input.PeerCerts = make([]string, len(input.peerCerts))
for i, cert := range input.peerCerts {
input.PeerCerts[i] = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
}
}
2020-06-04 07:18:24 +02:00
inputBytes, err := json.Marshal(input)
if err != nil {
return
}
outBytes, err := RunScript(config.Command, config.Args, inputBytes, config.Timeout, config.KillTimeout)
2020-06-04 07:18:24 +02:00
if err != nil {
return
}
err = json.Unmarshal(outBytes, &output)
2020-06-04 07:18:24 +02:00
if err != nil {
return
}
if output.Error != "" {
err = fmt.Errorf("Authentication process reported error: %s", output.Error)
2020-06-04 07:18:24 +02:00
}
return
}
2020-06-04 07:18:24 +02:00
type IPScriptResult uint
2020-06-04 07:18:24 +02:00
const (
IPNotChecked IPScriptResult = 0
IPAccepted IPScriptResult = 1
IPBanned IPScriptResult = 2
IPRequireSASL IPScriptResult = 3
)
type IPScriptInput struct {
IP string `json:"ip"`
2020-06-04 07:18:24 +02:00
}
type IPScriptOutput struct {
Result IPScriptResult `json:"result"`
BanMessage string `json:"banMessage"`
// for caching: the network to which this result is applicable, and a TTL in seconds:
CacheNet string `json:"cacheNet"`
CacheSeconds int `json:"cacheSeconds"`
Error string `json:"error"`
}
func CheckIPBan(sem utils.Semaphore, config ScriptConfig, addr net.IP) (output IPScriptOutput, err error) {
if sem != nil {
sem.Acquire()
defer sem.Release()
2020-06-04 07:18:24 +02:00
}
inputBytes, err := json.Marshal(IPScriptInput{IP: addr.String()})
if err != nil {
return
}
outBytes, err := RunScript(config.Command, config.Args, inputBytes, config.Timeout, config.KillTimeout)
if err != nil {
return
}
err = json.Unmarshal(outBytes, &output)
2020-06-04 07:18:24 +02:00
if err != nil {
return
2020-06-04 07:18:24 +02:00
}
if output.Error != "" {
err = fmt.Errorf("IP ban process reported error: %s", output.Error)
} else if !(IPAccepted <= output.Result && output.Result <= IPRequireSASL) {
err = fmt.Errorf("Invalid result from IP checking script: %d", output.Result)
}
return
2020-06-04 07:18:24 +02:00
}