Skip to content

Commit 2ff6469

Browse files
committed
Add support for "exec" action in max_log_file_action
See issue #220 auditd now accepts an exec action for max_log_file_action. When triggered, the daemon suspends logging, executes the configured program, and resumes logging automatically once the process exits. A new configuration field max_log_file_exe records the script to run. The daemon tracks the child PID and handles SIGCHLD to resume logging. - Documented the new “exec” option for max_log_file_action with usage details - Added the SZ_EXEC enum and the max_log_file_exe path in the configuration structure - Implemented execution handling and resuming logic in check_log_file_size - Exposed helper functions for child PID tracking - Updated child_handler in auditd to resume logging when the exec child exits
1 parent 193134b commit 2ff6469

File tree

6 files changed

+131
-70
lines changed

6 files changed

+131
-70
lines changed

docs/auditd.conf.5

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,18 @@ is reached, it will trigger a configurable action. The value given must be numer
110110
.I max_log_file_action
111111
This parameter tells the system what action to take when the system has
112112
detected that the max file size limit has been reached. Valid values are
113-
.IR ignore ", " syslog ", " suspend ", " rotate " and "keep_logs.
113+
.IR ignore ", " syslog ", " exec ", " suspend ", " rotate " and "keep_logs.
114114
If set to
115115
.IR ignore ,
116116
the audit daemon does nothing.
117117
.IR syslog
118118
means that it will issue a warning to syslog.
119+
.IR exec
120+
/path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action. This can be done by adding
121+
.I auditdctl --signal resume
122+
to the script. Also note that logging is stpped which this script runs. Whatever it does needs to be real quick because events are backing up in the kernel. The script
123+
.B MUST
124+
delete or rename /var/log/audit/audit.log or when logging resumes, it will retrigger executing the script.
119125
.IR suspend
120126
will cause the audit daemon to stop writing records to the disk. The daemon will still be alive. The
121127
.IR rotate

