Skip to content

Add SMTP TLS and Authentication using libcurl#2195

Open
atomicturtle wants to merge 4 commits intoossec:mainfrom
atomicturtle:smtp-auth-01
Open

Add SMTP TLS and Authentication using libcurl#2195
atomicturtle wants to merge 4 commits intoossec:mainfrom
atomicturtle:smtp-auth-01

Conversation

@atomicturtle
Copy link
Member

Enable authenticated and TLS SMTP for ossec-maild when built with
USE_CURL=yes (off by default). Uses libcurl for SMTP AUTH (PLAIN/LOGIN)
and TLS/STARTTLS; credentials and TLS are validated and sanitized.

Security hardening: header/envelope CR/LF sanitization, hostname
validation for smtp_server, timeouts, mandatory TLS when AUTH is on,
post-parse credential validation, and secure clearing of password
in config and at exit.

CA bundle and chroot
ossec-maild runs inside a chroot (e.g. /var/ossec). libcurl uses
CURLOPT_SSL_VERIFYPEER=1 and by default looks for the system CA bundle
(e.g. /etc/ssl/certs/ca-certificates.crt). After chroot, that path
is not visible, so TLS verification fails (CURLE_PEER_FAILED_VERIFICATION)
and mail is dropped unless the CA bundle is available inside the chroot.
Installation (or the admin) must copy or symlink the system CA bundle
into the chroot (e.g. /etc/ssl/certs/ca-certificates.crt) and
either set CURLOPT_CAINFO to that path in code or ensure the default
path resolves inside the chroot. Do not disable VERIFYPEER.

Original idea and initial implementation from alexbartlow via
Allow TLS Email sends as a compile-time option
#1360

