diff --git a/.gitignore b/.gitignore index 595440d..33b96f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -aide-0.18.6.tar.gz +aide-0.19.2.tar.gz +gpgkey-aide.gpg diff --git a/aide-0.19.2-syslog-format.patch b/aide-0.19.2-syslog-format.patch new file mode 100644 index 0000000..53f4ca7 --- /dev/null +++ b/aide-0.19.2-syslog-format.patch @@ -0,0 +1,657 @@ +From f3e62eb87e0a0e9c6fd43c933670447c8ab0517a Mon Sep 17 00:00:00 2001 +From: Cropi +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: 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 +--- + 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); + } ++"syslog_format" { ++ LOG_LEX_TOKEN(lex_log_level, CONFIGOPTION (SYSLOG_FORMAT_OPTION), conftext) ++ conflval.option = SYSLOG_FORMAT_OPTION; ++ BEGIN (STRINGEQHUNT); ++ return (CONFIGOPTION); ++} ++ + + "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 ++#include ++#include ++#include ++#include ++#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_=|[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_=A:|D: ++ * ++ * Both A and D are initialized to "" 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 = ""; ++ const char *D = ""; ++ 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 + diff --git a/aide-0.19.2.tar.gz.asc b/aide-0.19.2.tar.gz.asc new file mode 100644 index 0000000..99be423 --- /dev/null +++ b/aide-0.19.2.tar.gz.asc @@ -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----- diff --git a/aide-migrate-config b/aide-migrate-config new file mode 100755 index 0000000..43a6b31 --- /dev/null +++ b/aide-migrate-config @@ -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] +# +# --dry-run Report what would change; do not modify any file. +# --skip-init Migrate config files only; do not reinitialise the database. +# 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] " >&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 "$@" diff --git a/aide-tmpfiles.conf b/aide-tmpfiles.conf new file mode 100644 index 0000000..ec8f4b4 --- /dev/null +++ b/aide-tmpfiles.conf @@ -0,0 +1,2 @@ +d /var/log/aide 0700 root root - +d /var/lib/aide 0700 root root - diff --git a/aide-verbose.patch b/aide-verbose.patch deleted file mode 100644 index d95bb14..0000000 --- a/aide-verbose.patch +++ /dev/null @@ -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 */ diff --git a/aide.conf b/aide.conf index 6818526..4cdafbb 100644 --- a/aide.conf +++ b/aide.conf @@ -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 diff --git a/aide.spec b/aide.spec index 4b2b63c..cda44dd 100644 --- a/aide.spec +++ b/aide.spec @@ -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 - 0.18.6-8.2 -- RHEL 10.1.Z ERRATUM -- Fix lowercase group name definition -Resolves: RHEL-119647 +* Tue May 26 2026 Attila Lakatos - 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 - 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 - 0.19.2-4 +- Adjust default config to avoid false positives in /etc +Resolves: RHEL-39970 + +* Thu Oct 09 2025 Attila Lakatos - 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 - 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 - 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 - 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 - 0.18.6-7 diff --git a/escape-control-chars-CVE-2025-54389.patch b/escape-control-chars-CVE-2025-54389.patch deleted file mode 100644 index 0daa36f..0000000 --- a/escape-control-chars-CVE-2025-54389.patch +++ /dev/null @@ -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 | 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= 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) - { diff --git a/gnutls.patch b/gnutls.patch deleted file mode 100644 index 4a8607e..0000000 --- a/gnutls.patch +++ /dev/null @@ -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 - #endif -+#ifdef WITH_GNUTLS -+#include -+#include -+#endif - #include - #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 - #define NEED_LIBGCRYPT_VERSION "1.8.0" - #endif -+#ifdef WITH_GNUTLS -+#include -+#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 - #endif -+#ifdef WITH_GNUTLS -+#include -+#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 - #endif - -+#ifdef WITH_GNUTLS -+#include -+#include -+#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 -+#include -+ -+#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 -+ -+#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 -+ -+Suite *make_md_suite(void); diff --git a/lowercase-groupnames.patch b/lowercase-groupnames.patch deleted file mode 100644 index 4eb98ae..0000000 --- a/lowercase-groupnames.patch +++ /dev/null @@ -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); - } - --({O})+ { -+[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); - } diff --git a/sources b/sources index aab41a4..02ca917 100644 --- a/sources +++ b/sources @@ -1 +1,2 @@ -SHA512 (aide-0.18.6.tar.gz) = c0e7c366029a401bce4cf44762caecada4d4831bfc2f00ebab6cb818ba259fae5409fdfcc7386d2bc9ca91a8e8fe0eb78927205bc75513578b8a3ccd17183744 +SHA512 (aide-0.19.2.tar.gz) = 08506c2302e34794fa08a27caaa1e714ba736d46351c577234f2c3d2623ea82b243b3318061a369a46d6961a782f42fbb8edd42d1d4de6949e7fc30c87865830 +SHA512 (gpgkey-aide.gpg) = 8201dd947060616007712ae0b02908e940dd4466f07595d9e6694616014e36163e86c185bba062b686dc31aec52ae9ff9232bccb150cb4f08c712f9fb6f74c38