Browse Source

move authentication data from Client to Session

tags/v2.0.0-rc1
Shivaram Lingamneni 4 years ago
parent
commit
ad32356e34
7 changed files with 62 additions and 59 deletions
  1. 3
    3
      irc/accounts.go
  2. 20
    12
      irc/client.go
  3. 1
    1
      irc/gateways.go
  4. 2
    0
      irc/getters.go
  5. 21
    35
      irc/handlers.go
  6. 8
    5
      irc/nickserv.go
  7. 7
    3
      irc/server.go

+ 3
- 3
irc/accounts.go View File

@@ -1146,13 +1146,13 @@ func (am *AccountManager) ChannelsForAccount(account string) (channels []string)
1146 1146
 	return unmarshalRegisteredChannels(channelStr)
1147 1147
 }
1148 1148
 
1149
-func (am *AccountManager) AuthenticateByCertFP(client *Client, authzid string) error {
1150
-	if client.certfp == "" {
1149
+func (am *AccountManager) AuthenticateByCertFP(client *Client, certfp, authzid string) error {
1150
+	if certfp == "" {
1151 1151
 		return errAccountInvalidCredentials
1152 1152
 	}
1153 1153
 
1154 1154
 	var account string
1155
-	certFPKey := fmt.Sprintf(keyCertToAccount, client.certfp)
1155
+	certFPKey := fmt.Sprintf(keyCertToAccount, certfp)
1156 1156
 
1157 1157
 	err := am.server.store.View(func(tx *buntdb.Tx) error {
1158 1158
 		account, _ = tx.Get(certFPKey)

+ 20
- 12
irc/client.go View File

@@ -51,7 +51,6 @@ type Client struct {
51 51
 	away               bool
52 52
 	awayMessage        string
53 53
 	brbTimer           BrbTimer
54
-	certfp             string
55 54
 	channels           ChannelSet
56 55
 	ctime              time.Time
57 56
 	destroyed          bool
@@ -77,10 +76,6 @@ type Client struct {
77 76
 	realIP             net.IP
78 77
 	registered         bool
79 78
 	resumeID           string
80
-	saslInProgress     bool
81
-	saslMechanism      string
82
-	saslValue          string
83
-	sentPassCommand    bool
84 79
 	server             *Server
85 80
 	skeleton           string
86 81
 	sessions           []*Session
@@ -93,6 +88,15 @@ type Client struct {
93 88
 	writerSemaphore    utils.Semaphore // tier 1.5
94 89
 }
95 90
 
91
+type saslStatus struct {
92
+	mechanism string
93
+	value     string
94
+}
95
+
96
+func (s *saslStatus) Clear() {
97
+	*s = saslStatus{}
98
+}
99
+
96 100
 // Session is an individual client connection to the server (TCP connection
97 101
 // and associated per-connection data, such as capabilities). There is a
98 102
 // many-one relationship between sessions and clients.
@@ -112,6 +116,10 @@ type Session struct {
112 116
 	fakelag   Fakelag
113 117
 	destroyed uint32
114 118
 
119
+	certfp          string
120
+	sasl            saslStatus
121
+	sentPassCommand bool
122
+
115 123
 	batchCounter uint32
116 124
 
117 125
 	quitMessage string
@@ -271,7 +279,7 @@ func (server *Server) RunClient(conn clientConn, proxyLine string) {
271 279
 	if conn.Config.TLSConfig != nil {
272 280
 		client.SetMode(modes.TLS, true)
273 281
 		// error is not useful to us here anyways so we can ignore it
274
-		client.certfp, _ = socket.CertFP()
282
+		session.certfp, _ = socket.CertFP()
275 283
 	}
276 284
 
277 285
 	if conn.Config.Tor {
@@ -451,18 +459,18 @@ const (
451 459
 	authFailSaslRequired
452 460
 )
453 461
 
454
-func (client *Client) isAuthorized(config *Config, isTor bool) AuthOutcome {
462
+func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome {
455 463
 	saslSent := client.account != ""
456 464
 	// PASS requirement
457
-	if (config.Server.passwordBytes != nil) && !client.sentPassCommand && !(config.Accounts.SkipServerPassword && saslSent) {
465
+	if (config.Server.passwordBytes != nil) && !session.sentPassCommand && !(config.Accounts.SkipServerPassword && saslSent) {
458 466
 		return authFailPass
459 467
 	}
460 468
 	// Tor connections may be required to authenticate with SASL
461
-	if isTor && config.Server.TorListeners.RequireSasl && !saslSent {
469
+	if session.isTor && config.Server.TorListeners.RequireSasl && !saslSent {
462 470
 		return authFailTorSaslRequired
463 471
 	}
464 472
 	// finally, enforce require-sasl
465
-	if config.Accounts.RequireSasl.Enabled && !saslSent && !utils.IPInNets(client.IP(), config.Accounts.RequireSasl.exemptedNets) {
473
+	if config.Accounts.RequireSasl.Enabled && !saslSent && !utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) {
466 474
 		return authFailSaslRequired
467 475
 	}
468 476
 	return authSuccess
@@ -1518,11 +1526,11 @@ func (client *Client) CheckInvited(casefoldedChannel string) (invited bool) {
1518 1526
 // Implements auto-oper by certfp (scans for an auto-eligible operator block that matches
1519 1527
 // the client's cert, then applies it).
1520 1528
 func (client *Client) attemptAutoOper(session *Session) {
1521
-	if client.certfp == "" || client.HasMode(modes.Operator) {
1529
+	if session.certfp == "" || client.HasMode(modes.Operator) {
1522 1530
 		return
1523 1531
 	}
1524 1532
 	for _, oper := range client.server.Config().operators {
1525
-		if oper.Auto && oper.Pass == nil && oper.Fingerprint != "" && oper.Fingerprint == client.certfp {
1533
+		if oper.Auto && oper.Pass == nil && oper.Fingerprint != "" && oper.Fingerprint == session.certfp {
1526 1534
 			rb := NewResponseBuffer(session)
1527 1535
 			applyOper(client, oper, rb)
1528 1536
 			rb.Send(true)

+ 1
- 1
irc/gateways.go View File

@@ -88,7 +88,7 @@ func (client *Client) ApplyProxiedIP(session *Session, proxiedIP string, tls boo
88 88
 	session.proxiedIP = parsedProxiedIP
89 89
 	// nickmask will be updated when the client completes registration
90 90
 	// set tls info
91
-	client.certfp = ""
91
+	session.certfp = ""
92 92
 	client.SetMode(modes.TLS, tls)
93 93
 
94 94
 	return nil, ""

+ 2
- 0
irc/getters.go View File

@@ -65,6 +65,7 @@ type SessionData struct {
65 65
 	atime    time.Time
66 66
 	ip       net.IP
67 67
 	hostname string
68
+	certfp   string
68 69
 }
69 70
 
70 71
 func (client *Client) AllSessionData(currentSession *Session) (data []SessionData, currentIndex int) {
@@ -81,6 +82,7 @@ func (client *Client) AllSessionData(currentSession *Session) (data []SessionDat
81 82
 			atime:    session.atime,
82 83
 			ctime:    session.ctime,
83 84
 			hostname: session.rawHostname,
85
+			certfp:   session.certfp,
84 86
 		}
85 87
 		if session.proxiedIP != nil {
86 88
 			data[i].ip = session.proxiedIP

+ 21
- 35
irc/handlers.go View File

@@ -112,6 +112,7 @@ func sendSuccessfulAccountAuth(client *Client, rb *ResponseBuffer, forNS, forSAS
112 112
 
113 113
 // AUTHENTICATE [<mechanism>|<data>|*]
114 114
 func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
115
+	session := rb.session
115 116
 	config := server.Config()
116 117
 	details := client.Details()
117 118
 
@@ -128,20 +129,17 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage,
128 129
 	// sasl abort
129 130
 	if !server.AccountConfig().AuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" {
130 131
 		rb.Add(nil, server.name, ERR_SASLABORTED, details.nick, client.t("SASL authentication aborted"))
131
-		client.saslInProgress = false
132
-		client.saslMechanism = ""
133
-		client.saslValue = ""
132
+		session.sasl.Clear()
134 133
 		return false
135 134
 	}
136 135
 
137 136
 	// start new sasl session
138
-	if !client.saslInProgress {
137
+	if session.sasl.mechanism == "" {
139 138
 		mechanism := strings.ToUpper(msg.Params[0])
140 139
 		_, mechanismIsEnabled := EnabledSaslMechanisms[mechanism]
141 140
 
142 141
 		if mechanismIsEnabled {
143
-			client.saslInProgress = true
144
-			client.saslMechanism = mechanism
142
+			session.sasl.mechanism = mechanism
145 143
 			if !config.Server.Compatibility.SendUnprefixedSasl {
146 144
 				// normal behavior
147 145
 				rb.Add(nil, server.name, "AUTHENTICATE", "+")
@@ -162,58 +160,46 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage,
162 160
 
163 161
 	if len(rawData) > 400 {
164 162
 		rb.Add(nil, server.name, ERR_SASLTOOLONG, details.nick, client.t("SASL message too long"))
165
-		client.saslInProgress = false
166
-		client.saslMechanism = ""
167
-		client.saslValue = ""
163
+		session.sasl.Clear()
168 164
 		return false
169 165
 	} else if len(rawData) == 400 {
170
-		client.saslValue += rawData
171 166
 		// allow 4 'continuation' lines before rejecting for length
172
-		if len(client.saslValue) > 400*4 {
167
+		if len(session.sasl.value) >= 400*4 {
173 168
 			rb.Add(nil, server.name, ERR_SASLFAIL, details.nick, client.t("SASL authentication failed: Passphrase too long"))
174
-			client.saslInProgress = false
175
-			client.saslMechanism = ""
176
-			client.saslValue = ""
169
+			session.sasl.Clear()
177 170
 			return false
178 171
 		}
172
+		session.sasl.value += rawData
179 173
 		return false
180 174
 	}
181 175
 	if rawData != "+" {
182
-		client.saslValue += rawData
176
+		session.sasl.value += rawData
183 177
 	}
184 178
 
185 179
 	var data []byte
186 180
 	var err error
187
-	if client.saslValue != "+" {
188
-		data, err = base64.StdEncoding.DecodeString(client.saslValue)
181
+	if session.sasl.value != "+" {
182
+		data, err = base64.StdEncoding.DecodeString(session.sasl.value)
189 183
 		if err != nil {
190 184
 			rb.Add(nil, server.name, ERR_SASLFAIL, details.nick, client.t("SASL authentication failed: Invalid b64 encoding"))
191
-			client.saslInProgress = false
192
-			client.saslMechanism = ""
193
-			client.saslValue = ""
185
+			session.sasl.Clear()
194 186
 			return false
195 187
 		}
196 188
 	}
197 189
 
198 190
 	// call actual handler
199
-	handler, handlerExists := EnabledSaslMechanisms[client.saslMechanism]
191
+	handler, handlerExists := EnabledSaslMechanisms[session.sasl.mechanism]
200 192
 
201 193
 	// like 100% not required, but it's good to be safe I guess
202 194
 	if !handlerExists {
203 195
 		rb.Add(nil, server.name, ERR_SASLFAIL, details.nick, client.t("SASL authentication failed"))
204
-		client.saslInProgress = false
205
-		client.saslMechanism = ""
206
-		client.saslValue = ""
196
+		session.sasl.Clear()
207 197
 		return false
208 198
 	}
209 199
 
210 200
 	// let the SASL handler do its thing
211
-	exiting := handler(server, client, client.saslMechanism, data, rb)
212
-
213
-	// wait 'til SASL is done before emptying the sasl vars
214
-	client.saslInProgress = false
215
-	client.saslMechanism = ""
216
-	client.saslValue = ""
201
+	exiting := handler(server, client, session.sasl.mechanism, data, rb)
202
+	session.sasl.Clear()
217 203
 
218 204
 	return exiting
219 205
 }
@@ -270,7 +256,7 @@ func authErrorToMessage(server *Server, err error) (msg string) {
270 256
 
271 257
 // AUTHENTICATE EXTERNAL
272 258
 func authExternalHandler(server *Server, client *Client, mechanism string, value []byte, rb *ResponseBuffer) bool {
273
-	if client.certfp == "" {
259
+	if rb.session.certfp == "" {
274 260
 		rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed, you are not connecting with a certificate"))
275 261
 		return false
276 262
 	}
@@ -287,7 +273,7 @@ func authExternalHandler(server *Server, client *Client, mechanism string, value
287 273
 	}
288 274
 
289 275
 	if err == nil {
290
-		err = server.accounts.AuthenticateByCertFP(client, authzid)
276
+		err = server.accounts.AuthenticateByCertFP(client, rb.session.certfp, authzid)
291 277
 	}
292 278
 
293 279
 	if err != nil {
@@ -2055,7 +2041,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2055 2041
 	oper := server.GetOperator(msg.Params[0])
2056 2042
 	if oper != nil {
2057 2043
 		if oper.Fingerprint != "" {
2058
-			if oper.Fingerprint == client.certfp {
2044
+			if oper.Fingerprint == rb.session.certfp {
2059 2045
 				checkPassed = true
2060 2046
 			} else {
2061 2047
 				checkFailed = true
@@ -2144,7 +2130,7 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2144 2130
 
2145 2131
 	// check the provided password
2146 2132
 	password := []byte(msg.Params[0])
2147
-	client.sentPassCommand = bcrypt.CompareHashAndPassword(serverPassword, password) == nil
2133
+	rb.session.sentPassCommand = bcrypt.CompareHashAndPassword(serverPassword, password) == nil
2148 2134
 
2149 2135
 	// if they failed the check, we'll bounce them later when they try to complete registration
2150 2136
 	return false
@@ -2523,7 +2509,7 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2523 2509
 			if 0 < len(info.Password) && bcrypt.CompareHashAndPassword(info.Password, givenPassword) != nil {
2524 2510
 				continue
2525 2511
 			}
2526
-			if info.Fingerprint != "" && info.Fingerprint != client.certfp {
2512
+			if info.Fingerprint != "" && info.Fingerprint != rb.session.certfp {
2527 2513
 				continue
2528 2514
 			}
2529 2515
 

+ 8
- 5
irc/nickserv.go View File

@@ -605,7 +605,7 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
605 605
 
606 606
 	var username, passphrase string
607 607
 	if len(params) == 1 {
608
-		if client.certfp != "" {
608
+		if rb.session.certfp != "" {
609 609
 			username = params[0]
610 610
 		} else {
611 611
 			// XXX undocumented compatibility mode with other nickservs, allowing
@@ -628,8 +628,8 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
628 628
 	}
629 629
 
630 630
 	// try certfp
631
-	if !loginSuccessful && client.certfp != "" {
632
-		err := server.accounts.AuthenticateByCertFP(client, "")
631
+	if !loginSuccessful && rb.session.certfp != "" {
632
+		err := server.accounts.AuthenticateByCertFP(client, rb.session.certfp, "")
633 633
 		loginSuccessful = (err == nil)
634 634
 	}
635 635
 
@@ -693,7 +693,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
693 693
 		email = params[1]
694 694
 	}
695 695
 
696
-	certfp := client.certfp
696
+	certfp := rb.session.certfp
697 697
 	if passphrase == "*" {
698 698
 		if certfp == "" {
699 699
 			nsNotice(rb, client.t("You must be connected with TLS and a client certificate to do this"))
@@ -733,7 +733,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
733 733
 		}
734 734
 	}
735 735
 
736
-	err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, client.certfp)
736
+	err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, rb.session.certfp)
737 737
 	if err == nil {
738 738
 		if callbackNamespace == "*" {
739 739
 			err = server.accounts.Verify(client, account, "")
@@ -951,6 +951,9 @@ func nsSessionsHandler(server *Server, client *Client, command string, params []
951 951
 		nsNotice(rb, fmt.Sprintf(client.t("Hostname:    %s"), session.hostname))
952 952
 		nsNotice(rb, fmt.Sprintf(client.t("Created at:  %s"), session.ctime.Format(time.RFC1123)))
953 953
 		nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
954
+		if session.certfp != "" {
955
+			nsNotice(rb, fmt.Sprintf(client.t("Certfp:      %s"), session.certfp))
956
+		}
954 957
 	}
955 958
 }
956 959
 

+ 7
- 3
irc/server.go View File

@@ -321,7 +321,7 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
321 321
 
322 322
 	// client MUST send PASS if necessary, or authenticate with SASL if necessary,
323 323
 	// before completing the other registration commands
324
-	authOutcome := c.isAuthorized(server.Config(), session.isTor)
324
+	authOutcome := c.isAuthorized(server.Config(), session)
325 325
 	var quitMessage string
326 326
 	switch authOutcome {
327 327
 	case authFailPass:
@@ -508,8 +508,12 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
508 508
 		rb.Add(nil, client.server.name, RPL_WHOISBOT, cnick, tnick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.Config().Network.Name)))
509 509
 	}
510 510
 
511
-	if target.certfp != "" && (client.HasMode(modes.Operator) || client == target) {
512
-		rb.Add(nil, client.server.name, RPL_WHOISCERTFP, cnick, tnick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), target.certfp))
511
+	if client == target || client.HasMode(modes.Operator) {
512
+		for _, session := range target.Sessions() {
513
+			if session.certfp != "" {
514
+				rb.Add(nil, client.server.name, RPL_WHOISCERTFP, cnick, tnick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), session.certfp))
515
+			}
516
+		}
513 517
 	}
514 518
 	rb.Add(nil, client.server.name, RPL_WHOISIDLE, cnick, tnick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), client.t("seconds idle, signon time"))
515 519
 }

Loading…
Cancel
Save