aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/emersion/go-sasl
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/emersion/go-sasl')
-rw-r--r--vendor/github.com/emersion/go-sasl/.build.yml19
-rw-r--r--vendor/github.com/emersion/go-sasl/.gitignore24
-rw-r--r--vendor/github.com/emersion/go-sasl/LICENSE21
-rw-r--r--vendor/github.com/emersion/go-sasl/README.md17
-rw-r--r--vendor/github.com/emersion/go-sasl/anonymous.go56
-rw-r--r--vendor/github.com/emersion/go-sasl/external.go26
-rw-r--r--vendor/github.com/emersion/go-sasl/login.go89
-rw-r--r--vendor/github.com/emersion/go-sasl/oauthbearer.go191
-rw-r--r--vendor/github.com/emersion/go-sasl/plain.go77
-rw-r--r--vendor/github.com/emersion/go-sasl/sasl.go45
10 files changed, 565 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-sasl/.build.yml b/vendor/github.com/emersion/go-sasl/.build.yml
new file mode 100644
index 0000000..daa6006
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/.build.yml
@@ -0,0 +1,19 @@
+image: alpine/latest
+packages:
+ - go
+ # Required by codecov
+ - bash
+ - findutils
+sources:
+ - https://github.com/emersion/go-sasl
+tasks:
+ - build: |
+ cd go-sasl
+ go build -v ./...
+ - test: |
+ cd go-sasl
+ go test -coverprofile=coverage.txt -covermode=atomic ./...
+ - upload-coverage: |
+ cd go-sasl
+ export CODECOV_TOKEN=3f257f71-a128-4834-8f68-2b534e9f4cb1
+ curl -s https://codecov.io/bash | bash
diff --git a/vendor/github.com/emersion/go-sasl/.gitignore b/vendor/github.com/emersion/go-sasl/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/emersion/go-sasl/LICENSE b/vendor/github.com/emersion/go-sasl/LICENSE
new file mode 100644
index 0000000..dc1922e
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 emersion
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/emersion/go-sasl/README.md b/vendor/github.com/emersion/go-sasl/README.md
new file mode 100644
index 0000000..1f8a682
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/README.md
@@ -0,0 +1,17 @@
+# go-sasl
+
+[![GoDoc](https://godoc.org/github.com/emersion/go-sasl?status.svg)](https://godoc.org/github.com/emersion/go-sasl)
+[![Build Status](https://travis-ci.org/emersion/go-sasl.svg?branch=master)](https://travis-ci.org/emersion/go-sasl)
+
+A [SASL](https://tools.ietf.org/html/rfc4422) library written in Go.
+
+Implemented mechanisms:
+* [ANONYMOUS](https://tools.ietf.org/html/rfc4505)
+* [EXTERNAL](https://tools.ietf.org/html/rfc4422#appendix-A)
+* [LOGIN](https://tools.ietf.org/html/draft-murchison-sasl-login-00) (obsolete, use PLAIN instead)
+* [PLAIN](https://tools.ietf.org/html/rfc4616)
+* [OAUTHBEARER](https://tools.ietf.org/html/rfc7628)
+
+## License
+
+MIT
diff --git a/vendor/github.com/emersion/go-sasl/anonymous.go b/vendor/github.com/emersion/go-sasl/anonymous.go
new file mode 100644
index 0000000..8ccb817
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/anonymous.go
@@ -0,0 +1,56 @@
+package sasl
+
+// The ANONYMOUS mechanism name.
+const Anonymous = "ANONYMOUS"
+
+type anonymousClient struct {
+ Trace string
+}
+
+func (c *anonymousClient) Start() (mech string, ir []byte, err error) {
+ mech = Anonymous
+ ir = []byte(c.Trace)
+ return
+}
+
+func (c *anonymousClient) Next(challenge []byte) (response []byte, err error) {
+ return nil, ErrUnexpectedServerChallenge
+}
+
+// A client implementation of the ANONYMOUS authentication mechanism, as
+// described in RFC 4505.
+func NewAnonymousClient(trace string) Client {
+ return &anonymousClient{trace}
+}
+
+// Get trace information from clients logging in anonymously.
+type AnonymousAuthenticator func(trace string) error
+
+type anonymousServer struct {
+ done bool
+ authenticate AnonymousAuthenticator
+}
+
+func (s *anonymousServer) Next(response []byte) (challenge []byte, done bool, err error) {
+ if s.done {
+ err = ErrUnexpectedClientResponse
+ return
+ }
+
+ // No initial response, send an empty challenge
+ if response == nil {
+ return []byte{}, false, nil
+ }
+
+ s.done = true
+
+ err = s.authenticate(string(response))
+ done = true
+ return
+}
+
+// A server implementation of the ANONYMOUS authentication mechanism, as
+// described in RFC 4505.
+func NewAnonymousServer(authenticator AnonymousAuthenticator) Server {
+ return &anonymousServer{authenticate: authenticator}
+}
diff --git a/vendor/github.com/emersion/go-sasl/external.go b/vendor/github.com/emersion/go-sasl/external.go
new file mode 100644
index 0000000..da070c8
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/external.go
@@ -0,0 +1,26 @@
+package sasl
+
+// The EXTERNAL mechanism name.
+const External = "EXTERNAL"
+
+type externalClient struct {
+ Identity string
+}
+
+func (a *externalClient) Start() (mech string, ir []byte, err error) {
+ mech = External
+ ir = []byte(a.Identity)
+ return
+}
+
+func (a *externalClient) Next(challenge []byte) (response []byte, err error) {
+ return nil, ErrUnexpectedServerChallenge
+}
+
+// An implementation of the EXTERNAL authentication mechanism, as described in
+// RFC 4422. Authorization identity may be left blank to indicate that the
+// client is requesting to act as the identity associated with the
+// authentication credentials.
+func NewExternalClient(identity string) Client {
+ return &externalClient{identity}
+}
diff --git a/vendor/github.com/emersion/go-sasl/login.go b/vendor/github.com/emersion/go-sasl/login.go
new file mode 100644
index 0000000..3847ee1
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/login.go
@@ -0,0 +1,89 @@
+package sasl
+
+import (
+ "bytes"
+)
+
+// The LOGIN mechanism name.
+const Login = "LOGIN"
+
+var expectedChallenge = []byte("Password:")
+
+type loginClient struct {
+ Username string
+ Password string
+}
+
+func (a *loginClient) Start() (mech string, ir []byte, err error) {
+ mech = "LOGIN"
+ ir = []byte(a.Username)
+ return
+}
+
+func (a *loginClient) Next(challenge []byte) (response []byte, err error) {
+ if bytes.Compare(challenge, expectedChallenge) != 0 {
+ return nil, ErrUnexpectedServerChallenge
+ } else {
+ return []byte(a.Password), nil
+ }
+}
+
+// A client implementation of the LOGIN authentication mechanism for SMTP,
+// as described in http://www.iana.org/go/draft-murchison-sasl-login
+//
+// It is considered obsolete, and should not be used when other mechanisms are
+// available. For plaintext password authentication use PLAIN mechanism.
+func NewLoginClient(username, password string) Client {
+ return &loginClient{username, password}
+}
+
+// Authenticates users with an username and a password.
+type LoginAuthenticator func(username, password string) error
+
+type loginState int
+
+const (
+ loginNotStarted loginState = iota
+ loginWaitingUsername
+ loginWaitingPassword
+)
+
+type loginServer struct {
+ state loginState
+ username, password string
+ authenticate LoginAuthenticator
+}
+
+// A server implementation of the LOGIN authentication mechanism, as described
+// in https://tools.ietf.org/html/draft-murchison-sasl-login-00.
+//
+// LOGIN is obsolete and should only be enabled for legacy clients that cannot
+// be updated to use PLAIN.
+func NewLoginServer(authenticator LoginAuthenticator) Server {
+ return &loginServer{authenticate: authenticator}
+}
+
+func (a *loginServer) Next(response []byte) (challenge []byte, done bool, err error) {
+ switch a.state {
+ case loginNotStarted:
+ // Check for initial response field, as per RFC4422 section 3
+ if response == nil {
+ challenge = []byte("Username:")
+ break
+ }
+ a.state++
+ fallthrough
+ case loginWaitingUsername:
+ a.username = string(response)
+ challenge = []byte("Password:")
+ case loginWaitingPassword:
+ a.password = string(response)
+ err = a.authenticate(a.username, a.password)
+ done = true
+ default:
+ err = ErrUnexpectedClientResponse
+ }
+
+ a.state++
+ return
+}
diff --git a/vendor/github.com/emersion/go-sasl/oauthbearer.go b/vendor/github.com/emersion/go-sasl/oauthbearer.go
new file mode 100644
index 0000000..a0639b1
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/oauthbearer.go
@@ -0,0 +1,191 @@
+package sasl
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// The OAUTHBEARER mechanism name.
+const OAuthBearer = "OAUTHBEARER"
+
+type OAuthBearerError struct {
+ Status string `json:"status"`
+ Schemes string `json:"schemes"`
+ Scope string `json:"scope"`
+}
+
+type OAuthBearerOptions struct {
+ Username string
+ Token string
+ Host string
+ Port int
+}
+
+// Implements error
+func (err *OAuthBearerError) Error() string {
+ return fmt.Sprintf("OAUTHBEARER authentication error (%v)", err.Status)
+}
+
+type oauthBearerClient struct {
+ OAuthBearerOptions
+}
+
+func (a *oauthBearerClient) Start() (mech string, ir []byte, err error) {
+ mech = OAuthBearer
+ var str = "n,a=" + a.Username + ","
+
+ if a.Host != "" {
+ str += "\x01host=" + a.Host
+ }
+
+ if a.Port != 0 {
+ str += "\x01port=" + strconv.Itoa(a.Port)
+ }
+ str += "\x01auth=Bearer " + a.Token + "\x01\x01"
+ ir = []byte(str)
+ return
+}
+
+func (a *oauthBearerClient) Next(challenge []byte) ([]byte, error) {
+ authBearerErr := &OAuthBearerError{}
+ if err := json.Unmarshal(challenge, authBearerErr); err != nil {
+ return nil, err
+ } else {
+ return nil, authBearerErr
+ }
+}
+
+// An implementation of the OAUTHBEARER authentication mechanism, as
+// described in RFC 7628.
+func NewOAuthBearerClient(opt *OAuthBearerOptions) Client {
+ return &oauthBearerClient{*opt}
+}
+
+type OAuthBearerAuthenticator func(opts OAuthBearerOptions) *OAuthBearerError
+
+type oauthBearerServer struct {
+ done bool
+ failErr error
+ authenticate OAuthBearerAuthenticator
+}
+
+func (a *oauthBearerServer) fail(descr string) ([]byte, bool, error) {
+ blob, err := json.Marshal(OAuthBearerError{
+ Status: "invalid_request",
+ Schemes: "bearer",
+ })
+ if err != nil {
+ panic(err) // wtf
+ }
+ a.failErr = errors.New(descr)
+ return blob, false, nil
+}
+
+func (a *oauthBearerServer) Next(response []byte) (challenge []byte, done bool, err error) {
+ // Per RFC, we cannot just send an error, we need to return JSON-structured
+ // value as a challenge and then after getting dummy response from the
+ // client stop the exchange.
+ if a.failErr != nil {
+ // Server libraries (go-smtp, go-imap) will not call Next on
+ // protocol-specific SASL cancel response ('*'). However, GS2 (and
+ // indirectly OAUTHBEARER) defines a protocol-independent way to do so
+ // using 0x01.
+ if len(response) != 1 && response[0] != 0x01 {
+ return nil, true, errors.New("unexpected response")
+ }
+ return nil, true, a.failErr
+ }
+
+ if a.done {
+ err = ErrUnexpectedClientResponse
+ return
+ }
+
+ // Generate empty challenge.
+ if response == nil {
+ return []byte{}, false, nil
+ }
+
+ a.done = true
+
+ // Cut n,a=username,\x01host=...\x01auth=...
+ // into
+ // n
+ // a=username
+ // \x01host=...\x01auth=...\x01\x01
+ parts := bytes.SplitN(response, []byte{','}, 3)
+ if len(parts) != 3 {
+ return a.fail("Invalid response")
+ }
+ if !bytes.Equal(parts[0], []byte{'n'}) {
+ return a.fail("Invalid response, missing 'n'")
+ }
+ opts := OAuthBearerOptions{}
+ if !bytes.HasPrefix(parts[1], []byte("a=")) {
+ return a.fail("Invalid response, missing 'a'")
+ }
+ opts.Username = string(bytes.TrimPrefix(parts[1], []byte("a=")))
+
+ // Cut \x01host=...\x01auth=...\x01\x01
+ // into
+ // *empty*
+ // host=...
+ // auth=...
+ // *empty*
+ //
+ // Note that this code does not do a lot of checks to make sure the input
+ // follows the exact format specified by RFC.
+ params := bytes.Split(parts[2], []byte{0x01})
+ for _, p := range params {
+ // Skip empty fields (one at start and end).
+ if len(p) == 0 {
+ continue
+ }
+
+ pParts := bytes.SplitN(p, []byte{'='}, 2)
+ if len(pParts) != 2 {
+ return a.fail("Invalid response, missing '='")
+ }
+
+ switch string(pParts[0]) {
+ case "host":
+ opts.Host = string(pParts[1])
+ case "port":
+ port, err := strconv.ParseUint(string(pParts[1]), 10, 16)
+ if err != nil {
+ return a.fail("Invalid response, malformed 'port' value")
+ }
+ opts.Port = int(port)
+ case "auth":
+ const prefix = "bearer "
+ strValue := string(pParts[1])
+ // Token type is case-insensitive.
+ if !strings.HasPrefix(strings.ToLower(strValue), prefix) {
+ return a.fail("Unsupported token type")
+ }
+ opts.Token = strValue[len(prefix):]
+ default:
+ return a.fail("Invalid response, unknown parameter: " + string(pParts[0]))
+ }
+ }
+
+ authzErr := a.authenticate(opts)
+ if authzErr != nil {
+ blob, err := json.Marshal(authzErr)
+ if err != nil {
+ panic(err) // wtf
+ }
+ a.failErr = authzErr
+ return blob, false, nil
+ }
+
+ return nil, true, nil
+}
+
+func NewOAuthBearerServer(auth OAuthBearerAuthenticator) Server {
+ return &oauthBearerServer{authenticate: auth}
+}
diff --git a/vendor/github.com/emersion/go-sasl/plain.go b/vendor/github.com/emersion/go-sasl/plain.go
new file mode 100644
index 0000000..344ed17
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/plain.go
@@ -0,0 +1,77 @@
+package sasl
+
+import (
+ "bytes"
+ "errors"
+)
+
+// The PLAIN mechanism name.
+const Plain = "PLAIN"
+
+type plainClient struct {
+ Identity string
+ Username string
+ Password string
+}
+
+func (a *plainClient) Start() (mech string, ir []byte, err error) {
+ mech = "PLAIN"
+ ir = []byte(a.Identity + "\x00" + a.Username + "\x00" + a.Password)
+ return
+}
+
+func (a *plainClient) Next(challenge []byte) (response []byte, err error) {
+ return nil, ErrUnexpectedServerChallenge
+}
+
+// A client implementation of the PLAIN authentication mechanism, as described
+// in RFC 4616. Authorization identity may be left blank to indicate that it is
+// the same as the username.
+func NewPlainClient(identity, username, password string) Client {
+ return &plainClient{identity, username, password}
+}
+
+// Authenticates users with an identity, a username and a password. If the
+// identity is left blank, it indicates that it is the same as the username.
+// If identity is not empty and the server doesn't support it, an error must be
+// returned.
+type PlainAuthenticator func(identity, username, password string) error
+
+type plainServer struct {
+ done bool
+ authenticate PlainAuthenticator
+}
+
+func (a *plainServer) Next(response []byte) (challenge []byte, done bool, err error) {
+ if a.done {
+ err = ErrUnexpectedClientResponse
+ return
+ }
+
+ // No initial response, send an empty challenge
+ if response == nil {
+ return []byte{}, false, nil
+ }
+
+ a.done = true
+
+ parts := bytes.Split(response, []byte("\x00"))
+ if len(parts) != 3 {
+ err = errors.New("Invalid response")
+ return
+ }
+
+ identity := string(parts[0])
+ username := string(parts[1])
+ password := string(parts[2])
+
+ err = a.authenticate(identity, username, password)
+ done = true
+ return
+}
+
+// A server implementation of the PLAIN authentication mechanism, as described
+// in RFC 4616.
+func NewPlainServer(authenticator PlainAuthenticator) Server {
+ return &plainServer{authenticate: authenticator}
+}
diff --git a/vendor/github.com/emersion/go-sasl/sasl.go b/vendor/github.com/emersion/go-sasl/sasl.go
new file mode 100644
index 0000000..c209144
--- /dev/null
+++ b/vendor/github.com/emersion/go-sasl/sasl.go
@@ -0,0 +1,45 @@
+// Library for Simple Authentication and Security Layer (SASL) defined in RFC 4422.
+package sasl
+
+// Note:
+// Most of this code was copied, with some modifications, from net/smtp. It
+// would be better if Go provided a standard package (e.g. crypto/sasl) that
+// could be shared by SMTP, IMAP, and other packages.
+
+import (
+ "errors"
+)
+
+// Common SASL errors.
+var (
+ ErrUnexpectedClientResponse = errors.New("sasl: unexpected client response")
+ ErrUnexpectedServerChallenge = errors.New("sasl: unexpected server challenge")
+)
+
+// Client interface to perform challenge-response authentication.
+type Client interface {
+ // Begins SASL authentication with the server. It returns the
+ // authentication mechanism name and "initial response" data (if required by
+ // the selected mechanism). A non-nil error causes the client to abort the
+ // authentication attempt.
+ //
+ // A nil ir value is different from a zero-length value. The nil value
+ // indicates that the selected mechanism does not use an initial response,
+ // while a zero-length value indicates an empty initial response, which must
+ // be sent to the server.
+ Start() (mech string, ir []byte, err error)
+
+ // Continues challenge-response authentication. A non-nil error causes
+ // the client to abort the authentication attempt.
+ Next(challenge []byte) (response []byte, err error)
+}
+
+// Server interface to perform challenge-response authentication.
+type Server interface {
+ // Begins or continues challenge-response authentication. If the client
+ // supplies an initial response, response is non-nil.
+ //
+ // If the authentication is finished, done is set to true. If the
+ // authentication has failed, an error is returned.
+ Next(response []byte) (challenge []byte, done bool, err error)
+}