mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2024-11-27 16:24:04 +00:00
Add LOGIN/PLAIN/CRAM-MD5 authentication hooks
This commit is contained in:
parent
fc25fce4d2
commit
1a5fd826ec
4 changed files with 54 additions and 22 deletions
|
@ -1603,8 +1603,8 @@ var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
"strutil.js": &_bintree_t{assets_js_strutil_js, map[string]*_bintree_t{}},
|
"strutil.js": &_bintree_t{assets_js_strutil_js, map[string]*_bintree_t{}},
|
||||||
}},
|
}},
|
||||||
"templates": &_bintree_t{nil, map[string]*_bintree_t{
|
"templates": &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
"index.html": &_bintree_t{assets_templates_index_html, map[string]*_bintree_t{}},
|
|
||||||
"layout.html": &_bintree_t{assets_templates_layout_html, map[string]*_bintree_t{}},
|
"layout.html": &_bintree_t{assets_templates_layout_html, map[string]*_bintree_t{}},
|
||||||
|
"index.html": &_bintree_t{assets_templates_index_html, map[string]*_bintree_t{}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -24,7 +24,7 @@ type Protocol struct {
|
||||||
hostname string
|
hostname string
|
||||||
|
|
||||||
// LogHandler is called for each log message. If nil, log messages will
|
// LogHandler is called for each log message. If nil, log messages will
|
||||||
// be output using fmt.Printf instead.
|
// be output using log.Printf instead.
|
||||||
LogHandler func(message string, args ...interface{})
|
LogHandler func(message string, args ...interface{})
|
||||||
// MessageReceivedHandler is called for each message accepted by the
|
// MessageReceivedHandler is called for each message accepted by the
|
||||||
// SMTP protocol. It must return a MessageID or error. If nil, messages
|
// SMTP protocol. It must return a MessageID or error. If nil, messages
|
||||||
|
@ -39,7 +39,7 @@ type Protocol struct {
|
||||||
// ValidateAuthenticationhandler should return true if the authentication
|
// ValidateAuthenticationhandler should return true if the authentication
|
||||||
// parameters are valid, otherwise false. If nil, all authentication
|
// parameters are valid, otherwise false. If nil, all authentication
|
||||||
// attempts will be accepted.
|
// attempts will be accepted.
|
||||||
ValidateAuthenticationHandler func(mechanism string, args ...string) bool
|
ValidateAuthenticationHandler func(mechanism string, args ...string) (errorReply *Reply, ok bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProtocol returns a new SMTP state machine in INVALID state
|
// NewProtocol returns a new SMTP state machine in INVALID state
|
||||||
|
@ -167,14 +167,37 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) {
|
||||||
proto.logf("Got unknown command for ESTABLISH state: '%s'", command.verb)
|
proto.logf("Got unknown command for ESTABLISH state: '%s'", command.verb)
|
||||||
return ReplyUnrecognisedCommand()
|
return ReplyUnrecognisedCommand()
|
||||||
}
|
}
|
||||||
case AUTH == proto.state:
|
case AUTHPLAIN == proto.state:
|
||||||
proto.logf("Got authentication response: '%s', switching to MAIL state", command.args)
|
proto.logf("Got PLAIN authentication response: '%s', switching to MAIL state", command.args)
|
||||||
proto.state = MAIL
|
proto.state = MAIL
|
||||||
|
if proto.ValidateAuthenticationHandler != nil {
|
||||||
|
if reply, ok := proto.ValidateAuthenticationHandler("CRAM-MD5", command.args); !ok {
|
||||||
|
return reply
|
||||||
|
}
|
||||||
|
}
|
||||||
return ReplyAuthOk()
|
return ReplyAuthOk()
|
||||||
case AUTHLOGIN == proto.state:
|
case AUTHLOGIN == proto.state:
|
||||||
proto.logf("Got LOGIN authentication response: '%s', switching to AUTH state", command.args)
|
proto.logf("Got LOGIN authentication response: '%s', switching to AUTHLOGIN2 state", command.args)
|
||||||
proto.state = AUTH
|
proto.state = AUTHLOGIN2
|
||||||
return ReplyAuthResponse("UGFzc3dvcmQ6")
|
return ReplyAuthResponse("UGFzc3dvcmQ6")
|
||||||
|
case AUTHLOGIN2 == proto.state:
|
||||||
|
proto.logf("Got LOGIN authentication response: '%s', switching to MAIL state", command.args)
|
||||||
|
proto.state = MAIL
|
||||||
|
if proto.ValidateAuthenticationHandler != nil {
|
||||||
|
if reply, ok := proto.ValidateAuthenticationHandler("CRAM-MD5", command.args); !ok {
|
||||||
|
return reply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ReplyAuthOk()
|
||||||
|
case AUTHCRAMMD5 == proto.state:
|
||||||
|
proto.logf("Got CRAM-MD5 authentication response: '%s', switching to MAIL state", command.args)
|
||||||
|
proto.state = MAIL
|
||||||
|
if proto.ValidateAuthenticationHandler != nil {
|
||||||
|
if reply, ok := proto.ValidateAuthenticationHandler("CRAM-MD5", command.args); !ok {
|
||||||
|
return reply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ReplyAuthOk()
|
||||||
case MAIL == proto.state:
|
case MAIL == proto.state:
|
||||||
switch command.verb {
|
switch command.verb {
|
||||||
case "AUTH":
|
case "AUTH":
|
||||||
|
@ -182,6 +205,11 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(command.args, "PLAIN "):
|
case strings.HasPrefix(command.args, "PLAIN "):
|
||||||
proto.logf("Got PLAIN authentication: %s", strings.TrimPrefix(command.args, "PLAIN "))
|
proto.logf("Got PLAIN authentication: %s", strings.TrimPrefix(command.args, "PLAIN "))
|
||||||
|
if proto.ValidateAuthenticationHandler != nil {
|
||||||
|
if reply, ok := proto.ValidateAuthenticationHandler("PLAIN", strings.TrimPrefix(command.args, "PLAIN ")); !ok {
|
||||||
|
return reply
|
||||||
|
}
|
||||||
|
}
|
||||||
return ReplyAuthOk()
|
return ReplyAuthOk()
|
||||||
case "LOGIN" == command.args:
|
case "LOGIN" == command.args:
|
||||||
proto.logf("Got LOGIN authentication, switching to AUTH state")
|
proto.logf("Got LOGIN authentication, switching to AUTH state")
|
||||||
|
@ -189,17 +217,17 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) {
|
||||||
return ReplyAuthResponse("VXNlcm5hbWU6")
|
return ReplyAuthResponse("VXNlcm5hbWU6")
|
||||||
case "PLAIN" == command.args:
|
case "PLAIN" == command.args:
|
||||||
proto.logf("Got PLAIN authentication (no args), switching to AUTH2 state")
|
proto.logf("Got PLAIN authentication (no args), switching to AUTH2 state")
|
||||||
proto.state = AUTH
|
proto.state = AUTHPLAIN
|
||||||
return ReplyAuthResponse("")
|
return ReplyAuthResponse("")
|
||||||
case "CRAM-MD5" == command.args:
|
case "CRAM-MD5" == command.args:
|
||||||
proto.logf("Got CRAM-MD5 authentication, switching to AUTH state")
|
proto.logf("Got CRAM-MD5 authentication, switching to AUTH state")
|
||||||
proto.state = AUTH
|
proto.state = AUTHCRAMMD5
|
||||||
return ReplyAuthResponse("PDQxOTI5NDIzNDEuMTI4Mjg0NzJAc291cmNlZm91ci5hbmRyZXcuY211LmVkdT4=")
|
return ReplyAuthResponse("PDQxOTI5NDIzNDEuMTI4Mjg0NzJAc291cmNlZm91ci5hbmRyZXcuY211LmVkdT4=")
|
||||||
case strings.HasPrefix(command.args, "EXTERNAL "):
|
case strings.HasPrefix(command.args, "EXTERNAL "):
|
||||||
proto.logf("Got EXTERNAL authentication: %s", strings.TrimPrefix(command.args, "EXTERNAL "))
|
proto.logf("Got EXTERNAL authentication: %s", strings.TrimPrefix(command.args, "EXTERNAL "))
|
||||||
if proto.ValidateAuthenticationHandler != nil {
|
if proto.ValidateAuthenticationHandler != nil {
|
||||||
if !proto.ValidateAuthenticationHandler("EXTERNAL", command.args) {
|
if reply, ok := proto.ValidateAuthenticationHandler("EXTERNAL", strings.TrimPrefix(command.args, "EXTERNAL ")); !ok {
|
||||||
// TODO error reply
|
return reply
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ReplyAuthOk()
|
return ReplyAuthOk()
|
||||||
|
|
|
@ -7,8 +7,10 @@ type State int
|
||||||
const (
|
const (
|
||||||
INVALID = State(-1)
|
INVALID = State(-1)
|
||||||
ESTABLISH = State(iota)
|
ESTABLISH = State(iota)
|
||||||
AUTH
|
AUTHPLAIN
|
||||||
AUTHLOGIN
|
AUTHLOGIN
|
||||||
|
AUTHLOGIN2
|
||||||
|
AUTHCRAMMD5
|
||||||
MAIL
|
MAIL
|
||||||
RCPT
|
RCPT
|
||||||
DATA
|
DATA
|
||||||
|
@ -17,12 +19,14 @@ const (
|
||||||
|
|
||||||
// StateMap provides string representations of SMTP conversation states
|
// StateMap provides string representations of SMTP conversation states
|
||||||
var StateMap = map[State]string{
|
var StateMap = map[State]string{
|
||||||
INVALID: "INVALID",
|
INVALID: "INVALID",
|
||||||
ESTABLISH: "ESTABLISH",
|
ESTABLISH: "ESTABLISH",
|
||||||
AUTH: "AUTH",
|
AUTHPLAIN: "AUTHPLAIN",
|
||||||
AUTHLOGIN: "AUTHLOGIN",
|
AUTHLOGIN: "AUTHLOGIN",
|
||||||
MAIL: "MAIL",
|
AUTHLOGIN2: "AUTHLOGIN2",
|
||||||
RCPT: "RCPT",
|
AUTHCRAMMD5: "AUTHCRAMMD5",
|
||||||
DATA: "DATA",
|
MAIL: "MAIL",
|
||||||
DONE: "DONE",
|
RCPT: "RCPT",
|
||||||
|
DATA: "DATA",
|
||||||
|
DONE: "DONE",
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,8 @@ func Accept(conn *net.TCPConn, conf *config.Config) {
|
||||||
session.logf("Session ended")
|
session.logf("Session ended")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Session) validateAuthentication(mechanism string, args ...string) bool {
|
func (c *Session) validateAuthentication(mechanism string, args ...string) (errorReply *protocol.Reply, ok bool) {
|
||||||
return true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Session) validateRecipient(to string) bool {
|
func (c *Session) validateRecipient(to string) bool {
|
||||||
|
|
Loading…
Reference in a new issue