Credit: alexbartlow (PR #1360)

Starting on rocky.

Based on PR ossec#2158 by @cmac9203. Refactoring for make

Signed-off-by: Scott R. Shinn <scott@atomicorp.com>
Enable authenticated and TLS SMTP for ossec-maild when built with
USE_CURL=yes (off by default). Uses libcurl for SMTP AUTH (PLAIN/LOGIN)
and TLS/STARTTLS; credentials and TLS are validated and sanitized.

Security hardening: header/envelope CR/LF sanitization, hostname
validation for smtp_server, timeouts, mandatory TLS when AUTH is on,
post-parse credential validation, and secure clearing of password
in config and at exit.

CA bundle and chroot
  ossec-maild runs inside a chroot (e.g. /var/ossec). libcurl uses
  CURLOPT_SSL_VERIFYPEER=1 and by default looks for the system CA bundle
  (e.g. /etc/ssl/certs/ca-certificates.crt). After chroot, that path
  is not visible, so TLS verification fails (CURLE_PEER_FAILED_VERIFICATION)
  and mail is dropped unless the CA bundle is available inside the chroot.
  Installation (or the admin) must copy or symlink the system CA bundle
  into the chroot (e.g. <chroot>/etc/ssl/certs/ca-certificates.crt) and
  either set CURLOPT_CAINFO to that path in code or ensure the default
  path resolves inside the chroot. Do not disable VERIFYPEER.

Original idea and initial implementation from alexbartlow via
  Allow TLS Email sends as a compile-time option
  ossec#1360

Credit: alexbartlow (PR ossec#1360)

Signed-off-by: Scott R. Shinn <scott@atomicorp.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional libcurl-based SMTP delivery path to ossec-maild (enabled only when building with USE_CURL=yes) to support SMTP AUTH and TLS/STARTTLS while running inside the project’s chrooted environment.

Changes:

  • Introduces USE_CURL build flag (USE_SMTP_CURL) and a new libcurl-based SMTP sender implementation.
  • Extends mail/global config parsing with auth_smtp, secure_smtp, smtp_user, smtp_password, and smtp_port, plus pre-resolution support for chroot.
  • Adds a new GitHub Actions multi-platform Make build workflow (Rocky + Windows cross-compile).

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/os_maild/sendmail.c Compiles out legacy SMTP/local sendmail implementation when USE_SMTP_CURL is set.
src/os_maild/maild.c Adds SMTP secret clearing and pre-chroot hostname resolution for curl mode.
src/os_maild/curlmail.c New libcurl-based SMTP sender (TLS + AUTH) behind USE_SMTP_CURL.
src/os_maild/config.c Initializes new mail config fields and adds curl-gated validation for AUTH.
src/monitord/main.c Adjusts how monitord reads/validates smtp_server when USE_SMTP_CURL is defined.
src/config/mail-config.h Adds SMTP TLS/AUTH-related fields to MailConfig.
src/config/global-config.h Adds authsmtp/securesmtp flags to global config struct.
src/config/global-config.c Parses new SMTP TLS/AUTH-related XML elements and pre-resolves SMTP host in curl mode.
src/Makefile Adds USE_CURL toggle to enable USE_SMTP_CURL and link with -lcurl.
.github/workflows/make-multi-platform.yml New CI workflow for Rocky Linux container builds + Windows cross-compile.
.github/workflows/README.md Documents the workflows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Signed-off-by: Scott R. Shinn <scott@atomicorp.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional (compile-time) libcurl-based SMTP delivery path for ossec-maild, enabling SMTP AUTH and TLS/STARTTLS when built with USE_CURL=yes, along with related config parsing and build/CI wiring.

Changes:

  • Add new libcurl SMTP sender (OS_Sendmail) supporting AUTH + TLS/STARTTLS and hostname/IP pre-resolution for chrooted operation.
  • Extend global/mail config parsing with auth_smtp, secure_smtp, smtp_user, smtp_password, and smtp_port.
  • Add USE_CURL build switch (defines USE_SMTP_CURL, links -lcurl) and introduce a multi-platform GitHub Actions build workflow.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/os_maild/sendmail.c Excludes the legacy sendmail/plain SMTP implementation when USE_SMTP_CURL is enabled.
src/os_maild/maild.c Initializes/cleans up libcurl and pre-resolves SMTP hostnames before chroot; clears credentials in child.
src/os_maild/curlmail.c New libcurl-based SMTP implementation (TLS/AUTH, CRLF sanitization, timeouts, CURLOPT_RESOLVE).
src/os_maild/config.c Initializes new MailConfig fields and enforces option validity depending on USE_SMTP_CURL.
src/monitord/main.c Adjusts SMTP server validation/storage to preserve hostname for libcurl builds.
src/config/mail-config.h Extends MailConfig with curl/TLS/auth fields and pre-resolved SMTP IP.
src/config/global-config.h Extends global _Config with auth/TLS flags.
src/config/global-config.c Parses new SMTP auth/TLS options; pre-resolves SMTP host for chroot when curl is enabled.
src/Makefile Adds USE_CURL switch to enable USE_SMTP_CURL and link -lcurl.
.github/workflows/make-multi-platform.yml New CI workflow to build on Rocky Linux container + cross-compile Windows agent.
.github/workflows/README.md Documents the workflows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +61 to +63
if (Mail->authsmtp && (!Mail->smtp_user || !Mail->smtp_pass)) {
merror("%s: auth_smtp=yes requires both smtp_user and smtp_password to be set.", ARGV0);
return (OS_INVALID);
Comment on lines +121 to +148
if (!is_valid_smtp_host(mail->smtpserver)) {
merror("%s: Invalid smtp_server (hostname only, no path or invalid chars).", ARGV0);
return -1;
}

if (mail->authsmtp && (!mail->smtp_user || !mail->smtp_pass)) {
merror("%s: auth_smtp=yes requires smtp_user and smtp_password to be set.", ARGV0);
return -1;
}

/* Build URL: optional smtp_port overrides defaults (465/587/25 per mode) */
{
int port = mail->smtp_port;
int n;
if (port <= 0 || port > 65535) {
if (mail->securesmtp) {
port = 465;
} else if (mail->authsmtp) {
port = 587;
} else {
port = 25;
}
}
if (mail->securesmtp) {
n = snprintf(url, sizeof(url), "smtps://%s:%d", mail->smtpserver, port);
} else {
n = snprintf(url, sizeof(url), "smtp://%s:%d", mail->smtpserver, port);
}
Comment on lines +216 to +244
hostname[0] = '\0';
if (gethostname(hostname, sizeof(hostname)) != 0) {
strncpy(hostname, "localhost", sizeof(hostname) - 1);
hostname[sizeof(hostname) - 1] = '\0';
} else {
hostname[sizeof(hostname) - 1] = '\0';
}
strftime(message_id, sizeof(message_id), "%Y%m%d%H%M%S.%z", p);
strftime(date_buf, sizeof(date_buf), "%a, %d %b %Y %T %z", p);

sanitize_header_value(msg->subject ? msg->subject : "", sanitized_subject, sizeof(sanitized_subject));
sanitize_header_value(mail->from, sanitized_from, sizeof(sanitized_from));
sanitize_header_value(mail->to[0], sanitized_to, sizeof(sanitized_to));

body_len = msg->body ? strlen(msg->body) : 0;
{
int n = snprintf(header_buf, sizeof(header_buf),
"Date: %s\r\n"
"To: %s\r\n"
"From: %s\r\n"
"Message-ID: <%s@%s>\r\n"
"Subject: %s\r\n"
"\r\n",
date_buf,
sanitized_to,
sanitized_from,
message_id,
hostname,
sanitized_subject);
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants