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
 	delete(client.invitedTo, casefoldedChannel)
1354
 	delete(client.invitedTo, casefoldedChannel)
1355
 	return
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
 	WhoisLine   string `yaml:"whois-line"`
212
 	WhoisLine   string `yaml:"whois-line"`
213
 	Password    string
213
 	Password    string
214
 	Fingerprint string
214
 	Fingerprint string
215
+	Auto        bool
215
 	Modes       string
216
 	Modes       string
216
 }
217
 }
217
 
218
 
461
 	Vhost       string
462
 	Vhost       string
462
 	Pass        []byte
463
 	Pass        []byte
463
 	Fingerprint string
464
 	Fingerprint string
465
+	Auto        bool
464
 	Modes       []modes.ModeChange
466
 	Modes       []modes.ModeChange
465
 }
467
 }
466
 
468
 
477
 		}
479
 		}
478
 		oper.Name = name
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
 		oper.Fingerprint = opConf.Fingerprint
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
 		oper.Vhost = opConf.Vhost
495
 		oper.Vhost = opConf.Vhost
487
 		class, exists := oc[opConf.Class]
496
 		class, exists := oc[opConf.Class]

+ 25
- 15
irc/handlers.go View File

2177
 		return false
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
 	oper := server.GetOperator(msg.Params[0])
2182
 	oper := server.GetOperator(msg.Params[0])
2183
+	authorized := oper != nil
2182
 	if oper != nil {
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
 	if !authorized {
2195
 	if !authorized {
2193
 		return true
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
 	client.SetOper(oper)
2209
 	client.SetOper(oper)
2198
-	if client.NickMaskString() != oldNickmask {
2210
+	newNickmask := client.NickMaskString()
2211
+	if newNickmask != oldNickmask {
2199
 		client.sendChghost(oldNickmask, oper.Vhost)
2212
 		client.sendChghost(oldNickmask, oper.Vhost)
2200
 	}
2213
 	}
2201
 
2214
 
2208
 	copy(modeChanges[1:], oper.Modes)
2221
 	copy(modeChanges[1:], oper.Modes)
2209
 	applied := ApplyUserModeChanges(client, modeChanges, true)
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
 	for _, session := range client.Sessions() {
2228
 	for _, session := range client.Sessions() {
2229
+		// client may now be unthrottled by the fakelag system
2218
 		session.resetFakelag()
2230
 		session.resetFakelag()
2219
 	}
2231
 	}
2220
-
2221
-	return false
2222
 }
2232
 }
2223
 
2233
 
2224
 // PART <channel>{,<channel>} [<reason>]
2234
 // PART <channel>{,<channel>} [<reason>]

+ 12
- 0
irc/responsebuffer.go View File

77
 	rb.AddMessage(ircmsg.MakeMessage(tags, prefix, command, params...))
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
 // AddFromClient adds a new message from a specific client to our queue.
92
 // AddFromClient adds a new message from a specific client to our queue.
81
 func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) {
93
 func (rb *ResponseBuffer) AddFromClient(time time.Time, msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) {
82
 	msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...)
94
 	msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...)

+ 3
- 0
irc/server.go View File

440
 	if modestring != "+" {
440
 	if modestring != "+" {
441
 		session.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, modestring)
441
 		session.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, modestring)
442
 	}
442
 	}
443
+
444
+	c.attemptAutoOper(session)
445
+
443
 	if server.logger.IsLoggingRawIO() {
446
 	if server.logger.IsLoggingRawIO() {
444
 		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."))
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
         # modes are the modes to auto-set upon opering-up
446
         # modes are the modes to auto-set upon opering-up
447
         modes: +is acjknoqtux
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
         password: "$2a$04$LiytCxaY0lI.guDj2pBN4eLRD5cdM2OLDwqmGAgB6M2OPirbF5Jcu"
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
 # logging, takes inspiration from Insp
463
 # logging, takes inspiration from Insp
458
 logging:
464
 logging:

Loading…
Cancel
Save