Add authenticated domain/mailbox/alias/password admin console

Port 1027 used to be an unauthenticated console (see ServerCore's
ConsoleServer/ConsoleSession fix) that also exposed the raw SMTP
command set (AUTH/DATA/EHLO/EXPN/HELO/...) to anyone who could reach
it. It's now started only when barantmail.cfg has a real "admin"
section configured (BARANTMailConfig::hasAdmin/getAdminPort/
getAdminUser/getAdminPassword), matching the same optional-listener
pattern already used for smtpTLS/imap/pop3, and registers four real
commands instead of the SMTP command table:

  domain add|remove|list
  mailbox add|remove|list
  alias add|remove|list
  password reset

MailFileSystem gained the actual domain/mailbox/alias/password CRUD
these commands call (createDomain/removeDomain/listDomains,
createMailbox/removeMailbox/listMailboxes, createAlias/removeAlias/
listAliases, setPassword), matching its existing on-disk layout: a
domain is a directory, a mailbox is a directory with Inbox/Spam/Trash
and a .password file, an alias is a plain file listing target
mailboxes one per line.

getAliasMailboxes() was previously an unconditional stub that always
returned empty (confirmed via grep to have zero callers anywhere),
so an alias could be "created" but could never actually receive mail.
It's now wired for real: SMTPServer::inCreate() checks
ifAliasExists()/getAliasMailboxes() during local delivery and copies
the same spam-scanned message to every target mailbox (extracted into
a new deliverToMailbox() helper so the alias and single-mailbox
delivery paths share it).
This commit is contained in:
Brad Arant committed 2026-09-06 04:08:57 +00:00
1 parent cd5e0a9905
commit e8b2a79907
17 files changed
+667 -12

No files matched your search

+53 -3
View File
@@ -12,12 +12,27 @@
#include "SendMail.h"
#include "BARANTMailConfig.h"
#include "DKIMSigner.h"
#include "__ADMIN_DOMAIN.h"
#include "__ADMIN_MAILBOX.h"
#include "__ADMIN_ALIAS.h"
#include "__ADMIN_PASSWORD.h"
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <signal.h>
#include <sstream>
int main(int argc, char **argv) {
// Every socket write in this process (outbound relay in
// SendMail::deliverToHost(), and every client-facing SMTP/IMAP/
// POP3 session) can hit a peer that already reset the connection.
// The default SIGPIPE disposition kills the WHOLE process on the
// very next write to it - not just that one connection - which is
// exactly what repeated crash logs traced back to (a relay thread
// writing DATA to an MX host that reset mid-write, taking down
// every other in-flight connection with it). write()/send() still
// report the failure normally via EPIPE once this is ignored.
signal(SIGPIPE, SIG_IGN);
// /var/log is root:syslog-owned and not writable by an unprivileged
// user - this constructor throws immediately (before anything else
// in main() runs) rather than silently falling back, when run as
@@ -135,9 +150,44 @@ int main(int argc, char **argv) {
core::IPAddress(ipAddress, config.getImapPort()),
config.getTlsInfo());
core::ConsoleServer consoleServer(ePoll, core::IPAddress(ipAddress, 1027), nullptr);
consoleServer.commands.add(consoleServer.commands, "help");
consoleServer.commands.add(smtpServer.commands, "smtp");
// Real, authenticated domain/mailbox/alias/password administration -
// replaces what used to be an unauthenticated console that also
// exposed the raw SMTP command set (AUTH/DATA/EHLO/EXPN/HELO/...)
// to anyone who could reach the port; confirmed reachable
// externally (bound 0.0.0.0) with a login flow that never actually
// checked anything. Only started when barantmail.cfg has a real
// "admin" section configured - matching the same optional-listener
// pattern as smtpTLS/imap/pop3 above, rather than ever handing out
// an admin port with nothing to authenticate against.
core::ConsoleServer *consoleServer = nullptr;
mail::__ADMIN_DOMAIN *adminDomain = nullptr;
mail::__ADMIN_MAILBOX *adminMailbox = nullptr;
mail::__ADMIN_ALIAS *adminAlias = nullptr;
mail::__ADMIN_PASSWORD *adminPassword = nullptr;
if (config.hasAdmin()) {
std::string adminUser = config.getAdminUser();
std::string adminPasswordValue = config.getAdminPassword();
consoleServer = new core::ConsoleServer(ePoll, core::IPAddress(ipAddress, config.getAdminPort()), nullptr,
[adminUser, adminPasswordValue](std::string user, std::string password) {
return user == adminUser && password == adminPasswordValue;
});
adminDomain = new mail::__ADMIN_DOMAIN(mailFileSystem);
adminMailbox = new mail::__ADMIN_MAILBOX(mailFileSystem);
adminAlias = new mail::__ADMIN_ALIAS(mailFileSystem);
adminPassword = new mail::__ADMIN_PASSWORD(mailFileSystem);
// Command lookup upper-cases the typed keyword before matching
// against these registered names (see CommandList::processRequest) -
// every name here has to already be upper case or it can never
// be found at all, which is exactly why "help" silently did
// nothing even after CommandList::processCommand() itself was
// fixed to list commands: it was registered as lower-case "help"
// here and could never be looked up by the upper-cased key.
consoleServer->commands.add(consoleServer->commands, "HELP");
consoleServer->commands.add(*adminDomain, "DOMAIN");
consoleServer->commands.add(*adminMailbox, "MAILBOX");
consoleServer->commands.add(*adminAlias, "ALIAS");
consoleServer->commands.add(*adminPassword, "PASSWORD");
}
while(true)
sleep(3600);