summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2023-04-10 20:22:04 +0300
committerTimo Teräs <timo.teras@iki.fi>2023-04-11 20:55:13 +0300
commit18846bd08390e6ae52a0de0f94ff2a0f62aad4d7 (patch)
treed0e70797989c82e15d6e5aaf0bca156e0b62bfc8
parentb33825b6a41730d11e8e1dfd15ae479ce2ea8bc2 (diff)
downloadapk-tools-18846bd08390e6ae52a0de0f94ff2a0f62aad4d7.tar.gz
apk-tools-18846bd08390e6ae52a0de0f94ff2a0f62aad4d7.tar.bz2
apk-tools-18846bd08390e6ae52a0de0f94ff2a0f62aad4d7.tar.xz
apk-tools-18846bd08390e6ae52a0de0f94ff2a0f62aad4d7.zip
audit: add a new --full mode
-rw-r--r--doc/apk-audit.8.scd11
-rw-r--r--src/apk_database.h6
-rw-r--r--src/app_audit.c126
-rw-r--r--src/database.c10
4 files changed, 105 insertions, 48 deletions
diff --git a/doc/apk-audit.8.scd b/doc/apk-audit.8.scd
index 2294f8a..19a7ba9 100644
--- a/doc/apk-audit.8.scd
+++ b/doc/apk-audit.8.scd
@@ -15,8 +15,8 @@ the package database.
The audit can be done against configuration files only (--backup) to generate
list of files needed to be stored in the overlay in run-from-tmps configuration.
-Alternatively, it can audit all installed files (--system) to e.g. detect
-unauthorized modifications of system files.
+Alternatively, it can audit all installed files (--system or --full) to
+e.g. detect unauthorized modifications of system files.
By default, the output format is one file per line, for each modified file.
A character is printed indicating the change detected, followed by a space,
@@ -49,6 +49,12 @@ then the affected path. The changes detected are:
Check file permissions too. Namely, the uid, gid and file mode will
be checked in addition to the file content.
+*--full*
+ Audit all system files. Same as *--system*, but in addition reports
+ all added directories and files. A built-in default override for
+ protected paths is used, unless a *--protected-paths* is explicitly
+ specified.
+
*--packages*
Print only the packages with changed files. Instead of the full output
each modification, the set of packages with at least one modified file
@@ -65,6 +71,7 @@ then the affected path. The changes detected are:
Audit all system files. All files provided by packages are verified
for integrity with the exception of configuration files (listed in
protected_paths.d). This is useful detecting unauthorized file changes.
+ New files or directories are not reported.
*-r, --recursive*
Descend into directories and audit them as well.
diff --git a/src/apk_database.h b/src/apk_database.h
index 90fb292..d63da2d 100644
--- a/src/apk_database.h
+++ b/src/apk_database.h
@@ -45,11 +45,17 @@ struct apk_db_file {
enum apk_protect_mode {
APK_PROTECT_NONE = 0,
+ APK_PROTECT_IGNORE,
APK_PROTECT_CHANGED,
APK_PROTECT_SYMLINKS_ONLY,
APK_PROTECT_ALL,
};
+static inline int apk_protect_mode_none(enum apk_protect_mode mode)
+{
+ return mode == APK_PROTECT_NONE || mode == APK_PROTECT_IGNORE;
+}
+
struct apk_protected_path {
char *relative_pattern;
unsigned protect_mode : 3;
diff --git a/src/app_audit.c b/src/app_audit.c
index ecf3e22..c1d8b10 100644
--- a/src/app_audit.c
+++ b/src/app_audit.c
@@ -25,11 +25,12 @@
enum {
MODE_BACKUP = 0,
- MODE_SYSTEM
+ MODE_SYSTEM,
+ MODE_FULL,
};
struct audit_ctx {
- unsigned mode : 1;
+ unsigned mode : 2;
unsigned recursive : 1;
unsigned check_permissions : 1;
unsigned packages_only : 1;
@@ -38,6 +39,7 @@ struct audit_ctx {
#define AUDIT_OPTIONS(OPT) \
OPT(OPT_AUDIT_backup, "backup") \
OPT(OPT_AUDIT_check_permissions, "check-permissions") \
+ OPT(OPT_AUDIT_full, "full") \
OPT(OPT_AUDIT_packages, "packages") \
OPT(OPT_AUDIT_protected_paths, APK_OPT_ARG "protected-paths") \
OPT(OPT_AUDIT_recursive, APK_OPT_SH("r") "recursive") \
@@ -54,6 +56,24 @@ static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int opt
case OPT_AUDIT_backup:
actx->mode = MODE_BACKUP;
break;
+ case OPT_AUDIT_full:
+ actx->mode = MODE_FULL;
+ if (APK_BLOB_IS_NULL(dbopts->protected_paths))
+ dbopts->protected_paths = APK_BLOB_STR(
+ "+etc\n"
+ "@etc/init.d\n"
+ "-dev\n"
+ "-home\n"
+ "-lib/apk\n"
+ "-lib/rc/cache\n"
+ "-proc\n"
+ "-root\n"
+ "-run\n"
+ "-sys\n"
+ "-tmp\n"
+ "-var\n"
+ );
+ break;
case OPT_AUDIT_system:
actx->mode = MODE_SYSTEM;
break;
@@ -103,8 +123,6 @@ static int audit_file(struct audit_ctx *actx,
if (dbf == NULL)
return 'A';
- dbf->audited = 1;
-
if (apk_fileinfo_get(dirfd, name,
APK_FI_NOFOLLOW |
APK_FI_XATTR_CSUM(dbf->acl->xattr_csum.type ?: APK_CHECKSUM_DEFAULT) |
@@ -172,6 +190,23 @@ static void report_audit(struct audit_ctx *actx,
printf("%c " BLOB_FMT "\n", reason, BLOB_PRINTF(bfull));
}
+static int determine_file_protect_mode(struct apk_db_dir *dir, const char *name)
+{
+ struct apk_protected_path *ppath;
+ int protect_mode = dir->protect_mode;
+
+ /* inherit file's protection mask */
+ foreach_array_item(ppath, dir->protected_paths) {
+ char *slash = strchr(ppath->relative_pattern, '/');
+ if (slash == NULL) {
+ if (fnmatch(ppath->relative_pattern, name, FNM_PATHNAME) != 0)
+ continue;
+ protect_mode = ppath->protect_mode;
+ }
+ }
+ return protect_mode;
+}
+
static int audit_directory_tree_item(void *ctx, int dirfd, const char *name)
{
struct audit_tree_ctx *atctx = (struct audit_tree_ctx *) ctx;
@@ -194,17 +229,23 @@ static int audit_directory_tree_item(void *ctx, int dirfd, const char *name)
if (S_ISDIR(fi.mode)) {
int recurse = TRUE;
- if (actx->mode == MODE_BACKUP) {
+ switch (actx->mode) {
+ case MODE_BACKUP:
child = apk_db_dir_get(db, bfull);
if (!child->has_protected_children)
recurse = FALSE;
- if (child->protect_mode == APK_PROTECT_NONE)
+ if (apk_protect_mode_none(child->protect_mode))
goto recurse_check;
- } else {
+ break;
+ case MODE_SYSTEM:
child = apk_db_dir_query(db, bfull);
- if (child == NULL)
- goto done;
+ if (child == NULL) goto done;
child = apk_db_dir_ref(child);
+ break;
+ case MODE_FULL:
+ child = apk_db_dir_get(db, bfull);
+ if (child->protect_mode == APK_PROTECT_NONE) break;
+ goto recurse_check;
}
reason = audit_directory(actx, db, child, &fi);
@@ -226,47 +267,50 @@ recurse_check:
atctx->pathlen--;
} else {
struct apk_db_file *dbf;
- struct apk_protected_path *ppath;
- int protect_mode = dir->protect_mode;
-
- /* inherit file's protection mask */
- foreach_array_item(ppath, dir->protected_paths) {
- char *slash = strchr(ppath->relative_pattern, '/');
- if (slash == NULL) {
- if (fnmatch(ppath->relative_pattern, name, FNM_PATHNAME) != 0)
- continue;
- protect_mode = ppath->protect_mode;
- }
- }
+ int protect_mode = determine_file_protect_mode(dir, name);
- if (actx->mode == MODE_BACKUP) {
+ dbf = apk_db_file_query(db, bdir, bent);
+ if (dbf) dbf->audited = 1;
+
+ switch (actx->mode) {
+ case MODE_FULL:
+ switch (protect_mode) {
+ case APK_PROTECT_NONE:
+ break;
+ case APK_PROTECT_SYMLINKS_ONLY:
+ if (S_ISLNK(fi.mode)) goto done;
+ break;
+ case APK_PROTECT_IGNORE:
+ case APK_PROTECT_ALL:
+ case APK_PROTECT_CHANGED:
+ goto done;
+ }
+ break;
+ case MODE_BACKUP:
switch (protect_mode) {
case APK_PROTECT_NONE:
+ case APK_PROTECT_IGNORE:
goto done;
case APK_PROTECT_CHANGED:
break;
case APK_PROTECT_SYMLINKS_ONLY:
- if (!S_ISLNK(fi.mode))
- goto done;
+ if (!S_ISLNK(fi.mode)) goto done;
break;
case APK_PROTECT_ALL:
reason = 'A';
break;
}
+ if ((!dbf || reason == 'A') &&
+ apk_blob_ends_with(bent, APK_BLOB_STR(".apk-new")))
+ goto done;
+ break;
+ case MODE_SYSTEM:
+ if (!dbf || !apk_protect_mode_none(protect_mode)) goto done;
+ break;
}
- dbf = apk_db_file_query(db, bdir, bent);
- if (reason == 0)
- reason = audit_file(actx, db, dbf, dirfd, name);
- if (reason < 0)
- goto done;
- if (actx->mode == MODE_SYSTEM &&
- (reason == 'A' || protect_mode != APK_PROTECT_NONE))
- goto done;
- if (actx->mode == MODE_BACKUP &&
- reason == 'A' &&
- apk_blob_ends_with(bent, APK_BLOB_STR(".apk-new")))
- goto done;
+ if (reason == 0) reason = audit_file(actx, db, dbf, dirfd, name);
+ if (reason < 0) goto done;
report_audit(actx, reason, bfull, dbf ? dbf->diri->pkg : NULL);
}
@@ -306,11 +350,11 @@ static int audit_missing_files(apk_hash_item item, void *pctx)
if (file->audited) return 0;
dir = file->diri->dir;
- if (dir->mode & S_SEENFLAG) {
- len = snprintf(path, sizeof(path), DIR_FILE_FMT, DIR_FILE_PRINTF(dir, file));
- report_audit(actx, 'X', APK_BLOB_PTR_LEN(path, len), file->diri->pkg);
- }
+ if (!(dir->mode & S_SEENFLAG)) return 0;
+ if (determine_file_protect_mode(dir, file->name) == APK_PROTECT_IGNORE) return 0;
+ len = snprintf(path, sizeof(path), DIR_FILE_FMT, DIR_FILE_PRINTF(dir, file));
+ report_audit(actx, 'X', APK_BLOB_PTR_LEN(path, len), file->diri->pkg);
return 0;
}
@@ -344,7 +388,7 @@ static int audit_main(void *ctx, struct apk_database *db, struct apk_string_arra
r |= audit_directory_tree(&atctx, openat(db->root_fd, arg, O_RDONLY|O_CLOEXEC));
}
}
- if (actx->mode == MODE_SYSTEM)
+ if (actx->mode == MODE_SYSTEM || actx->mode == MODE_FULL)
apk_hash_foreach(&db->installed.files, audit_missing_files, ctx);
return r;
diff --git a/src/database.c b/src/database.c
index 282e3a0..140a9ef 100644
--- a/src/database.c
+++ b/src/database.c
@@ -341,7 +341,7 @@ struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name)
} else if (apk_blob_rsplit(name, '/', &bparent, NULL)) {
dir->parent = apk_db_dir_get(db, bparent);
dir->protect_mode = dir->parent->protect_mode;
- dir->has_protected_children = (dir->protect_mode != APK_PROTECT_NONE);
+ dir->has_protected_children = !apk_protect_mode_none(dir->protect_mode);
ppaths = dir->parent->protected_paths;
} else {
dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
@@ -372,7 +372,7 @@ struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name)
dir->protect_mode = ppath->protect_mode;
}
- dir->has_protected_children |= (ppath->protect_mode != APK_PROTECT_NONE);
+ dir->has_protected_children |= !apk_protect_mode_none(ppath->protect_mode);
}
return dir;
@@ -1287,7 +1287,7 @@ static int add_protected_path(void *ctx, apk_blob_t blob)
case '#':
return 0;
case '-':
- protect_mode = APK_PROTECT_NONE;
+ protect_mode = APK_PROTECT_IGNORE;
break;
case '+':
protect_mode = APK_PROTECT_CHANGED;
@@ -2735,7 +2735,7 @@ static void apk_db_purge_pkg(struct apk_database *db,
};
hash = apk_blob_hash_seed(key.filename, diri->dir->hash);
if (!is_installed ||
- (diri->dir->protect_mode == APK_PROTECT_NONE) ||
+ apk_protect_mode_none(diri->dir->protect_mode) ||
(apk_flags & APK_PURGE) ||
(file->csum.type != APK_CHECKSUM_NONE &&
apk_fileinfo_get(db->root_fd, name, APK_FI_NOFOLLOW | file->csum.type, &fi, &db->atoms) == 0 &&
@@ -2799,7 +2799,7 @@ static void apk_db_migrate_files(struct apk_database *db,
/* File was from overlay, delete the
* packages version */
unlinkat(db->root_fd, tmpname, 0);
- } else if ((diri->dir->protect_mode != APK_PROTECT_NONE) &&
+ } else if (!apk_protect_mode_none(diri->dir->protect_mode) &&
(r == 0) &&
(ofile == NULL ||
ofile->csum.type == APK_CHECKSUM_NONE ||