Browse Source

implement review feedback

1. If both fingerprint and hash are specified, require both instead of either
2. Implement auto-oper on connect
tags/v2.0.0-rc1
Shivaram Lingamneni 4 years ago
parent
commit
b717402b5e
6 changed files with 78 additions and 23 deletions
  1. 15
    0
      irc/client.go
  2. 12
    3
      irc/config.go
  3. 25
    15
      irc/handlers.go
  4. 12
    0
      irc/responsebuffer.go
  5. 3
    0
      irc/server.go
  6. 11
    5
      oragono.yaml

+ 15
- 0
irc/client.go View File

@@ -1354,3 +1354,18 @@ func (client *Client) CheckInvited(casefoldedChannel string) (invited bool) {
1354 1354
 	delete(client.invitedTo, casefoldedChannel)
1355 1355
 	return
1356 1356
 }
1357
+
1358
+// Implements auto-oper by certfp (scans for an auto-eligible operator block that matches
1359
+// the client's cert, then applies it).
1360
+func (client *Client) attemptAutoOper(session *Session) {
1361
+	if client.certfp == "" || client.HasMode(modes.Operator) {
1362
+		return
1363
+	}
1364
+	for _, oper := range client.server.Config().operators {
1365
+		if oper.Auto && oper.Pass == nil && utils.CertfpsMatch(oper.Fingerprint, client.certfp) {
1366
+			rb := NewResponseBuffer(session)
1367
+			applyOper(client, oper, rb)
1368
+			rb.Send(true)
1369
+		}
1370
+	}
1371
+}

+ 12
- 3
irc/config.go View File

@@ -212,6 +212,7 @@ type OperConfig struct {
212 212
 	WhoisLine   string `yaml:"whois-line"`
213 213
 	Password    string
214 214
 	Fingerprint string
215
+	Auto        bool
215 216
 	Modes       string
216 217
 }
217 218
 
@@ -461,6 +462,7 @@ type Oper struct {
461 462
 	Vhost       string
462 463
 	Pass        []byte
463 464
 	Fingerprint string
465
+	Auto        bool
464 466
 	Modes       []modes.ModeChange
465 467
 }
466 468
 
@@ -477,11 +479,18 @@ func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error
477 479
 		}
478 480
 		oper.Name = name
479 481
 
480
-		oper.Pass, err = decodeLegacyPasswordHash(opConf.Password)
481
-		if err != nil {
482
-			return nil, err
482
+		if opConf.Password != "" {
483
+			oper.Pass, err = decodeLegacyPasswordHash(opConf.Password)
484
+			if err != nil {
485
+				return nil, err
486
+			}
483 487
 		}
484 488
 		oper.Fingerprint = opConf.Fingerprint
489
+		oper.Auto = opConf.Auto
490
+
491
+		if oper.Pass == nil && oper.Fingerprint == "" {
492
+			return nil, fmt.Errorf("Oper %s has neither a password nor a fingerprint", name)
493
+		}
485 494
 
486 495
 		oper.Vhost = opConf.Vhost
487 496
 		class, exists := oc[opConf.Class]

+ 25
- 15
irc/handlers.go View File

@@ -2177,14 +2177,19 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2177 2177
 		return false
2178 2178
 	}
2179 2179
 
2180
-	authorized := false
2180
+	// must have a matching oper block and not fail any enabled checks
2181
+	// (config validation ensures that there is at least one check)
2181 2182
 	oper := server.GetOperator(msg.Params[0])
