reject wrong email in SMTP MAIL(), reject impersonation attempts
This commit is contained in:
@@ -125,7 +125,7 @@ func (b *Bot) Ban(addr net.Addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AllowAuth check if SMTP login (email) and password are valid
|
// AllowAuth check if SMTP login (email) and password are valid
|
||||||
func (b *Bot) AllowAuth(email, password string) bool {
|
func (b *Bot) AllowAuth(email, password string) (id.RoomID, bool) {
|
||||||
var suffix bool
|
var suffix bool
|
||||||
for _, domain := range b.domains {
|
for _, domain := range b.domains {
|
||||||
if strings.HasSuffix(email, "@"+domain) {
|
if strings.HasSuffix(email, "@"+domain) {
|
||||||
@@ -134,22 +134,27 @@ func (b *Bot) AllowAuth(email, password string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !suffix {
|
if !suffix {
|
||||||
return false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
roomID, ok := b.getMapping(utils.Mailbox(email))
|
roomID, ok := b.getMapping(utils.Mailbox(email))
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return "", false
|
||||||
}
|
}
|
||||||
cfg, err := b.getRoomSettings(roomID)
|
cfg, err := b.getRoomSettings(roomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.log.Error("failed to retrieve settings: %v", err)
|
b.log.Error("failed to retrieve settings: %v", err)
|
||||||
return false
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.NoSend() {
|
||||||
|
b.log.Warn("trying to send email from %q (%q), but it's receive-only", email, roomID)
|
||||||
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
allow, err := argon2pw.CompareHashWithPassword(cfg.Password(), password)
|
allow, err := argon2pw.CompareHashWithPassword(cfg.Password(), password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.log.Warn("Password for %s is not valid: %v", email, err)
|
b.log.Warn("Password for %s is not valid: %v", email, err)
|
||||||
}
|
}
|
||||||
return allow
|
return roomID, allow
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ type Manager struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type matrixbot interface {
|
type matrixbot interface {
|
||||||
AllowAuth(string, string) bool
|
AllowAuth(string, string) (id.RoomID, bool)
|
||||||
IsGreylisted(net.Addr) bool
|
IsGreylisted(net.Addr) bool
|
||||||
IsBanned(net.Addr) bool
|
IsBanned(net.Addr) bool
|
||||||
Ban(net.Addr)
|
Ban(net.Addr)
|
||||||
|
|||||||
@@ -47,20 +47,23 @@ func (m *mailServer) Login(state *smtp.ConnectionState, username, password strin
|
|||||||
return nil, ErrBanned
|
return nil, ErrBanned
|
||||||
}
|
}
|
||||||
|
|
||||||
if !m.bot.AllowAuth(username, password) {
|
roomID, allow := m.bot.AllowAuth(username, password)
|
||||||
|
if !allow {
|
||||||
m.log.Debug("username=%s or password=<redacted> is invalid", username)
|
m.log.Debug("username=%s or password=<redacted> is invalid", username)
|
||||||
m.bot.Ban(state.RemoteAddr)
|
m.bot.Ban(state.RemoteAddr)
|
||||||
return nil, ErrBanned
|
return nil, ErrBanned
|
||||||
}
|
}
|
||||||
|
|
||||||
return &outgoingSession{
|
return &outgoingSession{
|
||||||
ctx: sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone()),
|
ctx: sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone()),
|
||||||
sendmail: m.SendEmail,
|
sendmail: m.SendEmail,
|
||||||
privkey: m.bot.GetDKIMprivkey(),
|
privkey: m.bot.GetDKIMprivkey(),
|
||||||
from: username,
|
from: username,
|
||||||
log: m.log,
|
log: m.log,
|
||||||
domains: m.domains,
|
domains: m.domains,
|
||||||
tos: []string{},
|
getRoomID: m.bot.GetMapping,
|
||||||
|
fromRoom: roomID,
|
||||||
|
tos: []string{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,9 +48,10 @@ func (s *incomingSession) Mail(from string, opts smtp.MailOptions) error {
|
|||||||
func (s *incomingSession) Rcpt(to string) error {
|
func (s *incomingSession) Rcpt(to string) error {
|
||||||
sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to)
|
sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to)
|
||||||
s.tos = append(s.tos, to)
|
s.tos = append(s.tos, to)
|
||||||
|
hostname := utils.Hostname(to)
|
||||||
var domainok bool
|
var domainok bool
|
||||||
for _, domain := range s.domains {
|
for _, domain := range s.domains {
|
||||||
if utils.Hostname(to) == domain {
|
if hostname == domain {
|
||||||
domainok = true
|
domainok = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -105,14 +106,16 @@ func (s *incomingSession) Logout() error { return nil }
|
|||||||
|
|
||||||
// outgoingSession represents an SMTP-submission session sending emails from external scripts, using postmoogle as SMTP server
|
// outgoingSession represents an SMTP-submission session sending emails from external scripts, using postmoogle as SMTP server
|
||||||
type outgoingSession struct {
|
type outgoingSession struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
sendmail func(string, string, string) error
|
sendmail func(string, string, string) error
|
||||||
privkey string
|
privkey string
|
||||||
domains []string
|
domains []string
|
||||||
|
getRoomID func(string) (id.RoomID, bool)
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
tos []string
|
tos []string
|
||||||
from string
|
from string
|
||||||
|
fromRoom id.RoomID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *outgoingSession) Mail(from string, opts smtp.MailOptions) error {
|
func (s *outgoingSession) Mail(from string, opts smtp.MailOptions) error {
|
||||||
@@ -120,6 +123,28 @@ func (s *outgoingSession) Mail(from string, opts smtp.MailOptions) error {
|
|||||||
if !email.AddressValid(from) {
|
if !email.AddressValid(from) {
|
||||||
return errors.New("please, provide email address")
|
return errors.New("please, provide email address")
|
||||||
}
|
}
|
||||||
|
hostname := utils.Hostname(from)
|
||||||
|
var domainok bool
|
||||||
|
for _, domain := range s.domains {
|
||||||
|
if hostname == domain {
|
||||||
|
domainok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !domainok {
|
||||||
|
s.log.Debug("wrong domain of %s", from)
|
||||||
|
return ErrNoUser
|
||||||
|
}
|
||||||
|
|
||||||
|
roomID, ok := s.getRoomID(utils.Mailbox(from))
|
||||||
|
if !ok {
|
||||||
|
s.log.Debug("mapping for %s not found", from)
|
||||||
|
return ErrNoUser
|
||||||
|
}
|
||||||
|
if s.fromRoom != roomID {
|
||||||
|
s.log.Warn("sender from %q tries to impersonate %q", s.fromRoom, roomID)
|
||||||
|
return ErrNoUser
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user