diff -up shadow-4.1.5.1/src/chgpasswd.c.selinux-perms shadow-4.1.5.1/src/chgpasswd.c --- shadow-4.1.5.1/src/chgpasswd.c.selinux-perms 2016-05-04 13:44:55.633787764 +0200 +++ shadow-4.1.5.1/src/chgpasswd.c 2016-05-30 12:01:30.421587253 +0200 @@ -39,6 +39,13 @@ #include #include #include +#ifdef WITH_SELINUX +#include +#include +#endif +#ifdef WITH_LIBAUDIT +#include +#endif #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM #include "pam_defs.h" @@ -76,6 +83,9 @@ static bool sgr_locked = false; #endif static bool gr_locked = false; +/* The name of the caller */ +static char *myname = NULL; + /* local function prototypes */ static void fail_exit (int code); static /*@noreturn@*/void usage (int status); @@ -300,6 +310,63 @@ static void check_perms (void) #endif /* ACCT_TOOLS_SETUID */ } +#ifdef WITH_SELINUX +static int +log_callback (int type, const char *fmt, ...) +{ + int audit_fd; + va_list ap; + + va_start(ap, fmt); +#ifdef WITH_AUDIT + audit_fd = audit_open(); + + if (audit_fd >= 0) { + char *buf; + + if (vasprintf (&buf, fmt, ap) < 0) + goto ret; + audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, + NULL, 0); + audit_close(audit_fd); + free(buf); + goto ret; + } + +#endif + vsyslog (LOG_USER | LOG_INFO, fmt, ap); +ret: + va_end(ap); + return 0; +} + +static void +selinux_check_root (void) +{ + int status = -1; + security_context_t user_context; + union selinux_callback old_callback; + + if (is_selinux_enabled() < 1) + return; + + old_callback = selinux_get_callback(SELINUX_CB_LOG); + /* setup callbacks */ + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback); + if ((status = getprevcon(&user_context)) < 0) { + selinux_set_callback(SELINUX_CB_LOG, old_callback); + exit(1); + } + + status = selinux_check_access(user_context, user_context, "passwd", "passwd", NULL); + + selinux_set_callback(SELINUX_CB_LOG, old_callback); + freecon(user_context); + if (status != 0 && security_getenforce() != 0) + exit(1); +} +#endif + /* * open_files - lock and open the group databases */ @@ -393,6 +460,7 @@ int main (int argc, char **argv) const struct group *gr; struct group newgr; + struct passwd *pw = NULL; int errors = 0; int line = 0; @@ -408,8 +476,33 @@ int main (int argc, char **argv) OPENLOG ("chgpasswd"); +#ifdef WITH_AUDIT + audit_help_open (); +#endif + + /* + * Determine the name of the user that invoked this command. This + * is really hit or miss because there are so many ways that command + * can be executed and so many ways to trip up the routines that + * report the user name. + */ + pw = get_my_pwent (); + if (NULL == pw) { + fprintf (stderr, _("%s: Cannot determine your user name.\n"), + Prog); + SYSLOG ((LOG_WARN, + "Cannot determine the user name of the caller (UID %lu)", + (unsigned long) getuid ())); + exit (E_NOPERM); + } + myname = xstrdup (pw->pw_name); + check_perms (); +#ifdef WITH_SELINUX + selinux_check_root (); +#endif + #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif @@ -533,6 +626,15 @@ int main (int argc, char **argv) newgr.gr_passwd = cp; } +#ifdef WITH_AUDIT + { + + audit_logger_with_group (AUDIT_GRP_CHAUTHTOK, Prog, + "change-password", + myname, AUDIT_NO_ID, gr->gr_name, + SHADOW_AUDIT_SUCCESS); + } +#endif /* * The updated group file entry is then put back and will * be written to the group file later, after all the diff -up shadow-4.1.5.1/src/chpasswd.c.selinux-perms shadow-4.1.5.1/src/chpasswd.c --- shadow-4.1.5.1/src/chpasswd.c.selinux-perms 2016-05-04 13:44:55.633787764 +0200 +++ shadow-4.1.5.1/src/chpasswd.c 2016-05-30 12:01:42.877859957 +0200 @@ -39,6 +39,13 @@ #include #include #include +#ifdef WITH_SELINUX +#include +#include +#endif +#ifdef WITH_LIBAUDIT +#include +#endif #ifdef USE_PAM #include "pam_defs.h" #endif /* USE_PAM */ @@ -297,6 +304,63 @@ static void check_perms (void) #endif /* USE_PAM */ } +#ifdef WITH_SELINUX +static int +log_callback (int type, const char *fmt, ...) +{ + int audit_fd; + va_list ap; + + va_start(ap, fmt); +#ifdef WITH_AUDIT + audit_fd = audit_open(); + + if (audit_fd >= 0) { + char *buf; + + if (vasprintf (&buf, fmt, ap) < 0) + goto ret; + audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, + NULL, 0); + audit_close(audit_fd); + free(buf); + goto ret; + } + +#endif + vsyslog (LOG_USER | LOG_INFO, fmt, ap); +ret: + va_end(ap); + return 0; +} + +static void +selinux_check_root (void) +{ + int status = -1; + security_context_t user_context; + union selinux_callback old_callback; + + if (is_selinux_enabled() < 1) + return; + + old_callback = selinux_get_callback(SELINUX_CB_LOG); + /* setup callbacks */ + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback); + if ((status = getprevcon(&user_context)) < 0) { + selinux_set_callback(SELINUX_CB_LOG, old_callback); + exit(1); + } + + status = selinux_check_access(user_context, user_context, "passwd", "passwd", NULL); + + selinux_set_callback(SELINUX_CB_LOG, old_callback); + freecon(user_context); + if (status != 0 && security_getenforce() != 0) + exit(1); +} +#endif + /* * open_files - lock and open the password databases */ @@ -405,8 +469,16 @@ int main (int argc, char **argv) OPENLOG ("chpasswd"); +#ifdef WITH_AUDIT + audit_help_open (); +#endif + check_perms (); +#ifdef WITH_SELINUX + selinux_check_root (); +#endif + #ifdef USE_PAM if (!use_pam) #endif /* USE_PAM */ @@ -563,6 +635,11 @@ int main (int argc, char **argv) newpw.pw_passwd = cp; } +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_CHAUTHTOK, Prog, + "updating-password", + pw->pw_name, (unsigned int) pw->pw_uid, 1); +#endif /* * The updated password file entry is then put back and will * be written to the password file later, after all the diff -up shadow-4.1.5.1/src/Makefile.am.selinux-perms shadow-4.1.5.1/src/Makefile.am --- shadow-4.1.5.1/src/Makefile.am.selinux-perms 2016-05-04 13:44:55.647788082 +0200 +++ shadow-4.1.5.1/src/Makefile.am 2016-05-27 16:04:49.446582632 +0200 @@ -79,9 +79,9 @@ endif chage_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) -chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT) +chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT) chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) -chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) +chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT) gpasswd_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) diff -up shadow-4.1.5.1/src/Makefile.in.selinux-perms shadow-4.1.5.1/src/Makefile.in --- shadow-4.1.5.1/src/Makefile.in.selinux-perms 2016-05-04 13:44:55.647788082 +0200 +++ shadow-4.1.5.1/src/Makefile.in 2016-05-27 16:04:49.447582654 +0200 @@ -437,9 +437,9 @@ AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/l @USE_PAM_TRUE@LIBCRYPT_NOPAM = chage_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) -chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT) +chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT) chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) -chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) +chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT) gpasswd_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)