diff --git a/bot/access.go b/bot/access.go index 0a00d76..c8964ad 100644 --- a/bot/access.go +++ b/bot/access.go @@ -125,7 +125,7 @@ func (b *Bot) Ban(addr net.Addr) { } // 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 for _, domain := range b.domains { if strings.HasSuffix(email, "@"+domain) { @@ -134,22 +134,27 @@ func (b *Bot) AllowAuth(email, password string) bool { } } if !suffix { - return false + return "", false } roomID, ok := b.getMapping(utils.Mailbox(email)) if !ok { - return false + return "", false } cfg, err := b.getRoomSettings(roomID) if err != nil { 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) if err != nil { b.log.Warn("Password for %s is not valid: %v", email, err) } - return allow + return roomID, allow } diff --git a/smtp/manager.go b/smtp/manager.go index 4fafc4c..08227dc 100644 --- a/smtp/manager.go +++ b/smtp/manager.go @@ -40,7 +40,7 @@ type Manager struct { } type matrixbot interface { - AllowAuth(string, string) bool + AllowAuth(string, string) (id.RoomID, bool) IsGreylisted(net.Addr) bool IsBanned(net.Addr) bool Ban(net.Addr) diff --git a/smtp/server.go b/smtp/server.go index c96e85f..a9c69aa 100644 --- a/smtp/server.go +++ b/smtp/server.go @@ -47,20 +47,23 @@ func (m *mailServer) Login(state *smtp.ConnectionState, username, password strin 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= is invalid", username) m.bot.Ban(state.RemoteAddr) return nil, ErrBanned } return &outgoingSession{ - ctx: sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone()), - sendmail: m.SendEmail, - privkey: m.bot.GetDKIMprivkey(), - from: username, - log: m.log, - domains: m.domains, - tos: []string{}, + ctx: sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone()), + sendmail: m.SendEmail, + privkey: m.bot.GetDKIMprivkey(), + from: username, + log: m.log, + domains: m.domains, + getRoomID: m.bot.GetMapping, + fromRoom: roomID, + tos: []string{}, }, nil } diff --git a/smtp/session.go b/smtp/session.go index 3802198..ca44a85 100644 --- a/smtp/session.go +++ b/smtp/session.go @@ -48,9 +48,10 @@ func (s *incomingSession) Mail(from string, opts smtp.MailOptions) error { func (s *incomingSession) Rcpt(to string) error { sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to) s.tos = append(s.tos, to) + hostname := utils.Hostname(to) var domainok bool for _, domain := range s.domains { - if utils.Hostname(to) == domain { + if hostname == domain { domainok = true 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 type outgoingSession struct { - log *logger.Logger - sendmail func(string, string, string) error - privkey string - domains []string + log *logger.Logger + sendmail func(string, string, string) error + privkey string + domains []string + getRoomID func(string) (id.RoomID, bool) - ctx context.Context - tos []string - from string + ctx context.Context + tos []string + from string + fromRoom id.RoomID } 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) { 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 }