- Timestamp:
- Mar 13, 2020, 2:12:44 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/db.go
r93 r95 11 11 Username string 12 12 Password string // hashed 13 } 14 15 type SASL struct { 16 Mechanism string 17 18 Plain struct { 19 Username string 20 Password string 21 } 13 22 } 14 23 … … 20 29 Realname string 21 30 Pass string 31 SASL SASL 22 32 } 23 33 … … 44 54 defer db.lock.Unlock() 45 55 return db.Close() 56 } 57 58 func fromStringPtr(ptr *string) string { 59 if ptr == nil { 60 return "" 61 } 62 return *ptr 63 } 64 65 func toStringPtr(s string) *string { 66 if s == "" { 67 return nil 68 } 69 return &s 46 70 } 47 71 … … 63 87 return nil, err 64 88 } 65 if password != nil { 66 user.Password = *password 67 } 89 user.Password = fromStringPtr(password) 68 90 users = append(users, user) 69 91 } … … 79 101 defer db.lock.Unlock() 80 102 81 var password *string 82 if user.Password != "" { 83 password = &user.Password 84 } 103 password := toStringPtr(user.Password) 85 104 _, err := db.db.Exec("INSERT INTO User(username, password) VALUES (?, ?)", user.Username, password) 86 105 return err … … 91 110 defer db.lock.RUnlock() 92 111 93 rows, err := db.db.Query("SELECT id, addr, nick, username, realname, pass FROM Network WHERE user = ?", username) 112 rows, err := db.db.Query(`SELECT id, addr, nick, username, realname, pass, 113 sasl_mechanism, sasl_plain_username, sasl_plain_password 114 FROM Network 115 WHERE user = ?`, 116 username) 94 117 if err != nil { 95 118 return nil, err … … 101 124 var net Network 102 125 var username, realname, pass *string 103 if err := rows.Scan(&net.ID, &net.Addr, &net.Nick, &username, &realname, &pass); err != nil { 126 var saslMechanism, saslPlainUsername, saslPlainPassword *string 127 err := rows.Scan(&net.ID, &net.Addr, &net.Nick, &username, &realname, 128 &pass, &saslMechanism, &saslPlainUsername, &saslPlainPassword) 129 if err != nil { 104 130 return nil, err 105 131 } 106 if username != nil { 107 net.Username = *username 108 } 109 if realname != nil { 110 net.Realname = *realname 111 } 112 if pass != nil { 113 net.Pass = *pass 114 } 132 net.Username = fromStringPtr(username) 133 net.Realname = fromStringPtr(realname) 134 net.Pass = fromStringPtr(pass) 135 net.SASL.Mechanism = fromStringPtr(saslMechanism) 136 net.SASL.Plain.Username = fromStringPtr(saslPlainUsername) 137 net.SASL.Plain.Password = fromStringPtr(saslPlainPassword) 115 138 networks = append(networks, net) 116 139 } … … 126 149 defer db.lock.Unlock() 127 150 128 var netUsername, realname, pass *string 129 if network.Username != "" { 130 netUsername = &network.Username 131 } 132 if network.Realname != "" { 133 realname = &network.Realname 134 } 135 if network.Pass != "" { 136 pass = &network.Pass 151 netUsername := toStringPtr(network.Username) 152 realname := toStringPtr(network.Realname) 153 pass := toStringPtr(network.Pass) 154 155 var saslMechanism, saslPlainUsername, saslPlainPassword *string 156 if network.SASL.Mechanism != "" { 157 saslMechanism = &network.SASL.Mechanism 158 switch network.SASL.Mechanism { 159 case "PLAIN": 160 saslPlainUsername = toStringPtr(network.SASL.Plain.Username) 161 saslPlainPassword = toStringPtr(network.SASL.Plain.Password) 162 } 137 163 } 138 164 … … 140 166 if network.ID != 0 { 141 167 _, err = db.db.Exec(`UPDATE Network 142 SET addr = ?, nick = ?, username = ?, realname = ?, pass = ? 168 SET addr = ?, nick = ?, username = ?, realname = ?, pass = ?, 169 sasl_mechanism = ?, sasl_plain_username = ?, sasl_plain_password = ? 143 170 WHERE id = ?`, 144 network.Addr, network.Nick, netUsername, realname, pass, network.ID) 171 network.Addr, network.Nick, netUsername, realname, pass, 172 saslMechanism, saslPlainUsername, saslPlainPassword, network.ID) 145 173 } else { 146 174 var res sql.Result 147 175 res, err = db.db.Exec(`INSERT INTO Network(user, addr, nick, username, 148 realname, pass) 149 VALUES (?, ?, ?, ?, ?, ?)`, 150 username, network.Addr, network.Nick, netUsername, realname, pass) 176 realname, pass, sasl_mechanism, sasl_plain_username, 177 sasl_plain_password) 178 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, 179 username, network.Addr, network.Nick, netUsername, realname, pass, 180 saslMechanism, saslPlainUsername, saslPlainPassword) 151 181 if err != nil { 152 182 return err -
trunk/downstream.go
r93 r95 382 382 } 383 383 384 dc.logger.Printf("trying to connect to new upstream server%q", addr)384 dc.logger.Printf("trying to connect to new network %q", addr) 385 385 if err := sanityCheckServer(addr); err != nil { 386 386 dc.logger.Printf("failed to connect to %q: %v", addr, err) … … 391 391 } 392 392 393 dc.logger.Printf("auto- adding network %q", networkName)393 dc.logger.Printf("auto-saving network %q", networkName) 394 394 network, err = u.createNetwork(networkName, dc.nick) 395 395 if err != nil { … … 619 619 } 620 620 621 if upstreamName == "NickServ" { 622 dc.handleNickServPRIVMSG(uc, text) 623 } 624 621 625 uc.SendMessage(&irc.Message{ 622 626 Command: "PRIVMSG", … … 630 634 return nil 631 635 } 636 637 func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) { 638 username, password, ok := parseNickServCredentials(text, uc.nick) 639 if !ok { 640 return 641 } 642 643 dc.logger.Printf("auto-saving NickServ credentials with username %q", username) 644 n := uc.network 645 n.SASL.Mechanism = "PLAIN" 646 n.SASL.Plain.Username = username 647 n.SASL.Plain.Password = password 648 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil { 649 dc.logger.Printf("failed to save NickServ credentials: %v", err) 650 } 651 } 652 653 func parseNickServCredentials(text, nick string) (username, password string, ok bool) { 654 fields := strings.Fields(text) 655 if len(fields) < 2 { 656 return "", "", false 657 } 658 cmd := strings.ToUpper(fields[0]) 659 params := fields[1:] 660 switch cmd { 661 case "REGISTER": 662 username = nick 663 password = params[0] 664 case "IDENTIFY": 665 if len(params) == 1 { 666 username = nick 667 } else { 668 username = params[0] 669 } 670 password = params[1] 671 } 672 return username, password, true 673 } -
trunk/go.mod
r84 r95 4 4 5 5 require ( 6 github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b 6 7 github.com/mattn/go-sqlite3 v2.0.3+incompatible 7 8 golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 -
trunk/go.sum
r84 r95 1 github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs= 2 github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k= 1 3 github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c= 2 4 github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -
trunk/irc.go
r43 r95 12 12 rpl_globalusers = "266" 13 13 rpl_topicwhotime = "333" 14 rpl_loggedin = "900" 15 rpl_loggedout = "901" 16 err_nicklocked = "902" 17 rpl_saslsuccess = "903" 18 err_saslfail = "904" 19 err_sasltoolong = "905" 20 err_saslaborted = "906" 21 err_saslalready = "907" 22 rpl_saslmechs = "908" 14 23 ) 15 24 -
trunk/schema.sql
r94 r95 12 12 realname VARCHAR(255), 13 13 pass VARCHAR(255), 14 sasl_mechanism VARCHAR(255), 15 sasl_plain_username VARCHAR(255), 16 sasl_plain_password VARCHAR(255), 14 17 FOREIGN KEY(user) REFERENCES User(username), 15 18 UNIQUE(user, addr, nick) -
trunk/upstream.go
r93 r95 3 3 import ( 4 4 "crypto/tls" 5 "encoding/base64" 5 6 "fmt" 6 7 "io" … … 10 11 "time" 11 12 13 "github.com/emersion/go-sasl" 12 14 "gopkg.in/irc.v3" 13 15 ) … … 49 51 history map[string]uint64 50 52 caps map[string]string 53 54 saslClient sasl.Client 55 saslStarted bool 51 56 } 52 57 … … 170 175 uc.logger.Print(msg) 171 176 case "CAP": 172 if len(msg.Params) < 2 { 173 return newNeedMoreParamsError(msg.Command) 174 } 175 caps := strings.Fields(msg.Params[len(msg.Params)-1]) 176 more := msg.Params[len(msg.Params)-2] == "*" 177 178 for _, s := range caps { 179 kv := strings.SplitN(s, "=", 2) 180 k := strings.ToLower(kv[0]) 181 var v string 182 if len(kv) >= 2 { 183 v = kv[1] 184 } 185 uc.caps[k] = v 186 } 187 188 if !more { 177 var subCmd string 178 if err := parseMessageParams(msg, nil, &subCmd); err != nil { 179 return err 180 } 181 subCmd = strings.ToUpper(subCmd) 182 subParams := msg.Params[2:] 183 switch subCmd { 184 case "LS": 185 if len(subParams) < 1 { 186 return newNeedMoreParamsError(msg.Command) 187 } 188 caps := strings.Fields(subParams[len(subParams)-1]) 189 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*" 190 191 for _, s := range caps { 192 kv := strings.SplitN(s, "=", 2) 193 k := strings.ToLower(kv[0]) 194 var v string 195 if len(kv) == 2 { 196 v = kv[1] 197 } 198 uc.caps[k] = v 199 } 200 201 if more { 202 break // wait to receive all capabilities 203 } 204 205 if uc.requestSASL() { 206 uc.SendMessage(&irc.Message{ 207 Command: "CAP", 208 Params: []string{"REQ", "sasl"}, 209 }) 210 break // we'll send CAP END after authentication is completed 211 } 212 189 213 uc.SendMessage(&irc.Message{ 190 214 Command: "CAP", 191 215 Params: []string{"END"}, 192 216 }) 193 } 217 case "ACK", "NAK": 218 if len(subParams) < 1 { 219 return newNeedMoreParamsError(msg.Command) 220 } 221 caps := strings.Fields(subParams[0]) 222 223 for _, name := range caps { 224 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil { 225 return err 226 } 227 } 228 229 if uc.saslClient == nil { 230 uc.SendMessage(&irc.Message{ 231 Command: "CAP", 232 Params: []string{"END"}, 233 }) 234 } 235 default: 236 uc.logger.Printf("unhandled message: %v", msg) 237 } 238 case "AUTHENTICATE": 239 if uc.saslClient == nil { 240 return fmt.Errorf("received unexpected AUTHENTICATE message") 241 } 242 243 // TODO: if a challenge is 400 bytes long, buffer it 244 var challengeStr string 245 if err := parseMessageParams(msg, &challengeStr); err != nil { 246 uc.SendMessage(&irc.Message{ 247 Command: "AUTHENTICATE", 248 Params: []string{"*"}, 249 }) 250 return err 251 } 252 253 var challenge []byte 254 if challengeStr != "+" { 255 var err error 256 challenge, err = base64.StdEncoding.DecodeString(challengeStr) 257 if err != nil { 258 uc.SendMessage(&irc.Message{ 259 Command: "AUTHENTICATE", 260 Params: []string{"*"}, 261 }) 262 return err 263 } 264 } 265 266 var resp []byte 267 var err error 268 if !uc.saslStarted { 269 _, resp, err = uc.saslClient.Start() 270 uc.saslStarted = true 271 } else { 272 resp, err = uc.saslClient.Next(challenge) 273 } 274 if err != nil { 275 uc.SendMessage(&irc.Message{ 276 Command: "AUTHENTICATE", 277 Params: []string{"*"}, 278 }) 279 return err 280 } 281 282 // TODO: send response in multiple chunks if >= 400 bytes 283 var respStr = "+" 284 if resp != nil { 285 respStr = base64.StdEncoding.EncodeToString(resp) 286 } 287 288 uc.SendMessage(&irc.Message{ 289 Command: "AUTHENTICATE", 290 Params: []string{respStr}, 291 }) 292 case rpl_loggedin: 293 var account string 294 if err := parseMessageParams(msg, nil, nil, &account); err != nil { 295 return err 296 } 297 uc.logger.Printf("logged in with account %q", account) 298 case rpl_loggedout: 299 uc.logger.Printf("logged out") 300 case err_nicklocked, rpl_saslsuccess, err_saslfail, err_sasltoolong, err_saslaborted: 301 var info string 302 if err := parseMessageParams(msg, nil, &info); err != nil { 303 return err 304 } 305 switch msg.Command { 306 case err_nicklocked: 307 uc.logger.Printf("invalid nick used with SASL authentication: %v", info) 308 case err_saslfail: 309 uc.logger.Printf("SASL authentication failed: %v", info) 310 case err_sasltoolong: 311 uc.logger.Printf("SASL message too long: %v", info) 312 } 313 314 uc.saslClient = nil 315 uc.saslStarted = false 316 317 uc.SendMessage(&irc.Message{ 318 Command: "CAP", 319 Params: []string{"END"}, 320 }) 194 321 case irc.RPL_WELCOME: 195 322 uc.registered = true … … 440 567 // Ignore 441 568 default: 442 uc.logger.Printf("unhandled upstreammessage: %v", msg)569 uc.logger.Printf("unhandled message: %v", msg) 443 570 } 444 571 return nil … … 478 605 } 479 606 607 func (uc *upstreamConn) requestSASL() bool { 608 if uc.network.SASL.Mechanism == "" { 609 return false 610 } 611 612 v, ok := uc.caps["sasl"] 613 if !ok { 614 return false 615 } 616 if v != "" { 617 mechanisms := strings.Split(v, ",") 618 found := false 619 for _, mech := range mechanisms { 620 if strings.EqualFold(mech, uc.network.SASL.Mechanism) { 621 found = true 622 break 623 } 624 } 625 if !found { 626 return false 627 } 628 } 629 630 return true 631 } 632 633 func (uc *upstreamConn) handleCapAck(name string, ok bool) error { 634 auth := &uc.network.SASL 635 switch name { 636 case "sasl": 637 if !ok { 638 uc.logger.Printf("server refused to acknowledge the SASL capability") 639 return nil 640 } 641 642 switch auth.Mechanism { 643 case "PLAIN": 644 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username) 645 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password) 646 default: 647 return fmt.Errorf("unsupported SASL mechanism %q", name) 648 } 649 650 uc.SendMessage(&irc.Message{ 651 Command: "AUTHENTICATE", 652 Params: []string{auth.Mechanism}, 653 }) 654 } 655 return nil 656 } 657 480 658 func (uc *upstreamConn) readMessages() error { 481 659 for {
Note:
See TracChangeset
for help on using the changeset viewer.