3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-26 05:49:25 +01:00
This commit is contained in:
Shivaram Lingamneni 2019-12-18 15:44:06 -05:00
parent 9d56677691
commit e143aaa83f
7 changed files with 47 additions and 6 deletions

View File

@ -206,7 +206,7 @@ func init() {
}, },
"OPER": { "OPER": {
handler: operHandler, handler: operHandler,
minParams: 2, minParams: 1,
}, },
"PART": { "PART": {
handler: partHandler, handler: partHandler,

View File

@ -211,6 +211,7 @@ type OperConfig struct {
Vhost string Vhost string
WhoisLine string `yaml:"whois-line"` WhoisLine string `yaml:"whois-line"`
Password string Password string
Certfp string
Modes string Modes string
} }
@ -459,6 +460,7 @@ type Oper struct {
WhoisLine string WhoisLine string
Vhost string Vhost string
Pass []byte Pass []byte
Certfp string
Modes []modes.ModeChange Modes []modes.ModeChange
} }
@ -479,6 +481,7 @@ func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error
if err != nil { if err != nil {
return nil, err return nil, err
} }
oper.Certfp = opConf.Certfp
oper.Vhost = opConf.Vhost oper.Vhost = opConf.Vhost
class, exists := oc[opConf.Class] class, exists := oc[opConf.Class]

View File

@ -2170,7 +2170,7 @@ func npcaHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
return false return false
} }
// OPER <name> <password> // OPER <name> [password]
func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
if client.HasMode(modes.Operator) { if client.HasMode(modes.Operator) {
rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), "OPER", client.t("You're already opered-up!")) rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), "OPER", client.t("You're already opered-up!"))
@ -2180,9 +2180,13 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
authorized := false authorized := false
oper := server.GetOperator(msg.Params[0]) oper := server.GetOperator(msg.Params[0])
if oper != nil { if oper != nil {
if utils.CertfpsMatch(oper.Certfp, client.certfp) {
authorized = true
} else if 1 < len(msg.Params) {
password := []byte(msg.Params[1]) password := []byte(msg.Params[1])
authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil) authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil)
} }
}
if !authorized { if !authorized {
rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect")) rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect"))
client.Quit(client.t("Password incorrect"), rb.session) client.Quit(client.t("Password incorrect"), rb.session)

View File

@ -349,7 +349,7 @@ The NPC command is used to send an action to the target as the source.
Requires the roleplay mode (+E) to be set on the target.`, Requires the roleplay mode (+E) to be set on the target.`,
}, },
"oper": { "oper": {
text: `OPER <name> <password> text: `OPER <name> [password]
If the correct details are given, gives you IRCop privs.`, If the correct details are given, gives you IRCop privs.`,
}, },

View File

@ -8,6 +8,7 @@ import (
"crypto/subtle" "crypto/subtle"
"encoding/base32" "encoding/base32"
"encoding/base64" "encoding/base64"
"strings"
) )
var ( var (
@ -68,3 +69,15 @@ func GenerateSecretKey() string {
rand.Read(buf[:]) rand.Read(buf[:])
return base64.RawURLEncoding.EncodeToString(buf[:]) return base64.RawURLEncoding.EncodeToString(buf[:])
} }
func normalizeCertfp(certfp string) string {
return strings.ToLower(strings.Replace(certfp, ":", "", -1))
}
// Convenience to compare certfps as returned by different tools, e.g., openssl vs. oragono
func CertfpsMatch(storedCertfp, suppliedCertfp string) bool {
if storedCertfp == "" {
return false
}
return normalizeCertfp(storedCertfp) == normalizeCertfp(suppliedCertfp)
}

View File

@ -81,3 +81,21 @@ func BenchmarkMungeSecretToken(b *testing.B) {
t = MungeSecretToken(t) t = MungeSecretToken(t)
} }
} }
func TestCertfpComparisons(t *testing.T) {
opensslFP := "3D:6B:11:BF:B4:05:C3:F8:4B:38:CD:30:38:FB:EC:01:71:D5:03:54:79:04:07:88:4C:A5:5D:23:41:85:66:C9"
oragonoFP := "3d6b11bfb405c3f84b38cd3038fbec0171d50354790407884ca55d23418566c9"
badFP := "3d6b11bfb405c3f84b38cd3038fbec0171d50354790407884ca55d23418566c8"
if !CertfpsMatch(opensslFP, oragonoFP) {
t.Error("these certs should match")
}
if !CertfpsMatch(oragonoFP, opensslFP) {
t.Error("these certs should match")
}
if CertfpsMatch("", "") {
t.Error("empty stored certfp should not match empty provided certfp")
}
if CertfpsMatch(opensslFP, badFP) {
t.Error("these certs should not match")
}
}

View File

@ -121,7 +121,7 @@ server:
# one webirc block -- should correspond to one set of gateways # one webirc block -- should correspond to one set of gateways
- -
# tls fingerprint the gateway must connect with to use this webirc block # tls fingerprint the gateway must connect with to use this webirc block
fingerprint: 938dd33f4b76dcaf7ce5eb25c852369cb4b8fb47ba22fc235aa29c6623a5f182 fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# password the gateway uses to connect, made with oragono genpasswd # password the gateway uses to connect, made with oragono genpasswd
password: "$2a$04$sLEFDpIOyUp55e6gTMKbOeroT6tMXTjPFvA0eGvwvImVR9pkwv7ee" password: "$2a$04$sLEFDpIOyUp55e6gTMKbOeroT6tMXTjPFvA0eGvwvImVR9pkwv7ee"
@ -449,6 +449,9 @@ opers:
# generated using "oragono genpasswd" # generated using "oragono genpasswd"
password: "$2a$04$LiytCxaY0lI.guDj2pBN4eLRD5cdM2OLDwqmGAgB6M2OPirbF5Jcu" password: "$2a$04$LiytCxaY0lI.guDj2pBN4eLRD5cdM2OLDwqmGAgB6M2OPirbF5Jcu"
# being logged in with this client cert will let you /OPER without a password
certfp: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# logging, takes inspiration from Insp # logging, takes inspiration from Insp
logging: logging:
- -