src/auditd-config.c

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ static const struct nv_list size_actions[] =
230230
{
231231
{"ignore", SZ_IGNORE },
232232
{"syslog", SZ_SYSLOG },
233+
{"exec", SZ_EXEC },
233234
{"suspend", SZ_SUSPEND },
234235
{"rotate", SZ_ROTATE },
235236
{"keep_logs", SZ_KEEP_LOGS},
@@ -313,6 +314,7 @@ void clear_config(struct daemon_conf *config)
313314
config->node_name = NULL;
314315
config->max_log_size = 0L;
315316
config->max_log_size_action = SZ_IGNORE;
317+
config->max_log_file_exe = NULL;
316318
config->space_left = 0L;
317319
config->space_left_percent = 0;
318320
config->space_left_action = FA_IGNORE;
@@ -798,6 +800,52 @@ static int max_log_size_parser(const struct nv_pair *nv, int line,
798800
return 0;
799801
}
800802

803+
static int check_exe_name(const char *val, int line)
804+
{
805+
struct stat buf;
806+
807+
if (val == NULL) {
808+
audit_msg(LOG_ERR, "Executable path needed for line %d", line);
809+
return -1;
810+
}
811+
812+
if (*val != '/') {
813+
audit_msg(LOG_ERR, "Absolute path needed for %s - line %d",
814+
val, line);
815+
return -1;
816+
}
817+
818+
if (stat(val, &buf) < 0) {
819+
audit_msg(LOG_ERR, "Unable to stat %s (%s) - line %d", val,
820+
strerror(errno), line);
821+
return -1;
822+
}
823+
if (!S_ISREG(buf.st_mode)) {
824+
audit_msg(LOG_ERR, "%s is not a regular file - line %d", val,
825+
line);
826+
return -1;
827+
}
828+
if (buf.st_uid != 0) {
829+
audit_msg(LOG_ERR, "%s is not owned by root - line %d", val,
830+
line);
831+
return -1;
832+
}
833+
if ((buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
834+
(S_IRWXU|S_IRGRP|S_IXGRP) &&
835+
(buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
836+
(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) &&
837+
(buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
838+
(S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) &&
839+
(buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
840+
(S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
841+
audit_msg(LOG_ERR,
842+
"%s permissions should be 0750, 0755, 0555 or 0550 - line %d",
843+
val, line);
844+
return -1;
845+
}
846+
return 0;
847+
}
848+
801849
static int max_log_size_action_parser(const struct nv_pair *nv, int line,
802850
struct daemon_conf *config)
803851
{
@@ -807,6 +855,11 @@ static int max_log_size_action_parser(const struct nv_pair *nv, int line,
807855
nv->value);
808856
for (i=0; size_actions[i].name != NULL; i++) {
809857
if (strcasecmp(nv->value, size_actions[i].name) == 0) {
858+
if (size_actions[i].option == SZ_EXEC) {
859+
if (check_exe_name(nv->option, line))
860+
return 1;
861+
config->max_log_file_exe = strdup(nv->option);
862+
}
810863
config->max_log_size_action = size_actions[i].option;
811864
return 0;
812865
}
@@ -974,52 +1027,6 @@ static int space_left_parser(const struct nv_pair *nv, int line,
9741027
return 0;
9751028
}
9761029

977-
static int check_exe_name(const char *val, int line)
978-
{
979-
struct stat buf;
980-
981-
if (val == NULL) {
982-
audit_msg(LOG_ERR, "Executable path needed for line %d", line);
983-
return -1;
984-
}
985-
986-
if (*val != '/') {
987-
audit_msg(LOG_ERR, "Absolute path needed for %s - line %d",
988-
val, line);
989-
return -1;
990-
}
991-
992-
if (stat(val, &buf) < 0) {
993-
audit_msg(LOG_ERR, "Unable to stat %s (%s) - line %d", val,
994-
strerror(errno), line);
995-
return -1;
996-
}
997-
if (!S_ISREG(buf.st_mode)) {
998-
audit_msg(LOG_ERR, "%s is not a regular file - line %d", val,
999-
line);
1000-
return -1;
1001-
}
1002-
if (buf.st_uid != 0) {
1003-
audit_msg(LOG_ERR, "%s is not owned by root - line %d", val,
1004-
line);
1005-
return -1;
1006-
}
1007-
if ((buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
1008-
(S_IRWXU|S_IRGRP|S_IXGRP) &&
1009-
(buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
1010-
(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) &&
1011-
(buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
1012-
(S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) &&
1013-
(buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
1014-
(S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
1015-
audit_msg(LOG_ERR,
1016-
"%s permissions should be 0750, 0755, 0555 or 0550 - line %d",
1017-
val, line);
1018-
return -1;
1019-
}
1020-
return 0;
1021-
}
1022-
10231030
static int space_action_parser(const struct nv_pair *nv, int line,
10241031
struct daemon_conf *config)
10251032
{
@@ -2033,14 +2040,15 @@ void free_config(struct daemon_conf *config)
20332040
free((void *)config->log_file);
20342041
free((void *)config->node_name);
20352042
free((void *)config->action_mail_acct);
2043+
free((void *)config->max_log_file_exe);
20362044
free((void *)config->space_left_exe);
2037-
free((void *)config->admin_space_left_exe);
2038-
free((void *)config->disk_full_exe);
2039-
free((void *)config->disk_error_exe);
2040-
free((void *)config->krb5_principal);
2041-
free((void *)config->krb5_key_file);
2045+
free((void *)config->admin_space_left_exe);
2046+
free((void *)config->disk_full_exe);
2047+
free((void *)config->disk_error_exe);
2048+
free((void *)config->krb5_principal);
2049+
free((void *)config->krb5_key_file);
20422050
free((void *)config->plugin_dir);
2043-
free((void *)config_dir);
2051+
free((void *)config_dir);
20442052
free(config_file);
20452053
config_file = NULL;
20462054
config->config_dir = NULL;

src/auditd-config.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ typedef enum { LF_RAW, LF_NOLOG, LF_ENRICHED } logging_formats;
3636
typedef enum { FT_NONE, FT_INCREMENTAL, FT_INCREMENTAL_ASYNC, FT_DATA, FT_SYNC } flush_technique;
3737
typedef enum { FA_IGNORE, FA_SYSLOG, FA_ROTATE, FA_EMAIL, FA_EXEC, FA_SUSPEND,
3838
FA_SINGLE, FA_HALT } failure_action_t;
39-
typedef enum { SZ_IGNORE, SZ_SYSLOG, SZ_SUSPEND, SZ_ROTATE,
40-
SZ_KEEP_LOGS } size_action;
39+
typedef enum { SZ_IGNORE, SZ_SYSLOG, SZ_EXEC, SZ_SUSPEND, SZ_ROTATE,
40+
SZ_KEEP_LOGS } size_action;
4141
typedef enum { TEST_AUDITD, TEST_SEARCH } log_test_t;
4242
typedef enum { N_NONE, N_HOSTNAME, N_FQD, N_NUMERIC, N_USER } node_t;
4343
typedef enum { O_IGNORE, O_SYSLOG, O_SUSPEND, O_SINGLE,
@@ -63,6 +63,7 @@ struct daemon_conf
6363
const char *node_name;
6464
unsigned long max_log_size;
6565
size_action max_log_size_action;
66+
const char *max_log_file_exe;
6667
unsigned long space_left;
6768
unsigned int space_left_percent;
6869
failure_action_t space_left_action;

src/auditd-event.c

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,10 @@ static void rotate_logs(unsigned int num_logs, unsigned int keep_logs);
7171
static void shift_logs(void);
7272
static int open_audit_log(void);
7373
static void change_runlevel(const char *level);
74-
static void safe_exec(const char *exe);
74+
static pid_t safe_exec(const char *exe);
7575
static void reconfigure(struct auditd_event *e);
7676
static void init_flush_thread(void);
7777

78-
7978
/* Local Data */
8079
static struct daemon_conf *config;
8180
static volatile int log_fd;
@@ -86,6 +85,7 @@ static int fs_admin_space_warning = 0;
8685
static int fs_space_left = 1;
8786
static int logging_suspended = 0;
8887
static unsigned int known_logs = 0;
88+
static pid_t exec_child_pid = -1;
8989
static const char *SINGLE = "1";
9090
static const char *HALT = "0";
9191
static char *format_buf = NULL;
@@ -107,6 +107,16 @@ int dispatch_network_events(void)
107107
return config->distribute_network_events;
108108
}
109109

110+
pid_t auditd_get_exec_pid(void)
111+
{
112+
return exec_child_pid;
113+
}
114+
115+
void auditd_clear_exec_pid(void)
116+
{
117+
exec_child_pid = -1;
118+
}
119+
110120
void write_logging_state(FILE *f)
111121
{
112122
fprintf(f, "writing to logs = %s\n", config->write_logs ? "yes" : "no");
@@ -774,6 +784,15 @@ static void check_log_file_size(void)
774784
audit_msg(LOG_ERR,
775785
"Audit daemon log file is larger than max size");
776786
break;
787+
case SZ_EXEC:
788+
if (log_file)
789+
fclose(log_file);
790+
log_file = NULL;
791+
log_fd = -1;
792+
logging_suspended = 1;
793+
exec_child_pid =
794+
safe_exec(config->max_log_file_exe);
795+
break;
777796
case SZ_SUSPEND:
778797
audit_msg(LOG_ERR,
779798
"Audit daemon is suspending logging due to logfile size.");
@@ -1467,35 +1486,41 @@ static void change_runlevel(const char *level)
14671486
exit(1);
14681487
}
14691488

1470-
static void safe_exec(const char *exe)
1489+
/*
1490+
* This function executes a new process. It returns -1 on failure to fork.
1491+
* It returns a positive number to the parent. This positive number is the
1492+
* pid of the child. If the child fails to exec the new process, it exits
1493+
* with a 1. The exit code can be picked up in the sigchld handler.
1494+
*/
1495+
static pid_t safe_exec(const char *exe)
14711496
{
14721497
char *argv[2];
1473-
int pid;
1498+
pid_t pid;
14741499
struct sigaction sa;
14751500

14761501
if (exe == NULL) {
14771502
audit_msg(LOG_ALERT,
14781503
"Safe_exec passed NULL for program to execute");
1479-
return;
1504+
return -1;
14801505
}
14811506

14821507
pid = fork();
14831508
if (pid < 0) {
14841509
audit_msg(LOG_ALERT,
14851510
"Audit daemon failed to fork doing safe_exec");
1486-
return;
1511+
return -1;
14871512
}
1488-
if (pid) /* Parent */
1489-
return;
1513+
if (pid) /* Parent */
1514+
return pid;
14901515
/* Child */
1491-
sigfillset (&sa.sa_mask);
1492-
sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0);
1516+
sigfillset(&sa.sa_mask);
1517+
sigprocmask(SIG_UNBLOCK, &sa.sa_mask, 0);
14931518

14941519
argv[0] = (char *)exe;
14951520
argv[1] = NULL;
14961521
execve(exe, argv, NULL);
14971522
audit_msg(LOG_ALERT, "Audit daemon failed to exec %s", exe);
1498-
exit(1);
1523+
exit(1); /* FIXME: Maybe this should error instead of exit */
14991524
}
15001525

15011526
static void reconfigure(struct auditd_event *e)
@@ -1608,6 +1633,19 @@ static void reconfigure(struct auditd_event *e)
16081633
need_size_check = 1;
16091634
}
16101635

1636+
// max log exe
1637+
if (oconf->max_log_file_exe || nconf->max_log_file_exe) {
1638+
if (nconf->max_log_file_exe == NULL)
1639+
;
1640+
else if (oconf->max_log_file_exe == NULL && nconf->max_log_file_exe)
1641+
need_size_check = 1;
1642+
else if (strcmp(oconf->max_log_file_exe,
1643+
nconf->max_log_file_exe))
1644+
need_size_check = 1;
1645+
free((char *)oconf->max_log_file_exe);
1646+
oconf->max_log_file_exe = nconf->max_log_file_exe;
1647+
}
1648+
16111649
if (need_size_check) {
16121650
logging_suspended = 0;
16131651
check_log_file_size();

src/auditd-event.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/* auditd-event.h --
2-
* Copyright 2004,2005,2008,2016,2018 Red Hat Inc., Durham, North Carolina.
1+
/* auditd-event.h --
2+
* Copyright 2004,2005,2008,2016,2018 Red Hat Inc.
33
* All Rights Reserved.
44
*
55
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
1818
*
1919
* Authors:
2020
* Steve Grubb <[email protected]>
21-
*
21+
*
2222
*/
2323

2424
#ifndef AUDITD_EVENT_H
@@ -43,7 +43,11 @@ int dispatch_network_events(void);
4343
void write_logging_state(FILE *f);
4444
void shutdown_events(void);
4545
int init_event(struct daemon_conf *config);
46+
pid_t auditd_get_exec_pid(void);
47+
void auditd_clear_exec_pid(void);
4648
void resume_logging(void);
49+
pid_t auditd_get_exec_pid(void);
50+
void auditd_clear_exec_pid(void);
4751
void cleanup_event(struct auditd_event *e);
4852
void format_event(struct auditd_event *e);
4953
void enqueue_event(struct auditd_event *e);

src/auditd.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ static void user2_handler( struct ev_loop *loop, struct ev_signal *sig, int reve
174174
}
175175

176176
/*
177-
* Used with email alerts to cleanup
177+
* Used with email alerts and max_log_size_exec to cleanup
178178
*/
179179
static void child_handler(struct ev_loop *loop, struct ev_signal *sig,
180180
int revents)
@@ -184,6 +184,10 @@ static void child_handler(struct ev_loop *loop, struct ev_signal *sig,
184184
while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
185185
if (pid == dispatcher_pid())
186186
dispatcher_reaped();
187+
else if (pid == auditd_get_exec_pid()) {
188+
resume_logging();
189+
auditd_clear_exec_pid();
190+
}
187191
}
188192
}
189193

0 commit comments

Comments
 (0)