2183
+	authorized := oper != nil
2182 2184
 	if oper != nil {
2183
-		if utils.CertfpsMatch(oper.Fingerprint, client.certfp) {
2184
-			authorized = true
2185
-		} else if 1 < len(msg.Params) {
2186
-			password := []byte(msg.Params[1])
2187
-			authorized = (bcrypt.CompareHashAndPassword(oper.Pass, password) == nil)
2185
+		if oper.Fingerprint != "" && !utils.CertfpsMatch(oper.Fingerprint, client.certfp) {
2186
+			authorized = false
2187
+		} else if oper.Pass != nil {
2188
+			if len(msg.Params) == 1 {
2189
+				authorized = false
2190
+			} else if bcrypt.CompareHashAndPassword(oper.Pass, []byte(msg.Params[1])) != nil {
2191
+				authorized = false
2192
+			}
2188 2193
 		}
2189 2194
 	}
2190 2195
 	if !authorized {
@@ -2193,9 +2198,17 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2193 2198
 		return true
2194 2199
 	}
2195 2200
 
2196
-	oldNickmask := client.NickMaskString()
2201
+	applyOper(client, oper, rb)
2202
+	return false
2203
+}
2204
+
2205
+// applies operator status to a client, who MUST NOT already be an operator
2206
+func applyOper(client *Client, oper *Oper, rb *ResponseBuffer) {
2207
+	details := client.Details()
2208
+	oldNickmask := details.nickMask
2197 2209
 	client.SetOper(oper)
2198
-	if client.NickMaskString() != oldNickmask {
2210
+	newNickmask := client.NickMaskString()
2211
+	if newNickmask != oldNickmask {
2199 2212
 		client.sendChghost(oldNickmask, oper.Vhost)
2200 2213
 	}
2201 2214
 
@@ -2208,17 +2221,14 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2208 2221
 	copy(modeChanges[1:], oper.Modes)
2209 2222
 	applied := ApplyUserModeChanges(client, modeChanges, true)
2210 2223
 
2211
-	rb.Add(nil, server.name, RPL_YOUREOPER, client.nick, client.t("You are now an IRC operator"))
2212
-	rb.Add(nil, server.name, "MODE", client.nick, applied.String())
2213
-
2214
-	server.snomasks.Send(sno.LocalOpers, fmt.Sprintf(ircfmt.Unescape("Client opered up $c[grey][$r%s$c[grey], $r%s$c[grey]]"), client.nickMaskString, oper.Name))
2224
+	client.server.snomasks.Send(sno.LocalOpers, fmt.Sprintf(ircfmt.Unescape("Client opered up $c[grey][$r%s$c[grey], $r%s$c[grey]]"), client.nickMaskString, oper.Name))
2215 2225
 
2216
-	// client may now be unthrottled by the fakelag system
2226
+	rb.Broadcast(nil, client.server.name, RPL_YOUREOPER, details.nick, client.t("You are now an IRC operator"))
2227
+	rb.Broadcast(nil, client.server.name, "MODE", details.nick, applied.String())
2217 2228
 	for _, session := range client.Sessions() {
2229
+		// client may now be unthrottled by the fakelag system
2218 2230
 		session.resetFakelag()
2219 2231
 	}
2220
-
2221
-	return false
2222 2232
 }
2223 2233
 
2224 2234
 // PART <channel>{,<channel>} [<reason>]

+ 12
- 0
irc/responsebuffer.go View File

@@ -77,6 +77,18 @@ func (rb *ResponseBuffer) Add(tags map[string]string, prefix string, command str
77 77
 	rb.AddMessage(ircmsg.MakeMessage(tags, prefix, command, params...))
78 78
 }
79 79
 
80
+// Broadcast adds a standard new message to our queue, then sends an unlabeled copy
81
+// to all other sessions.
82
+func (rb *ResponseBuffer) Broadcast(tags map[string]string, prefix string, command string, params ...string) {
83
+	// can't reuse the IrcMessage object because of tag pollution :-\
84
+	rb.Add(tags, prefix, command, params...)
85
+	for _, session := range rb.session.client.Sessions() {
86
+		if session != rb.session {
87
+			session.Send(tags, prefix, command, params...)
88
+		}
89
+	}
90
+}
91
+
80 92
 // AddFromClient adds a new message from a specific client to our queue.
81 93
 func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) {
82 94
 	msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...)

+ 3
- 0
irc/server.go View File

@@ -440,6 +440,9 @@ func (server *Server) playRegistrationBurst(session *Session) {
440 440
 	if modestring != "+" {
441 441
 		session.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, modestring)
442 442
 	}
443
+
444
+	c.attemptAutoOper(session)
445
+
443 446
 	if server.logger.IsLoggingRawIO() {
444 447
 		session.Send(nil, c.server.name, "NOTICE", d.nick, c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
445 448
 	}

+ 11
- 5
oragono.yaml View File

@@ -446,13 +446,19 @@ opers:
446 446
         # modes are the modes to auto-set upon opering-up
447 447
         modes: +is acjknoqtux
448 448
 
449
-        # password to login with /OPER command
450
-        # generated using  "oragono genpasswd"
449
+        # operators can be authenticated either by password (with the /OPER command),
450
+        # or by certificate fingerprint, or both. if a password hash is set, then a
451
+        # password is required to oper up (e.g., /OPER dan mypassword). to generate
452
+        # the hash, use `oragono genpasswd`.
451 453
         password: "$2a$04$LiytCxaY0lI.guDj2pBN4eLRD5cdM2OLDwqmGAgB6M2OPirbF5Jcu"
452 454
 
453
-        # if you're logged in using the client cert with this SHA-256 fingerprint,
454
-        # you'll be able to /OPER without a password
455
-        fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
455
+        # if a SHA-256 certificate fingerprint is configured here, then it will be
456
+        # required to /OPER. if you comment out the password hash above, then you can
457
+        # /OPER without a password.
458
+        #fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
459
+        # if 'auto' is set (and no password hash is set), operator permissions will be
460
+        # granted automatically as soon as you connect with the right fingerprint.
461
+        #auto: true
456 462
 
457 463
 # logging, takes inspiration from Insp
458 464
 logging:

Loading…
Cancel
Save