Added files.
This commit is contained in:
148
imapserver/authenticate.go
Normal file
148
imapserver/authenticate.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package imapserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-sasl"
|
||||
|
||||
"github.com/emersion/go-imap/v2"
|
||||
"github.com/emersion/go-imap/v2/internal"
|
||||
"github.com/emersion/go-imap/v2/internal/imapwire"
|
||||
)
|
||||
|
||||
func (c *Conn) handleAuthenticate(tag string, dec *imapwire.Decoder) error {
|
||||
var mech string
|
||||
if !dec.ExpectSP() || !dec.ExpectAtom(&mech) {
|
||||
return dec.Err()
|
||||
}
|
||||
mech = strings.ToUpper(mech)
|
||||
|
||||
var initialResp []byte
|
||||
if dec.SP() {
|
||||
var initialRespStr string
|
||||
if !dec.ExpectText(&initialRespStr) {
|
||||
return dec.Err()
|
||||
}
|
||||
var err error
|
||||
initialResp, err = internal.DecodeSASL(initialRespStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !dec.ExpectCRLF() {
|
||||
return dec.Err()
|
||||
}
|
||||
|
||||
if err := c.checkState(imap.ConnStateNotAuthenticated); err != nil {
|
||||
return err
|
||||
}
|
||||
if !c.canAuth() {
|
||||
return &imap.Error{
|
||||
Type: imap.StatusResponseTypeNo,
|
||||
Code: imap.ResponseCodePrivacyRequired,
|
||||
Text: "TLS is required to authenticate",
|
||||
}
|
||||
}
|
||||
|
||||
var saslServer sasl.Server
|
||||
if authSess, ok := c.session.(SessionSASL); ok {
|
||||
var err error
|
||||
saslServer, err = authSess.Authenticate(mech)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if mech != "PLAIN" {
|
||||
return &imap.Error{
|
||||
Type: imap.StatusResponseTypeNo,
|
||||
Text: "SASL mechanism not supported",
|
||||
}
|
||||
}
|
||||
saslServer = sasl.NewPlainServer(func(identity, username, password string) error {
|
||||
if identity != "" && identity != username {
|
||||
return &imap.Error{
|
||||
Type: imap.StatusResponseTypeNo,
|
||||
Code: imap.ResponseCodeAuthorizationFailed,
|
||||
Text: "SASL identity not supported",
|
||||
}
|
||||
}
|
||||
return c.session.Login(username, password)
|
||||
})
|
||||
}
|
||||
|
||||
enc := newResponseEncoder(c)
|
||||
defer enc.end()
|
||||
|
||||
resp := initialResp
|
||||
for {
|
||||
challenge, done, err := saslServer.Next(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if done {
|
||||
break
|
||||
}
|
||||
|
||||
var challengeStr string
|
||||
if challenge != nil {
|
||||
challengeStr = internal.EncodeSASL(challenge)
|
||||
}
|
||||
if err := writeContReq(enc.Encoder, challengeStr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encodedResp, isPrefix, err := c.br.ReadLine()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if isPrefix {
|
||||
return fmt.Errorf("SASL response too long")
|
||||
} else if string(encodedResp) == "*" {
|
||||
return &imap.Error{
|
||||
Type: imap.StatusResponseTypeBad,
|
||||
Text: "AUTHENTICATE cancelled",
|
||||
}
|
||||
}
|
||||
|
||||
resp, err = decodeSASL(string(encodedResp))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.state = imap.ConnStateAuthenticated
|
||||
text := fmt.Sprintf("%v authentication successful", mech)
|
||||
return writeCapabilityOK(enc.Encoder, tag, c.availableCaps(), text)
|
||||
}
|
||||
|
||||
func decodeSASL(s string) ([]byte, error) {
|
||||
b, err := internal.DecodeSASL(s)
|
||||
if err != nil {
|
||||
return nil, &imap.Error{
|
||||
Type: imap.StatusResponseTypeBad,
|
||||
Text: "Malformed SASL response",
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (c *Conn) handleUnauthenticate(dec *imapwire.Decoder) error {
|
||||
if !dec.ExpectCRLF() {
|
||||
return dec.Err()
|
||||
}
|
||||
if err := c.checkState(imap.ConnStateAuthenticated); err != nil {
|
||||
return err
|
||||
}
|
||||
session, ok := c.session.(SessionUnauthenticate)
|
||||
if !ok {
|
||||
return newClientBugError("UNAUTHENTICATE is not supported")
|
||||
}
|
||||
if err := session.Unauthenticate(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.state = imap.ConnStateNotAuthenticated
|
||||
c.mutex.Lock()
|
||||
c.enabled = make(imap.CapSet)
|
||||
c.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user