import Oracle_OSS aide-0.19.2-4.el10_2.1

This commit is contained in:
AlmaLinux RelEng Bot 2026-07-21 08:33:49 -04:00
parent addf3db81c
commit e8dd91a093
12 changed files with 1593 additions and 1115 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
aide-0.18.6.tar.gz
aide-0.19.2.tar.gz
gpgkey-aide.gpg

View File

@ -0,0 +1,657 @@
From f3e62eb87e0a0e9c6fd43c933670447c8ab0517a Mon Sep 17 00:00:00 2001
From: Cropi <alakatos@redhat.com>
Date: Thu, 28 May 2026 14:50:34 +0200
Subject: [PATCH] conf, report: add syslog_format config option
Re-implement the syslog_format option that existed as a Red Hat downstream
patch against aide 0.16 but was dropped during the rebase to 0.19.2.
Customers upgrading from RHEL 9.7 (aide 0.16) to RHEL 9.8 (aide 0.19.2)
received a fatal parse error on startup if their aide.conf contained
'syslog_format = true' (RHEL-178317).
The option is implemented as a new REPORT_FORMAT_SYSLOG value in the
existing report format module system, rather than as a standalone boolean,
which fits the 0.19.2 architecture cleanly.
syslog_format = yes/true is equivalent to report_format = syslog
Both spellings are accepted; last-write wins.
When active, the standard multi-line report is replaced with a compact
semicolon-delimited format where every file event is one line:
AIDE found differences between database and filesystem!!
summary;total_number_of_files=N;added_files=N;removed_files=N;changed_files=N
file=/usr/sbin/sshd;Mtime_old=...;Mtime_new=...;SHA256_old=...;SHA256_new=...
dir=/etc/cron.d; added
file=/usr/bin/old; removed
Each line is emitted with a single report_printf() call so that when used
with report_url=syslog:<FACILITY> exactly one syslog message is produced
per file event.
The module implements its own unconditional tree walker (not gated on
report_level) so added and removed entries are always included, matching
the original patch behaviour. ACL and xattr values are formatted directly
from db_line fields rather than through get_attribute_values() to avoid
embedded newlines breaking the single-line invariant. The original patch's
uninitialized 'char *A' in the ACL path is fixed.
Signed-off-by: Cropi <alakatos@redhat.com>
---
Makefile.am | 1 +
doc/aide.conf.5 | 28 ++++
include/conf_ast.h | 1 +
include/db_config.h | 1 +
include/report.h | 3 +
include/report_syslog.h | 28 ++++
src/aide.c | 1 +
src/conf_ast.c | 1 +
src/conf_eval.c | 8 +
src/conf_lex.l | 7 +
src/report.c | 16 +-
src/report_syslog.c | 338 ++++++++++++++++++++++++++++++++++++++++
12 files changed, 432 insertions(+), 1 deletion(-)
create mode 100644 include/report_syslog.h
create mode 100644 src/report_syslog.c
diff --git a/Makefile.am b/Makefile.am
index f78a96c..356b983 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,6 +31,7 @@ aide_SOURCES = src/aide.c include/aide.h \
include/report.h src/report.c \
include/report_plain.h src/report_plain.c \
include/report_json.h src/report_json.c \
+ include/report_syslog.h src/report_syslog.c \
include/conf_ast.h src/conf_ast.c \
include/conf_eval.h src/conf_eval.c \
include/conf_lex.h src/conf_lex.l \
diff --git a/doc/aide.conf.5 b/doc/aide.conf.5
index 4ad9f3e..5366be4 100644
--- a/doc/aide.conf.5
+++ b/doc/aide.conf.5
@@ -266,6 +266,34 @@ The report format to use. The available report formats are as follows:
\fBjson\fP: Print report in json machine-readable format.
.RE
+.IP "syslog_format (type: bool, default: \fBno\fR)"
+Valid values are \fByes\fR, \fBtrue\fR, \fBno\fR and \fBfalse\fR.
+
+When enabled, the standard multi-line report is replaced with a compact
+semicolon-delimited format where every file event is emitted as a single line.
+This ensures that when used with \fBreport_url=syslog:\fILOG_FACILITY\fR, exactly
+one syslog message is produced per changed, added, or removed file.
+
+Output starts with a header line when differences are found:
+.nf
+AIDE found differences between database and filesystem!!
+.fi
+Followed by a summary line:
+.nf
+summary;total_number_of_files=\fIN\fP;added_files=\fIN\fP;removed_files=\fIN\fP;changed_files=\fIN\fP
+.fi
+Then one line per changed, added, or removed entry:
+.nf
+file=/usr/sbin/sshd;Mtime_old=...;Mtime_new=...;SHA256_old=...;SHA256_new=...
+dir=/etc/cron.d; added
+file=/usr/bin/old; removed
+.fi
+
+The maximum size of a single syslog message depends on the syslog daemon
+(typically 1\(en8\ KB). Lines exceeding this limit will be silently truncated
+by the syslog daemon. This is not controlled by AIDE.
+
+The \fBreport_summarize_changes\fR option has no effect in this format.
.IP "report_base16 (type: bool, default: \fBfalse\fR, added in AIDE v0.17)"
Base16 encode the checksums in the report. The default is to
diff --git a/include/conf_ast.h b/include/conf_ast.h
index 8892a05..424f734 100644
--- a/include/conf_ast.h
+++ b/include/conf_ast.h
@@ -53,6 +53,7 @@ typedef enum config_option {
REPORT_FORMAT_OPTION,
LIMIT_CMDLINE_OPTION,
NUM_WORKERS,
+ SYSLOG_FORMAT_OPTION,
} config_option;
typedef struct {
diff --git a/include/db_config.h b/include/db_config.h
index 4173a4b..363631e 100644
--- a/include/db_config.h
+++ b/include/db_config.h
@@ -133,6 +133,7 @@ typedef struct db_config {
int report_detailed_init;
int report_base16;
int report_quiet;
+ int syslog_format;
bool report_append;
DB_ATTR_TYPE report_ignore_added_attrs;
diff --git a/include/report.h b/include/report.h
index 2ec3539..b2efa55 100644
--- a/include/report.h
+++ b/include/report.h
@@ -48,6 +48,7 @@ typedef enum {
REPORT_FORMAT_UNKNOWN = 0,
REPORT_FORMAT_PLAIN = 1,
REPORT_FORMAT_JSON = 2,
+ REPORT_FORMAT_SYSLOG = 3,
} REPORT_FORMAT;
extern const ATTRIBUTE report_attrs_order[];
@@ -138,6 +139,8 @@ typedef struct report_format_module {
void (*print_report_summary)(report_t*);
} report_format_module;
+DB_ATTR_TYPE get_report_attributes(seltree*, report_t*);
+
char* get_file_type_string(mode_t);
char* get_summarize_changes_string(report_t*, seltree*);
char* get_summary_string(report_t*);
diff --git a/include/report_syslog.h b/include/report_syslog.h
new file mode 100644
index 0000000..4da9ae4
--- /dev/null
+++ b/include/report_syslog.h
@@ -0,0 +1,28 @@
+/*
+ * AIDE (Advanced Intrusion Detection Environment)
+ *
+ * Copyright (C) 2025 Hannes von Haugwitz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _REPORT_SYSLOG_H_INCLUDED
+#define _REPORT_SYSLOG_H_INCLUDED
+
+#include "report.h"
+
+extern report_format_module report_module_syslog;
+
+#endif
diff --git a/src/aide.c b/src/aide.c
index 6f728a9..f9a9cd4 100644
--- a/src/aide.c
+++ b/src/aide.c
@@ -439,6 +439,7 @@ static void setdefaults_before_config(void)
conf->report_detailed_init=0;
conf->report_base16=0;
conf->report_quiet=0;
+ conf->syslog_format=0;
conf->report_append=false;
conf->report_ignore_added_attrs = 0;
conf->report_ignore_removed_attrs = 0;
diff --git a/src/conf_ast.c b/src/conf_ast.c
index 366bc3f..7b66aed 100644
--- a/src/conf_ast.c
+++ b/src/conf_ast.c
@@ -58,6 +58,7 @@ config_option_t config_options[] = {
{ REPORT_FORMAT_OPTION, NULL, NULL },
{ LIMIT_CMDLINE_OPTION, "limit", "Limit" },
{ NUM_WORKERS, NULL, NULL },
+ { SYSLOG_FORMAT_OPTION, NULL, NULL },
};
static ast* new_ast_node(void) {
diff --git a/src/conf_eval.c b/src/conf_eval.c
index 5774ce6..bb39610 100644
--- a/src/conf_eval.c
+++ b/src/conf_eval.c
@@ -264,6 +264,9 @@ static void eval_config_statement(config_option_statement statement, int linenum
REPORT_FORMAT report_format = get_report_format(str);
if (report_format != REPORT_FORMAT_UNKNOWN) {
conf->report_format = report_format;
+ for (list *l = conf->report_urls; l; l = l->next) {
+ ((report_t *)l->data)->format = report_format;
+ }
LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, "set 'report_format' option to '%s' (raw: %d)", str, report_format)
} else {
LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "invalid report format: '%s'", str);
@@ -315,6 +318,11 @@ static void eval_config_statement(config_option_statement statement, int linenum
LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_NOTICE, "'num_workers' option already set (ignore new value '%s')", str)
}
break;
+ case SYSLOG_FORMAT_OPTION:
+ b = string_expression_to_bool(statement.e, linenumber, filename, linebuf);
+ conf->syslog_format = b;
+ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, "set 'syslog_format' to '%s'", btoa(b))
+ break;
}
}
diff --git a/src/conf_lex.l b/src/conf_lex.l
index 877a125..1a9654f 100644
--- a/src/conf_lex.l
+++ b/src/conf_lex.l
@@ -362,6 +362,13 @@ LOG_LEVEL lex_log_level = LOG_LEVEL_DEBUG;
BEGIN (STRINGEQHUNT);
return (CONFIGOPTION);
}
+<CONFIG>"syslog_format" {
+ LOG_LEX_TOKEN(lex_log_level, CONFIGOPTION (SYSLOG_FORMAT_OPTION), conftext)
+ conflval.option = SYSLOG_FORMAT_OPTION;
+ BEGIN (STRINGEQHUNT);
+ return (CONFIGOPTION);
+}
+
<CONFIG>"report_level" {
LOG_LEX_TOKEN(lex_log_level, CONFIGOPTION (REPORT_LEVEL_OPTION), conftext)
diff --git a/src/report.c b/src/report.c
index 9e4d0d0..f87754c 100644
--- a/src/report.c
+++ b/src/report.c
@@ -59,6 +59,7 @@
#include "report.h"
#include "report_plain.h"
#include "report_json.h"
+#include "report_syslog.h"
/*for locale support*/
#include "locale-aide.h"
/*for locale support*/
@@ -146,6 +147,7 @@ struct report_format {
static struct report_format report_format_array[] = {
{ REPORT_FORMAT_PLAIN, "plain" },
{ REPORT_FORMAT_JSON, "json" },
+ { REPORT_FORMAT_SYSLOG, "syslog" },
{ REPORT_FORMAT_UNKNOWN, NULL }
};
@@ -509,6 +511,15 @@ bool init_report_urls(void) {
}
}
+ }
+ /* syslog_format is a downstream-only option that must win over report_format
+ * regardless of declaration order in the config file. Enforce it here,
+ * after the entire config AST has been evaluated, so no subsequent
+ * report_format setting can accidentally override the user's intent. */
+ if (conf->syslog_format) {
+ for (l=conf->report_urls; l; l=l->next) {
+ ((report_t *)l->data)->format = REPORT_FORMAT_SYSLOG;
+ }
}
return true;
}
@@ -677,7 +688,7 @@ char* get_summarize_changes_string(report_t* report, seltree* node) {
-static DB_ATTR_TYPE get_report_attributes(seltree* node, report_t *report) {
+DB_ATTR_TYPE get_report_attributes(seltree* node, report_t *report) {
db_line* oline = node->old_data;
db_line* nline = node->new_data;
DB_ATTR_TYPE attrs = node->changed_attrs;
@@ -966,6 +977,9 @@ int gen_report(seltree* node) {
case REPORT_FORMAT_JSON:
print_report(report, node, report_module_json);
break;
+ case REPORT_FORMAT_SYSLOG:
+ print_report(report, node, report_module_syslog);
+ break;
case REPORT_FORMAT_UNKNOWN:
/* skip unknown report format */
break;
diff --git a/src/report_syslog.c b/src/report_syslog.c
new file mode 100644
index 0000000..920e927
--- /dev/null
+++ b/src/report_syslog.c
@@ -0,0 +1,338 @@
+/*
+ * AIDE (Advanced Intrusion Detection Environment)
+ *
+ * Copyright (C) 2025 Hannes von Haugwitz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "aide.h"
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "attributes.h"
+#include "base64.h"
+#include "db.h"
+#include "db_line.h"
+#include "hashsum.h"
+#include "report.h"
+#include "report_syslog.h"
+#include "seltree.h"
+#include "seltree_struct.h"
+#include "tree.h"
+#include "util.h"
+
+/* Returns the compact syslog file-type prefix, or NULL when mode is unknown
+ * (mode & S_IFMT == 0), in which case callers omit the "type=" prefix. */
+static const char *get_syslog_type_prefix(mode_t mode) {
+ switch (mode & S_IFMT) {
+ case S_IFREG: return "file";
+ case S_IFDIR: return "dir";
+ case S_IFLNK: return "link";
+ case S_IFBLK: return "blockd";
+ case S_IFCHR: return "chard";
+#ifdef S_IFIFO
+ case S_IFIFO: return "fifo";
+#endif
+#ifdef S_IFSOCK
+ case S_IFSOCK: return "socket";
+#endif
+ case 0: return NULL;
+ default: return "unknown";
+ }
+}
+
+/* Returns the key name for attribute a. attr_sizeg is the only exception:
+ * its details_string is "Size (>)" which contains '>', so we substitute
+ * "Size" to match the original patch's explicit details_string[] change. */
+static const char *get_attr_key(ATTRIBUTE a) {
+ if (a == attr_sizeg) {
+ return "Size";
+ }
+ return attributes[a].details_string;
+}
+
+#ifdef WITH_XATTR
+
+#define SYSLOG_PRINTABLE_XATTR_VALS \
+ "0123456789" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ ".-_:;,[]{}<>()!@#$%^&*|\\/?~"
+
+/* Appends one side's xattr values to stream in compact syslog format.
+ * Format: ;XAttrs_<side>=|[1]key=val|[2]key=val| */
+static void build_xattrs_compact(FILE *stream, db_line *line, const char *side) {
+ xattrs_type *xattrs = line ? line->xattrs : NULL;
+
+ if (!xattrs || xattrs->num == 0) {
+ fprintf(stream, ";XAttrs_%s=|num=0|", side);
+ return;
+ }
+
+ fprintf(stream, ";XAttrs_%s=|num=%zu|", side, xattrs->num);
+
+ for (size_t i = 0; i < xattrs->num; i++) {
+ const char *key = xattrs->ents[i].key;
+ const char *val = (const char *)xattrs->ents[i].val;
+ size_t vsz = xattrs->ents[i].vsz;
+
+ /* Check printability (replicates xstrnspn logic from report.c). */
+ size_t plen = 0;
+ while (plen < vsz && strchr(SYSLOG_PRINTABLE_XATTR_VALS, val[plen]))
+ plen++;
+ bool printable = (plen == vsz) || (plen == vsz - 1 && val[plen] == '\0');
+
+ if (printable) {
+ fprintf(stream, "[%zu]%s=%s|", i + 1, key, val);
+ } else {
+ char *b64 = encode_base64((byte *)xattrs->ents[i].val, vsz);
+ fprintf(stream, "[%zu]%s<=>%s|", i + 1, key, b64 ? b64 : "");
+ free(b64);
+ }
+ }
+}
+#endif /* WITH_XATTR */
+
+#ifdef WITH_POSIX_ACL
+/* Appends one side's ACL to stream in compact syslog format.
+ * Format: ;ACL_<side>=A:<acl_a_newlines_as_spaces>|D:<acl_d_newlines_as_spaces>
+ *
+ * Both A and D are initialized to "<NONE>" before the conditionals, fixing
+ * the uninitialized-pointer bug in the original 0.16 patch. */
+static void build_acl_compact(FILE *stream, db_line *line, const char *side) {
+ acl_type *acl = line ? line->acl : NULL;
+
+ const char *A = "<NONE>";
+ const char *D = "<NONE>";
+ if (acl) {
+ if (acl->acl_a) { A = acl->acl_a; }
+ if (acl->acl_d) { D = acl->acl_d; }
+ }
+
+ /* Write A component, replacing newlines with spaces. */
+ fprintf(stream, ";ACL_%s=A:", side);
+ for (const char *p = A; *p; p++) {
+ fputc(*p == '\n' ? ' ' : *p, stream);
+ }
+
+ /* Write D component, replacing newlines with spaces. */
+ fprintf(stream, "|D:");
+ for (const char *p = D; *p; p++) {
+ fputc(*p == '\n' ? ' ' : *p, stream);
+ }
+}
+#endif /* WITH_POSIX_ACL */
+
+/* Assembles a complete syslog line for one file event into *out.
+ *
+ * Invariant: this function never calls report_printf(). The caller emits
+ * the result with exactly one report_printf() call to avoid fragmenting
+ * syslog messages (each report_printf to url_syslog calls vsyslog() once).
+ *
+ * Cases:
+ * oline != NULL && nline != NULL → changed entry
+ * oline == NULL → added entry
+ * nline == NULL → removed entry
+ *
+ * *out is heap-allocated by open_memstream; caller must free() it. */
+static void build_syslog_line(report_t *report, db_line *oline, db_line *nline,
+ DB_ATTR_TYPE attrs, char **out) {
+ db_line *ref = nline ? nline : oline;
+ const char *type = get_syslog_type_prefix(ref->perm);
+
+ char *buf = NULL;
+ size_t bufsz = 0;
+ FILE *stream = open_memstream(&buf, &bufsz);
+
+ /* Write "type=path" prefix. */
+ if (type) {
+ fprintf(stream, "%s=%s", type, ref->filename);
+ } else {
+ fprintf(stream, "%s", ref->filename);
+ }
+
+ if (oline && nline) {
+ /* Changed entry: emit only differing attributes. */
+ for (int j = 0; j < report_attrs_order_length; j++) {
+ ATTRIBUTE a = report_attrs_order[j];
+
+ switch (a) {
+ case attr_allhashsums:
+ /* Expand to each compiled-in hash, mirroring print_dbline_attrs(). */
+ for (int i = 0; i < num_hashes; i++) {
+ if (!(ATTR(hashsums[i].attribute) & attrs)) { continue; }
+ const char *key = get_attr_key(hashsums[i].attribute);
+ if (!key) { continue; }
+ char **oval = NULL, **nval = NULL;
+ get_attribute_values(ATTR(hashsums[i].attribute), oline, &oval, report);
+ get_attribute_values(ATTR(hashsums[i].attribute), nline, &nval, report);
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
+ if (oval) { free(oval[0]); free(oval); }
+ if (nval) { free(nval[0]); free(nval); }
+ }
+ break;
+
+ case attr_size:
+ /* attr_size and attr_sizeg share this slot in report_attrs_order. */
+ if (ATTR(attr_size) & attrs) {
+ const char *key = get_attr_key(attr_size);
+ if (key) {
+ char **oval = NULL, **nval = NULL;
+ get_attribute_values(ATTR(attr_size), oline, &oval, report);
+ get_attribute_values(ATTR(attr_size), nline, &nval, report);
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
+ if (oval) { free(oval[0]); free(oval); }
+ if (nval) { free(nval[0]); free(nval); }
+ }
+ }
+ if (ATTR(attr_sizeg) & attrs) {
+ const char *key = get_attr_key(attr_sizeg); /* returns "Size" */
+ if (key) {
+ char **oval = NULL, **nval = NULL;
+ get_attribute_values(ATTR(attr_sizeg), oline, &oval, report);
+ get_attribute_values(ATTR(attr_sizeg), nline, &nval, report);
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
+ if (oval) { free(oval[0]); free(oval); }
+ if (nval) { free(nval[0]); free(nval); }
+ }
+ }
+ break;
+
+ default:
+ if (!(ATTR(a) & attrs)) { break; }
+#ifdef WITH_XATTR
+ if (a == attr_xattrs) {
+ build_xattrs_compact(stream, oline, "old");
+ build_xattrs_compact(stream, nline, "new");
+ break;
+ }
+#endif
+#ifdef WITH_POSIX_ACL
+ if (a == attr_acl) {
+ build_acl_compact(stream, oline, "old");
+ build_acl_compact(stream, nline, "new");
+ break;
+ }
+#endif
+ {
+ const char *key = get_attr_key(a);
+ if (!key) { break; }
+ char **oval = NULL, **nval = NULL;
+ get_attribute_values(ATTR(a), oline, &oval, report);
+ get_attribute_values(ATTR(a), nline, &nval, report);
+ fprintf(stream, ";%s_old=%s;%s_new=%s",
+ key, oval ? oval[0] : "", key, nval ? nval[0] : "");
+ if (oval) { free(oval[0]); free(oval); }
+ if (nval) { free(nval[0]); free(nval); }
+ }
+ break;
+ }
+ }
+ } else if (!oline) {
+ fprintf(stream, "; added");
+ } else {
+ fprintf(stream, "; removed");
+ }
+
+ fclose(stream);
+ *out = buf;
+}
+
+/* Emits exactly one syslog line for a file event. */
+static void emit_syslog_entry(report_t *report, db_line *oline, db_line *nline,
+ DB_ATTR_TYPE attrs) {
+ char *line = NULL;
+ build_syslog_line(report, oline, nline, attrs, &line);
+ report_printf(report, "%s\n", line);
+ free(line);
+}
+
+/* Unconditional tree walker — does not gate added/removed on report->level,
+ * matching the original patch's print_syslog_format() behavior. */
+static void syslog_walk_tree(report_t *report, seltree *node) {
+ pthread_rwlock_rdlock(&node->rwlock);
+
+ if (node->checked & NODE_CHANGED) {
+ emit_syslog_entry(report, node->old_data, node->new_data,
+ get_report_attributes(node, report));
+ }
+ if (node->checked & NODE_ADDED) {
+ emit_syslog_entry(report, NULL, node->new_data,
+ node->new_data->attr & ~report->ignore_added_attrs);
+ }
+ if (node->checked & NODE_REMOVED) {
+ emit_syslog_entry(report, node->old_data, NULL,
+ node->old_data->attr & ~report->ignore_removed_attrs);
+ }
+
+ for (tree_node *x = tree_walk_first(node->children); x != NULL; x = tree_walk_next(x)) {
+ syslog_walk_tree(report, tree_get_data(x));
+ }
+
+ pthread_rwlock_unlock(&node->rwlock);
+}
+
+/* ── Module callbacks ─────────────────────────────────────────────────── */
+
+static void noop_header(report_t *report) { (void)report; }
+static void noop_footer(report_t *report) { (void)report; }
+static void noop_databases(report_t *report) { (void)report; }
+static void noop_config_options(report_t *report) { (void)report; }
+static void noop_report_options(report_t *report) { (void)report; }
+static void noop_starttime_version(report_t *r, const char *t, const char *v) { (void)r; (void)t; (void)v; }
+static void noop_endtime_runtime(report_t *r, const char *t, long rt) { (void)r; (void)t; (void)rt; }
+static void noop_new_database_written(report_t *report) { (void)report; }
+static void noop_entries(report_t *r, seltree *n, const int f) { (void)r; (void)n; (void)f; }
+static void noop_diff_attrs(report_t *report) { (void)report; }
+static void noop_summary(report_t *report) { (void)report; }
+
+/* Emits header + summary when there are differences. Two separate
+ * report_printf() calls — each becomes one syslog message. */
+static void syslog_outline(report_t *report) {
+ if (report->nadd || report->nrem || report->nchg) {
+ report_printf(report, "AIDE found differences between database and filesystem!!\n");
+ report_printf(report,
+ "summary;total_number_of_files=%ld;added_files=%ld;"
+ "removed_files=%ld;changed_files=%ld\n",
+ report->ntotal, report->nadd, report->nrem, report->nchg);
+ }
+}
+
+static void syslog_details(report_t *report, seltree *node) {
+ syslog_walk_tree(report, node);
+}
+
+report_format_module report_module_syslog = {
+ .print_report_config_options = noop_config_options,
+ .print_report_databases = noop_databases,
+ .print_report_details = syslog_details,
+ .print_report_diff_attrs_entries = noop_diff_attrs,
+ .print_report_endtime_runtime = noop_endtime_runtime,
+ .print_report_entries = noop_entries,
+ .print_report_footer = noop_footer,
+ .print_report_header = noop_header,
+ .print_report_new_database_written = noop_new_database_written,
+ .print_report_outline = syslog_outline,
+ .print_report_report_options = noop_report_options,
+ .print_report_starttime_version = noop_starttime_version,
+ .print_report_summary = noop_summary,
+};
--
2.54.0

14
aide-0.19.2.tar.gz.asc Normal file
View File

@ -0,0 +1,14 @@
-----BEGIN PGP SIGNATURE-----
iQGzBAABCgAdFiEEVJXNoXyawXqyOEGnGO6GOGAi71cFAmict6oACgkQGO6GOGAi
71e/Awv/cP4/UJSVJlaUjRLoeRIayqFxAxZeku1OK1cNuGo5aRp47WsxZFpGkkMc
eNhrtOpukD0CohFdsBFabd2KweaaY67pbCSTXxQBjhEMtYLb+Z5b441cJChJQgeL
q2P09mCGmOoJ4li7VxF2Kjy4uTj4C64SoXFnkYgMzfgBQBC0wD3HBisN8tRG9M4w
4kQa8ZvsO5hFEiFuVhBfLwDKLaUXf5MDcst1Q9MeAGxwvQN5xaSivOwSoCqhVgSv
cDBo1RmhYSf43vUuXTAgclC3WD1y6qm0Si/naVnwVvvN2ij4cGfnb89ixd6qGlKY
HV4klaVQWRQwSyDlhbTcHVxvwnopW+5YZ5SeF3Yl7XohaL89hLEEDxFcLQkrslzV
wR0B0lCtmbZfVhWsnZScNZvT9Cnco7p4FMkkYIHOEVad6kFH3RRXHH320kUD3aWl
hAKXLvuEnYmE//prriUlnh7Q4rsyh9N6ZLgdZxrdPcVbNSAzRiMd8A4kh/H5oMY9
rFqw9YnF
=JV2j
-----END PGP SIGNATURE-----

588
aide-migrate-config Executable file
View File

@ -0,0 +1,588 @@
#!/bin/bash
# aide-migrate-config -- migrate AIDE configuration files from pre-0.19 syntax
#
# Usage: aide-migrate-config [--dry-run] [--skip-init] <config-file>
#
# --dry-run Report what would change; do not modify any file.
# --skip-init Migrate config files only; do not reinitialise the database.
# <config-file> Path to the main AIDE config (e.g. /etc/aide.conf).
set -euo pipefail
# ---------------------------------------------------------------------------
# Globals
# ---------------------------------------------------------------------------
readonly SCRIPT="$(basename "$0")"
readonly TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
DRY_RUN=false
SKIP_INIT=false
REINIT_NEEDED=false
# Parallel arrays: BACKUP_ORIG[i] → BACKUP_COPY[i]
BACKUP_ORIG=()
BACKUP_COPY=()
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
usage() {
echo "Usage: $SCRIPT [--dry-run] [--skip-init] <config-file>" >&2
exit 1
}
die() {
echo "$SCRIPT: error: $*" >&2
exit 1
}
info() {
echo "$SCRIPT: $*" >&2
}
# log_action MESSAGE
# Log a migration action. Prefixes with "would " in dry-run mode.
log_action() {
if $DRY_RUN; then
info " would $*"
else
info " $*"
fi
}
# config_has FILE KEY
# Return 0 if FILE contains a KEY= directive.
config_has() {
local file="$1" key="$2"
grep -qE "^[[:space:]]*${key}[[:space:]]*=" "$file"
}
# config_del FILE KEY
# Delete all lines matching KEY= from FILE in-place.
config_del() {
local file="$1" key="$2"
sed -E -i "/^[[:space:]]*${key}[[:space:]]*=/d" "$file"
}
# config_rename FILE OLD_KEY NEW_KEY
# Rename OLD_KEY= to NEW_KEY= in FILE in-place.
config_rename() {
local file="$1" old_key="$2" new_key="$3"
sed -E -i "s/^([[:space:]]*)${old_key}([[:space:]]*=)/\1${new_key}\2/" "$file"
}
# append_setting FILE CONTENT
# Append "CONTENT\n" to FILE, guaranteeing it starts on a fresh line.
# No-op if the key extracted from CONTENT already exists in FILE.
append_setting() {
local file="$1" content="$2"
local key="${content%%=*}"
config_has "$file" "$key" && return 0
if [[ -s "$file" ]] && [[ "$(tail -c1 "$file" | wc -l)" -eq 0 ]]; then
printf '\n' >> "$file"
fi
printf '%s\n' "$content" >> "$file"
}
# backup_file FILE
# Create a timestamped backup of FILE. No-op in dry-run mode.
backup_file() {
local file="$1"
local backup="${file}.bak.${TIMESTAMP}"
if $DRY_RUN; then
return 0
fi
cp -p -- "$file" "$backup"
BACKUP_ORIG+=("$file")
BACKUP_COPY+=("$backup")
info "backup: $backup"
}
# restore_backups
# Called by ERR trap; restores every backed-up file.
restore_backups() {
local i
for i in "${!BACKUP_ORIG[@]}"; do
cp -p -- "${BACKUP_COPY[$i]}" "${BACKUP_ORIG[$i]}" && \
info "restored: ${BACKUP_ORIG[$i]}" || \
info "WARNING: could not restore ${BACKUP_ORIG[$i]} from ${BACKUP_COPY[$i]}"
done
}
# ---------------------------------------------------------------------------
# Include-file discovery
# ---------------------------------------------------------------------------
# extract_includes CONFIG_FILE
# Print one absolute path per line for each file pulled in by @@include directives.
extract_includes() {
local config="$1"
local dir
dir="$(dirname "$config")"
grep -E '^[[:space:]]*@@include[[:space:]]' "$config" 2>/dev/null | \
while IFS= read -r line; do
# Strip the @@include keyword
local args
args="${line#*@@include}"
args="${args#"${args%%[! ]*}"}" # ltrim
# Count tokens to distinguish FILE vs DIRECTORY REGEX forms
local tok1 tok2
tok1="${args%% *}"
tok2="${args#* }"
if [[ "$tok1" == "$args" ]]; then
# Single token: @@include FILE
if [[ "$tok1" = /* ]]; then
echo "$tok1"
else
echo "${dir}/${tok1}"
fi
else
# Two tokens: @@include DIRECTORY REGEX
local incdir regex
incdir="$tok1"
regex="$tok2"
if [[ "$incdir" != /* ]]; then
incdir="${dir}/${incdir}"
fi
if [[ -d "$incdir" ]]; then
find "$incdir" -maxdepth 1 -type f -regex "$regex" | sort
fi
fi
done
}
# ---------------------------------------------------------------------------
# Config file migration
# ---------------------------------------------------------------------------
# needs_migration FILE
# Return 0 (true) if FILE contains any pattern requiring migration.
needs_migration() {
local f="$1"
# Removed/renamed config options
local _key
for _key in database grouped summarize_changes ignore_list report_attributes verbose; do
config_has "$f" "$_key" && return 0
done
# sql: value starts with sql: (PostgreSQL backend removed)
grep -qE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' "$f" && return 0
# any h character in report_ignore_e2fsattrs value
grep -qE '^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=.*h' "$f" && return 0
# removed/deprecated hashsums; dash at end of character class to avoid range error in GNU sed
grep -v '^[[:space:]]*#' "$f" | \
grep -qE '[+[:space:]=-](crc32b|crc32|tiger|haval|whirlpool|md5|sha1|rmd160|gost)([+[:space:]-]|$)' && return 0
# standalone S attribute in an expression (skip comment lines)
grep -v '^[[:space:]]*#' "$f" | grep -qE '(^|[+=[:space:]-])S([+[:space:]-]|$)' && return 0
# deprecated preprocessor macros
grep -qE '^[[:space:]]*@@(ifdef|ifndef|ifhost|ifnhost)[[:space:]]' "$f" && return 0
# missing trailing newline
[[ -s "$f" ]] && [[ "$(tail -c1 "$f" | wc -l)" -eq 0 ]] && return 0
return 1
}
# remove_hashsum FILE HASHSUM
# Remove a single hashsum token from all group/rule expressions in FILE (in-place).
# Handles all positions: +hash, -hash, hash+ (first), and hash$ (last with minus prefix).
remove_hashsum() {
local file="$1" hash="$2"
# Pass 1: remove hash when it is preceded by an operator (+ or -)
# Keep whatever follows (next operator, space, or end-of-line).
sed -E -i \
-e "/^[[:space:]]*#/!s/[+-]${hash}([+[:space:]-])/\1/g" \
-e "/^[[:space:]]*#/!s/[+-]${hash}$//" \
"$file"
# Pass 2: remove hash at the start of an expression (after = or whitespace),
# not preceded by an operator (those were handled in pass 1).
sed -E -i \
-e "/^[[:space:]]*#/!s/(=[[:space:]]*)${hash}([+[:space:]-])/\1\2/g" \
-e "/^[[:space:]]*#/!s/(=[[:space:]]*)${hash}$/\1/" \
-e "/^[[:space:]]*#/!s/([[:space:]])${hash}([+[:space:]-])/\1\2/g" \
-e "/^[[:space:]]*#/!s/([[:space:]])${hash}$/\1/" \
"$file"
# Clean up artifacts: double operators, dangling operator after '=', trailing operator.
sed -E -i \
-e '/^[[:space:]]*#/!s/[+][+]/+/g' \
-e '/^[[:space:]]*#/!s/[+][-]/+/g' \
-e '/^[[:space:]]*#/!s/[-][+]/-/g' \
-e '/^[[:space:]]*#/!s/(=[[:space:]]*)[+-]/\1/g' \
-e '/^[[:space:]]*#/!s/[+-]$//' \
"$file"
}
# migrate_config_file FILE
# Apply all config transformations to FILE. Sets global REINIT_NEEDED when needed.
migrate_config_file() {
local file="$1"
if [[ ! -f "$file" ]]; then
info "WARNING: $file not found, skipping"
return 0
fi
if [[ ! -r "$file" ]]; then
info "WARNING: $file is not readable, skipping"
return 0
fi
if ! needs_migration "$file"; then
info "no migration needed: $file"
return 0
fi
info "migrating: $file"
backup_file "$file"
local tmpfile
tmpfile="$(mktemp "${file}.XXXXXX")"
# Ensure tmpfile is cleaned up if we exit abnormally before the final mv
trap "rm -f '$tmpfile'; restore_backups" ERR
cp -p -- "$file" "$tmpfile"
# -----------------------------------------------------------------------
# Rename removed config options
# The 'database' key anchors to KEY= so it cannot match 'database_in'/'database_out'.
# -----------------------------------------------------------------------
config_has "$tmpfile" database && log_action "rename: database= → database_in="
config_has "$tmpfile" grouped && log_action "rename: grouped= → report_grouped="
config_has "$tmpfile" summarize_changes && log_action "rename: summarize_changes= → report_summarize_changes="
config_has "$tmpfile" ignore_list && log_action "rename: ignore_list= → report_ignore_changed_attrs="
config_has "$tmpfile" report_attributes && log_action "rename: report_attributes= → report_force_attrs="
if ! $DRY_RUN; then
config_rename "$tmpfile" database database_in
config_rename "$tmpfile" grouped report_grouped
config_rename "$tmpfile" summarize_changes report_summarize_changes
config_rename "$tmpfile" ignore_list report_ignore_changed_attrs
config_rename "$tmpfile" report_attributes report_force_attrs
fi
# -----------------------------------------------------------------------
# Replace verbose= with the equivalent 0.19 options
# -----------------------------------------------------------------------
if config_has "$tmpfile" verbose; then
log_action "remove verbose="
config_has "$tmpfile" log_level || log_action "add log_level=warning"
config_has "$tmpfile" report_level || log_action "add report_level=changed_attributes"
if ! $DRY_RUN; then
config_del "$tmpfile" verbose
append_setting "$tmpfile" 'log_level=warning'
append_setting "$tmpfile" 'report_level=changed_attributes'
fi
fi
# -----------------------------------------------------------------------
# Remove sql: database URL lines (PostgreSQL backend removed)
# Match lines whose value (after =) begins with sql:.
# -----------------------------------------------------------------------
if grep -qE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' "$tmpfile"; then
log_action "remove sql: database URL lines"
if $DRY_RUN; then
# Check which keys survive after sql: removal (i.e. have at least one non-sql: value)
grep -E '^[[:space:]]*database_out[[:space:]]*=' "$tmpfile" | \
grep -qvE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' || \
info " would add default database_out=file:/var/lib/aide/aide.db.new.gz"
grep -E '^[[:space:]]*database_in[[:space:]]*=' "$tmpfile" | \
grep -qvE '^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:' || \
info " would add default database_in=file:/var/lib/aide/aide.db.gz"
fi
if ! $DRY_RUN; then
sed -E -i '/^[[:space:]]*[a-z_]+[[:space:]]*=[[:space:]]*sql:/d' "$tmpfile"
config_has "$tmpfile" database_out || {
append_setting "$tmpfile" 'database_out=file:/var/lib/aide/aide.db.new.gz'
info " WARNING: sql: URL removed; default database_out added." \
"Verify storage path before running aide --init."
}
config_has "$tmpfile" database_in || {
append_setting "$tmpfile" 'database_in=file:/var/lib/aide/aide.db.gz'
info " WARNING: sql: database_in removed; default database_in added." \
"Verify path before running aide --init."
}
fi
REINIT_NEEDED=true
fi
# -----------------------------------------------------------------------
# Remove 'h' from report_ignore_e2fsattrs
# Use the sed address form to remove ALL 'h' characters from the value line.
# -----------------------------------------------------------------------
if grep -qE '^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=.*h' "$tmpfile"; then
log_action "remove 'h' from report_ignore_e2fsattrs"
if ! $DRY_RUN; then
sed -E -i '/^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=/s/h//g' "$tmpfile"
sed -E -i '/^[[:space:]]*report_ignore_e2fsattrs[[:space:]]*=[[:space:]]*$/d' "$tmpfile"
fi
fi
# -----------------------------------------------------------------------
# Remove deprecated and removed hashsums
# Process crc32b before crc32 to avoid prefix collision.
# Character classes use dash at end to prevent GNU sed range-error.
# -----------------------------------------------------------------------
local hash changed_hashes=false
for hash in crc32b crc32 tiger haval whirlpool md5 sha1 rmd160 gost; do
if grep -v '^[[:space:]]*#' "$tmpfile" | grep -qE "(^|[+[:space:]=-])${hash}([+[:space:]-]|\$)"; then
log_action "remove hashsum: $hash"
if ! $DRY_RUN; then
remove_hashsum "$tmpfile" "$hash"
fi
changed_hashes=true
REINIT_NEEDED=true
fi
done
# Post-removal: fill any group definition whose RHS became empty with sha256
if $changed_hashes; then
if ! $DRY_RUN; then
while IFS= read -r lineno; do
[[ -z "$lineno" ]] && continue
sed -i "${lineno}s/=.*/= sha256/" "$tmpfile"
info " group on line $lineno became empty after hashsum removal; added sha256"
done < <(grep -nE '^[A-Za-z0-9]+[[:space:]]*=[[:space:]]*[+-]?[[:space:]]*$' \
"$tmpfile" | cut -d: -f1)
else
info " note: any group containing only deprecated hashsums will have sha256 substituted"
fi
fi
# -----------------------------------------------------------------------
# Replace deprecated S attribute with growing+s
# Applies since AIDE 0.16 is being replaced; growing+s is unknown to 0.16.
# Character classes use dash at end to prevent GNU sed range-error.
# -----------------------------------------------------------------------
if grep -qE '(^|[+=[:space:]-])S([+[:space:]-]|$)' "$tmpfile"; then
log_action "replace S attribute with growing+s"
if ! $DRY_RUN; then
sed -E -i \
-e '/^[[:space:]]*#/!s/([+=[:space:]-])S([+[:space:]-]|$)/\1growing+s\2/g' \
-e '/^[[:space:]]*#/!s/^S([+[:space:]-]|$)/growing+s\1/g' \
"$tmpfile"
fi
fi
# -----------------------------------------------------------------------
# Replace deprecated @@ifdef/@@ifndef/@@ifhost/@@ifnhost macros
# -----------------------------------------------------------------------
if grep -qE '^[[:space:]]*@@(ifdef|ifndef|ifhost|ifnhost)[[:space:]]' "$tmpfile"; then
log_action "replace deprecated @@ifdef/@@ifndef/@@ifhost/@@ifnhost macros"
if ! $DRY_RUN; then
sed -E -i \
-e 's/^([[:space:]]*)@@ifdef([[:space:]])/\1@@if defined\2/g' \
-e 's/^([[:space:]]*)@@ifndef([[:space:]])/\1@@if not defined\2/g' \
-e 's/^([[:space:]]*)@@ifhost([[:space:]])/\1@@if hostname\2/g' \
-e 's/^([[:space:]]*)@@ifnhost([[:space:]])/\1@@if not hostname\2/g' \
"$tmpfile"
fi
fi
# -----------------------------------------------------------------------
# Ensure file ends with a newline
# -----------------------------------------------------------------------
local last_byte
last_byte="$(tail -c1 "$tmpfile" | od -An -tx1 | tr -d ' \n')"
if [[ -n "$last_byte" && "$last_byte" != '0a' ]]; then
log_action "add missing trailing newline"
$DRY_RUN || echo "" >> "$tmpfile"
fi
# H group's content changed in 0.19; warn if used without a custom definition.
# Only reached when the file had real 0.16-style options, so this never fires on a
# clean 0.19 config.
if grep -qE '(^|[+[:space:]-])H([+[:space:]-]|$)' "$tmpfile" 2>/dev/null; then
if ! grep -qE '^[[:space:]]*H[[:space:]]*=' "$tmpfile"; then
info "NOTE: built-in H group in use without custom definition;" \
"H content changed in 0.19 — run 'aide --init' to rebuild the database"
fi
fi
# -----------------------------------------------------------------------
# Commit changes
# -----------------------------------------------------------------------
if ! $DRY_RUN; then
mv -- "$tmpfile" "$file"
# Restore original permissions
chmod --reference="${BACKUP_COPY[-1]}" "$file" 2>/dev/null || true
else
rm -f "$tmpfile"
fi
# Disarm the local ERR trap and re-arm the global one
trap - ERR
trap restore_backups ERR
}
# ---------------------------------------------------------------------------
# Post-migration warnings
# ---------------------------------------------------------------------------
check_and_warn() {
local file="$1"
# Warn if a rule path starts with a macro variable.
# Only flag lines where @@{...} appears at the very start (after optional whitespace);
# mid-path macros like /path/@@{VAR}/sub are valid and must not be flagged.
local macro_rules
macro_rules="$(grep -nE '^[[:space:]]*@@\{[^}]+\}' "$file" 2>/dev/null || true)"
if [[ -n "$macro_rules" ]]; then
info "WARNING ($file): the following rule paths start with a macro variable." \
"Rewrite them so the path begins with a literal '/':"
echo "$macro_rules" >&2
fi
# Warn if a group name contains non-alphanumeric characters.
# Require an uppercase first letter to avoid false positives on config option names.
# Underscores are accepted by AIDE 0.19.2; only '-' and '.' cause parse errors.
local bad_groups
bad_groups="$(grep -nE '^[A-Z][A-Za-z0-9]*[-.][A-Za-z0-9][^=]*[[:space:]]*=' \
"$file" 2>/dev/null || true)"
if [[ -n "$bad_groups" ]]; then
info "WARNING ($file): the following group names contain non-alphanumeric characters." \
"Rename groups and all their references to [A-Za-z0-9] only:"
echo "$bad_groups" >&2
fi
}
# ---------------------------------------------------------------------------
# Database reinitialisation
# ---------------------------------------------------------------------------
# expand_aide_macros CONFIG VALUE
# Expand @@{NAME} references in VALUE using @@define lines from CONFIG.
expand_aide_macros() {
local config="$1" value="$2"
local key val
while IFS= read -r defline; do
key="$(echo "$defline" | awk '{print $2}')"
val="$(echo "$defline" | awk '{$1=$2=""; print substr($0,3)}')"
value="${value//@@\{${key}\}/$val}"
done < <(grep -E '^[[:space:]]*@@define[[:space:]]' "$config" || true)
echo "$value"
}
# reinit_database CONFIG_FILE
# Backup the existing database, run aide --init, move the new DB into place.
reinit_database() {
local config="$1"
# Parse database_in path (file: URLs only)
local raw_in
raw_in="$(grep -E '^[[:space:]]*database_in[[:space:]]*=' "$config" | \
head -1 | sed -E 's/^[^=]+=file://')" || true
[[ -z "$raw_in" ]] && { info "WARNING: database_in not found in config; skipping reinit"; return 0; }
local db_in
db_in="$(expand_aide_macros "$config" "$raw_in")"
# Parse database_out path (file: URLs only)
local raw_out
raw_out="$(grep -E '^[[:space:]]*database_out[[:space:]]*=' "$config" | \
head -1 | sed -E 's/^[^=]+=file://')" || true
[[ -z "$raw_out" ]] && { info "WARNING: database_out not found in config; skipping reinit"; return 0; }
local db_out
db_out="$(expand_aide_macros "$config" "$raw_out")"
if [[ ! -f "$db_in" ]]; then
info "no existing database at $db_in; run 'aide --init -c $config' when ready"
return 0
fi
info "reinitialising database (this may take several minutes)..."
backup_file "$db_in"
aide --init -c "$config"
if [[ ! -f "$db_out" ]]; then
die "aide --init completed but output database not found at $db_out"
fi
mv -- "$db_out" "$db_in"
chmod 0600 "$db_in"
chown root:root "$db_in"
info "database reinitialised: $db_in"
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
main() {
# CLI flags are out of scope for this script
info "Note: This script migrates aide config files only." \
"If you use '--verbose' or '--report' flags in wrapper scripts," \
"cron jobs, or systemd units, remove those flags manually."
# Argument parsing
local config_file=""
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
--skip-init) SKIP_INIT=true; shift ;;
-h|--help) usage ;;
--) shift; break ;;
-*) die "unknown option: $1" ;;
*) config_file="$1"; shift ;;
esac
done
[[ $# -gt 0 ]] && die "unexpected argument: $1"
[[ -z "$config_file" ]] && usage
[[ -f "$config_file" ]] || die "config file not found: $config_file"
[[ -r "$config_file" ]] || die "config file not readable: $config_file"
$DRY_RUN || [[ -w "$config_file" ]] || die "config file not writable: $config_file"
# Verify aide >= 0.19 is installed
local aide_ver
aide_ver="$(aide --version 2>&1 | grep -oE '[0-9]+\.[0-9]+' | head -1)" || true
if [[ -z "$aide_ver" ]]; then
info "aide not found; skipping migration"
exit 0
fi
local aide_major aide_minor
aide_major="${aide_ver%%.*}"
aide_minor="${aide_ver#*.}"
if [[ "$aide_major" -lt 1 && "$aide_minor" -lt 19 ]]; then
info "aide $aide_ver < 0.19; skipping migration"
exit 0
fi
$DRY_RUN && info "DRY-RUN mode: no files will be modified"
# Global ERR trap for cleanup
trap restore_backups ERR
# Collect all config files to process
local -a config_files=("$config_file")
while IFS= read -r inc; do
[[ -f "$inc" ]] && config_files+=("$inc")
done < <(extract_includes "$config_file")
# Migrate each config file
local f
for f in "${config_files[@]}"; do
migrate_config_file "$f"
done
# Post-migration warnings
for f in "${config_files[@]}"; do
check_and_warn "$f"
done
# Validate the resulting config
if ! $DRY_RUN; then
if ! aide --config-check -c "$config_file" >/dev/null 2>&1; then
info "ERROR: aide --config-check failed after migration; restoring all backups"
restore_backups
exit 1
fi
info "aide --config-check passed"
fi
# Database reinit
if $REINIT_NEEDED && ! $SKIP_INIT && ! $DRY_RUN; then
reinit_database "$config_file"
elif $REINIT_NEEDED && $SKIP_INIT && ! $DRY_RUN; then
info "database reinitialisation required but --skip-init set;" \
"run 'aide --init -c $config_file' manually"
elif $REINIT_NEEDED && $DRY_RUN; then
info "would require database reinitialisation after migration"
fi
info "migration complete"
}
main "$@"

2
aide-tmpfiles.conf Normal file
View File

@ -0,0 +1,2 @@
d /var/log/aide 0700 root root -
d /var/lib/aide 0700 root root -

View File

@ -1,34 +0,0 @@
diff -up ./src/conf_eval.c.verbose ./src/conf_eval.c
--- ./src/conf_eval.c.verbose 2023-04-01 18:25:38.000000000 +0200
+++ ./src/conf_eval.c 2024-05-15 00:08:41.040033220 +0200
@@ -187,6 +187,7 @@ static void set_database_attr_option(DB_
static void eval_config_statement(config_option_statement statement, int linenumber, char *filename, char* linebuf) {
char *str;
bool b;
+ long num;
DB_ATTR_TYPE attr;
switch (statement.option) {
ATTRIBUTE_CONFIG_OPTION_CASE(REPORT_IGNORE_ADDED_ATTRS_OPTION, report_ignore_added_attrs)
@@ -298,8 +299,20 @@ static void eval_config_statement(config
LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_CONFIG, "set 'config_version' option to '%s'", str)
break;
case VERBOSE_OPTION:
- log_msg(LOG_LEVEL_ERROR, "%s:%d: 'verbose' option is no longer supported, use 'log_level' and 'report_level' options instead (see man aide.conf for details) (line: '%s')", conf_filename, conf_linenumber, conf_linebuf);
- exit(INVALID_CONFIGURELINE_ERROR);
+ log_msg(LOG_LEVEL_CONFIG, "%s:%d: 'verbose' option is deprecated, use 'log_level' and 'report_level' options instead (see man aide.conf for details) (line: '%s')", conf_filename, conf_linenumber, conf_linebuf);
+ str = eval_string_expression(statement.e, linenumber, filename, linebuf);
+ num = strtol(str, NULL, 10);
+
+ if (num < 0 || num > 255) {
+ LOG_CONFIG_FORMAT_LINE(LOG_LEVEL_ERROR, "invalid verbose level: '%s'", str);
+ exit(INVALID_CONFIGURELINE_ERROR);
+ }
+
+ if (num >= 10) {
+ set_log_level(LOG_LEVEL_DEBUG);
+ }
+
+ free(str);
break;
case LIMIT_CMDLINE_OPTION:
/* command-line options are ignored here */

396
aide.conf
View File

@ -14,20 +14,49 @@ database_out=file:@@{DBDIR}/aide.db.new.gz
# Whether to gzip the output to database
gzip_dbout=yes
# Database attributes to include in report (H = all compiled hashsums, default)
database_attrs=H
# Add metadata to database (version info, timestamps)
database_add_metadata=yes
# Warn about unrestricted rules during config check (default: false)
config_check_warn_unrestricted_rules=false
# Number of workers for parallel processing (default: 1, can use percentage)
num_workers=1
# Default.
log_level=warning
report_level=changed_attributes
# Report format (plain or json)
report_format=plain
# Group files in report by added/removed/changed
report_grouped=yes
# Summarize changes in report
report_summarize_changes=yes
# Don't report if no differences found
report_quiet=no
# Report encoding (base64 is default, base16 available)
report_base16=no
report_url=file:@@{LOGDIR}/aide.log
report_url=stdout
#report_url=stderr
#NOT IMPLEMENTED report_url=mailto:root@foo.com
#NOT IMPLEMENTED report_url=syslog:LOG_AUTH
#report_url=syslog:LOG_AUTH
# These are the default rules.
#
#ftype: file type
#fstype: file system type (Linux-only)
#p: permissions
#i: inode:
#i: inode
#l: link name (symbolic links only)
#n: number of links
#u: user
#g: group
@ -36,55 +65,64 @@ report_url=stdout
#m: mtime
#a: atime
#c: ctime
#S: check for growing size
#acl: Access Control Lists
#selinux SELinux security context
#xattrs: Extended file attributes
#md5: md5 checksum
#sha1: sha1 checksum
#e2fsattrs: file attributes on Linux file system
#caps: file capabilities (Linux-only)
# Hashsums attributes (regular files only)
#sha256: sha256 checksum
#sha512: sha512 checksum
#rmd160: rmd160 checksum
#tiger: tiger checksum
#sha512_256: SHA-512 checksum truncated to 256 output bits
#sha3_256: SHA3-256 checksum (modern)
#sha3_512: SHA3-512 checksum (modern)
#stribog256: GOST R 34.11-2012, 256 bit
#stribog512: GOST R 34.11-2012, 512 bit
#haval: haval checksum (MHASH only)
#gost: gost checksum (MHASH only)
#crc32: crc32 checksum (MHASH only)
#whirlpool: whirlpool checksum (MHASH only)
# Special attributes for advanced use cases:
#I: ignore changed filename - detects moved files by inode
#growing: ignore growing file size/timestamps for logs
#compressed: ignore compression - compares uncompressed content
#ANF: allow new files - new files ignored in report
#ARF: allow removed files - missing files ignored in report
FIPSR = p+i+n+u+g+s+acl+selinux+xattrs+sha256
#R: p+i+n+u+g+s+m+c+acl+selinux+xattrs+md5
#L: p+i+n+u+g+acl+selinux+xattrs
#E: Empty group
#>: Growing logfile p+u+g+i+n+S+acl+selinux+xattrs
# Default groups in AIDE v0.19:
# R = p+ftype+i+l+n+u+g+s+m+c+sha3_256+X
# L = p+ftype+i+l+n+u+g+X
# > = Growing file p+ftype+l+u+g+i+n+s+growing+X
# H = all compiled in (and not deprecated) hashsums
# X = acl+selinux+xattrs+e2fsattrs+caps (if compiled in)
# E = Empty group
# Use 'aide --version' to list the default compound groups.
# You can create custom rules like this.
# With MHASH...
# ALLXTRAHASHES = sha1+rmd160+sha256+sha512+whirlpool+tiger+haval+gost+crc32
ALLXTRAHASHES = sha1+rmd160+sha256+sha512+tiger
# Everything but access time (Ie. all changes)
# Note: Removed deprecated/removed hashsums (tiger, haval, crc32, crc32b, whirlpool, md5, sha1, rmd160, gost)
ALLXTRAHASHES = sha256+sha512+sha512_256+sha3_256+sha3_512+stribog256+stribog512
# Everything but access time (Ie. all changes) - updated with modern hashsums
EVERYTHING = R+ALLXTRAHASHES
# Sane, with multiple hashes
# NORMAL = R+rmd160+sha256+whirlpool
NORMAL = FIPSR+sha512
# Base + sha512 (strong)
NORMAL = R+sha512-m-c
# For directories, don't bother doing hashes
DIR = p+i+n+u+g+acl+selinux+xattrs
# Content only - added file type and strong hash
CONTENT = ftype+sha512
# Access control only
PERMS = p+i+u+g+acl+selinux
# For directories, don't bother doing hashes - added file type and link name
DIR = ftype+p+i+l+n+u+g+acl+selinux+xattrs
# Logfile are special, in that they often change
LOG = >
# Access control only - added file type and link name
PERMS = ftype+p+u+g+acl+selinux+xattrs
# Just do sha256 and sha512 hashes
LSPP = FIPSR+sha512
# Logfiles are special, in that they often change due to log rotation
# Track only: permissions, file type, user, group, number of links, SELinux context, extended attributes
# Allow new files (ANF) and allow removed files (ARF) due to log rotation techniques
# Don't track: size, inodes, timestamps, checksums and some special attributes (these change frequently with log rotation)
LOG = p+ftype+u+g+n+ANF+ARF+selinux+xattrs
# Some files get updated automatically, so the inode/ctime/mtime change
# but we want to know when the data inside them changes
DATAONLY = p+n+u+g+s+acl+selinux+xattrs+sha256
# but we want to know when the data inside them changes - updated with modern hash
DATAONLY = ftype+p+l+n+u+g+s+acl+selinux+xattrs+sha256
# Next decide what directories/files you want in the database.
@ -93,124 +131,220 @@ DATAONLY = p+n+u+g+s+acl+selinux+xattrs+sha256
/sbin NORMAL
/lib NORMAL
/lib64 NORMAL
/opt NORMAL
# Monitor /opt selectively to avoid noise from auto-updating applications
/opt CONTENT
/usr NORMAL
/root NORMAL
# These are too volatile
!/usr/src
!/usr/tmp
# Admins dot files constantly change, just check perms
/root/\..* PERMS
!/root/.xauth*
/root NORMAL
# Check only permissions, inode, user and group for /etc, but
# cover some important files closely.
/etc PERMS
!/etc/mtab
# Ignore backup files
!/etc/.*~
/etc/exports NORMAL
/etc/fstab NORMAL
/etc/passwd NORMAL
/etc/group NORMAL
/etc/gshadow NORMAL
/etc/shadow NORMAL
/etc/security/opasswd NORMAL
/etc/hosts.allow NORMAL
/etc/hosts.deny NORMAL
# trusted databases
/etc/hosts$ NORMAL
/etc/host.conf$ NORMAL
/etc/hostname$ NORMAL
/etc/issue$ NORMAL
/etc/issue.net$ NORMAL
/etc/protocols$ NORMAL
/etc/services$ NORMAL
/etc/localtime$ NORMAL
/etc/alternatives NORMAL
/etc/mime.types$ NORMAL
/etc/terminfo NORMAL
/etc/exports$ NORMAL
/etc/fstab$ NORMAL
/etc/passwd$ NORMAL
/etc/group$ NORMAL
/etc/gshadow$ NORMAL
/etc/shadow$ NORMAL
/etc/subgid$ NORMAL
/etc/subuid$ NORMAL
/etc/skel NORMAL
/etc/sssd NORMAL
/etc/swid NORMAL
/etc/system-release-cpe$ NORMAL
/etc/tmux.conf$ NORMAL
/etc/xattr.conf$ NORMAL
/etc/sudoers NORMAL
/etc/skel NORMAL
# networking
/etc/firewalld NORMAL
!/etc/NetworkManager/system-connections
/etc/NetworkManager NORMAL
/etc/networks$ NORMAL
/etc/dhcp NORMAL
/etc/wpa_supplicant NORMAL
/etc/resolv.conf$ DATAONLY
/etc/logrotate.d NORMAL
/etc/resolv.conf DATAONLY
/etc/nscd.conf NORMAL
/etc/securetty NORMAL
# logins and accounts
/etc/login.defs$ NORMAL
/etc/libuser.conf$ NORMAL
/var/log/faillog$ PERMS
/var/log/lastlog$ PERMS
/var/run/faillock PERMS
/etc/pam.d NORMAL
/etc/security NORMAL
/etc/securetty$ NORMAL
/etc/polkit-1 NORMAL
/etc/sudo.conf$ NORMAL
/etc/sudoers$ NORMAL
/etc/sudoers.d NORMAL
# Shell/X starting files
/etc/profile NORMAL
/etc/bashrc NORMAL
/etc/bash_completion.d/ NORMAL
/etc/login.defs NORMAL
/etc/zprofile NORMAL
/etc/zshrc NORMAL
/etc/zlogin NORMAL
/etc/zlogout NORMAL
/etc/profile.d/ NORMAL
/etc/X11/ NORMAL
/etc/profile$ NORMAL
/etc/profile.d NORMAL
/etc/bashrc$ NORMAL
/etc/bash_completion.d NORMAL
/etc/zprofile$ NORMAL
/etc/zshrc$ NORMAL
/etc/zlogin$ NORMAL
/etc/zlogout$ NORMAL
/etc/X11 NORMAL
/etc/shells$ NORMAL
# Pkg manager
/etc/yum.conf NORMAL
/etc/yumex.conf NORMAL
/etc/yumex.profiles.conf NORMAL
/etc/yum/ NORMAL
/etc/yum.repos.d/ NORMAL
/etc/dnf NORMAL
/etc/yum.repos.d NORMAL
# auditing
# AIDE produces an audit record, so this becomes perpetual motion.
/var/log/audit PERMS
/etc/audit NORMAL
/etc/libaudit.conf$ NORMAL
/etc/aide.conf$ NORMAL
# System logs with proper logrotate handling
/etc/rsyslog.conf$ NORMAL
/etc/rsyslog.d NORMAL
/etc/logrotate.conf$ NORMAL
/etc/logrotate.d NORMAL
/etc/systemd/journald.conf$ NORMAL
# Log directory
/var/log LOG
# Journal files - exclude xattrs and link count due to systemd journal's user.crtime_usec extended attribute changes and new directory creation
/var/log/journal LOG-xattrs-n
/var/log LOG
/var/run/utmp LOG
# secrets
/etc/pkcs11 NORMAL
/etc/pki NORMAL
/etc/ssl NORMAL
/etc/certmonger NORMAL
/var/lib/systemd/random-seed$ PERMS
# init system
/etc/systemd NORMAL
/etc/sysconfig NORMAL
/etc/rc.d NORMAL
/etc/tmpfiles.d NORMAL
/etc/machine-id$ NORMAL
# boot config
/etc/default NORMAL
/etc/grub.d NORMAL
/etc/grub2.cfg$ NORMAL
/etc/dracut.conf$ NORMAL
/etc/dracut.conf.d NORMAL
# glibc linker
/etc/ld.so.cache$ NORMAL
/etc/ld.so.conf$ NORMAL
/etc/ld.so.conf.d NORMAL
/etc/ld.so.preload$ NORMAL
# kernel config
/etc/sysctl.conf$ NORMAL
/etc/sysctl.d NORMAL
/etc/modprobe.d NORMAL
/etc/modules-load.d NORMAL
/etc/depmod.d NORMAL
/etc/udev NORMAL
/etc/crypttab$ NORMAL
#### Daemons ####
# cron jobs
/var/spool/at CONTENT
/etc/at.allow$ CONTENT
/etc/at.deny$ CONTENT
/etc/anacrontab$ NORMAL
/etc/cron.allow$ NORMAL
/etc/cron.deny$ NORMAL
/etc/cron.d NORMAL
/etc/cron.daily NORMAL
/etc/cron.hourly NORMAL
/etc/cron.monthly NORMAL
/etc/cron.weekly NORMAL
/etc/crontab$ NORMAL
/var/spool/cron/root CONTENT
# time keeping
/etc/chrony.conf$ NORMAL
/etc/chrony.keys$ NORMAL
# mail
/etc/aliases$ NORMAL
/etc/aliases.db$ NORMAL
/etc/postfix NORMAL
# ssh
/etc/ssh/sshd_config$ NORMAL
/etc/ssh/ssh_config$ NORMAL
# stunnel
/etc/stunnel NORMAL
# ftp
/etc/vsftpd CONTENT
# printing
/etc/cups NORMAL
/etc/cupshelpers NORMAL
/etc/avahi NORMAL
# web server
/etc/httpd NORMAL
# dns
/etc/named NORMAL
/etc/named.conf$ NORMAL
/etc/named.iscdlv.key$ NORMAL
/etc/named.rfc1912.zones$ NORMAL
/etc/named.root.key$ NORMAL
# xinetd
/etc/xinetd.conf$ NORMAL
/etc/xinetd.d NORMAL
# IPsec
/etc/ipsec.conf$ NORMAL
/etc/ipsec.secrets$ NORMAL
/etc/ipsec.d NORMAL
# USBGuard
/etc/usbguard NORMAL
# Now everything else
/etc PERMS
# This gets new/removes-old filenames daily
!/var/log/sa
# As we are checking it, we've truncated yesterdays size to zero.
!/var/log/aide.log
# LSPP rules...
# AIDE produces an audit record, so this becomes perpetual motion.
# /var/log/audit/ LSPP
/etc/audit/ LSPP
/etc/libaudit.conf LSPP
/usr/sbin/stunnel LSPP
/var/spool/at LSPP
/etc/at.allow LSPP
/etc/at.deny LSPP
/etc/cron.allow LSPP
/etc/cron.deny LSPP
/etc/cron.d/ LSPP
/etc/cron.daily/ LSPP
/etc/cron.hourly/ LSPP
/etc/cron.monthly/ LSPP
/etc/cron.weekly/ LSPP
/etc/crontab LSPP
/var/spool/cron/root LSPP
/etc/login.defs LSPP
/etc/securetty LSPP
/var/log/faillog LSPP
/var/log/lastlog LSPP
/etc/hosts LSPP
/etc/sysconfig LSPP
/etc/inittab LSPP
/etc/grub/ LSPP
/etc/rc.d LSPP
/etc/ld.so.conf LSPP
/etc/localtime LSPP
/etc/sysctl.conf LSPP
/etc/modprobe.conf LSPP
/etc/pam.d LSPP
/etc/security LSPP
/etc/aliases LSPP
/etc/postfix LSPP
/etc/ssh/sshd_config LSPP
/etc/ssh/ssh_config LSPP
/etc/stunnel LSPP
/etc/vsftpd.ftpusers LSPP
/etc/vsftpd LSPP
/etc/issue LSPP
/etc/issue.net LSPP
/etc/cups LSPP
# With AIDE's default verbosity level of 5, these would give lots of
# warnings upon tree traversal. It might change with future version.
#
@ -218,8 +352,6 @@ DATAONLY = p+n+u+g+s+acl+selinux+xattrs+sha256
#=/home DIR
# Ditto /var/log/sa reason...
!/var/log/httpd
# /boot/grub2/grubenv's timestamp is getting modified continuously due to "boot_success" implementation
!/boot/grub2/grubenv
!/var/log/and-httpd
# Admins dot files constantly change, just check perms
/root/\..* PERMS

101
aide.spec
View File

@ -1,19 +1,26 @@
Summary: Intrusion detection environment
Name: aide
Version: 0.18.6
Release: 8%{?dist}.2
URL: http://sourceforge.net/projects/aide
Version: 0.19.2
Release: 4%{?dist}.1
URL: https://github.com/aide/aide
License: GPL-2.0-or-later
Source0: %{url}/files/aide/%{version}/%{name}-%{version}.tar.gz
Source1: aide.conf
Source2: README.quickstart
Source3: aide.logrotate
Source0: %{url}/releases/download/v%{version}/%{name}-%{version}.tar.gz
Source1: %{url}/releases/download/v%{version}/%{name}-%{version}.tar.gz.asc
# gpg2 --recv-keys 2BBBD30FAAB29B3253BCFBA6F6947DAB68E7B931
# gpg2 --export --export-options export-minimal 2BBBD30FAAB29B3253BCFBA6F6947DAB68E7B931 >gpgkey-aide.gpg
Source2: gpgkey-aide.gpg
Source3: aide.conf
Source4: README.quickstart
Source5: aide.logrotate
Source6: aide-tmpfiles.conf
Source7: aide-migrate-config
Patch0: aide-0.19.2-syslog-format.patch
BuildRequires: gcc
BuildRequires: make
BuildRequires: bison flex
BuildRequires: pcre2-devel
BuildRequires: libgpg-error-devel gnutls-devel
BuildRequires: libgpg-error-devel nettle-devel
BuildRequires: zlib-devel
BuildRequires: libcurl-devel
BuildRequires: libacl-devel
@ -22,36 +29,26 @@ BuildRequires: libattr-devel
BuildRequires: e2fsprogs-devel
BuildRequires: audit-libs-devel
BuildRequires: autoconf autoconf-archive
BuildRequires: automake libtool
Patch1: aide-verbose.patch
Patch2: gnutls.patch
Patch3: escape-control-chars-CVE-2025-54389.patch
Patch4: lowercase-groupnames.patch
BuildRequires: automake libtool systemd-rpm-macros
# For verifying signatures
BuildRequires: gnupg2
%description
AIDE (Advanced Intrusion Detection Environment) is a file integrity
checker and intrusion detection program.
%prep
%setup
#%%autosetup -p1
cp -a %{S:2} .
%patch -P 1 -p1 -b .verbose
%patch -P 2 -p1 -b .gnutls
%patch -P 3 -p1 -b .CVE-2025-54389
%patch -P 4 -p1 -b .lowercase-groupnames
%{gpgverify} --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}'
%autosetup -p1
cp -a %{SOURCE4} .
%build
autoreconf -ivf
%configure \
--disable-static \
--with-config_file=%{_sysconfdir}/aide.conf \
--with-gnutls \
--without-gcrypt \
--with-nettle \
--with-zlib \
--with-curl \
--with-posix-acl \
@ -63,37 +60,67 @@ autoreconf -ivf
%install
%make_install bindir=%{_sbindir}
install -Dpm0644 -t %{buildroot}%{_sysconfdir} %{S:1}
install -Dpm0644 %{S:3} %{buildroot}%{_sysconfdir}/logrotate.d/aide
install -Dpm0644 -t %{buildroot}%{_sysconfdir} %{SOURCE3}
install -Dpm0644 %{SOURCE5} %{buildroot}%{_sysconfdir}/logrotate.d/aide
mkdir -p %{buildroot}%{_localstatedir}/log/aide
mkdir -p -m0700 %{buildroot}%{_localstatedir}/lib/aide
# Install tmpfiles config
install -Dpm0644 %{SOURCE6} %{buildroot}%{_tmpfilesdir}/aide.conf
install -Dpm0755 %{SOURCE7} %{buildroot}%{_sbindir}/aide-migrate-config
%files
%license COPYING
%doc AUTHORS ChangeLog NEWS README contrib/
%doc AUTHORS ChangeLog NEWS README
%doc README.quickstart
%{_sbindir}/aide
%{_sbindir}/aide-migrate-config
%{_mandir}/man1/*.1*
%{_mandir}/man5/*.5*
%config(noreplace) %attr(0600,root,root) %{_sysconfdir}/aide.conf
%config(noreplace) %{_sysconfdir}/logrotate.d/aide
%dir %attr(0700,root,root) %{_localstatedir}/lib/aide
%dir %attr(0700,root,root) %{_localstatedir}/log/aide
%{_tmpfilesdir}/aide.conf
%post
if [ $1 -ge 2 ]; then
/usr/sbin/aide-migrate-config /etc/aide.conf 2>&1 | \
tee -a /var/log/aide/aide-migrate.log || :
fi
%changelog
* Tue Oct 14 2025 Attila Lakatos <alakatos@redhat.com> - 0.18.6-8.2
- RHEL 10.1.Z ERRATUM
- Fix lowercase group name definition
Resolves: RHEL-119647
* Tue May 26 2026 Attila Lakatos <alakatos@redhat.com> - 0.19.2-4.1
- Re-add syslog_format config option dropped during rebase to 0.19.2
Resolves: RHEL-178845
- Add aide-migrate-config to automate config migration from pre-0.19 syntax
* Tue Aug 19 2025 Attila Lakatos <alakatos@redhat.com> - 0.18.6-8.1
RHEL 10.1 ERRATUM
- CVE-2025-54389 aide: improper output neutralization enables bypassing
Resolves: RHEL-108928
* Wed Oct 15 2025 Attila Lakatos <alakatos@redhat.com> - 0.19.2-4
- Adjust default config to avoid false positives in /etc
Resolves: RHEL-39970
* Thu Oct 09 2025 Attila Lakatos <alakatos@redhat.com> - 0.19.2-3
- /boot/grub2/grubenv is excluded from check due to boot_success implementation
- Do not monitor link count in /var/log/journal
Resolves: RHEL-39970
* Thu Sep 25 2025 Attila Lakatos <alakatos@redhat.com> - 0.19.2-2
- Modernize aide config file
Resolves: RHEL-39970
- No path reference ends with '/'
Resolves: RHEL-39959
* Tue Aug 05 2025 Attila Lakatos <alakatos@redhat.com> - 0.19.2-1
- rebase to 0.19.2
Resolves: RHEL-110572
- exclude directory but include subitems
Resolves: RHEL-1382
- prevent aide from exiting if a file is truncated during check
Resolves: RHEL-1383
- Switch to libnettle for hashing
Resolves: RHEL-59170
* Wed Jan 29 2025 Radovan Sroka <rsroka@redhat.com> - 0.18.6-8
RHEL 10.0 ERRATUM
- /boot/grub2/grubenv's timestamp is getting modified continuously due to "boot_success" implementation
Resolves: RHEL-4320
* Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 0.18.6-7

View File

@ -1,402 +0,0 @@
diff -U0 aide-0.18.6/ChangeLog.orig aide-0.18.6/ChangeLog
diff -up aide-0.18.6/doc/aide.1.orig aide-0.18.6/doc/aide.1
--- aide-0.18.6/doc/aide.1.orig 2025-08-19 11:35:36.082823977 +0200
+++ aide-0.18.6/doc/aide.1 2025-08-19 11:35:36.082823977 +0200
@@ -130,12 +130,25 @@ SIGUSR1 toggles the log_level between cu
.PP
.SH NOTES
+.IP "Checksum encoding"
+
The checksums in the database and in the output are by default base64
encoded (see also report_base16 option).
To decode them you can use the following shell command:
echo <encoded_checksum> | base64 \-d | hexdump \-v \-e '32/1 "%02x" "\\n"'
+.IP "Control characters"
+
+Control characters (00-31 and 127) are always escaped in log and plain report
+output. They are escaped by a literal backslash (\\) followed by exactly 3
+digits representing the character in octal notation (e.g. a newline is output
+as "\fB\\012\fR"). A literal backslash is not escaped unless it is followed by
+3 digits (0-9), in this case the literal backslash is escaped as
+"\fB\\134\fR". Reports in JSON format are escaped according to the JSON specs
+(e.g. a newline is output as "\fB\\b\fR" or an escape (\fBESC\fR) is output as
+"\fB\\u001b\fR")
+
.PP
.SH FILES
diff -up aide-0.18.6/include/util.h.orig aide-0.18.6/include/util.h
--- aide-0.18.6/include/util.h.orig 2025-08-19 11:35:36.081823966 +0200
+++ aide-0.18.6/include/util.h 2025-08-19 11:35:36.080823957 +0200
@@ -57,6 +57,9 @@ int cmpurl(url_t*, url_t*);
int contains_unsafe(const char*);
+char *strnesc(const char *, size_t);
+char *stresc(const char *);
+
void decode_string(char*);
char* encode_string(const char*);
diff -up aide-0.18.6/src/aide.c.orig aide-0.18.6/src/aide.c
--- aide-0.18.6/src/aide.c.orig 2025-08-19 11:35:36.083823987 +0200
+++ aide-0.18.6/src/aide.c 2025-08-19 11:35:36.083823987 +0200
@@ -285,8 +285,8 @@ static void read_param(int argc,char**ar
if((conf->limit_crx=pcre2_compile((PCRE2_SPTR) conf->limit, PCRE2_ZERO_TERMINATED, PCRE2_UTF|PCRE2_ANCHORED, &pcre2_errorcode, &pcre2_erroffset, NULL)) == NULL) {
PCRE2_UCHAR pcre2_error[128];
pcre2_get_error_message(pcre2_errorcode, pcre2_error, 128);
- INVALID_ARGUMENT("--limit", error in regular expression '%s' at %zu: %s, conf->limit, pcre2_erroffset, pcre2_error)
-
+ char * limit_safe = stresc(conf->limit);
+ INVALID_ARGUMENT("--limit", error in regular expression '%s' at %zu: %s, limit_safe, pcre2_erroffset, pcre2_error)
}
conf->limit_md = pcre2_match_data_create_from_pattern(conf->limit_crx, NULL);
if (conf->limit_md == NULL) {
diff -up aide-0.18.6/src/gen_list.c.orig aide-0.18.6/src/gen_list.c
--- aide-0.18.6/src/gen_list.c.orig 2025-08-19 11:35:36.083823987 +0200
+++ aide-0.18.6/src/gen_list.c 2025-08-19 11:35:36.083823987 +0200
@@ -339,35 +339,40 @@ void print_match(char* filename, rx_rule
char * str;
char* attr_str;
char file_type = get_restriction_char(restriction);
+ char *filename_safe = stresc(filename);
+ char *limit_safe = conf->limit?stresc(conf->limit):NULL;
+
switch (match) {
case RESULT_SELECTIVE_MATCH:
str = get_restriction_string(rule->restriction);
attr_str = diff_attributes(0, rule->attr);
- fprintf(stdout, "[X] %c '%s': selective rule: '%s %s %s' (%s:%d: '%s%s%s')\n", file_type, filename, rule->rx, str, attr_str, rule->config_filename, rule->config_linenumber, rule->config_line, rule->prefix?"', prefix: '":"", rule->prefix?rule->prefix:"");
+ fprintf(stdout, "[X] %c '%s': selective rule: '%s %s %s' (%s:%d: '%s%s%s')\n", file_type, filename_safe, rule->rx, str, attr_str, rule->config_filename, rule->config_linenumber, rule->config_line, rule->prefix?"', prefix: '":"", rule->prefix?rule->prefix:"");
free(attr_str);
free(str);
break;
case RESULT_EQUAL_MATCH:
str = get_restriction_string(rule->restriction);
attr_str = diff_attributes(0, rule->attr);
- fprintf(stdout, "[X] %c '%s': equal rule: '=%s %s %s' (%s:%d: '%s%s%s')\n", file_type, filename, rule->rx, str, attr_str, rule->config_filename, rule->config_linenumber, rule->config_line, rule->prefix?"', prefix: '":"", rule->prefix?rule->prefix:"");
+ fprintf(stdout, "[X] %c '%s': equal rule: '=%s %s %s' (%s:%d: '%s%s%s')\n", file_type, filename_safe, rule->rx, str, attr_str, rule->config_filename, rule->config_linenumber, rule->config_line, rule->prefix?"', prefix: '":"", rule->prefix?rule->prefix:"");
free(attr_str);
free(str);
break;
case RESULT_PARTIAL_MATCH:
case RESULT_NO_MATCH:
if (rule) {
- fprintf(stdout, "[ ] %c '%s': negative rule: '!%s %s' (%s:%d: '%s%s%s')\n", file_type, filename, rule->rx, str = get_restriction_string(rule->restriction), rule->config_filename, rule->config_linenumber, rule->config_line, rule->prefix?"', prefix: '":"", rule->prefix?rule->prefix:"");
+ fprintf(stdout, "[ ] %c '%s': negative rule: '!%s %s' (%s:%d: '%s%s%s')\n", file_type, filename_safe, rule->rx, str = get_restriction_string(rule->restriction), rule->config_filename, rule->config_linenumber, rule->config_line, rule->prefix?"', prefix: '":"", rule->prefix?rule->prefix:"");
free(str);
} else {
- fprintf(stdout, "[ ] %c '%s': no matching rule\n", file_type, filename);
+ fprintf(stdout, "[ ] %c '%s': no matching rule\n", file_type, filename_safe);
}
break;
case RESULT_PARTIAL_LIMIT_MATCH:
case RESULT_NO_LIMIT_MATCH:
- fprintf(stdout, "[ ] %c '%s': outside of limit '%s'\n", file_type, filename, conf->limit);
+ fprintf(stdout, "[ ] %c '%s': outside of limit '%s'\n", file_type, filename_safe, limit_safe);
break;
}
+ free(filename_safe);
+ free(limit_safe);
}
/*
diff -up aide-0.18.6/src/log.c.orig aide-0.18.6/src/log.c
--- aide-0.18.6/src/log.c.orig 2025-08-19 11:35:36.083823987 +0200
+++ aide-0.18.6/src/log.c 2025-08-19 11:42:16.887261761 +0200
@@ -30,6 +30,7 @@
#include "log.h"
#include "errorcodes.h"
+#include "util.h"
LOG_LEVEL prev_log_level = LOG_LEVEL_UNSET;
LOG_LEVEL log_level = LOG_LEVEL_UNSET;
@@ -118,7 +119,9 @@ static void log_cached_lines(void) {
for(int i = 0; i < ncachedlines; ++i) {
LOG_LEVEL level = cached_lines[i].level;
if (level == LOG_LEVEL_ERROR || level <= log_level) {
- fprintf(url, "%s: %s\n", log_level_array[level-1].log_string, cached_lines[i].message);
+ char *msg_safe = stresc(cached_lines[i].message);
+ fprintf(url, "%s: %s\n", log_level_array[level-1].log_string, msg_safe);
+ free(msg_safe);
}
free(cached_lines[i].message);
}
@@ -135,9 +138,24 @@ static void vlog_msg(LOG_LEVEL level,con
FILE* url = stderr;
if (level == LOG_LEVEL_ERROR || level <= log_level) {
- fprintf(url, "%s: ", log_level_array[level-1].log_string );
- vfprintf(url, format, ap);
- fprintf(url, "\n");
+ va_list aq;
+ va_copy(aq, ap);
+ int n = vsnprintf(NULL, 0, format, aq) + 1;
+ va_end(aq);
+
+ int size = n * sizeof(char);
+ char *msg_unsafe = malloc(size);
+ if (msg_unsafe == NULL) {
+ fprintf(url, "%s: malloc failed to allocate %d bytes of memory\n", log_level_array[LOG_LEVEL_ERROR-1].log_string, size);
+ exit(MEMORY_ALLOCATION_FAILURE);
+ }
+
+ vsnprintf(msg_unsafe, n, format, ap);
+ char *msg_safe = stresc(msg_unsafe);
+ free(msg_unsafe);
+ fprintf(url, "%s: %s\n", log_level_array[level-1].log_string, msg_safe);
+ free(msg_safe);
+
} else if (log_level == LOG_LEVEL_UNSET) {
cache_line(level, format, ap);
}
diff -up aide-0.18.6/src/report_json.c.orig aide-0.18.6/src/report_json.c
--- aide-0.18.6/src/report_json.c.orig 2025-08-19 11:35:36.084823997 +0200
+++ aide-0.18.6/src/report_json.c 2025-08-19 11:35:36.083823987 +0200
@@ -57,12 +57,53 @@ static int _escape_json_string(const cha
int n = 0;
for (i = 0; i < strlen(src); ++i) {
- if (src[i] == '\\') {
- if (escaped_string) { escaped_string[n] = '\\'; }
- n++;
+ switch(src[i]) {
+ case '\n':
+ if (escaped_string) { escaped_string[n] = '\\'; }
+ n++;
+ if (escaped_string) { escaped_string[n] = 'n'; }
+ n++;
+ break;
+ case '\t':
+ if (escaped_string) { escaped_string[n] = '\\'; }
+ n++;
+ if (escaped_string) { escaped_string[n] = 't'; }
+ n++;
+ break;
+ case '\b':
+ if (escaped_string) { escaped_string[n] = '\\'; }
+ n++;
+ if (escaped_string) { escaped_string[n] = 'b'; }
+ n++;
+ break;
+ case '\f':
+ if (escaped_string) { escaped_string[n] = '\\'; }
+ n++;
+ if (escaped_string) { escaped_string[n] = 'f'; }
+ n++;
+ break;
+ case '\r':
+ if (escaped_string) { escaped_string[n] = '\\'; }
+ n++;
+ if (escaped_string) { escaped_string[n] = 'r'; }
+ n++;
+ break;
+ case '"':
+ case '\\':
+ if (escaped_string) { escaped_string[n] = '\\'; }
+ n++;
+ if (escaped_string) { escaped_string[n] = src[i]; }
+ n++;
+ break;
+ default:
+ if (src[i] >= 0 && (src[i] < 0x1f || src[i] == 0x7f)) {
+ if (escaped_string) { snprintf(&escaped_string[n], 7, "\\u%04x", src[i]); }
+ n += 6;
+ } else {
+ if (escaped_string) { escaped_string[n] = src[i]; }
+ n++;
+ }
}
- if (escaped_string) { escaped_string[n] = src[i]; }
- n++;
}
if (escaped_string) { escaped_string[n] = '\0'; }
n++;
diff -up aide-0.18.6/src/report_plain.c.orig aide-0.18.6/src/report_plain.c
--- aide-0.18.6/src/report_plain.c.orig 2025-08-19 11:35:36.084823997 +0200
+++ aide-0.18.6/src/report_plain.c 2025-08-19 11:35:36.083823987 +0200
@@ -55,7 +55,9 @@ static char* _get_not_grouped_list_strin
static void _print_config_option(report_t *report, config_option option, const char* value) {
if (first) { first=false; }
else { report_printf(report," | "); }
- report_printf(report, "%s: %s", config_options[option].report_string, value);
+ char *value_safe = stresc(value);
+ report_printf(report, "%s: %s", config_options[option].report_string, value_safe);
+ free(value_safe);
}
static void _print_report_option(report_t *report, config_option option, const char* value) {
@@ -63,37 +65,49 @@ static void _print_report_option(report_
}
static void _print_attribute(report_t *report, db_line* oline, db_line* nline, ATTRIBUTE attribute) {
- char **ovalue = NULL;
- char **nvalue = NULL;
+ char **ovalues = NULL;
+ char **nvalues = NULL;
int onumber, nnumber, i, c;
int p = (width_details-(4 + MAX_WIDTH_DETAILS_STRING))/2;
DB_ATTR_TYPE attr = ATTR(attribute);
const char* name = attributes[attribute].details_string;
- onumber=get_attribute_values(attr, oline, &ovalue, report);
- nnumber=get_attribute_values(attr, nline, &nvalue, report);
+ onumber=get_attribute_values(attr, oline, &ovalues, report);
+ nnumber=get_attribute_values(attr, nline, &nvalues, report);
i = 0;
while (i<onumber || i<nnumber) {
- int olen = i<onumber?strlen(ovalue[i]):0;
- int nlen = i<nnumber?strlen(nvalue[i]):0;
+ char *ovalue = NULL;
+ char *nvalue = NULL;
+ int olen = 0;
+ int nlen = 0;
+ if (i<onumber){
+ ovalue = stresc(ovalues[i]);
+ olen = strlen(ovalue);
+ }
+ if (i<nnumber) {
+ nvalue = stresc(nvalues[i]);
+ nlen = strlen(nvalue);
+ }
int k = 0;
while (olen-p*k >= 0 || nlen-p*k >= 0) {
c = k*(p-1);
if (!onumber) {
- report_printf(report," %-*s%c %-*c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, ' ', p-1, nlen-c>0?&nvalue[i][c]:"");
+ report_printf(report," %-*s%c %-*c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, ' ', p-1, nlen-c>0?&nvalue[c]:"");
} else if (!nnumber) {
- report_printf(report," %-*s%c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p-1, olen-c>0?&ovalue[i][c]:"");
+ report_printf(report," %-*s%c %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p-1, olen-c>0?&ovalue[c]:"");
} else {
- report_printf(report," %-*s%c %-*.*s| %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, p-1, olen-c>0?&ovalue[i][c]:"", p-1, nlen-c>0?&nvalue[i][c]:"");
+ report_printf(report," %-*s%c %-*.*s| %.*s\n", MAX_WIDTH_DETAILS_STRING, (i+k)?"":name, (i+k)?' ':':', p, p-1, olen-c>0?&ovalue[c]:"", p-1, nlen-c>0?&nvalue[c]:"");
}
k++;
}
++i;
+ free(ovalue);
+ free(nvalue);
}
- for(i=0; i < onumber; ++i) { free(ovalue[i]); ovalue[i]=NULL; } free(ovalue); ovalue=NULL;
- for(i=0; i < nnumber; ++i) { free(nvalue[i]); nvalue[i]=NULL; } free(nvalue); nvalue=NULL;
+ for(i=0; i < onumber; ++i) { free(ovalues[i]); ovalues[i]=NULL; } free(ovalues); ovalues=NULL;
+ for(i=0; i < nnumber; ++i) { free(nvalues[i]); nvalues[i]=NULL; } free(nvalues); nvalues=NULL;
}
static void _print_database_attributes(report_t *report, db_line* db) {
@@ -136,17 +150,29 @@ static void print_report_summary_plain(r
}
static void print_line_plain(report_t* report, seltree* node) {
- if(report->summarize_changes) {
+ if(report->summarize_changes) {
+ char *filename = ((node->checked&NODE_REMOVED)?node->old_data:node->new_data)->filename;
+ char *filename_safe = stresc(filename);
char* summary = get_summarize_changes_string(report, node);
- report_printf(report, "\n%s: %s", summary, ((node->checked&NODE_REMOVED)?node->old_data:node->new_data)->filename);
+ report_printf(report, "\n%s: %s", summary, filename_safe);
free(summary); summary=NULL;
+ free(filename_safe);
} else {
if (node->checked&NODE_ADDED) {
- report_printf(report, _("\nadded: %s"),(node->new_data)->filename);
+ char *filename = (node->new_data)->filename;
+ char *filename_safe = stresc(filename);
+ report_printf(report, _("\nadded: %s"), filename_safe);
+ free(filename_safe);
} else if (node->checked&NODE_REMOVED) {
- report_printf(report, _("\nremoved: %s"),(node->old_data)->filename);
+ char *filename = (node->old_data)->filename;
+ char *filename_safe = stresc(filename);
+ report_printf(report, _("\nremoved: %s"), filename_safe);
+ free(filename_safe);
} else if (node->checked&NODE_CHANGED) {
- report_printf(report, _("\nchanged: %s"),(node->new_data)->filename);
+ char *filename = (node->new_data)->filename;
+ char *filename_safe = stresc(filename);
+ report_printf(report, _("\nchanged: %s"), filename_safe);
+ free(filename_safe);
}
}
}
@@ -154,11 +180,14 @@ static void print_line_plain(report_t* r
static void print_report_dbline_attributes_plain(report_t *report, db_line* oline, db_line* nline, DB_ATTR_TYPE report_attrs) {
if (report_attrs) {
char *file_type = get_file_type_string((nline==NULL?oline:nline)->perm);
+ db_line* line = nline==NULL?oline:nline;
report_printf(report, "\n");
if (file_type) {
report_printf(report, "%s: ", file_type);
}
- report_printf(report, "%s\n", (nline==NULL?oline:nline)->filename);
+ char *filename_safe = stresc(line->filename);
+ report_printf(report, "%s\n", filename_safe);
+ free(filename_safe);
print_dbline_attrs(report, oline, nline, report_attrs, _print_attribute);
}
@@ -195,9 +224,11 @@ static void print_report_details_plain(r
static void print_report_diff_attrs_entries_plain(report_t *report) {
for(int i = 0; i < report->num_diff_attrs_entries; ++i) {
char *str = NULL;
+ char *entry_safe = stresc(report->diff_attrs_entries[i].entry);
report_printf(report, "Entry %s in databases has different attributes: %s\n",
- report->diff_attrs_entries[i].entry,
+ entry_safe,
str= diff_attributes(report->diff_attrs_entries[i].old_attrs, report->diff_attrs_entries[i].new_attrs));
+ free(entry_safe);
free(str);
}
report->num_diff_attrs_entries = 0;
diff -up aide-0.18.6/src/util.c.orig aide-0.18.6/src/util.c
--- aide-0.18.6/src/util.c.orig 2025-08-19 11:35:36.084823997 +0200
+++ aide-0.18.6/src/util.c 2025-08-19 11:35:36.082823977 +0200
@@ -105,6 +105,41 @@ int cmpurl(url_t* u1,url_t* u2)
return RETOK;
}
+static size_t escape_str(const char *unescaped_str, char *str, size_t s) {
+ size_t n = 0;
+ size_t i = 0;
+ char c;
+ while (i < s && (c = unescaped_str[i])) {
+ if ((c >= 0 && (c < 0x1f || c == 0x7f)) ||
+ (c == '\\' && isdigit(unescaped_str[i+1])
+ && isdigit(unescaped_str[i+2])
+ && isdigit(unescaped_str[i+3])
+ ) ) {
+ if (str) { snprintf(&str[n], 5, "\\%03o", c); }
+ n += 4;
+ } else {
+ if (str) { str[n] = c; }
+ n++;
+ }
+ i++;
+ }
+ if (str) { str[n] = '\0'; }
+ n++;
+ return n;
+}
+
+char *strnesc(const char *unescaped_str, size_t s) {
+ int n = escape_str(unescaped_str, NULL, s);
+ char *str = checked_malloc(n);
+ escape_str(unescaped_str, str, s);
+ return str;
+}
+
+char *stresc(const char *unescaped_str) {
+ return strnesc(unescaped_str, strlen(unescaped_str));
+}
+
+
/* Returns 1 if the string contains unsafe characters, 0 otherwise. */
int contains_unsafe (const char *s)
{

View File

@ -1,487 +0,0 @@
diff -up ./configure.ac.gnutls ./configure.ac
--- ./configure.ac.gnutls 2023-06-13 20:53:43.000000000 +0200
+++ ./configure.ac 2024-05-14 19:09:47.419448389 +0200
@@ -350,6 +350,10 @@ AC_MSG_CHECKING(for Mhash)
AC_ARG_WITH([mhash], AS_HELP_STRING([--with-mhash], [use Mhash (default: check)]), [with_mhash=$withval], [with_mhash=check])
AC_MSG_RESULT([$with_mhash])
+AC_MSG_CHECKING(for GnuTLS)
+AC_ARG_WITH([gnutls], AS_HELP_STRING([--with-gnutls], [use GnuTLS library (default: check)]), [with_gnutls=$withval], [with_gnutls=check])
+AC_MSG_RESULT([$with_gnutls])
+
AC_MSG_CHECKING(for GNU crypto library)
AC_ARG_WITH([gcrypt], AS_HELP_STRING([--with-gcrypt], [use GNU crypto library (default: check)]), [with_gcrypt=$withval], [with_gcrypt=check])
AC_MSG_RESULT([$with_gcrypt])
@@ -363,19 +367,29 @@ AS_IF([test x"$with_mhash" = xyes], [
)],AC_DEFINE(HAVE_MHASH_WHIRLPOOL,1,[mhash has whirlpool]))
AS_IF([test x"$with_gcrypt" = xcheck], [
with_gcrypt=no
+ with_gnutls=no
])
])
AIDE_PKG_CHECK_MODULES_OPTIONAL(gcrypt, GCRYPT, libgcrypt)
+AIDE_PKG_CHECK_MODULES_OPTIONAL(gnutls, GNUTLS, gnutls)
AS_IF([test x"$with_mhash" != xno && test x"$with_gcrypt" != xno], [
AC_MSG_ERROR([Using gcrypt together with mhash makes no sense. To disable mhash use --without-mhash])
])
-AS_IF([test x"$with_mhash" = xno && test x"$with_gcrypt" = xno], [
- AC_MSG_ERROR([AIDE requires mhash or libcrypt for hashsum calculation])
+AS_IF([test x"$with_mhash" != xno && test x"$with_gnutls" != xno], [
+ AC_MSG_ERROR([Using gnutls together with mhash makes no sense. To disable mhash use --without-mhash])
+])
+AS_IF([test x"$with_gcrypt" != xno && test x"$with_gnutls" != xno], [
+ AC_MSG_ERROR([Using gnutls together with gcrypt makes no sense. To disable gcrypt use --without-gcrypt])
+])
+AS_IF([test x"$with_mhash" = xno && test x"$with_gcrypt" = xno && test x"$with_gnutls" == xno], [
+ AC_MSG_ERROR([AIDE requires mhash, gnutls or libcrypt for hashsum calculation])
])
compoptionstring="${compoptionstring}use Mhash: $with_mhash\\n"
AM_CONDITIONAL(HAVE_MHASH, [test "x$MHASH_LIBS" != "x"])
compoptionstring="${compoptionstring}use GNU crypto library: $with_gcrypt\\n"
AM_CONDITIONAL(HAVE_GCRYPT, [test "x$GCRYPT_LIBS" != "x"])
+compoptionstring="${compoptionstring}use GnuTLS: $with_gnutls\\n"
+AM_CONDITIONAL(HAVE_GNUTLS, [test "x$GNUTLS_LIBS" != "x"])
AIDE_PKG_CHECK(audit, Linux Auditing Framework, no, AUDIT, audit)
diff -up ./doc/aide.conf.5.gnutls ./doc/aide.conf.5
--- ./doc/aide.conf.5.gnutls 2023-08-01 10:47:59.000000000 +0200
+++ ./doc/aide.conf.5 2024-05-14 19:09:47.420448380 +0200
@@ -866,6 +866,7 @@ haval256 checksum
.TP
.B "crc32"
crc32 checksum
+(\fIlibmhash\fR and \fIlibgcrypt\fR only)
.TP
.B "crc32b"
crc32 checksum
@@ -876,14 +877,15 @@ GOST R 34.11-94 checksum
.TP
.B "whirlpool"
whirlpool checksum
+(\fIlibgcrypt\fR and \fIlibmhash\fRonly)
.TP
.B "stribog256"
GOST R 34.11-2012, 256 bit checksum
-(\fIlibgcrypt\fR only, added in AIDE v0.17)
+(\fIlibgcrypt\fR and \fIgnutls\fR only, added in AIDE v0.17)
.TP
.B "stribog512"
GOST R 34.11-2012, 512 bit checksum
-(\fIlibgcrypt\fR only, added in AIDE v0.17)
+(\fIlibgcrypt\fR and \fIgnutls\fR only, added in AIDE v0.17)
.PP
Use 'aide --version' to show which hashsums are available.
diff -up ./include/md.h.gnutls ./include/md.h
--- ./include/md.h.gnutls 2023-04-01 18:25:38.000000000 +0200
+++ ./include/md.h 2024-05-14 19:09:47.420448380 +0200
@@ -29,6 +29,10 @@
#ifdef WITH_GCRYPT
#include <gcrypt.h>
#endif
+#ifdef WITH_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
#include <sys/types.h>
#include "attributes.h"
#include "hashsum.h"
@@ -61,6 +65,10 @@ typedef struct md_container {
gcry_md_hd_t mdh;
#endif
+#ifdef WITH_GNUTLS
+ gnutls_hash_hd_t gnutls_mdh[num_hashes];
+#endif
+
} md_container;
typedef struct md_hashsums {
diff -up ./Makefile.am.gnutls ./Makefile.am
--- ./Makefile.am.gnutls 2024-05-14 19:09:47.420448380 +0200
+++ ./Makefile.am 2024-05-14 19:23:09.347757387 +0200
@@ -64,17 +64,35 @@ if HAVE_CURL
aide_SOURCES += include/fopen.h src/fopen.c
endif
-aide_CFLAGS = @AIDE_DEFS@ -W -Wall -g ${PTHREAD_CFLAGS}
-aide_LDADD = -lm ${PCRE2_LIBS} ${ZLIB_LIBS} ${MHASH_LIBS} ${GCRYPT_LIBS} ${POSIX_ACL_LIBS} ${SELINUX_LIBS} ${AUDIT_LIBS} ${XATTR_LIBS} ${ELF_LIBS} ${E2FSATTRS_LIBS} ${CAPABILITIES_LIBS} ${CURL_LIBS} ${PTHREAD_LIBS}
+aide_CFLAGS = @AIDE_DEFS@ -W -Wall -g ${PTHREAD_CFLAGS} ${GNUTLS_CFLAGS}
+aide_LDADD = -lm ${PCRE2_LIBS} ${ZLIB_LIBS} ${MHASH_LIBS} ${GNUTLS_LIBS} ${GCRYPT_LIBS} ${POSIX_ACL_LIBS} ${SELINUX_LIBS} ${AUDIT_LIBS} ${XATTR_LIBS} ${ELF_LIBS} ${E2FSATTRS_LIBS} ${CAPABILITIES_LIBS} ${CURL_LIBS} ${PTHREAD_LIBS}
if HAVE_CHECK
-TESTS = check_aide
-check_PROGRAMS = check_aide
+TESTS = check_aide check_md
+check_PROGRAMS = check_aide check_md
check_aide_SOURCES = tests/check_aide.c tests/check_aide.h \
tests/check_attributes.c src/attributes.c \
src/log.c src/util.c
-check_aide_CFLAGS = -I$(top_srcdir)/include $(CHECK_CFLAGS)
-check_aide_LDADD = -lm ${PCRE2_LIBS} ${MHASH_LIBS} ${GCRYPT_LIBS} $(CHECK_LIBS)
+check_aide_CFLAGS = -I$(top_srcdir)/include $(CHECK_CFLAGS) ${GNUTLS_CFLAGS}
+check_aide_LDADD = -lm ${PCRE2_LIBS} ${MHASH_LIBS} ${GCRYPT_LIBS} $(CHECK_LIBS) ${GNUTLS_LIBS}
+
+check_md_SOURCES = tests/check_md.c tests/check_md.h \
+ tests/check_hashes.c \
+ src/log.c src/util.c src/md.c src/base64.c src/hashsum.c src/attributes.c
+
+check_md_CFLAGS = -I$(top_srcdir)/include \
+ $(CHECK_CFLAGS) \
+ $(GCRYPT_CFLAGS) \
+ $(GNUTLS_CFLAGS) \
+ $(MHASH_CFLAGS) \
+ $(PCRE2_CFLAGS)
+check_md_LDADD = -lm \
+ $(CHECK_LIBS) \
+ ${GCRYPT_LIBS} \
+ ${GNUTLS_LIBS} \
+ ${MHASH_LIBS} \
+ ${PCRE2_LIBS}
+
endif # HAVE_CHECK
AM_CFLAGS = @AIDE_DEFS@ -W -Wall -g
diff -up ./README.gnutls ./README
--- ./README.gnutls 2023-08-01 10:47:59.000000000 +0200
+++ ./README 2024-05-14 19:09:47.419448389 +0200
@@ -132,11 +132,15 @@
o GNU make.
o pkg-config
o PCRE2 library
- o Mhash (optional, but highly recommended). Mhash is currently
- available from http://mhash.sourceforge.net/. A static version of
- libmhash needs to be build using the --enable-static=yes
- configure option.
+
+ One of the following crypto libraries:
+
+ o Mhash. Mhash is currently available from
+ http://mhash.sourceforge.net/. A static version of libmhash needs
+ to be build using the --enable-static=yes configure option.
Aide requires at least mhash version 0.9.2
+ o GNU libgcrypt
+ o GnuTLS
o libcheck (optional, needed for 'make check', license: LGPL-2.1)
diff -up ./src/aide.c.gnutls ./src/aide.c
--- ./src/aide.c.gnutls 2023-06-13 20:52:39.000000000 +0200
+++ ./src/aide.c 2024-05-14 19:09:47.420448380 +0200
@@ -66,6 +66,9 @@ char* after = NULL;
#include <gcrypt.h>
#define NEED_LIBGCRYPT_VERSION "1.8.0"
#endif
+#ifdef WITH_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
static void usage(int exitvalue)
{
@@ -522,9 +525,6 @@ static void setdefaults_before_config()
DB_ATTR_TYPE common_attrs = ATTR(attr_perm)|ATTR(attr_ftype)|ATTR(attr_inode)|ATTR(attr_linkcount)|ATTR(attr_uid)|ATTR(attr_gid);
DB_ATTR_TYPE GROUP_R_HASHES=0LLU;
-#ifdef WITH_MHASH
- GROUP_R_HASHES=ATTR(attr_md5);
-#endif
#ifdef WITH_GCRYPT
if (gcry_fips_mode_active()) {
char* str;
@@ -533,6 +533,8 @@ static void setdefaults_before_config()
} else {
GROUP_R_HASHES = ATTR(attr_md5);
}
+#else /* WITH_MHASH or WITH_GNUTLS */
+ GROUP_R_HASHES=ATTR(attr_md5);
#endif
log_msg(LOG_LEVEL_INFO, "define default groups definitions");
diff -up ./src/hashsum.c.gnutls ./src/hashsum.c
--- ./src/hashsum.c.gnutls 2023-04-01 18:25:38.000000000 +0200
+++ ./src/hashsum.c 2024-05-14 19:09:47.420448380 +0200
@@ -29,6 +29,9 @@
#ifdef WITH_GCRYPT
#include <gcrypt.h>
#endif
+#ifdef WITH_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
hashsum_t hashsums[] = {
{ attr_md5, 16 },
@@ -86,6 +89,24 @@ int algorithms[] = { /* order must match
};
#endif
+#ifdef WITH_GNUTLS
+int algorithms[] = { /* order must match hashsums array */
+ GNUTLS_DIG_MD5,
+ GNUTLS_DIG_SHA1,
+ GNUTLS_DIG_SHA256,
+ GNUTLS_DIG_SHA512,
+ GNUTLS_DIG_RMD160,
+ -1, /* TIGER is not available */
+ -1, /* CRC32 is not available */
+ -1, /* CRC32B is not available */
+ -1, /* GCRY_MD_HAVAL is not available */
+ -1, /* WHIRLPOOL is not available */
+ -1, /* GNUTLS_DIG_GOSTR_94 gives different results than Gcrypt */
+ GNUTLS_DIG_STREEBOG_256,
+ GNUTLS_DIG_STREEBOG_512,
+};
+#endif
+
DB_ATTR_TYPE get_hashes(bool include_unsupported) {
DB_ATTR_TYPE attr = 0LLU;
for (int i = 0; i < num_hashes; ++i) {
diff -up ./src/md.c.gnutls ./src/md.c
--- ./src/md.c.gnutls 2023-04-01 18:25:38.000000000 +0200
+++ ./src/md.c 2024-05-14 19:28:09.651209390 +0200
@@ -40,6 +40,11 @@
#include <gcrypt.h>
#endif
+#ifdef WITH_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#endif
+
/*
Initialise md_container according its todo_attr field
*/
@@ -90,6 +95,22 @@ int init_md(struct md_container* md, con
}
}
#endif
+#ifdef WITH_GNUTLS
+ for (HASHSUM i = 0 ; i < num_hashes ; ++i) {
+ DB_ATTR_TYPE h = ATTR(hashsums[i].attribute);
+ if (h&md->todo_attr) {
+ if(gnutls_hash_init(&(md->gnutls_mdh[i]),algorithms[i])>=0){
+ md->calc_attr|=h;
+ } else {
+ log_msg(LOG_LEVEL_WARNING,"%s: gnutls_hash_init (%s) failed for '%s'", filename, attributes[hashsums[i].attribute].db_name, filename);
+ md->todo_attr&=~h;
+ md->gnutls_mdh[i] = NULL;
+ }
+ } else {
+ md->gnutls_mdh[i] = NULL;
+ }
+ }
+#endif
char *str;
log_msg(LOG_LEVEL_DEBUG, "%s> initialized md_container: %s (%p)", filename, str = diff_attributes(0, md->calc_attr), md);
free(str);
@@ -120,6 +141,13 @@ int update_md(struct md_container* md,vo
#ifdef WITH_GCRYPT
gcry_md_write(md->mdh, data, size);
#endif
+#ifdef WITH_GNUTLS
+ for (HASHSUM i = 0 ; i < num_hashes ; ++i) {
+ if(md->gnutls_mdh[i] != NULL){
+ gnutls_hash(md->gnutls_mdh[i], data, size);
+ }
+ }
+#endif
return RETOK;
}
@@ -163,6 +191,14 @@ int close_md(struct md_container* md, md
}
}
#endif
+#ifdef WITH_GNUTLS
+ for (HASHSUM i = 0 ; i < num_hashes ; ++i) {
+ if(md->gnutls_mdh[i] != NULL){
+ gnutls_hash_deinit(md->gnutls_mdh[i], hs?hs->hashsums[i]:NULL);
+ md->gnutls_mdh[i] = NULL;
+ }
+ }
+#endif /* WITH_MHASH */
if (hs) {
hs->attrs = md->calc_attr;
}
diff -up ./tests/check_hashes.c.gnutls ./tests/check_hashes.c
--- ./tests/check_hashes.c.gnutls 2024-05-14 19:09:47.420448380 +0200
+++ ./tests/check_hashes.c 2024-05-14 19:09:47.420448380 +0200
@@ -0,0 +1,111 @@
+/*
+ * AIDE (Advanced Intrusion Detection Environment)
+ *
+ * Copyright (C) 2024 Jakub Jelen
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <check.h>
+#include <stdlib.h>
+
+#include "hashsum.h"
+#include "md.h"
+
+typedef struct {
+ const char *input;
+ ssize_t input_len;
+ md_hashsums expected;
+} diff_digests_t;
+
+static diff_digests_t diff_digests_tests[] = {
+ { "", 0, {{
+ "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e",
+ "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09",
+ "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55",
+ "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd\xf1\x54\x28\x50\xd6\x6d\x80\x07\xd6\x20\xe4\x05\x0b\x57\x15\xdc\x83\xf4\xa9\x21\xd3\x6c\xe9\xce\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0\xff\x83\x18\xd2\x87\x7e\xec\x2f\x63\xb9\x31\xbd\x47\x41\x7a\x81\xa5\x38\x32\x7a\xf9\x27\xda\x3e",
+ "\x9c\x11\x85\xa5\xc5\xe9\xfc\x54\x61\x28\x08\x97\x7e\xe8\xf5\x48\xb2\x25\x8d\x31",
+ "\x24\xf0\x13\x0c\x63\xac\x93\x32\x16\x16\x6e\x76\xb1\xbb\x92\x5f\xf3\x73\xde\x2d\x49\x58\x4e\x7a",
+ "\x00\x00\x00\x00",
+ "\x00\x00\x00\x00",
+ "\x4f\x69\x38\x53\x1f\x0b\xc8\x99\x1f\x62\xda\x7b\xbd\x6f\x7d\xe3\xfa\xd4\x45\x62\xb8\xc6\xf4\xeb\xf1\x46\xd5\xb4\xe4\x6f\x7c\x17",
+ "\x19\xfa\x61\xd7\x55\x22\xa4\x66\x9b\x44\xe3\x9c\x1d\x2e\x17\x26\xc5\x30\x23\x21\x30\xd4\x07\xf8\x9a\xfe\xe0\x96\x49\x97\xf7\xa7\x3e\x83\xbe\x69\x8b\x28\x8f\xeb\xcf\x88\xe3\xe0\x3c\x4f\x07\x57\xea\x89\x64\xe5\x9b\x63\xd9\x37\x08\xb1\x38\xcc\x42\xa6\x6e\xb3",
+ "\xce\x85\xb9\x9c\xc4\x67\x52\xff\xfe\xe3\x5c\xab\x9a\x7b\x02\x78\xab\xb4\xc2\xd2\x05\x5c\xff\x68\x5a\xf4\x91\x2c\x49\x49\x0f\x8d",
+ "\x3f\x53\x9a\x21\x3e\x97\xc8\x02\xcc\x22\x9d\x47\x4c\x6a\xa3\x2a\x82\x5a\x36\x0b\x2a\x93\x3a\x94\x9f\xd9\x25\x20\x8d\x9c\xe1\xbb",
+ "\x8e\x94\x5d\xa2\x09\xaa\x86\x9f\x04\x55\x92\x85\x29\xbc\xae\x46\x79\xe9\x87\x3a\xb7\x07\xb5\x53\x15\xf5\x6c\xeb\x98\xbe\xf0\xa7\x36\x2f\x71\x55\x28\x35\x6e\xe8\x3c\xda\x5f\x2a\xac\x4c\x6a\xd2\xba\x3a\x71\x5c\x1b\xcd\x81\xcb\x8e\x9f\x90\xbf\x4c\x1c\x1a\x8a" }
+ }},
+ { "hello", 5, {{
+ "\x5d\x41\x40\x2a\xbc\x4b\x2a\x76\xb9\x71\x9d\x91\x10\x17\xc5\x92",
+ "\xaa\xf4\xc6\x1d\xdc\xc5\xe8\xa2\xda\xbe\xde\x0f\x3b\x48\x2c\xd9\xae\xa9\x43\x4d",
+ "\x2c\xf2\x4d\xba\x5f\xb0\xa3\x0e\x26\xe8\x3b\x2a\xc5\xb9\xe2\x9e\x1b\x16\x1e\x5c\x1f\xa7\x42\x5e\x73\x04\x33\x62\x93\x8b\x98\x24",
+ "\x9b\x71\xd2\x24\xbd\x62\xf3\x78\x5d\x96\xd4\x6a\xd3\xea\x3d\x73\x31\x9b\xfb\xc2\x89\x0c\xaa\xda\xe2\xdf\xf7\x25\x19\x67\x3c\xa7\x23\x23\xc3\xd9\x9b\xa5\xc1\x1d\x7c\x7a\xcc\x6e\x14\xb8\xc5\xda\x0c\x46\x63\x47\x5c\x2e\x5c\x3a\xde\xf4\x6f\x73\xbc\xde\xc0\x43",
+ "\x10\x8f\x07\xb8\x38\x24\x12\x61\x2c\x04\x8d\x07\xd1\x3f\x81\x41\x18\x44\x5a\xcd",
+ "\xa7\x88\x62\x33\x6f\x7f\xfd\x2c\x8a\x38\x74\xf8\x9b\x1b\x74\xf2\xf2\x7b\xdb\xca\x39\x66\x02\x54",
+#ifdef WITH_MHASH
+ "\x3d\x65\x31\x19",
+#else
+ "\x36\x10\xa6\x86",
+#endif
+ "\x86\xa6\x10\x36",
+ "\x26\x71\x8e\x4f\xb0\x55\x95\xcb\x87\x03\xa6\x72\xa8\xae\x91\xee\xa0\x71\xca\xc5\xe7\x42\x61\x73\xd4\xc2\x5a\x61\x1c\x4b\x80\x22",
+ "\x0a\x25\xf5\x5d\x73\x08\xec\xa6\xb9\x56\x7a\x7e\xd3\xbd\x1b\x46\x32\x7f\x0f\x1f\xfd\xc8\x04\xdd\x8b\xb5\xaf\x40\xe8\x8d\x78\xb8\x8d\xf0\xd0\x02\xa8\x9e\x2f\xdb\xd5\x87\x6c\x52\x3f\x1b\x67\xbc\x44\xe9\xf8\x70\x47\x59\x8e\x75\x48\x29\x8e\xa1\xc8\x1c\xfd\x73",
+ "\xa7\xeb\x5d\x08\xdd\xf2\x36\x3f\x1e\xa0\x31\x7a\x80\x3f\xce\xf8\x1d\x33\x86\x3c\x8b\x2f\x9f\x6d\x7d\x14\x95\x1d\x22\x9f\x45\x67",
+ "\x3f\xb0\x70\x0a\x41\xce\x6e\x41\x41\x3b\xa7\x64\xf9\x8b\xf2\x13\x5b\xa6\xde\xd5\x16\xbe\xa2\xfa\xe8\x42\x9c\xc5\xbd\xd4\x6d\x6d",
+ "\x8d\xf4\x14\x26\x09\x66\xbe\xb7\xb3\x4d\x92\x07\x63\x07\x9e\x15\xdf\x1f\x63\x29\x7e\xb3\xdd\x43\x11\xe8\xb5\x85\xd4\xbf\x2f\x59\x23\x21\x4f\x1d\xfe\xd3\xfd\xee\x4a\xaf\x01\x83\x30\xa1\x2a\xcd\xe0\xef\xcc\x33\x8e\xb5\x29\x22\xf3\xe5\x71\x21\x2d\x42\xc8\xde" }
+ }},
+};
+
+static int num_diff_digests_tests = sizeof diff_digests_tests / sizeof(diff_digests_t);
+
+START_TEST (test_diff_digests) {
+ const char *filename = "filename"; /* used only in the debug logs */
+ md_hashsums hs = {0};
+ struct md_container *mdc = calloc(1, sizeof(struct md_container));
+ mdc->todo_attr = get_hashes(false);
+
+#ifdef WITH_GCRYPT
+ gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+
+ init_md(mdc, filename);
+ update_md(mdc, (void *)diff_digests_tests[_i].input, diff_digests_tests[_i].input_len);
+ close_md(mdc, &hs, filename);
+ free(mdc);
+
+ for (HASHSUM i = 0 ; i < num_hashes ; ++i) {
+ DB_ATTR_TYPE attr = ATTR(hashsums[i].attribute);
+ if (algorithms[i] >= 0 && hs.attrs&attr) {
+ ck_assert_mem_eq(diff_digests_tests[_i].expected.hashsums[i], hs.hashsums[i], hashsums[i].length);
+ }
+ }
+}
+END_TEST
+
+Suite *make_md_suite(void) {
+
+ Suite *s = suite_create ("md");
+
+ TCase *tc_diff_digests = tcase_create ("diff_digests");
+
+ tcase_add_loop_test (tc_diff_digests, test_diff_digests, 0, num_diff_digests_tests);
+
+ suite_add_tcase (s, tc_diff_digests);
+
+ return s;
+}
+
diff -up ./tests/check_md.c.gnutls ./tests/check_md.c
--- ./tests/check_md.c.gnutls 2024-05-14 19:09:47.420448380 +0200
+++ ./tests/check_md.c 2024-05-14 19:09:47.420448380 +0200
@@ -0,0 +1,36 @@
+/*
+ * AIDE (Advanced Intrusion Detection Environment)
+ *
+ * Copyright (C) 2024 Jakub Jelen
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+
+#include "check_md.h"
+
+int main (void) {
+ int number_failed;
+ SRunner *sr;
+
+ sr = srunner_create (make_md_suite());
+
+ srunner_run_all (sr, CK_NORMAL);
+ number_failed = srunner_ntests_failed (sr);
+
+ srunner_free (sr);
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff -up ./tests/check_md.h.gnutls ./tests/check_md.h
--- ./tests/check_md.h.gnutls 2024-05-14 19:09:47.421448372 +0200
+++ ./tests/check_md.h 2024-05-14 19:09:47.421448372 +0200
@@ -0,0 +1,23 @@
+/*
+ * AIDE (Advanced Intrusion Detection Environment)
+ *
+ * Copyright (C) 2024 Jakub Jelen
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <check.h>
+
+Suite *make_md_suite(void);

View File

@ -1,21 +0,0 @@
diff -up aide-0.18.6/src/conf_lex.l.orig aide-0.18.6/src/conf_lex.l
--- aide-0.18.6/src/conf_lex.l.orig 2025-10-14 08:06:03.148161714 +0200
+++ aide-0.18.6/src/conf_lex.l 2025-10-14 08:06:52.742286876 +0200
@@ -5,8 +5,6 @@ G [a-zA-Z0-9]
V [a-zA-Z_]+[a-zA-Z0-9_]*
E [\ ]*"="[\ ]*
-O [a-z_]
-
%{
#define YYDEBUG 1
@@ -483,7 +481,7 @@ LOG_LEVEL lex_log_level = LOG_LEVEL_CONF
return (CONFIGOPTION);
}
-<CONFIG>({O})+ {
+<CONFIG>[a-z]+(_[a-z]+)+ {
log_msg(LOG_LEVEL_ERROR,"%s:%d: unknown config option: '%s' (line: '%s')", conf_filename, conf_linenumber, conftext, conf_linebuf);
exit(INVALID_CONFIGURELINE_ERROR);
}

View File

@ -1 +1,2 @@
SHA512 (aide-0.18.6.tar.gz) = c0e7c366029a401bce4cf44762caecada4d4831bfc2f00ebab6cb818ba259fae5409fdfcc7386d2bc9ca91a8e8fe0eb78927205bc75513578b8a3ccd17183744
SHA512 (aide-0.19.2.tar.gz) = 08506c2302e34794fa08a27caaa1e714ba736d46351c577234f2c3d2623ea82b243b3318061a369a46d6961a782f42fbb8edd42d1d4de6949e7fc30c87865830
SHA512 (gpgkey-aide.gpg) = 8201dd947060616007712ae0b02908e940dd4466f07595d9e6694616014e36163e86c185bba062b686dc31aec52ae9ff9232bccb150cb4f08c712f9fb6f74c38