Skip to content

Commit 7caaa78

Browse files
authored
Merge pull request ossec#2195 from atomicturtle/smtp-auth-01
Add SMTP TLS and Authentication using libcurl
2 parents d15740a + bcf5085 commit 7caaa78

File tree

15 files changed

+1285
-82
lines changed

15 files changed

+1285
-82
lines changed

.github/workflows/make-multi-platform.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ on:
1212

1313
jobs:
1414
build-rocky:
15-
name: Rocky Linux 9 (${{ matrix.target }})
15+
name: Rocky Linux 9 (${{ matrix.target }}, USE_CURL=${{ matrix.use_curl }})
1616
runs-on: ubuntu-latest
1717
container: rockylinux:9
1818
strategy:
1919
fail-fast: false
2020
matrix:
2121
target: [ server, agent ]
22+
# Exercise optional libcurl SMTP path (curlmail.c) in CI, not only local builds.
23+
use_curl: [ no, yes ]
2224

2325
steps:
2426
- uses: actions/checkout@v4
@@ -30,11 +32,18 @@ jobs:
3032
dnf install -y gcc make openssl-devel pcre2-devel zlib-devel \
3133
systemd-devel libevent-devel
3234
dnf install -y file-devel || true
35+
if [ "${{ matrix.use_curl }}" = "yes" ]; then
36+
dnf install -y libcurl-devel
37+
fi
3338
3439
- name: Build
3540
run: |
3641
cd src
37-
make TARGET=${{ matrix.target }} PCRE2_SYSTEM=yes -j$(nproc)
42+
if [ "${{ matrix.use_curl }}" = "yes" ]; then
43+
make TARGET=${{ matrix.target }} PCRE2_SYSTEM=yes USE_CURL=yes -j$(nproc)
44+
else
45+
make TARGET=${{ matrix.target }} PCRE2_SYSTEM=yes -j$(nproc)
46+
fi
3847
3948
build-windows-agent:
4049
name: Windows agent (cross-compile)

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ The current stable releases are available on the ossec website.
1717
* Releases can be downloaded from: [Downloads](https://www.ossec.net/downloads/)
1818
* Release documentation is available at: [docs](https://www.ossec.net/docs/)
1919

20+
## ossec-maild and libcurl SMTP TLS ##
21+
22+
When built with `USE_CURL=yes`, failed SMTP sends log detailed libcurl errors in `ossec.log`: a TLS verification category line when certificate checks fail, `curl_easy_strerror` text, optional `CURLOPT_ERRORBUFFER` detail, `CURLINFO_SSL_VERIFYRESULT` (backend-specific), and `CURLINFO_OS_ERRNO` when set. See comments at the top of `src/os_maild/curlmail.c`.
23+
2024
## Development ##
2125

2226
The development version is hosted on GitHub and just a simple git clone away.

src/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ INSTALL_RESOLVCONF?=yes
2727
USE_PRELUDE?=no
2828
USE_ZEROMQ?=no
2929
USE_GEOIP?=no
30+
USE_CURL?=no
3031
USE_INOTIFY=no
3132
USE_PCRE2_JIT=yes
3233
USE_SYSTEMD?=yes
@@ -260,6 +261,11 @@ ifneq (,$(filter ${USE_GEOIP},auto yes y Y 1))
260261
OSSEC_LDFLAGS+=-lGeoIP
261262
endif # USE_GEOIP
262263

264+
ifneq (,$(filter ${USE_CURL},yes y Y 1))
265+
DEFINES+=-DUSE_SMTP_CURL
266+
OSSEC_LDFLAGS+=-lcurl
267+
endif # USE_CURL
268+
263269
ifneq (,$(filter ${USE_SQLITE},auto yes y Y 1))
264270
DEFINES+=-DSQLITE_ENABLED
265271
ANALYSISD_FLAGS="-lsqlite3"
@@ -619,6 +625,7 @@ settings:
619625
@echo "USE settings:"
620626
@echo " USE_ZEROMQ: ${USE_ZEROMQ}"
621627
@echo " USE_GEOIP: ${USE_GEOIP}"
628+
@echo " USE_CURL: ${USE_CURL}"
622629
@echo " USE_PRELUDE: ${USE_PRELUDE}"
623630
@echo " USE_OPENSSL: ${USE_OPENSSL}"
624631
@echo " USE_INOTIFY: ${USE_INOTIFY}"

src/config/global-config.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ int Read_Global(XML_NODE node, void *configp, void *mailp)
122122
const char *xml_smtpserver = "smtp_server";
123123
const char *xml_heloserver = "helo_server";
124124
const char *xml_mailmaxperhour = "email_maxperhour";
125+
const char *xml_auth_smtp = "auth_smtp";
126+
const char *xml_smtp_user = "smtp_user";
127+
const char *xml_smtp_password = "smtp_password";
128+
const char *xml_secure_smtp = "secure_smtp";
129+
const char *xml_smtp_port = "smtp_port";
125130

126131
#ifdef LIBGEOIP_ENABLED
127132
const char *xml_geoip_db_path = "geoip_db_path";
@@ -453,17 +458,82 @@ int Read_Global(XML_NODE node, void *configp, void *mailp)
453458
}
454459
os_strdup(node[i]->content, Mail->idsname);
455460
}
461+
} else if (strcmp(node[i]->element, xml_auth_smtp) == 0) {
462+
if (strcmp(node[i]->content, "yes") == 0) {
463+
if (Config) {
464+
Config->authsmtp = 1;
465+
}
466+
if (Mail) {
467+
Mail->authsmtp = 1;
468+
}
469+
} else if (strcmp(node[i]->content, "no") == 0) {
470+
if (Config) {
471+
Config->authsmtp = 0;
472+
}
473+
if (Mail) {
474+
Mail->authsmtp = 0;
475+
}
476+
} else {
477+
return (OS_INVALID);
478+
}
479+
} else if (strcmp(node[i]->element, xml_secure_smtp) == 0) {
480+
if (strcmp(node[i]->content, "yes") == 0) {
481+
if (Config) {
482+
Config->securesmtp = 1;
483+
}
484+
if (Mail) {
485+
Mail->securesmtp = 1;
486+
}
487+
} else if (strcmp(node[i]->content, "no") == 0) {
488+
if (Config) {
489+
Config->securesmtp = 0;
490+
}
491+
if (Mail) {
492+
Mail->securesmtp = 0;
493+
}
494+
} else {
495+
return (OS_INVALID);
496+
}
497+
} else if (strcmp(node[i]->element, xml_smtp_user) == 0) {
498+
if (Mail) {
499+
if (Mail->smtp_user) {
500+
free(Mail->smtp_user);
501+
}
502+
os_strdup(node[i]->content, Mail->smtp_user);
503+
}
504+
} else if (strcmp(node[i]->element, xml_smtp_password) == 0) {
505+
if (Mail) {
506+
if (Mail->smtp_pass) {
507+
memset_secure(Mail->smtp_pass, 0, strlen(Mail->smtp_pass));
508+
free(Mail->smtp_pass);
509+
}
510+
os_strdup(node[i]->content, Mail->smtp_pass);
511+
}
456512
} else if (strcmp(node[i]->element, xml_smtpserver) == 0) {
457513
#ifndef WIN32
458514
if (Mail && (Mail->mn)) {
459515
if (node[i]->content[0] == '/') {
460516
os_strdup(node[i]->content, Mail->smtpserver);
461517
} else {
518+
#ifdef USE_SMTP_CURL
519+
/* Pre-resolve for CURLOPT_RESOLVE; DNS is unavailable after chroot */
520+
if (Mail->smtpserver_resolved) {
521+
free(Mail->smtpserver_resolved);
522+
Mail->smtpserver_resolved = NULL;
523+
}
524+
Mail->smtpserver_resolved = OS_GetHost(node[i]->content, 5);
525+
if (!Mail->smtpserver_resolved) {
526+
merror(INVALID_SMTP, __local_name, node[i]->content);
527+
return (OS_INVALID);
528+
}
529+
/* Hostname as-is for libcurl; common free + os_strdup below */
530+
#else
462531
Mail->smtpserver = OS_GetHost(node[i]->content, 5);
463532
if (!Mail->smtpserver) {
464533
merror(INVALID_SMTP, __local_name, node[i]->content);
465534
return (OS_INVALID);
466535
}
536+
#endif
467537
}
468538
free(Mail->smtpserver);
469539
os_strdup(node[i]->content, Mail->smtpserver);
@@ -473,6 +543,18 @@ int Read_Global(XML_NODE node, void *configp, void *mailp)
473543
if (Mail && (Mail->mn)) {
474544
os_strdup(node[i]->content, Mail->heloserver);
475545
}
546+
} else if (strcmp(node[i]->element, xml_smtp_port) == 0) {
547+
if (Mail && (Mail->mn)) {
548+
if (!OS_StrIsNum(node[i]->content)) {
549+
merror(XML_VALUEERR, __local_name, node[i]->element, node[i]->content);
550+
return (OS_INVALID);
551+
}
552+
Mail->smtp_port = atoi(node[i]->content);
553+
if (Mail->smtp_port < 1 || Mail->smtp_port > 65535) {
554+
merror(XML_VALUEERR, __local_name, node[i]->element, node[i]->content);
555+
return (OS_INVALID);
556+
}
557+
}
476558
} else if (strcmp(node[i]->element, xml_mailmaxperhour) == 0) {
477559
if (Mail) {
478560
if (!OS_StrIsNum(node[i]->content)) {

src/config/global-config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ typedef struct __Config {
5353
/* Mail alerting */
5454
short int mailnotify;
5555

56+
/* SMTP auth (USE_CURL build only) */
57+
short int authsmtp;
58+
short int securesmtp;
59+
5660
/* Custom Alert output*/
5761
short int custom_alert_output;
5862
char *custom_alert_output_format;

src/config/mail-config.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ typedef struct _MailConfig {
3434
char *idsname;
3535
char *smtpserver;
3636
char *heloserver;
37+
char *smtpserver_resolved; /* pre-resolved IP for CURLOPT_RESOLVE when chrooted (USE_SMTP_CURL) */
38+
39+
/* SMTP auth (USE_CURL build only) */
40+
int authsmtp; /* 0 = off (default), 1 = on */
41+
int securesmtp; /* 0 = off (default), 1 = on */
42+
int smtp_port; /* 0 = use default per mode (465/587/25); else override */
43+
char *smtp_user;
44+
char *smtp_pass;
3745

3846
/* Granular e-mail options */
3947
unsigned int *gran_level;

src/config/reports-config.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,17 @@ typedef struct _monitor_config {
3131
int notify_time;
3232

3333
char *smtpserver;
34+
char *smtpserver_resolved;
3435
char *emailfrom;
3536
char *emailidsname;
3637

38+
/* SMTP auth (USE_SMTP_CURL only) */
39+
int authsmtp;
40+
int securesmtp;
41+
int smtp_port;
42+
char *smtp_user;
43+
char *smtp_pass;
44+
3745
char **agents;
3846
report_config **reports;
3947
} monitor_config;

src/monitord/generate_reports.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,8 @@ void generate_reports(int cday, int cmon, int cyear)
7878
merror("%s: INFO: Report '%s' empty.", ARGV0, mond.reports[s]->title);
7979
} else if (OS_SendCustomEmail2(mond.reports[s]->emailto,
8080
mond.reports[s]->title,
81-
mond.smtpserver,
82-
mond.emailfrom,
83-
mond.emailidsname,
84-
fname)
81+
fname,
82+
&mond)
8583
!= 0) {
8684
merror("%s: WARN: Unable to send report email.", ARGV0);
8785
}

0 commit comments

Comments
 (0)