new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
// Package SASL implements Simple Authentication and Security Layer, RFC 4422.
|
|
|
|
package sasl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/md5"
|
|
|
|
"crypto/sha1"
|
|
|
|
"crypto/sha256"
|
2023-12-24 01:07:21 +03:00
|
|
|
"crypto/tls"
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
"fmt"
|
|
|
|
"hash"
|
|
|
|
"strings"
|
|
|
|
|
2024-03-09 01:29:15 +03:00
|
|
|
"golang.org/x/text/secure/precis"
|
|
|
|
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
"github.com/mjl-/mox/scram"
|
|
|
|
)
|
|
|
|
|
2023-12-12 17:47:26 +03:00
|
|
|
// Client is a SASL client.
|
|
|
|
//
|
|
|
|
// A SASL client can be used for authentication in IMAP, SMTP and other protocols.
|
|
|
|
// A client and server exchange messages in step lock. In IMAP and SMTP, these
|
|
|
|
// messages are encoded with base64. Each SASL mechanism has predefined steps, but
|
|
|
|
// the transaction can be aborted by either side at any time. An IMAP or SMTP
|
|
|
|
// client must choose a SASL mechanism, instantiate a SASL client, and call Next
|
|
|
|
// with a nil parameter. The resulting data must be written to the server, properly
|
|
|
|
// encoded. The client must then read the response from the server and feed it to
|
|
|
|
// the SASL client, which will return more data to send, or an error.
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
type Client interface {
|
2023-12-12 17:47:26 +03:00
|
|
|
// Name as used in SMTP or IMAP authentication, e.g. PLAIN, CRAM-MD5,
|
|
|
|
// SCRAM-SHA-256. cleartextCredentials indicates if credentials are exchanged in
|
|
|
|
// clear text, which can be used to decide if the exchange is logged.
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
Info() (name string, cleartextCredentials bool)
|
|
|
|
|
2023-12-12 17:47:26 +03:00
|
|
|
// Next must be called for each step of the SASL transaction. The first call has a
|
|
|
|
// nil fromServer and serves to get a possible "initial response" from the client
|
|
|
|
// to the server. When last is true, the message from client to server is the last
|
|
|
|
// one, and the server must send a verdict. If err is set, the transaction must be
|
|
|
|
// aborted.
|
|
|
|
//
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
// For the first toServer ("initial response"), a nil toServer indicates there is
|
|
|
|
// no data, which is different from a non-nil zero-length toServer.
|
|
|
|
Next(fromServer []byte) (toServer []byte, last bool, err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type clientPlain struct {
|
|
|
|
Username, Password string
|
|
|
|
step int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Client = (*clientPlain)(nil)
|
|
|
|
|
|
|
|
// NewClientPlain returns a client for SASL PLAIN authentication.
|
2023-12-12 17:47:26 +03:00
|
|
|
//
|
|
|
|
// PLAIN is specified in RFC 4616, The PLAIN Simple Authentication and Security
|
|
|
|
// Layer (SASL) Mechanism.
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
func NewClientPlain(username, password string) Client {
|
2024-03-09 01:29:15 +03:00
|
|
|
// No "precis" processing, remote can clean password up. ../rfc/8265:679
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
return &clientPlain{username, password, 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientPlain) Info() (name string, hasCleartextCredentials bool) {
|
|
|
|
return "PLAIN", true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientPlain) Next(fromServer []byte) (toServer []byte, last bool, rerr error) {
|
|
|
|
defer func() { a.step++ }()
|
|
|
|
switch a.step {
|
|
|
|
case 0:
|
|
|
|
return []byte(fmt.Sprintf("\u0000%s\u0000%s", a.Username, a.Password)), true, nil
|
|
|
|
default:
|
|
|
|
return nil, false, fmt.Errorf("invalid step %d", a.step)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-22 23:44:55 +03:00
|
|
|
type clientLogin struct {
|
|
|
|
Username, Password string
|
|
|
|
step int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Client = (*clientLogin)(nil)
|
|
|
|
|
|
|
|
// NewClientLogin returns a client for the obsolete SASL LOGIN authentication.
|
2023-12-12 17:47:26 +03:00
|
|
|
//
|
2023-11-22 23:44:55 +03:00
|
|
|
// See https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
|
|
|
|
func NewClientLogin(username, password string) Client {
|
2024-03-09 01:29:15 +03:00
|
|
|
// No "precis" processing, remote can clean password up. ../rfc/8265:679
|
2023-11-22 23:44:55 +03:00
|
|
|
return &clientLogin{username, password, 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientLogin) Info() (name string, hasCleartextCredentials bool) {
|
|
|
|
return "LOGIN", true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientLogin) Next(fromServer []byte) (toServer []byte, last bool, rerr error) {
|
|
|
|
defer func() { a.step++ }()
|
|
|
|
switch a.step {
|
|
|
|
case 0:
|
|
|
|
return []byte(a.Username), false, nil
|
|
|
|
case 1:
|
|
|
|
return []byte(a.Password), true, nil
|
|
|
|
default:
|
|
|
|
return nil, false, fmt.Errorf("invalid step %d", a.step)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-09 01:29:15 +03:00
|
|
|
// Cleanup password with precis, like remote should have done. If the password
|
|
|
|
// appears invalid, we'll return the original, there is a chance the server also
|
|
|
|
// doesn't enforce requirements and accepts it. ../rfc/8265:679
|
|
|
|
func precisPassword(password string) string {
|
|
|
|
pw, err := precis.OpaqueString.String(password)
|
|
|
|
if err != nil {
|
|
|
|
return password
|
|
|
|
}
|
|
|
|
return pw
|
|
|
|
}
|
|
|
|
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
type clientCRAMMD5 struct {
|
|
|
|
Username, Password string
|
|
|
|
step int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Client = (*clientCRAMMD5)(nil)
|
|
|
|
|
|
|
|
// NewClientCRAMMD5 returns a client for SASL CRAM-MD5 authentication.
|
2023-12-12 17:47:26 +03:00
|
|
|
//
|
|
|
|
// CRAM-MD5 is specified in RFC 2195, IMAP/POP AUTHorize Extension for Simple
|
|
|
|
// Challenge/Response.
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
func NewClientCRAMMD5(username, password string) Client {
|
2024-03-09 01:29:15 +03:00
|
|
|
password = precisPassword(password)
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
return &clientCRAMMD5{username, password, 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientCRAMMD5) Info() (name string, hasCleartextCredentials bool) {
|
|
|
|
return "CRAM-MD5", false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientCRAMMD5) Next(fromServer []byte) (toServer []byte, last bool, rerr error) {
|
|
|
|
defer func() { a.step++ }()
|
|
|
|
switch a.step {
|
|
|
|
case 0:
|
|
|
|
return nil, false, nil
|
|
|
|
case 1:
|
|
|
|
// Validate the challenge.
|
|
|
|
// ../rfc/2195:82
|
|
|
|
s := string(fromServer)
|
|
|
|
if !strings.HasPrefix(s, "<") || !strings.HasSuffix(s, ">") {
|
|
|
|
return nil, false, fmt.Errorf("invalid challenge, missing angle brackets")
|
|
|
|
}
|
|
|
|
t := strings.SplitN(s, ".", 2)
|
|
|
|
if len(t) != 2 || t[0] == "" {
|
|
|
|
return nil, false, fmt.Errorf("invalid challenge, missing dot or random digits")
|
|
|
|
}
|
|
|
|
t = strings.Split(t[1], "@")
|
|
|
|
if len(t) == 1 || t[0] == "" || t[len(t)-1] == "" {
|
|
|
|
return nil, false, fmt.Errorf("invalid challenge, empty timestamp or empty hostname")
|
|
|
|
}
|
|
|
|
|
|
|
|
// ../rfc/2195:138
|
|
|
|
key := []byte(a.Password)
|
|
|
|
if len(key) > 64 {
|
|
|
|
t := md5.Sum(key)
|
|
|
|
key = t[:]
|
|
|
|
}
|
|
|
|
ipad := make([]byte, md5.BlockSize)
|
|
|
|
opad := make([]byte, md5.BlockSize)
|
|
|
|
copy(ipad, key)
|
|
|
|
copy(opad, key)
|
|
|
|
for i := range ipad {
|
|
|
|
ipad[i] ^= 0x36
|
|
|
|
opad[i] ^= 0x5c
|
|
|
|
}
|
|
|
|
ipadh := md5.New()
|
|
|
|
ipadh.Write(ipad)
|
|
|
|
ipadh.Write([]byte(fromServer))
|
|
|
|
|
|
|
|
opadh := md5.New()
|
|
|
|
opadh.Write(opad)
|
|
|
|
opadh.Write(ipadh.Sum(nil))
|
|
|
|
|
|
|
|
// ../rfc/2195:88
|
|
|
|
return []byte(fmt.Sprintf("%s %x", a.Username, opadh.Sum(nil))), true, nil
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, false, fmt.Errorf("invalid step %d", a.step)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type clientSCRAMSHA struct {
|
|
|
|
Username, Password string
|
|
|
|
|
2023-12-24 01:07:21 +03:00
|
|
|
hash func() hash.Hash
|
|
|
|
|
|
|
|
plus bool
|
|
|
|
cs tls.ConnectionState
|
|
|
|
|
|
|
|
// When not doing PLUS variant, this field indicates whether that is because the
|
|
|
|
// server doesn't support the PLUS variant. Used for detecting MitM attempts.
|
|
|
|
noServerPlus bool
|
|
|
|
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
name string
|
|
|
|
step int
|
|
|
|
scram *scram.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Client = (*clientSCRAMSHA)(nil)
|
|
|
|
|
|
|
|
// NewClientSCRAMSHA1 returns a client for SASL SCRAM-SHA-1 authentication.
|
2023-12-12 17:47:26 +03:00
|
|
|
//
|
2023-12-24 01:07:21 +03:00
|
|
|
// Clients should prefer using the PLUS-variant with TLS channel binding, if
|
|
|
|
// supported by a server. If noServerPlus is set, this mechanism was chosen because
|
|
|
|
// the PLUS-variant was not supported by the server. If the server actually does
|
|
|
|
// implement the PLUS variant, this can indicate a MitM attempt, which is detected
|
|
|
|
// by the server and causes the authentication attempt to be aborted.
|
|
|
|
//
|
|
|
|
// SCRAM-SHA-1 is specified in RFC 5802, "Salted Challenge Response Authentication
|
|
|
|
// Mechanism (SCRAM) SASL and GSS-API Mechanisms".
|
|
|
|
func NewClientSCRAMSHA1(username, password string, noServerPlus bool) Client {
|
2024-03-09 01:29:15 +03:00
|
|
|
password = precisPassword(password)
|
2023-12-24 01:07:21 +03:00
|
|
|
return &clientSCRAMSHA{username, password, sha1.New, false, tls.ConnectionState{}, noServerPlus, "SCRAM-SHA-1", 0, nil}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewClientSCRAMSHA1PLUS returns a client for SASL SCRAM-SHA-1-PLUS authentication.
|
|
|
|
//
|
|
|
|
// The PLUS-variant binds the authentication exchange to the TLS connection,
|
|
|
|
// detecting any MitM attempt.
|
|
|
|
//
|
|
|
|
// SCRAM-SHA-1-PLUS is specified in RFC 5802, "Salted Challenge Response
|
|
|
|
// Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms".
|
|
|
|
func NewClientSCRAMSHA1PLUS(username, password string, cs tls.ConnectionState) Client {
|
2024-03-09 01:29:15 +03:00
|
|
|
password = precisPassword(password)
|
2023-12-24 01:07:21 +03:00
|
|
|
return &clientSCRAMSHA{username, password, sha1.New, true, cs, false, "SCRAM-SHA-1-PLUS", 0, nil}
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewClientSCRAMSHA256 returns a client for SASL SCRAM-SHA-256 authentication.
|
2023-12-12 17:47:26 +03:00
|
|
|
//
|
2023-12-24 01:07:21 +03:00
|
|
|
// Clients should prefer using the PLUS-variant with TLS channel binding, if
|
|
|
|
// supported by a server. If noServerPlus is set, this mechanism was chosen because
|
|
|
|
// the PLUS-variant was not supported by the server. If the server actually does
|
|
|
|
// implement the PLUS variant, this can indicate a MitM attempt, which is detected
|
|
|
|
// by the server and causes the authentication attempt to be aborted.
|
|
|
|
//
|
|
|
|
// SCRAM-SHA-256 is specified in RFC 7677, "SCRAM-SHA-256 and SCRAM-SHA-256-PLUS
|
|
|
|
// Simple Authentication and Security Layer (SASL) Mechanisms".
|
|
|
|
func NewClientSCRAMSHA256(username, password string, noServerPlus bool) Client {
|
2024-03-09 01:29:15 +03:00
|
|
|
password = precisPassword(password)
|
2023-12-24 01:07:21 +03:00
|
|
|
return &clientSCRAMSHA{username, password, sha256.New, false, tls.ConnectionState{}, noServerPlus, "SCRAM-SHA-256", 0, nil}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewClientSCRAMSHA256PLUS returns a client for SASL SCRAM-SHA-256-PLUS authentication.
|
|
|
|
//
|
|
|
|
// The PLUS-variant binds the authentication exchange to the TLS connection,
|
|
|
|
// detecting any MitM attempt.
|
|
|
|
//
|
|
|
|
// SCRAM-SHA-256-PLUS is specified in RFC 7677, "SCRAM-SHA-256 and SCRAM-SHA-256-PLUS
|
|
|
|
// Simple Authentication and Security Layer (SASL) Mechanisms".
|
|
|
|
func NewClientSCRAMSHA256PLUS(username, password string, cs tls.ConnectionState) Client {
|
2024-03-09 01:29:15 +03:00
|
|
|
password = precisPassword(password)
|
2023-12-24 01:07:21 +03:00
|
|
|
return &clientSCRAMSHA{username, password, sha256.New, true, cs, false, "SCRAM-SHA-256-PLUS", 0, nil}
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientSCRAMSHA) Info() (name string, hasCleartextCredentials bool) {
|
|
|
|
return a.name, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientSCRAMSHA) Next(fromServer []byte) (toServer []byte, last bool, rerr error) {
|
|
|
|
defer func() { a.step++ }()
|
|
|
|
switch a.step {
|
|
|
|
case 0:
|
2023-12-24 01:07:21 +03:00
|
|
|
var cs *tls.ConnectionState
|
|
|
|
if a.plus {
|
|
|
|
cs = &a.cs
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
}
|
2023-12-24 01:07:21 +03:00
|
|
|
a.scram = scram.NewClient(a.hash, a.Username, "", a.noServerPlus, cs)
|
new feature: when delivering messages from the queue, make it possible to use a "transport"
the default transport is still just "direct delivery", where we connect to the
destination domain's MX servers.
other transports are:
- regular smtp without authentication, this is relaying to a smarthost.
- submission with authentication, e.g. to a third party email sending service.
- direct delivery, but with with connections going through a socks proxy. this
can be helpful if your ip is blocked, you need to get email out, and you have
another IP that isn't blocked.
keep in mind that for all of the above, appropriate SPF/DKIM settings have to
be configured. the "dnscheck" for a domain does a check for any SOCKS IP in the
SPF record. SPF for smtp/submission (ranges? includes?) and any DKIM
requirements cannot really be checked.
which transport is used can be configured through routes. routes can be set on
an account, a domain, or globally. the routes are evaluated in that order, with
the first match selecting the transport. these routes are evaluated for each
delivery attempt. common selection criteria are recipient domain and sender
domain, but also which delivery attempt this is. you could configured mox to
attempt sending through a 3rd party from the 4th attempt onwards.
routes and transports are optional. if no route matches, or an empty/zero
transport is selected, normal direct delivery is done.
we could already "submit" emails with 3rd party accounts with "sendmail". but
we now support more SASL authentication mechanisms with SMTP (not only PLAIN,
but also SCRAM-SHA-256, SCRAM-SHA-1 and CRAM-MD5), which sendmail now also
supports. sendmail will use the most secure mechanism supported by the server,
or the explicitly configured mechanism.
for issue #36 by dmikushin. also based on earlier discussion on hackernews.
2023-06-16 19:38:28 +03:00
|
|
|
toserver, err := a.scram.ClientFirst()
|
|
|
|
return []byte(toserver), false, err
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
clientFinal, err := a.scram.ServerFirst(fromServer, a.Password)
|
|
|
|
return []byte(clientFinal), false, err
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
err := a.scram.ServerFinal(fromServer)
|
|
|
|
return nil, true, err
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, false, fmt.Errorf("invalid step %d", a.step)
|
|
|
|
}
|
|
|
|
}
|
implement tls client certificate authentication
the imap & smtp servers now allow logging in with tls client authentication and
the "external" sasl authentication mechanism. email clients like thunderbird,
fairemail, k9, macos mail implement it. this seems to be the most secure among
the authentication mechanism commonly implemented by clients. a useful property
is that an account can have a separate tls public key for each device/email
client. with tls client cert auth, authentication is also bound to the tls
connection. a mitm cannot pass the credentials on to another tls connection,
similar to scram-*-plus. though part of scram-*-plus is that clients verify
that the server knows the client credentials.
for tls client auth with imap, we send a "preauth" untagged message by default.
that puts the connection in authenticated state. given the imap connection
state machine, further authentication commands are not allowed. some clients
don't recognize the preauth message, and try to authenticate anyway, which
fails. a tls public key has a config option to disable preauth, keeping new
connections in unauthenticated state, to work with such email clients.
for smtp (submission), we don't require an explicit auth command.
both for imap and smtp, we allow a client to authenticate with another
mechanism than "external". in that case, credentials are verified, and have to
be for the same account as the tls client auth, but the adress can be another
one than the login address configured with the tls public key.
only the public key is used to identify the account that is authenticating. we
ignore the rest of the certificate. expiration dates, names, constraints, etc
are not verified. no certificate authorities are involved.
users can upload their own (minimal) certificate. the account web interface
shows openssl commands you can run to generate a private key, minimal cert, and
a p12 file (the format that email clients seem to like...) containing both
private key and certificate.
the imapclient & smtpclient packages can now also use tls client auth. and so
does "mox sendmail", either with a pem file with private key and certificate,
or with just an ed25519 private key.
there are new subcommands "mox config tlspubkey ..." for
adding/removing/listing tls public keys from the cli, by the admin.
2024-12-06 00:41:49 +03:00
|
|
|
|
|
|
|
type clientExternal struct {
|
|
|
|
Username string
|
|
|
|
step int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Client = (*clientExternal)(nil)
|
|
|
|
|
|
|
|
// NewClientExternal returns a client for SASL EXTERNAL authentication.
|
|
|
|
//
|
|
|
|
// Username is optional.
|
|
|
|
func NewClientExternal(username string) Client {
|
|
|
|
return &clientExternal{username, 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientExternal) Info() (name string, hasCleartextCredentials bool) {
|
|
|
|
return "EXTERNAL", false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *clientExternal) Next(fromServer []byte) (toServer []byte, last bool, rerr error) {
|
|
|
|
defer func() { a.step++ }()
|
|
|
|
switch a.step {
|
|
|
|
case 0:
|
|
|
|
return []byte(a.Username), true, nil
|
|
|
|
default:
|
|
|
|
return nil, false, fmt.Errorf("invalid step %d", a.step)
|
|
|
|
}
|
|
|
|
}
|