From dac2182069bcd2b9d601b409e52c79ea07546060 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Fri, 19 Jul 2024 11:43:18 +0200 Subject: [PATCH] confd: initial audit trail support This commit introduces a new SECURITY() log macro that logs to facility 'audit' (sometimes called 'security') with severity set to 'alert'. It is then used to instrument the single most important things to log; all changes to users and their privileges. We also add logging to sysrepo, which knows the username for changes to running-config, copies to startup-config, and RPCs. Fixes #521 Signed-off-by: Joachim Wiberg --- package/confd/confd.conf | 5 +- .../0007-Introduce-new-log-level-SEC.patch | 82 +++++++++++++++++++ ...ail-for-high-priority-system-changes.patch | 66 +++++++++++++++ src/confd/src/ietf-system.c | 36 ++++---- src/libsrx/src/common.h | 7 +- 5 files changed, 175 insertions(+), 21 deletions(-) create mode 100644 patches/sysrepo/2.10.1/0007-Introduce-new-log-level-SEC.patch create mode 100644 patches/sysrepo/2.10.1/0008-Add-audit-trail-for-high-priority-system-changes.patch diff --git a/package/confd/confd.conf b/package/confd/confd.conf index 92832d09..e7bd94c0 100644 --- a/package/confd/confd.conf +++ b/package/confd/confd.conf @@ -8,7 +8,8 @@ run name:error :1 log:console norestart if: \ [S] /usr/libexec/confd/error -- service name:confd log:prio:daemon.err \ - [S12345] sysrepo-plugind -f -p /run/confd.pid -n -- Configuration daemon + [S12345] sysrepo-plugind -f -p /run/confd.pid -n -v warning \ + -- Configuration daemon # Bootstrap system with startup-config run name:startup log:prio:user.notice norestart \ @@ -24,7 +25,7 @@ run name:error :2 log:console norestart if: \ [S] /usr/libexec/confd/error -- service name:netopeer notify:none log \ - [12345] netopeer2-server -F -t 60 \ + [12345] netopeer2-server -F -t 60 -v 1 \ -- NETCONF server # Create initial /etc/resolv.conf after successful bootstrap diff --git a/patches/sysrepo/2.10.1/0007-Introduce-new-log-level-SEC.patch b/patches/sysrepo/2.10.1/0007-Introduce-new-log-level-SEC.patch new file mode 100644 index 00000000..ab503202 --- /dev/null +++ b/patches/sysrepo/2.10.1/0007-Introduce-new-log-level-SEC.patch @@ -0,0 +1,82 @@ +From da765b90bca45b91f72fd6525e680040eebd2d4b Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 21 Aug 2024 16:00:35 +0200 +Subject: [PATCH 7/8] Introduce new log level [SEC] +Organization: Addiva Elektronik + +This adds a new log level for security and audit trail related log +messages. E.g., nacm user applied a change, copied a ds, etc. + +The new log level is added last to not affect the advertised command +line log levels. A security notice has higher actual priorty than +DBG, of course, so we remap it to WRN. The construct allows us to +have another [label] than [WRN], which might otherwise be read as +a bug/problem rather than just a high-priority-notification. + +Signed-off-by: Joachim Wiberg +--- + src/log.c | 5 +++++ + src/log.h | 1 + + src/sysrepo_types.h | 3 ++- + tests/tcommon.c | 3 +++ + 4 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/src/log.c b/src/log.c +index e15055ac..b89ffacf 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -122,6 +122,11 @@ sr_log_msg(int plugin, sr_log_level_t ll, const char *msg) + priority = LOG_INFO; + severity = "INF"; + break; ++ case SR_LL_SEC: ++ priority = LOG_WARNING; ++ severity = "SEC"; ++ ll = SR_LL_WRN; /* remap to higher level. */ ++ break; + case SR_LL_DBG: + priority = LOG_DEBUG; + severity = "DBG"; +diff --git a/src/log.h b/src/log.h +index d7e65b88..8722e51d 100644 +--- a/src/log.h ++++ b/src/log.h +@@ -32,6 +32,7 @@ + + #define SR_LOG_WRN(...) sr_log(SR_LL_WRN, __VA_ARGS__) + #define SR_LOG_INF(...) sr_log(SR_LL_INF, __VA_ARGS__) ++#define SR_LOG_SEC(...) sr_log(SR_LL_SEC, __VA_ARGS__) + #define SR_LOG_DBG(...) sr_log(SR_LL_DBG, __VA_ARGS__) + + #define SR_CHECK_MEM_GOTO(cond, err_info, go) if (cond) { SR_ERRINFO_MEM(&(err_info)); goto go; } +diff --git a/src/sysrepo_types.h b/src/sysrepo_types.h +index 9f820b84..9a0de2be 100644 +--- a/src/sysrepo_types.h ++++ b/src/sysrepo_types.h +@@ -65,7 +65,8 @@ typedef enum { + SR_LL_ERR, /**< Print only error messages. */ + SR_LL_WRN, /**< Print error and warning messages. */ + SR_LL_INF, /**< Besides errors and warnings, print some other informational messages. */ +- SR_LL_DBG /**< Print all messages including some development debug messages. */ ++ SR_LL_DBG, /**< Print all messages including some development debug messages. */ ++ SR_LL_SEC, /**< Security, e.g., audit trail, high priority remapped to SR_LL_WRN */ + } sr_log_level_t; + + /** +diff --git a/tests/tcommon.c b/tests/tcommon.c +index 0dcdd0b4..49de738d 100644 +--- a/tests/tcommon.c ++++ b/tests/tcommon.c +@@ -35,6 +35,9 @@ _test_log_msg(sr_log_level_t level, const char *message, const char *prefix) + case SR_LL_WRN: + severity = "WRN"; + break; ++ case SR_LL_SEC: ++ severity = "SEC"; ++ break; + case SR_LL_INF: + severity = "INF"; + break; +-- +2.43.0 + diff --git a/patches/sysrepo/2.10.1/0008-Add-audit-trail-for-high-priority-system-changes.patch b/patches/sysrepo/2.10.1/0008-Add-audit-trail-for-high-priority-system-changes.patch new file mode 100644 index 00000000..833a5e52 --- /dev/null +++ b/patches/sysrepo/2.10.1/0008-Add-audit-trail-for-high-priority-system-changes.patch @@ -0,0 +1,66 @@ +From 7e49f394e0afedc0259d4364f5a9a83296fe2b72 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +Date: Wed, 21 Aug 2024 16:04:43 +0200 +Subject: [PATCH 8/8] Add audit trail for high priority system changes +Organization: Addiva Elektronik + +Committing a change to running, copying to a datastore, or calling an +RPC may be restricted by NACM. In these cases a system administrator +may want to know who made a change or sent an RPC. + +For commit, we are only interested in changes to the running ds, which +is when the system actually activates the changes. Copying to startup +or other datastores is handled separately. + +Signed-off-by: Joachim Wiberg +--- + src/sysrepo.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/sysrepo.c b/src/sysrepo.c +index 86d694e5..c7b97e53 100644 +--- a/src/sysrepo.c ++++ b/src/sysrepo.c +@@ -3946,6 +3946,9 @@ store: + goto cleanup; + } + ++ if (session && session->nacm_user && mod_info->ds == SR_DS_RUNNING) ++ SR_LOG_SEC("user \"%s\" commiting changes to %s ...", session->nacm_user, sr_ds2str(mod_info->ds)); ++ + /* publish "done" event, all changes were applied */ + if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms, cb_err_info))) { + goto cleanup; +@@ -3957,6 +3960,9 @@ store: + } + + cleanup: ++ if (session && session->nacm_user && mod_info->ds == SR_DS_RUNNING) ++ SR_LOG_SEC("user \"%s\" committed changes to %s.", session->nacm_user, sr_ds2str(mod_info->ds)); ++ + if (change_sub_lock) { + assert(change_sub_lock == SR_LOCK_READ); + +@@ -4285,6 +4291,9 @@ sr_copy_config(sr_session_ctx_t *session, const char *module_name, sr_datastore_ + } + } + ++ if (session->nacm_user) ++ SR_LOG_SEC("user \"%s\" copied %s to %s", session->nacm_user, sr_ds2str(src_datastore), sr_ds2str(session->ds)); ++ + cleanup: + /* MODULES UNLOCK */ + sr_shmmod_modinfo_unlock(&mod_info); +@@ -6552,6 +6561,9 @@ sr_rpc_send_tree(sr_session_ctx_t *session, struct lyd_node *input, uint32_t tim + } + } + ++ if (session->nacm_user && path) ++ SR_LOG_SEC("user \"%s\" called RPC %s", session->nacm_user, path); ++ + if (LYD_CTX(input_top) != LYD_CTX(input_op)) { + /* different contexts if these are data of an extension (schema-mount) */ + for (ext_parent = input_op; ext_parent && !(ext_parent->flags & LYD_EXT); ext_parent = lyd_parent(ext_parent)) {} +-- +2.43.0 + diff --git a/src/confd/src/ietf-system.c b/src/confd/src/ietf-system.c index 414b0567..fb67e6e9 100644 --- a/src/confd/src/ietf-system.c +++ b/src/confd/src/ietf-system.c @@ -607,9 +607,9 @@ static void add_group(const char *user, const char *group) return; /* already group member */ if (systemf("adduser %s %s", user, group)) - ERROR("Failed giving user %s UNIX %s permissions.", user, group); + SECURITY("Failed giving user %s UNIX %s permissions.", user, group); else - NOTE("User %s added to UNIX %s group.", user, group); + SECURITY("User %s added to UNIX %s group.", user, group); } static void del_group(const char *user, const char *group) @@ -620,9 +620,9 @@ static void del_group(const char *user, const char *group) return; /* not member of group */ if (systemf("delgroup %s %s", user, group)) - ERROR("Failed removing user %s from UNIX %s group.", user, group); + SECURITY("Failed removing user %s from UNIX %s group.", user, group); else - NOTE("User %s removed from UNIX %s group.", user, group); + SECURITY("User %s removed from UNIX %s group.", user, group); } /* Users with a valid shell are also allowed CLI access */ @@ -840,15 +840,15 @@ static int sys_add_user(sr_session_ctx_t *sess, char *name) /* Verify IDs aren't already used, like BusyBox adduser */ if (getpwuid(st.st_uid) || getgrgid(st.st_uid) || getgrgid(st.st_gid)) { /* Exists but owned by someone else. */ - ERROR("Failed mapping user %s to /home/%s, uid:gid (%d:%d) already exists.", + SECURITY("Failed mapping user %s to /home/%s, uid:gid (%d:%d) already exists.", name, name, st.st_uid, st.st_gid); err = sys_call_adduser(sess, name, 0, 0); } else { - DEBUG("Reusing uid:gid %d:%d and /home/%s for new user %s", - st.st_uid, st.st_gid, name, name); + SECURITY("Reusing uid:gid %d:%d and /home/%s for new user %s", + st.st_uid, st.st_gid, name, name); err = sys_call_adduser(sess, name, st.st_uid, st.st_gid); if (err) { - ERROR("Failed reusing uid:gid from /home/%s, retrying create user ...", name); + SECURITY("Failed reusing uid:gid from /home/%s, retrying create user ...", name); err = sys_call_adduser(sess, name, 0, 0); } else reused = true; @@ -857,11 +857,11 @@ static int sys_add_user(sr_session_ctx_t *sess, char *name) err = sys_call_adduser(sess, name, 0, 0); if (err) { - ERROR("Failed creating new user \"%s\"", name); + SECURITY("Failed creating new user \"%s\"", name); return SR_ERR_SYS; } - NOTE("User \"%s\" created%s.", name, reused ? ", mapped to existing home directory" : ""); + SECURITY("User \"%s\" created%s.", name, reused ? ", mapped to existing home directory" : ""); /* * OpenSSH in Infix has been set up to use /var/run/sshd/%s.keys @@ -1016,7 +1016,7 @@ fail: endspent(); ulckpwdf(); exit: - ERRNO("Failed setting password for %s", user); + SECURITY("Failed setting password for %s", user); return -1; } @@ -1072,7 +1072,7 @@ static sr_error_t handle_sr_passwd_update(sr_session_ctx_t *, struct confd *conf assert(change->new); if (change->new->type != SR_STRING_T) { - ERROR("Internal error, expected pass to be string type."); + SECURITY("Internal error, expected user %s password to be string type.", user); err = SR_ERR_INTERNAL; break; } @@ -1094,7 +1094,7 @@ static sr_error_t handle_sr_passwd_update(sr_session_ctx_t *, struct confd *conf else if (!strcmp(hash, "*")) NOTE("Password login disabled for user %s", user); else - NOTE("Password updated for user %s", user); + SECURITY("Password updated for user %s", user); break; case SR_OP_DELETED: if (set_password(user, "*", false)) @@ -1125,10 +1125,10 @@ static sr_error_t handle_sr_shell_update(sr_session_ctx_t *sess, struct confd *c shell = sys_find_usable_shell(sess, (char *)user, is_admin_user(sess, user)); if (set_shell(user, shell)) { - ERROR("Failed updating shell to %s for user %s", shell, user); + SECURITY("Failed updating shell to %s for user %s", shell, user); err = SR_ERR_SYS; } else { - DEBUG("Login shell updated for user %s", user); + SECURITY("Login shell updated for user %s", user); err = SR_ERR_OK; } free(shell); @@ -1148,7 +1148,7 @@ static sr_error_t check_sr_user_update(sr_session_ctx_t *, struct confd *, struc name = sr_xpath_key_value(val->xpath, "user", "name", &state); if (!is_valid_username(name)) { - ERROR("Invalid username \"%s\"", name); + SECURITY("Invalid username \"%s\"", name); return SR_ERR_VALIDATION_FAILED; } NOTE("Username \"%s\" is valid", name); @@ -1361,7 +1361,7 @@ static sr_error_t change_auth_done(struct confd *confd, sr_session_ctx_t *sessio err = generate_auth_keys(session, XPATH_AUTH_"/user//."); if (err) { - ERROR("failed saving authorized-key data."); + SECURITY("failed saving authorized-key data."); goto cleanup; } @@ -1436,7 +1436,7 @@ static int change_nacm(sr_session_ctx_t *session, uint32_t sub_id, const char *m shell = sys_find_usable_shell(session, (char *)user, is_admin); if (set_shell(user, shell)) - ERROR("Failed adjusting shell for user %s", user); + SECURITY("Failed adjusting shell for user %s", user); if (is_admin) add_group(user, "wheel"); diff --git a/src/libsrx/src/common.h b/src/libsrx/src/common.h index 6505305f..d208f042 100644 --- a/src/libsrx/src/common.h +++ b/src/libsrx/src/common.h @@ -5,12 +5,16 @@ #include #include -#include #include "common.h" extern int debug; +/* In IETF referred to LOG_AUDIT */ +#ifndef LOG_SECURITY +#define LOG_SECURITY (1 << 13) +#endif + #ifndef HAVE_VASPRINTF int vasprintf(char **strp, const char *fmt, va_list ap); #endif @@ -29,5 +33,6 @@ int asprintf(char **strp, const char *fmt, ...); #define EMERG(fmt, ...) syslog(LOG_EMERG, fmt, ##__VA_ARGS__) #define ERROR(fmt, ...) syslog(LOG_ERR, fmt, ##__VA_ARGS__) #define ERRNO(fmt, ...) syslog(LOG_ERR, fmt ": %s", ##__VA_ARGS__, strerror(errno)) +#define SECURITY(fmt, ...) syslog(LOG_SECURITY | LOG_NOTICE, fmt, ##__VA_ARGS__) #endif /* CONFD_COMMON_H_ */