Changeset 112 in code for trunk


Ignore:
Timestamp:
Mar 16, 2020, 3:16:27 PM (5 years ago)
Author:
contact
Message:

Add downstream SASL support

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/downstream.go

    r111 r112  
    33import (
    44        "crypto/tls"
     5        "encoding/base64"
    56        "fmt"
    67        "io"
     
    1112        "time"
    1213
     14        "github.com/emersion/go-sasl"
    1315        "golang.org/x/crypto/bcrypt"
    1416        "gopkg.in/irc.v3"
     
    7678        capVersion      int
    7779        caps            map[string]bool
     80
     81        saslServer sasl.Server
    7882
    7983        lock        sync.Mutex
     
    343347                        return err
    344348                }
     349        case "AUTHENTICATE":
     350                if !dc.caps["sasl"] {
     351                        return ircError{&irc.Message{
     352                                Command: err_saslfail,
     353                                Params:  []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
     354                        }}
     355                }
     356                if len(msg.Params) == 0 {
     357                        return ircError{&irc.Message{
     358                                Command: err_saslfail,
     359                                Params:  []string{"*", "Missing AUTHENTICATE argument"},
     360                        }}
     361                }
     362                if dc.nick == "" {
     363                        return ircError{&irc.Message{
     364                                Command: err_saslfail,
     365                                Params:  []string{"*", "Expected NICK command before AUTHENTICATE"},
     366                        }}
     367                }
     368
     369                var resp []byte
     370                if dc.saslServer == nil {
     371                        mech := strings.ToUpper(msg.Params[0])
     372                        switch mech {
     373                        case "PLAIN":
     374                                dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
     375                                        return dc.authenticate(username, password)
     376                                }))
     377                        default:
     378                                return ircError{&irc.Message{
     379                                        Command: err_saslfail,
     380                                        Params:  []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
     381                                }}
     382                        }
     383                } else if msg.Params[0] == "*" {
     384                        dc.saslServer = nil
     385                        return ircError{&irc.Message{
     386                                Command: err_saslaborted,
     387                                Params:  []string{"*", "SASL authentication aborted"},
     388                        }}
     389                } else if msg.Params[0] == "+" {
     390                        resp = nil
     391                } else {
     392                        // TODO: multi-line messages
     393                        var err error
     394                        resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
     395                        if err != nil {
     396                                dc.saslServer = nil
     397                                return ircError{&irc.Message{
     398                                        Command: err_saslfail,
     399                                        Params:  []string{"*", "Invalid base64-encoded response"},
     400                                }}
     401                        }
     402                }
     403
     404                challenge, done, err := dc.saslServer.Next(resp)
     405                if err != nil {
     406                        dc.saslServer = nil
     407                        if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
     408                                return ircError{&irc.Message{
     409                                        Command: err_saslfail,
     410                                        Params:  []string{"*", ircErr.Message.Params[1]},
     411                                }}
     412                        }
     413                        dc.SendMessage(&irc.Message{
     414                                Prefix:  dc.srv.prefix(),
     415                                Command: err_saslfail,
     416                                Params:  []string{"*", "SASL error"},
     417                        })
     418                        return fmt.Errorf("SASL authentication failed: %v", err)
     419                } else if done {
     420                        dc.saslServer = nil
     421                        dc.SendMessage(&irc.Message{
     422                                Prefix:  dc.srv.prefix(),
     423                                Command: rpl_loggedin,
     424                                Params:  []string{dc.nick, dc.nick, dc.user.Username, "You are now logged in"},
     425                        })
     426                        dc.SendMessage(&irc.Message{
     427                                Prefix:  dc.srv.prefix(),
     428                                Command: rpl_saslsuccess,
     429                                Params:  []string{dc.nick, "SASL authentication successful"},
     430                        })
     431                } else {
     432                        challengeStr := "+"
     433                        if challenge != nil {
     434                                challengeStr = base64.StdEncoding.EncodeToString(challenge)
     435                        }
     436
     437                        // TODO: multi-line messages
     438                        dc.SendMessage(&irc.Message{
     439                                Prefix:  dc.srv.prefix(),
     440                                Command: "AUTHENTICATE",
     441                                Params:  []string{challengeStr},
     442                        })
     443                }
    345444        default:
    346445                dc.logger.Printf("unhandled message: %v", msg)
     
    371470
    372471                var caps []string
    373                 /*if dc.capVersion >= 302 {
     472                if dc.capVersion >= 302 {
    374473                        caps = append(caps, "sasl=PLAIN")
    375474                } else {
    376475                        caps = append(caps, "sasl")
    377                 }*/
     476                }
    378477
    379478                // TODO: multi-line replies
     
    422521
    423522                        switch name {
    424                         /*case "sasl":
    425                                 dc.caps[name] = enable*/
     523                        case "sasl":
     524                                dc.caps[name] = enable
    426525                        default:
    427526                                ack = false
     
    458557}
    459558
    460 func (dc *downstreamConn) register() error {
    461         username := dc.rawUsername
    462         var networkName string
     559func unmarshalUsername(rawUsername string) (username, network string) {
     560        username = rawUsername
    463561        if i := strings.LastIndexAny(username, "/@"); i >= 0 {
    464                 networkName = username[i+1:]
     562                network = username[i+1:]
    465563        }
    466564        if i := strings.IndexAny(username, "/@"); i >= 0 {
    467565                username = username[:i]
    468566        }
    469         dc.username = "~" + username
    470 
    471         password := dc.password
    472         dc.password = ""
     567        return username, network
     568}
     569
     570func (dc *downstreamConn) setNetwork(networkName string) error {
     571        if networkName == "" {
     572                return nil
     573        }
     574
     575        network := dc.user.getNetwork(networkName)
     576        if network == nil {
     577                addr := networkName
     578                if !strings.ContainsRune(addr, ':') {
     579                        addr = addr + ":6697"
     580                }
     581
     582                dc.logger.Printf("trying to connect to new network %q", addr)
     583                if err := sanityCheckServer(addr); err != nil {
     584                        dc.logger.Printf("failed to connect to %q: %v", addr, err)
     585                        return ircError{&irc.Message{
     586                                Command: irc.ERR_PASSWDMISMATCH,
     587                                Params:  []string{"*", fmt.Sprintf("Failed to connect to %q", networkName)},
     588                        }}
     589                }
     590
     591                dc.logger.Printf("auto-saving network %q", networkName)
     592                var err error
     593                network, err = dc.user.createNetwork(networkName, dc.nick)
     594                if err != nil {
     595                        return err
     596                }
     597        }
     598
     599        dc.network = network
     600        return nil
     601}
     602
     603func (dc *downstreamConn) authenticate(username, password string) error {
     604        username, networkName := unmarshalUsername(username)
    473605
    474606        u := dc.srv.getUser(username)
     
    484616        }
    485617
    486         var network *network
    487         if networkName != "" {
    488                 network = u.getNetwork(networkName)
    489                 if network == nil {
    490                         addr := networkName
    491                         if !strings.ContainsRune(addr, ':') {
    492                                 addr = addr + ":6697"
    493                         }
    494 
    495                         dc.logger.Printf("trying to connect to new network %q", addr)
    496                         if err := sanityCheckServer(addr); err != nil {
    497                                 dc.logger.Printf("failed to connect to %q: %v", addr, err)
    498                                 return ircError{&irc.Message{
    499                                         Command: irc.ERR_PASSWDMISMATCH,
    500                                         Params:  []string{"*", fmt.Sprintf("Failed to connect to %q", networkName)},
    501                                 }}
    502                         }
    503 
    504                         dc.logger.Printf("auto-saving network %q", networkName)
    505                         network, err = u.createNetwork(networkName, dc.nick)
    506                         if err != nil {
    507                                 return err
    508                         }
     618        dc.user = u
     619
     620        return dc.setNetwork(networkName)
     621}
     622
     623func (dc *downstreamConn) register() error {
     624        password := dc.password
     625        dc.password = ""
     626        if dc.user == nil {
     627                if err := dc.authenticate(dc.rawUsername, password); err != nil {
     628                        return err
     629                }
     630        } else if dc.network == nil {
     631                _, networkName := unmarshalUsername(dc.rawUsername)
     632                if err := dc.setNetwork(networkName); err != nil {
     633                        return err
    509634                }
    510635        }
    511636
    512637        dc.registered = true
    513         dc.user = u
    514         dc.network = network
    515 
    516         u.lock.Lock()
    517         firstDownstream := len(u.downstreamConns) == 0
    518         u.downstreamConns = append(u.downstreamConns, dc)
    519         u.lock.Unlock()
     638        dc.username = dc.user.Username
     639
     640        dc.user.lock.Lock()
     641        firstDownstream := len(dc.user.downstreamConns) == 0
     642        dc.user.downstreamConns = append(dc.user.downstreamConns, dc)
     643        dc.user.lock.Unlock()
    520644
    521645        dc.SendMessage(&irc.Message{
Note: See TracChangeset for help on using the changeset viewer.