[shadow-utils] update auditing to cover more events and fix some incorrect audit records

Tomáš Mráz tmraz at fedoraproject.org
Fri Oct 17 15:03:35 UTC 2014


commit e1ce821d4521d7ede777a6cd51f4cbcfeb1b5d9a
Author: Tomas Mraz <tmraz at fedoraproject.org>
Date:   Fri Oct 17 17:03:29 2014 +0200

    update auditing to cover more events and fix some incorrect audit records
    
      (auditing patch by Steve Grubb (#1151580))
    - apply the same new allocation algorithm to uids as for gids

 shadow-4.1.5.1-audit-update.patch | 2250 +++++++++++++++++++++++++++++++++++++
 shadow-4.1.5.1-group-alloc.patch  |  642 -----------
 shadow-4.1.5.1-id-alloc.patch     | 1219 ++++++++++++++++++++
 shadow-utils.spec                 |   13 +-
 4 files changed, 3479 insertions(+), 645 deletions(-)
---
diff --git a/shadow-4.1.5.1-audit-update.patch b/shadow-4.1.5.1-audit-update.patch
new file mode 100644
index 0000000..c25db61
--- /dev/null
+++ b/shadow-4.1.5.1-audit-update.patch
@@ -0,0 +1,2250 @@
+diff -urp shadow-4.1.5.1.orig/lib/prototypes.h shadow-4.1.5.1/lib/prototypes.h
+--- shadow-4.1.5.1.orig/lib/prototypes.h	2014-09-13 15:45:54.953829562 -0400
++++ shadow-4.1.5.1/lib/prototypes.h	2014-10-14 08:39:23.785884075 -0400
+@@ -195,12 +195,21 @@ extern int audit_fd;
+ extern void audit_help_open (void);
+ /* Use AUDIT_NO_ID when a name is provided to audit_logger instead of an ID */
+ #define AUDIT_NO_ID	((unsigned int) -1)
++#ifndef AUDIT_GRP_MGMT
++#define AUDIT_GRP_MGMT          1132    /* Group account was modified */
++#endif
++#ifndef AUDIT_GRP_CHAUTHTOK
++#define AUDIT_GRP_CHAUTHTOK     1133    /* Group account password was changed */
++#endif
+ typedef enum {
+ 	SHADOW_AUDIT_FAILURE = 0,
+ 	SHADOW_AUDIT_SUCCESS = 1} shadow_audit_result;
+ extern void audit_logger (int type, const char *pgname, const char *op,
+                           const char *name, unsigned int id,
+                           shadow_audit_result result);
++void audit_logger_with_group (int type, unused const char *pgname,
++                const char *op, const char *name, unsigned int id, 
++                const char *grp, shadow_audit_result result);
+ void audit_logger_message (const char *message, shadow_audit_result result);
+ #endif
+ 
+diff -urp shadow-4.1.5.1.orig/libmisc/audit_help.c shadow-4.1.5.1/libmisc/audit_help.c
+--- shadow-4.1.5.1.orig/libmisc/audit_help.c	2010-08-21 07:41:28.000000000 -0400
++++ shadow-4.1.5.1/libmisc/audit_help.c	2014-10-14 08:39:23.785884075 -0400
+@@ -68,7 +68,7 @@ void audit_help_open (void)
+  * This function will log a message to the audit system using a predefined
+  * message format. Parameter usage is as follows:
+  *
+- * type - type of message: AUDIT_USER_CHAUTHTOK for changing any account 
++ * type - type of message: AUDIT_USER_MGMT for changing any account 
+  *	  attributes.
+  * pgname - program's name
+  * op  -  operation. "adding user", "changing finger info", "deleting group"
+@@ -88,6 +88,39 @@ void audit_logger (int type, unused cons
+ 	}
+ }
+ 
++/*
++ * This function will log a message to the audit system using a predefined
++ * message format. Parameter usage is as follows:
++ *
++ * type - type of message: AUDIT_USER_MGMT for changing any account 
++ *	  attributes.
++ * pgname - program's name
++ * op  -  operation. "adding user", "changing finger info", "deleting group"
++ * name - user's account or group name. If not available use NULL.
++ * id  -  uid or gid that the operation is being performed on. This is used
++ *	  only when user is NULL.
++ * grp - group name associated with event
++ */
++void audit_logger_with_group (int type, unused const char *pgname,
++		const char *op, const char *name, unsigned int id,
++		const char *grp, shadow_audit_result result)
++{
++	int len;
++	char enc_group[(GROUP_NAME_MAX_LENGTH*2)+1], buf[1024];
++	if (audit_fd < 0) {
++		return;
++	}
++	len = strnlen(grp, sizeof(enc_group)/2);
++	if (audit_value_needs_encoding(grp, len)) {
++		snprintf(buf, sizeof(buf), "%s grp=%s", op,
++			audit_encode_value(enc_group, grp, len));
++	} else {
++		snprintf(buf, sizeof(buf), "%s grp=\"%s\"", op, grp);
++	}
++	audit_log_acct_message (audit_fd, type, NULL, buf, name, id,
++		                        NULL, NULL, NULL, (int) result);
++}
++
+ void audit_logger_message (const char *message, shadow_audit_result result)
+ {
+ 	if (audit_fd < 0) {
+diff -urp shadow-4.1.5.1.orig/libmisc/cleanup_group.c shadow-4.1.5.1/libmisc/cleanup_group.c
+--- shadow-4.1.5.1.orig/libmisc/cleanup_group.c	2008-12-23 17:45:18.000000000 -0500
++++ shadow-4.1.5.1/libmisc/cleanup_group.c	2014-10-14 09:00:33.594753105 -0400
+@@ -83,7 +83,7 @@ void cleanup_report_mod_group (void *cle
+ 	         gr_dbname (),
+ 	         info->action));
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_USER_ACCT, Prog,
++	audit_logger (AUDIT_GRP_MGMT, Prog,
+ 	              info->audit_msg,
+ 	              info->name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+@@ -101,7 +101,7 @@ void cleanup_report_mod_gshadow (void *c
+ 	         sgr_dbname (),
+ 	         info->action));
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_USER_ACCT, Prog,
++	audit_logger (AUDIT_GRP_MGMT, Prog,
+ 	              info->audit_msg,
+ 	              info->name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+@@ -122,7 +122,7 @@ void cleanup_report_add_group_group (voi
+ 	SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, gr_dbname ()));
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_ADD_GROUP, Prog,
+-	              "adding group to /etc/group",
++	              "adding-group",
+ 	              name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -141,8 +141,8 @@ void cleanup_report_add_group_gshadow (v
+ 
+ 	SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, sgr_dbname ()));
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_ADD_GROUP, Prog,
+-	              "adding group to /etc/gshadow",
++	audit_logger (AUDIT_GRP_MGMT, Prog,
++	              "adding-shadow-group",
+ 	              name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -164,8 +164,8 @@ void cleanup_report_del_group_group (voi
+ 	         "failed to remove group %s from %s",
+ 	         name, gr_dbname ()));
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_ADD_GROUP, Prog,
+-	              "removing group from /etc/group",
++	audit_logger (AUDIT_DEL_GROUP, Prog,
++	              "removing-group",
+ 	              name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -187,8 +187,8 @@ void cleanup_report_del_group_gshadow (v
+ 	         "failed to remove group %s from %s",
+ 	         name, sgr_dbname ()));
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_ADD_GROUP, Prog,
+-	              "removing group from /etc/gshadow",
++	audit_logger (AUDIT_GRP_MGMT, Prog,
++	              "removing-shadow-group",
+ 	              name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -208,7 +208,7 @@ void cleanup_unlock_group (unused void *
+ 		         Prog, gr_dbname ());
+ 		SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+ #ifdef WITH_AUDIT
+-		audit_logger_message ("unlocking group file",
++		audit_logger_message ("unlocking-group",
+ 		                      SHADOW_AUDIT_FAILURE);
+ #endif
+ 	}
+@@ -228,7 +228,7 @@ void cleanup_unlock_gshadow (unused void
+ 		         Prog, sgr_dbname ());
+ 		SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+ #ifdef WITH_AUDIT
+-		audit_logger_message ("unlocking gshadow file",
++		audit_logger_message ("unlocking-gshadow",
+ 		                      SHADOW_AUDIT_FAILURE);
+ #endif
+ 	}
+diff -urp shadow-4.1.5.1.orig/libmisc/cleanup_user.c shadow-4.1.5.1/libmisc/cleanup_user.c
+--- shadow-4.1.5.1.orig/libmisc/cleanup_user.c	2008-12-23 17:45:18.000000000 -0500
++++ shadow-4.1.5.1/libmisc/cleanup_user.c	2014-10-14 09:01:51.878745031 -0400
+@@ -65,7 +65,7 @@ void cleanup_report_mod_passwd (void *cl
+ 	         pw_dbname (),
+ 	         info->action));
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_USER_ACCT, Prog,
++	audit_logger (AUDIT_USER_MGMT, Prog,
+ 	              info->audit_msg,
+ 	              info->name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+@@ -86,7 +86,7 @@ void cleanup_report_add_user_passwd (voi
+ 	SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, pw_dbname ()));
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_ADD_USER, Prog,
+-	              "adding user to /etc/passwd",
++	              "adding-user",
+ 	              name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -105,8 +105,8 @@ void cleanup_report_add_user_shadow (voi
+ 
+ 	SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, spw_dbname ()));
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_ADD_USER, Prog,
+-	              "adding user to /etc/shadow",
++	audit_logger (AUDIT_USER_MGMT, Prog,
++	              "adding-shadow-user",
+ 	              name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -125,7 +125,7 @@ void cleanup_unlock_passwd (unused void
+ 		         Prog, pw_dbname ());
+ 		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+ #ifdef WITH_AUDIT
+-		audit_logger_message ("unlocking passwd file",
++		audit_logger_message ("unlocking-passwd",
+ 		                      SHADOW_AUDIT_FAILURE);
+ #endif
+ 	}
+@@ -144,7 +144,7 @@ void cleanup_unlock_shadow (unused void
+ 		         Prog, spw_dbname ());
+ 		SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+ #ifdef WITH_AUDIT
+-		audit_logger_message ("unlocking shadow file",
++		audit_logger_message ("unlocking-shadow",
+ 		                      SHADOW_AUDIT_FAILURE);
+ #endif
+ 	}
+diff -urp shadow-4.1.5.1.orig/src/chage.c shadow-4.1.5.1/src/chage.c
+--- shadow-4.1.5.1.orig/src/chage.c	2011-11-19 17:54:47.000000000 -0500
++++ shadow-4.1.5.1/src/chage.c	2014-10-14 08:39:23.787884075 -0400
+@@ -126,9 +126,10 @@ static /*@noreturn@*/void fail_exit (int
+ 
+ #ifdef WITH_AUDIT
+ 	if (E_SUCCESS != code) {
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "change age",
+-		              user_name, (unsigned int) user_uid, 0);
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "change-age",
++		              user_name, (unsigned int) user_uid,
++		              SHADOW_AUDIT_FAILURE);
+ 	}
+ #endif
+ 
+@@ -873,11 +874,7 @@ int main (int argc, char **argv)
+ 			fprintf (stderr, _("%s: Permission denied.\n"), Prog);
+ 			fail_exit (E_NOPERM);
+ 		}
+-#ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "display aging info",
+-		              user_name, (unsigned int) user_uid, 1);
+-#endif
++		/* Displaying fields is not of interest to audit */
+ 		list_fields ();
+ 		fail_exit (E_SUCCESS);
+ 	}
+@@ -896,41 +893,43 @@ int main (int argc, char **argv)
+ 		}
+ #ifdef WITH_AUDIT
+ 		else {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "change all aging information",
+-			              user_name, (unsigned int) user_uid, 1);
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "change-all-aging-information",
++			              user_name, (unsigned int) user_uid,
++			              SHADOW_AUDIT_SUCCESS);
+ 		}
+ #endif
+ 	} else {
+ #ifdef WITH_AUDIT
+ 		if (Mflg) {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "change max age",
+-			              user_name, (unsigned int) user_uid, 1);
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "change-max-age",
++			              user_name, (unsigned int) user_uid,
++			              SHADOW_AUDIT_SUCCESS);
+ 		}
+ 		if (mflg) {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "change min age",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "change-min-age",
+ 			              user_name, (unsigned int) user_uid, 1);
+ 		}
+ 		if (dflg) {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "change last change date",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "change-last-change-date",
+ 			              user_name, (unsigned int) user_uid, 1);
+ 		}
+ 		if (Wflg) {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "change passwd warning",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "change-passwd-warning",
+ 			              user_name, (unsigned int) user_uid, 1);
+ 		}
+ 		if (Iflg) {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "change inactive days",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "change-inactive-days",
+ 			              user_name, (unsigned int) user_uid, 1);
+ 		}
+ 		if (Eflg) {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "change passwd expiration",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "change-passwd-expiration",
+ 			              user_name, (unsigned int) user_uid, 1);
+ 		}
+ #endif
+diff -urp shadow-4.1.5.1.orig/src/gpasswd.c shadow-4.1.5.1/src/gpasswd.c
+--- shadow-4.1.5.1.orig/src/gpasswd.c	2014-09-13 15:45:54.989829559 -0400
++++ shadow-4.1.5.1/src/gpasswd.c	2014-10-14 08:43:07.393861012 -0400
+@@ -137,7 +137,7 @@ static void usage (int status)
+ 	(void) fputs (_("  -d, --delete USER             remove USER from GROUP\n"), usageout);
+ 	(void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+ 	(void) fputs (_("  -Q, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+-	(void) fputs (_("  -r, --remove-password         remove the GROUP's password\n"), usageout);
++	(void) fputs (_("  -r, --delete-password         remove the GROUP's password\n"), usageout);
+ 	(void) fputs (_("  -R, --restrict                restrict access to GROUP to its members\n"), usageout);
+ 	(void) fputs (_("  -M, --members USER,...        set the list of members of GROUP\n"), usageout);
+ #ifdef SHADOWGRP
+@@ -397,21 +397,14 @@ static void open_files (void)
+ 
+ static void log_gpasswd_failure (const char *suffix)
+ {
+-#ifdef WITH_AUDIT
+-	char buf[1024];
+-#endif
+ 	if (aflg) {
+ 		SYSLOG ((LOG_ERR,
+ 		         "%s failed to add user %s to group %s%s",
+ 		         myname, user, group, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "%s failed to add user %s to group %s%s",
+-		          myname, user, group, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_ACCT, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "add-user-to-group",
++		              user, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+ 	} else if (dflg) {
+@@ -419,13 +412,9 @@ static void log_gpasswd_failure (const c
+ 		         "%s failed to remove user %s from group %s%s",
+ 		         myname, user, group, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "%s failed to remove user %s from group %s%s",
+-		          myname, user, group, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_ACCT, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "delete-user-from-group",
++		              user, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+ 	} else if (rflg) {
+@@ -433,13 +422,9 @@ static void log_gpasswd_failure (const c
+ 		         "%s failed to remove password of group %s%s",
+ 		         myname, group, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "%s failed to remove password of group %s%s",
+-		          myname, group, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_GRP_CHAUTHTOK, Prog,
++		              "delete-group-password",
++		              myname, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+ 	} else if (Rflg) {
+@@ -447,13 +432,9 @@ static void log_gpasswd_failure (const c
+ 		         "%s failed to restrict access to group %s%s",
+ 		         myname, group, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "%s failed to restrict access to group %s%s",
+-		          myname, group, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_GRP_MGMT, Prog,
++		              "restrict-group",
++		              myname, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+ 	} else if (Aflg || Mflg) {
+@@ -463,13 +444,9 @@ static void log_gpasswd_failure (const c
+ 			         "%s failed to set the administrators of group %s to %s%s",
+ 			         myname, group, admins, suffix));
+ #ifdef WITH_AUDIT
+-			snprintf (buf, 1023,
+-			          "%s failed to set the administrators of group %s to %s%s",
+-			          myname, group, admins, suffix);
+-			buf[1023] = '\0';
+-			audit_logger (AUDIT_USER_ACCT, Prog,
+-			              buf,
+-			              group, AUDIT_NO_ID,
++			audit_logger_with_group (AUDIT_GRP_MGMT, Prog,
++			              "set-admins-of-group",
++			              admins, AUDIT_NO_ID, group,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif
+ 		}
+@@ -479,13 +456,9 @@ static void log_gpasswd_failure (const c
+ 			         "%s failed to set the members of group %s to %s%s",
+ 			         myname, group, members, suffix));
+ #ifdef WITH_AUDIT
+-			snprintf (buf, 1023,
+-			          "%s failed to set the members of group %s to %s%s",
+-			          myname, group, members, suffix);
+-			buf[1023] = '\0';
+-			audit_logger (AUDIT_USER_ACCT, Prog,
+-			              buf,
+-			              group, AUDIT_NO_ID,
++			audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++			              "add-users-to-group",
++			              members, AUDIT_NO_ID, group,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif
+ 		}
+@@ -494,13 +467,9 @@ static void log_gpasswd_failure (const c
+ 		         "%s failed to change password of group %s%s",
+ 		         myname, group, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "%s failed to change password of group %s%s",
+-		          myname, group, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_GRP_CHAUTHTOK, Prog,
++		              "change-password",
++		              myname, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+ 	}
+@@ -531,21 +500,14 @@ static void log_gpasswd_failure_gshadow
+ 
+ static void log_gpasswd_success (const char *suffix)
+ {
+-#ifdef WITH_AUDIT
+-	char buf[1024];
+-#endif
+ 	if (aflg) {
+ 		SYSLOG ((LOG_INFO,
+ 		         "user %s added by %s to group %s%s",
+ 		         user, myname, group, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "user %s added by %s to group %s%s",
+-		          user, myname, group, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_ACCT, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "add-user-to-group",
++		              user, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 	} else if (dflg) {
+@@ -553,13 +515,9 @@ static void log_gpasswd_success (const c
+ 		         "user %s removed by %s from group %s%s",
+ 		         user, myname, group, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "user %s removed by %s from group %s%s",
+-		          user, myname, group, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_ACCT, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "delete-user-from-group",
++		              user, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 	} else if (rflg) {
+@@ -567,13 +525,9 @@ static void log_gpasswd_success (const c
+ 		         "password of group %s removed by %s%s",
+ 		         group, myname, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "password of group %s removed by %s%s",
+-		          group, myname, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_GRP_CHAUTHTOK, Prog,
++		              "delete-group-password",
++		              myname, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 	} else if (Rflg) {
+@@ -581,13 +535,9 @@ static void log_gpasswd_success (const c
+ 		         "access to group %s restricted by %s%s",
+ 		         group, myname, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "access to group %s restricted by %s%s",
+-		          group, myname, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_GRP_MGMT, Prog,
++		              "restrict-group",
++		              myname, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 	} else if (Aflg || Mflg) {
+@@ -597,13 +547,9 @@ static void log_gpasswd_success (const c
+ 			         "administrators of group %s set by %s to %s%s",
+ 			         group, myname, admins, suffix));
+ #ifdef WITH_AUDIT
+-			snprintf (buf, 1023,
+-			          "administrators of group %s set by %s to %s%s",
+-			          group, myname, admins, suffix);
+-			buf[1023] = '\0';
+-			audit_logger (AUDIT_USER_ACCT, Prog,
+-			              buf,
+-			              group, AUDIT_NO_ID,
++			audit_logger_with_group (AUDIT_GRP_MGMT, Prog,
++			              "set-admins-of-group",
++			              admins, AUDIT_NO_ID, group,
+ 			              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 		}
+@@ -613,13 +559,9 @@ static void log_gpasswd_success (const c
+ 			         "members of group %s set by %s to %s%s",
+ 			         group, myname, members, suffix));
+ #ifdef WITH_AUDIT
+-			snprintf (buf, 1023,
+-			          "members of group %s set by %s to %s%s",
+-			          group, myname, members, suffix);
+-			buf[1023] = '\0';
+-			audit_logger (AUDIT_USER_ACCT, Prog,
+-			              buf,
+-			              group, AUDIT_NO_ID,
++			audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++			              "add-users-to-group",
++			              members, AUDIT_NO_ID, group,
+ 			              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 		}
+@@ -628,13 +570,9 @@ static void log_gpasswd_success (const c
+ 		         "password of group %s changed by %s%s",
+ 		         group, myname, suffix));
+ #ifdef WITH_AUDIT
+-		snprintf (buf, 1023,
+-		          "password of group %s changed by %s%s",
+-		          group, myname, suffix);
+-		buf[1023] = '\0';
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              buf,
+-		              group, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_GRP_CHAUTHTOK, Prog,
++		              "change-password",
++		              myname, AUDIT_NO_ID, group,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 	}
+diff -urp shadow-4.1.5.1.orig/src/groupadd.c shadow-4.1.5.1/src/groupadd.c
+--- shadow-4.1.5.1.orig/src/groupadd.c	2011-11-18 16:23:30.000000000 -0500
++++ shadow-4.1.5.1/src/groupadd.c	2014-10-14 08:39:23.800884073 -0400
+@@ -127,6 +127,15 @@ static /*@noreturn@*/void usage (int sta
+ 	exit (status);
+ }
+ 
++static void fail_exit(int status)
++{
++#ifdef WITH_AUDIT
++	audit_logger(AUDIT_ADD_GROUP, Prog, "add-group", group_name,
++			AUDIT_NO_ID, SHADOW_AUDIT_FAILURE);
++#endif
++	exit (status);
++}
++
+ /*
+  * new_grent - initialize the values in a group file entry
+  *
+@@ -210,7 +219,7 @@ static void grp_update (void)
+ 		fprintf (stderr,
+ 		         _("%s: failed to prepare the new %s entry '%s'\n"),
+ 		         Prog, gr_dbname (), grp.gr_name);
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ #ifdef	SHADOWGRP
+ 	/*
+@@ -220,7 +229,7 @@ static void grp_update (void)
+ 		fprintf (stderr,
+ 		         _("%s: failed to prepare the new %s entry '%s'\n"),
+ 		         Prog, sgr_dbname (), sgrp.sg_name);
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ #endif				/* SHADOWGRP */
+ }
+@@ -244,7 +253,7 @@ static void check_new_name (void)
+ 	fprintf (stderr, _("%s: '%s' is not a valid group name\n"),
+ 	         Prog, group_name);
+ 
+-	exit (E_BAD_ARG);
++	fail_exit (E_BAD_ARG);
+ }
+ 
+ /*
+@@ -260,11 +269,11 @@ static void close_files (void)
+ 		fprintf (stderr,
+ 		         _("%s: failure while writing changes to %s\n"),
+ 		         Prog, gr_dbname ());
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_ADD_GROUP, Prog,
+-	              "adding group to /etc/group",
++	              "add-group",
+ 	              group_name, (unsigned int) group_id,
+ 	              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -282,11 +291,11 @@ static void close_files (void)
+ 			fprintf (stderr,
+ 			         _("%s: failure while writing changes to %s\n"),
+ 			         Prog, sgr_dbname ());
+-			exit (E_GRP_UPDATE);
++			fail_exit (E_GRP_UPDATE);
+ 		}
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_ADD_GROUP, Prog,
+-		              "adding group to /etc/gshadow",
++		audit_logger (AUDIT_GRP_MGMT, Prog,
++		              "add-shadow-group",
+ 		              group_name, (unsigned int) group_id,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -300,12 +309,6 @@ static void close_files (void)
+ #endif				/* SHADOWGRP */
+ 
+ 	/* Report success at the system level */
+-#ifdef WITH_AUDIT
+-	audit_logger (AUDIT_ADD_GROUP, Prog,
+-	              "",
+-	              group_name, (unsigned int) group_id,
+-	              SHADOW_AUDIT_SUCCESS);
+-#endif
+ 	SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
+ 	         group_name, (unsigned int) group_id));
+ 	del_cleanup (cleanup_report_add_group);
+@@ -323,7 +326,7 @@ static void open_files (void)
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+ 		         Prog, gr_dbname ());
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ 	add_cleanup (cleanup_unlock_group, NULL);
+ 
+@@ -333,7 +336,7 @@ static void open_files (void)
+ 			fprintf (stderr,
+ 			         _("%s: cannot lock %s; try again later.\n"),
+ 			         Prog, sgr_dbname ());
+-			exit (E_GRP_UPDATE);
++			fail_exit (E_GRP_UPDATE);
+ 		}
+ 		add_cleanup (cleanup_unlock_gshadow, NULL);
+ 	}
+@@ -349,7 +352,7 @@ static void open_files (void)
+ 	if (gr_open (O_RDWR) == 0) {
+ 		fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
+ 		SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ 
+ #ifdef	SHADOWGRP
+@@ -359,7 +362,7 @@ static void open_files (void)
+ 			         _("%s: cannot open %s\n"),
+ 			         Prog, sgr_dbname ());
+ 			SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
+-			exit (E_GRP_UPDATE);
++			fail_exit (E_GRP_UPDATE);
+ 		}
+ 	}
+ #endif				/* SHADOWGRP */
+@@ -489,7 +492,7 @@ static void check_flags (void)
+ 		fprintf (stderr,
+ 		         _("%s: group '%s' already exists\n"),
+ 		         Prog, group_name);
+-		exit (E_NAME_IN_USE);
++		fail_exit (E_NAME_IN_USE);
+ 	}
+ 
+ 	if (gflg && (getgrgid (group_id) != NULL)) {
+@@ -508,7 +511,7 @@ static void check_flags (void)
+ 			fprintf (stderr,
+ 			         _("%s: GID '%lu' already exists\n"),
+ 			         Prog, (unsigned long int) group_id);
+-			exit (E_GID_IN_USE);
++			fail_exit (E_GID_IN_USE);
+ 		}
+ 	}
+ }
+@@ -536,7 +539,7 @@ static void check_perms (void)
+ 		fprintf (stderr,
+ 		         _("%s: Cannot determine your user name.\n"),
+ 		         Prog);
+-		exit (1);
++		fail_exit (1);
+ 	}
+ 
+ 	retval = pam_start ("groupadd", pampw->pw_name, &conv, &pamh);
+@@ -556,7 +559,7 @@ static void check_perms (void)
+ 		if (NULL != pamh) {
+ 			(void) pam_end (pamh, retval);
+ 		}
+-		exit (1);
++		fail_exit (1);
+ 	}
+ 	(void) pam_end (pamh, retval);
+ #endif				/* USE_PAM */
+@@ -588,7 +591,7 @@ int main (int argc, char **argv)
+ 		fprintf (stderr,
+ 		         _("%s: Cannot setup cleanup service.\n"),
+ 		         Prog);
+-		exit (1);
++		fail_exit (1);
+ 	}
+ 
+ 	/*
+@@ -610,7 +613,7 @@ int main (int argc, char **argv)
+ 
+ 	if (!gflg) {
+ 		if (find_new_gid (rflg, &group_id, NULL) < 0) {
+-			exit (E_GID_IN_USE);
++			fail_exit (E_GID_IN_USE);
+ 		}
+ 	}
+ 
+diff -urp shadow-4.1.5.1.orig/src/groupdel.c shadow-4.1.5.1/src/groupdel.c
+--- shadow-4.1.5.1.orig/src/groupdel.c	2011-11-18 16:23:30.000000000 -0500
++++ shadow-4.1.5.1/src/groupdel.c	2014-10-14 08:39:23.801884073 -0400
+@@ -100,6 +100,15 @@ static /*@noreturn@*/void usage (int sta
+ 	exit (status);
+ }
+ 
++static void fail_exit(int status)
++{
++#ifdef WITH_AUDIT
++	audit_logger(AUDIT_GRP_MGMT, Prog, "delete-group", group_name,
++                        AUDIT_NO_ID, SHADOW_AUDIT_FAILURE);
++#endif
++	exit (status);
++}
++
+ /*
+  * grp_update - update group file entries
+  *
+@@ -126,7 +135,7 @@ static void grp_update (void)
+ 		fprintf (stderr,
+ 		         _("%s: cannot remove entry '%s' from %s\n"),
+ 		         Prog, group_name, gr_dbname ());
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ 
+ #ifdef	SHADOWGRP
+@@ -138,7 +147,7 @@ static void grp_update (void)
+ 			fprintf (stderr,
+ 			         _("%s: cannot remove entry '%s' from %s\n"),
+ 			         Prog, group_name, sgr_dbname ());
+-			exit (E_GRP_UPDATE);
++			fail_exit (E_GRP_UPDATE);
+ 		}
+ 	}
+ #endif				/* SHADOWGRP */
+@@ -157,12 +166,12 @@ static void close_files (void)
+ 		fprintf (stderr,
+ 		         _("%s: failure while writing changes to %s\n"),
+ 		         Prog, gr_dbname ());
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ 
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_DEL_GROUP, Prog,
+-	              "removing group from /etc/group",
++	              "delete-group",
+ 	              group_name, (unsigned int) group_id,
+ 	              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -182,12 +191,12 @@ static void close_files (void)
+ 			fprintf (stderr,
+ 			         _("%s: failure while writing changes to %s\n"),
+ 			         Prog, sgr_dbname ());
+-			exit (E_GRP_UPDATE);
++			fail_exit (E_GRP_UPDATE);
+ 		}
+ 
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_GROUP, Prog,
+-		              "removing group from /etc/gshadow",
++		audit_logger (AUDIT_GRP_MGMT, Prog,
++		              "delete-shadow-group",
+ 		              group_name, (unsigned int) group_id,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -201,13 +210,6 @@ static void close_files (void)
+ 	}
+ #endif				/* SHADOWGRP */
+ 
+-	/* Report success at the system level */
+-#ifdef WITH_AUDIT
+-	audit_logger (AUDIT_DEL_GROUP, Prog,
+-	              "",
+-	              group_name, (unsigned int) group_id,
+-	              SHADOW_AUDIT_SUCCESS);
+-#endif
+ 	SYSLOG ((LOG_INFO, "group '%s' removed\n", group_name));
+ 	del_cleanup (cleanup_report_del_group);
+ }
+@@ -224,7 +226,7 @@ static void open_files (void)
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+ 		         Prog, gr_dbname ());
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ 	add_cleanup (cleanup_unlock_group, NULL);
+ #ifdef	SHADOWGRP
+@@ -233,7 +235,7 @@ static void open_files (void)
+ 			fprintf (stderr,
+ 			         _("%s: cannot lock %s; try again later.\n"),
+ 			         Prog, sgr_dbname ());
+-			exit (E_GRP_UPDATE);
++			fail_exit (E_GRP_UPDATE);
+ 		}
+ 		add_cleanup (cleanup_unlock_gshadow, NULL);
+ 	}
+@@ -251,7 +253,7 @@ static void open_files (void)
+ 		         _("%s: cannot open %s\n"),
+ 		         Prog, gr_dbname ());
+ 		SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
+-		exit (E_GRP_UPDATE);
++		fail_exit (E_GRP_UPDATE);
+ 	}
+ #ifdef	SHADOWGRP
+ 	if (is_shadow_grp) {
+@@ -260,7 +262,7 @@ static void open_files (void)
+ 			         _("%s: cannot open %s\n"),
+ 			         Prog, sgr_dbname ());
+ 			SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
+-			exit (E_GRP_UPDATE);
++			fail_exit (E_GRP_UPDATE);
+ 		}
+ 	}
+ #endif				/* SHADOWGRP */
+@@ -301,7 +303,7 @@ static void group_busy (gid_t gid)
+ 	fprintf (stderr,
+ 	         _("%s: cannot remove the primary group of user '%s'\n"),
+ 	         Prog, pwd->pw_name);
+-	exit (E_GROUP_BUSY);
++	fail_exit (E_GROUP_BUSY);
+ }
+ 
+ /*
+@@ -379,7 +381,7 @@ int main (int argc, char **argv)
+ 		fprintf (stderr,
+ 		         _("%s: Cannot setup cleanup service.\n"),
+ 		         Prog);
+-		exit (1);
++		fail_exit (1);
+ 	}
+ 
+ 	process_flags (argc, argv);
+@@ -393,7 +395,7 @@ int main (int argc, char **argv)
+ 			fprintf (stderr,
+ 			         _("%s: Cannot determine your user name.\n"),
+ 			         Prog);
+-			exit (1);
++			fail_exit (1);
+ 		}
+ 
+ 		retval = pam_start ("groupdel", pampw->pw_name, &conv, &pamh);
+@@ -414,7 +416,7 @@ int main (int argc, char **argv)
+ 		if (NULL != pamh) {
+ 			(void) pam_end (pamh, retval);
+ 		}
+-		exit (1);
++		fail_exit (1);
+ 	}
+ 	(void) pam_end (pamh, retval);
+ #endif				/* USE_PAM */
+@@ -434,7 +436,7 @@ int main (int argc, char **argv)
+ 			fprintf (stderr,
+ 			         _("%s: group '%s' does not exist\n"),
+ 			         Prog, group_name);
+-			exit (E_NOTFOUND);
++			fail_exit (E_NOTFOUND);
+ 		}
+ 
+ 		group_id = grp->gr_gid;
+@@ -458,7 +460,7 @@ int main (int argc, char **argv)
+ 			         _("%s: %s is the NIS master\n"),
+ 			         Prog, nis_master);
+ 		}
+-		exit (E_NOTFOUND);
++		fail_exit (E_NOTFOUND);
+ 	}
+ #endif
+ 
+diff -urp shadow-4.1.5.1.orig/src/groupmod.c shadow-4.1.5.1/src/groupmod.c
+--- shadow-4.1.5.1.orig/src/groupmod.c	2011-11-18 16:23:30.000000000 -0500
++++ shadow-4.1.5.1/src/groupmod.c	2014-10-14 08:49:28.517821702 -0400
+@@ -438,7 +438,7 @@ static void close_files (void)
+ 		exit (E_GRP_UPDATE);
+ 	}
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_USER_ACCT, Prog,
++	audit_logger (AUDIT_GRP_MGMT, Prog,
+ 	              info_group.audit_msg,
+ 	              group_name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_SUCCESS);
+@@ -461,7 +461,7 @@ static void close_files (void)
+ 			exit (E_GRP_UPDATE);
+ 		}
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_ACCT, Prog,
++		audit_logger (AUDIT_GRP_MGMT, Prog,
+ 		              info_gshadow.audit_msg,
+ 		              group_name, AUDIT_NO_ID,
+ 		              SHADOW_AUDIT_SUCCESS);
+@@ -484,7 +484,7 @@ static void close_files (void)
+ 			exit (E_GRP_UPDATE);
+ 		}
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_ACCT, Prog,
++		audit_logger (AUDIT_GRP_MGMT, Prog,
+ 		              info_passwd.audit_msg,
+ 		              group_name, AUDIT_NO_ID,
+ 		              SHADOW_AUDIT_SUCCESS);
+@@ -499,8 +499,8 @@ static void close_files (void)
+ 	}
+ 
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_USER_ACCT, Prog,
+-	              "modifying group",
++	audit_logger (AUDIT_GRP_MGMT, Prog,
++	              "modify-group",
+ 	              group_name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -512,6 +512,8 @@ static void close_files (void)
+  */
+ static void prepare_failure_reports (void)
+ {
++	char *nv_pair, nv[64];
++
+ 	info_group.name   = group_name;
+ #ifdef	SHADOWGRP
+ 	info_gshadow.name = group_name;
+@@ -524,76 +526,106 @@ static void prepare_failure_reports (voi
+ #endif
+ 	info_passwd.audit_msg  = xmalloc (512);
+ 
+-	(void) snprintf (info_group.audit_msg, 511,
+-	                 "changing %s; ", gr_dbname ());
++	info_group.action   = xmalloc (512);
+ #ifdef	SHADOWGRP
+-	(void) snprintf (info_gshadow.audit_msg, 511,
+-	                 "changing %s; ", sgr_dbname ());
++	info_gshadow.action = xmalloc (512);
+ #endif
+-	(void) snprintf (info_passwd.audit_msg, 511,
+-	                 "changing %s; ", pw_dbname ());
++	info_passwd.action  = xmalloc (512);
+ 
+-	info_group.action   =   info_group.audit_msg
+-	                      + strlen (info_group.audit_msg);
++	(void) snprintf (info_group.audit_msg, 511,
++	                 "changing-group");
+ #ifdef	SHADOWGRP
+-	info_gshadow.action =   info_gshadow.audit_msg
+-	                      + strlen (info_gshadow.audit_msg);
++	(void) snprintf (info_gshadow.audit_msg, 511,
++	                 "changing-shadow-group");
+ #endif
+-	info_passwd.action  =   info_passwd.audit_msg
+-	                      + strlen (info_passwd.audit_msg);
++	(void) snprintf (info_passwd.audit_msg, 511,
++	                 "changing-group-passwd");
+ 
++	nv_pair = audit_encode_nv_string(" grp", group_name,
++			strlen(group_name));
++	if(nv_pair) {
++		strncat(info_group.audit_msg, nv_pair,
++			511 - strlen(info_group.audit_msg));
++#ifdef	SHADOWGRP
++		strncat(info_gshadow.audit_msg, nv_pair,
++			511 - strlen(info_gshadow.audit_msg));
++#endif
++		strncat(info_passwd.audit_msg, nv_pair,
++			511 - strlen(info_passwd.audit_msg));
++		free(nv_pair);
++	}
++	snprintf(nv, sizeof(nv), " gid=%lu", group_id);
++	strncat(info_group.audit_msg, nv, 511 - strlen(info_group.audit_msg));
++	strncat(info_passwd.audit_msg, nv, 511 - strlen(info_passwd.audit_msg));
++	
+ 	(void) snprintf (info_group.action,
+-	                 511 - strlen (info_group.audit_msg),
++	                 511,
+ 	                 "group %s/%lu",
+ 	                 group_name, (unsigned long int) group_id);
+ #ifdef	SHADOWGRP
+ 	(void) snprintf (info_gshadow.action,
+-	                 511 - strlen (info_group.audit_msg),
++	                 511,
+ 	                 "group %s", group_name);
+ #endif
+ 	(void) snprintf (info_passwd.action,
+-	                 511 - strlen (info_group.audit_msg),
++	                 511,
+ 	                 "group %s/%lu",
+ 	                 group_name, (unsigned long int) group_id);
+ 
+ 	if (nflg) {
++		nv_pair = audit_encode_nv_string(" new_group", group_newname,
++				strlen(group_newname));
++		strncat(info_group.audit_msg, nv_pair,
++				511 - strlen(info_group.audit_msg));
+ 		strncat (info_group.action, ", new name: ",
+-		         511 - strlen (info_group.audit_msg));
++		         511 - strlen (info_group.action));
+ 		strncat (info_group.action, group_newname,
+-		         511 - strlen (info_group.audit_msg));
++		         511 - strlen (info_group.action));
+ 
+ #ifdef	SHADOWGRP
++		strncat(info_gshadow.audit_msg, nv_pair,
++				511 - strlen(info_gshadow.audit_msg));
+ 		strncat (info_gshadow.action, ", new name: ",
+-		         511 - strlen (info_gshadow.audit_msg));
++		         511 - strlen (info_gshadow.action));
+ 		strncat (info_gshadow.action, group_newname,
+-		         511 - strlen (info_gshadow.audit_msg));
++		         511 - strlen (info_gshadow.action));
+ #endif
+ 
++		strncat(info_passwd.audit_msg, nv_pair,
++				511 - strlen(info_passwd.audit_msg));
+ 		strncat (info_passwd.action, ", new name: ",
+-		         511 - strlen (info_passwd.audit_msg));
++		         511 - strlen (info_passwd.action));
+ 		strncat (info_passwd.action, group_newname,
+-		         511 - strlen (info_passwd.audit_msg));
++		         511 - strlen (info_passwd.action));
++		free(nv_pair);
+ 	}
+ 	if (pflg) {
++		/* Note: audit doesn't want this value recorded */
+ 		strncat (info_group.action, ", new password",
+-		         511 - strlen (info_group.audit_msg));
++		         511 - strlen (info_group.action));
+ 
+ #ifdef	SHADOWGRP
+ 		strncat (info_gshadow.action, ", new password",
+-		         511 - strlen (info_gshadow.audit_msg));
++		         511 - strlen (info_gshadow.action));
+ #endif
+ 	}
+ 	if (gflg) {
++		snprintf(nv, sizeof(nv), " new_gid=%lu", group_newid);
++		strncat(info_group.audit_msg, nv,
++				511 - strlen(info_group.audit_msg));
++		strncat(info_passwd.audit_msg, nv,
++				511 - strlen(info_passwd.audit_msg));
++
+ 		strncat (info_group.action, ", new gid: ",
+-		         511 - strlen (info_group.audit_msg));
++		         511 - strlen (info_group.action));
+ 		(void) snprintf (info_group.action+strlen (info_group.action),
+-		                 511 - strlen (info_group.audit_msg),
++		                 511 - strlen (info_group.action),
+ 		                 "%lu", (unsigned long int) group_newid);
+ 
+ 		strncat (info_passwd.action, ", new gid: ",
+-		         511 - strlen (info_passwd.audit_msg));
++		         511 - strlen (info_passwd.action));
+ 		(void) snprintf (info_passwd.action+strlen (info_passwd.action),
+-		                 511 - strlen (info_passwd.audit_msg),
++		                 511 - strlen (info_passwd.action),
+ 		                 "%lu", (unsigned long int) group_newid);
+ 	}
+ 	info_group.audit_msg[511]   = '\0';
+@@ -601,6 +633,11 @@ static void prepare_failure_reports (voi
+ 	info_gshadow.audit_msg[511] = '\0';
+ #endif
+ 	info_passwd.audit_msg[511]  = '\0';
++	info_group.action[511]   = '\0';
++#ifdef	SHADOWGRP
++	info_gshadow.action[511] = '\0';
++#endif
++	info_passwd.action[511]  = '\0';
+ 
+ // FIXME: add a system cleanup
+ 	add_cleanup (cleanup_report_mod_group, &info_group);
+diff -urp shadow-4.1.5.1.orig/src/newgrp.c shadow-4.1.5.1/src/newgrp.c
+--- shadow-4.1.5.1.orig/src/newgrp.c	2014-09-13 15:45:55.010829557 -0400
++++ shadow-4.1.5.1/src/newgrp.c	2014-10-14 08:39:23.802884073 -0400
+@@ -197,11 +197,12 @@ static void check_perms (const struct gr
+ 		    strcmp (cpasswd, grp->gr_passwd) != 0) {
+ #ifdef WITH_AUDIT
+ 			snprintf (audit_buf, sizeof(audit_buf),
+-			          "authentication new-gid=%lu",
++			          "authentication new_gid=%lu",
+ 			          (unsigned long) grp->gr_gid);
+ 			audit_logger (AUDIT_GRP_AUTH, Prog,
+ 			              audit_buf, NULL,
+-			              (unsigned int) getuid (), 0);
++			              (unsigned int) getuid (),
++			              SHADOW_AUDIT_FAILURE);
+ #endif
+ 			SYSLOG ((LOG_INFO,
+ 				 "Invalid password for group '%s' from '%s'",
+@@ -212,11 +213,12 @@ static void check_perms (const struct gr
+ 		}
+ #ifdef WITH_AUDIT
+ 		snprintf (audit_buf, sizeof(audit_buf),
+-		          "authentication new-gid=%lu",
++		          "authentication new_gid=%lu",
+ 		          (unsigned long) grp->gr_gid);
+ 		audit_logger (AUDIT_GRP_AUTH, Prog,
+ 		              audit_buf, NULL,
+-		              (unsigned int) getuid (), 1);
++		              (unsigned int) getuid (),
++		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 	}
+ 
+@@ -227,19 +229,6 @@ failure:
+ 	 * harm.  -- JWP
+ 	 */
+ 	closelog ();
+-#ifdef WITH_AUDIT
+-	if (groupname) {
+-		snprintf (audit_buf, sizeof(audit_buf),
+-		          "changing new-group=%s", groupname);
+-		audit_logger (AUDIT_CHGRP_ID, Prog,
+-		              audit_buf, NULL,
+-		              (unsigned int) getuid (), 0);
+-	} else {
+-		audit_logger (AUDIT_CHGRP_ID, Prog,
+-		              "changing", NULL,
+-		              (unsigned int) getuid (), 0);
+-	}
+-#endif
+ 	exit (EXIT_FAILURE);
+ }
+ 
+@@ -308,15 +297,27 @@ static void syslog_sg (const char *name,
+ 				 is_newgrp ? "newgrp" : "sg", strerror (errno));
+ #ifdef WITH_AUDIT
+ 			if (group) {
+-				snprintf (audit_buf, sizeof(audit_buf),
+-				          "changing new-group=%s", group);
++				char enc_group[(GROUP_NAME_MAX_LENGTH*2)+1];
++				int len = strnlen(group, sizeof(enc_group)/2);
++				if (audit_value_needs_encoding(group, len)) {
++					snprintf (audit_buf, sizeof(audit_buf),
++					          "changing new_group=%s",
++					          audit_encode_value(enc_group,
++					          group, len));
++				} else {
++					snprintf (audit_buf, sizeof(audit_buf),
++					          "changing new_group=\"%s\"",
++					          group);
++				}
+ 				audit_logger (AUDIT_CHGRP_ID, Prog,
+ 				              audit_buf, NULL,
+-				              (unsigned int) getuid (), 0);
++				              (unsigned int) getuid (),
++				              SHADOW_AUDIT_FAILURE);
+ 			} else {
+ 				audit_logger (AUDIT_CHGRP_ID, Prog,
+ 				              "changing", NULL,
+-				              (unsigned int) getuid (), 0);
++				              (unsigned int) getuid (),
++				              SHADOW_AUDIT_FAILURE);
+ 			}
+ #endif
+ 			exit (EXIT_FAILURE);
+@@ -442,7 +443,7 @@ int main (int argc, char **argv)
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_CHGRP_ID, Prog,
+ 		              "changing", NULL,
+-		              (unsigned int) getuid (), 0);
++		              (unsigned int) getuid (), SHADOW_AUDIT_FAILURE);
+ #endif
+ 		SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
+ 		         (unsigned long) getuid ()));
+@@ -558,15 +559,26 @@ int main (int argc, char **argv)
+ 		perror ("getgroups");
+ #ifdef WITH_AUDIT
+ 		if (group) {
+-			snprintf (audit_buf, sizeof(audit_buf),
+-			          "changing new-group=%s", group);
++			char enc_group[(GROUP_NAME_MAX_LENGTH*2)+1];
++			int len = strnlen(group, sizeof(enc_group)/2);
++			if (audit_value_needs_encoding(group, len)) {
++				snprintf (audit_buf, sizeof(audit_buf),
++				          "changing new_group=%s",
++				          audit_encode_value(enc_group,
++				          group, len));
++			} else {
++				snprintf (audit_buf, sizeof(audit_buf),
++				          "changing new_group=\"%s\"", group);
++			}
+ 			audit_logger (AUDIT_CHGRP_ID, Prog,
+ 			              audit_buf, NULL,
+-			              (unsigned int) getuid (), 0);
++			              (unsigned int) getuid (),
++			              SHADOW_AUDIT_FAILURE);
+ 		} else {
+ 			audit_logger (AUDIT_CHGRP_ID, Prog,
+ 			              "changing", NULL,
+-			              (unsigned int) getuid (), 0);
++			              (unsigned int) getuid (),
++			              SHADOW_AUDIT_FAILURE);
+ 		}
+ #endif
+ 		exit (EXIT_FAILURE);
+@@ -707,10 +719,10 @@ int main (int argc, char **argv)
+ 		perror ("setgid");
+ #ifdef WITH_AUDIT
+ 		snprintf (audit_buf, sizeof(audit_buf),
+-		          "changing new-gid=%lu", (unsigned long) gid);
++		          "changing new_gid=%lu", (unsigned long) gid);
+ 		audit_logger (AUDIT_CHGRP_ID, Prog,
+ 		              audit_buf, NULL,
+-		              (unsigned int) getuid (), 0);
++		              (unsigned int) getuid (), SHADOW_AUDIT_FAILURE);
+ #endif
+ 		exit (EXIT_FAILURE);
+ 	}
+@@ -719,10 +731,10 @@ int main (int argc, char **argv)
+ 		perror ("setuid");
+ #ifdef WITH_AUDIT
+ 		snprintf (audit_buf, sizeof(audit_buf),
+-		          "changing new-gid=%lu", (unsigned long) gid);
++		          "changing new_gid=%lu", (unsigned long) gid);
+ 		audit_logger (AUDIT_CHGRP_ID, Prog,
+ 		              audit_buf, NULL,
+-		              (unsigned int) getuid (), 0);
++		              (unsigned int) getuid (), SHADOW_AUDIT_FAILURE);
+ #endif
+ 		exit (EXIT_FAILURE);
+ 	}
+@@ -736,10 +748,10 @@ int main (int argc, char **argv)
+ 		execl (SHELL, "sh", "-c", command, (char *) 0);
+ #ifdef WITH_AUDIT
+ 		snprintf (audit_buf, sizeof(audit_buf),
+-		          "changing new-gid=%lu", (unsigned long) gid);
++		          "changing new_gid=%lu", (unsigned long) gid);
+ 		audit_logger (AUDIT_CHGRP_ID, Prog,
+ 		              audit_buf, NULL,
+-		              (unsigned int) getuid (), 0);
++		              (unsigned int) getuid (), SHADOW_AUDIT_FAILURE);
+ #endif
+ 		perror (SHELL);
+ 		exit ((errno == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
+@@ -803,11 +815,11 @@ int main (int argc, char **argv)
+ 	}
+ 
+ #ifdef WITH_AUDIT
+-	snprintf (audit_buf, sizeof(audit_buf), "changing new-gid=%lu",
++	snprintf (audit_buf, sizeof(audit_buf), "changing new_gid=%lu",
+ 	          (unsigned long) gid);
+ 	audit_logger (AUDIT_CHGRP_ID, Prog,
+ 	              audit_buf, NULL,
+-	              (unsigned int) getuid (), 1);
++	              (unsigned int) getuid (), SHADOW_AUDIT_SUCCESS);
+ #endif
+ 	/*
+ 	 * Exec the login shell and go away. We are trying to get back to
+@@ -831,15 +843,24 @@ int main (int argc, char **argv)
+ 	closelog ();
+ #ifdef WITH_AUDIT
+ 	if (NULL != group) {
+-		snprintf (audit_buf, sizeof(audit_buf),
+-		          "changing new-group=%s", group);
++		char enc_group[(GROUP_NAME_MAX_LENGTH*2)+1];
++		int len = strnlen(group, sizeof(enc_group)/2);
++		if (audit_value_needs_encoding(group, len)) {
++			snprintf (audit_buf, sizeof(audit_buf),
++			          "changing new_group=%s",
++			          audit_encode_value(enc_group,
++			          group, len));
++		} else {
++			snprintf (audit_buf, sizeof(audit_buf),
++			          "changing new_group=\"%s\"", group);
++		}
+ 		audit_logger (AUDIT_CHGRP_ID, Prog, 
+ 		              audit_buf, NULL,
+-		              (unsigned int) getuid (), 0);
++		              (unsigned int) getuid (), SHADOW_AUDIT_FAILURE);
+ 	} else {
+ 		audit_logger (AUDIT_CHGRP_ID, Prog,
+ 		              "changing", NULL,
+-		              (unsigned int) getuid (), 0);
++		              (unsigned int) getuid (), SHADOW_AUDIT_FAILURE);
+ 	}
+ #endif
+ 	exit (EXIT_FAILURE);
+diff -urp shadow-4.1.5.1.orig/src/useradd.c shadow-4.1.5.1/src/useradd.c
+--- shadow-4.1.5.1.orig/src/useradd.c	2014-09-13 15:45:54.957829561 -0400
++++ shadow-4.1.5.1/src/useradd.c	2014-10-14 08:52:53.066800605 -0400
+@@ -205,6 +205,8 @@ static void create_mail (void);
+  */
+ static void fail_exit (int code)
+ {
++	int type;
++
+ 	if (home_added) {
+ 		if (rmdir (user_home) != 0) {
+ 			fprintf (stderr,
+@@ -218,12 +220,6 @@ static void fail_exit (int code)
+ 		if (spw_unlock () == 0) {
+ 			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+ 			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "unlocking shadow file",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			/* continue */
+ 		}
+ 	}
+@@ -231,12 +227,6 @@ static void fail_exit (int code)
+ 		if (pw_unlock () == 0) {
+ 			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+ 			SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "unlocking passwd file",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			/* continue */
+ 		}
+ 	}
+@@ -244,12 +234,6 @@ static void fail_exit (int code)
+ 		if (gr_unlock () == 0) {
+ 			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
+ 			SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "unlocking group file",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			/* continue */
+ 		}
+ 	}
+@@ -258,20 +242,19 @@ static void fail_exit (int code)
+ 		if (sgr_unlock () == 0) {
+ 			fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
+ 			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "unlocking gshadow file",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			/* continue */
+ 		}
+ 	}
+ #endif
+ 
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_ADD_USER, Prog,
+-	              "adding user",
++	if (code == E_PW_UPDATE || code >= E_GRP_UPDATE)
++		type = AUDIT_USER_MGMT;
++	else
++		type = AUDIT_ADD_USER;
++
++	audit_logger (type, Prog,
++	              "add-user",
+ 	              user_name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -578,7 +561,7 @@ static int set_defaults (void)
+ 	}
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_USYS_CONFIG, Prog,
+-	              "changing useradd defaults",
++	              "changing-useradd-defaults",
+ 	              NULL, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -848,12 +831,6 @@ static void grp_update (void)
+ 			         _("%s: Out of memory. Cannot update %s.\n"),
+ 			         Prog, gr_dbname ());
+ 			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding user to group",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			fail_exit (E_GRP_UPDATE);	/* XXX */
+ 		}
+ 
+@@ -867,18 +844,12 @@ static void grp_update (void)
+ 			         _("%s: failed to prepare the new %s entry '%s'\n"),
+ 			         Prog, gr_dbname (), ngrp->gr_name);
+ 			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding user to group",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			fail_exit (E_GRP_UPDATE);
+ 		}
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_ADD_USER, Prog,
+-		              "adding user to group",
+-		              user_name, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "add-user-to-group",
++		              user_name, AUDIT_NO_ID, ngrp->gr_name,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -923,12 +894,6 @@ static void grp_update (void)
+ 			         _("%s: Out of memory. Cannot update %s.\n"),
+ 			         Prog, sgr_dbname ());
+ 			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding user to shadow group",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			fail_exit (E_GRP_UPDATE);	/* XXX */
+ 		}
+ 
+@@ -942,18 +907,13 @@ static void grp_update (void)
+ 			         _("%s: failed to prepare the new %s entry '%s'\n"),
+ 			         Prog, sgr_dbname (), nsgrp->sg_name);
+ 			SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name));
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding user to shadow group",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
++
+ 			fail_exit (E_GRP_UPDATE);
+ 		}
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_ADD_USER, Prog,
+-		              "adding user to shadow group",
+-		              user_name, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "add-to-shadow-group",
++		              user_name, AUDIT_NO_ID, nsgrp->sg_name,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -1296,7 +1256,7 @@ static void process_flags (int argc, cha
+ 			         Prog, user_name);
+ #ifdef WITH_AUDIT
+ 			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding user",
++			              "add-user",
+ 			              user_name, AUDIT_NO_ID,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -1385,7 +1345,7 @@ static void close_files (void)
+ 			SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+ #ifdef WITH_AUDIT
+ 			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "unlocking shadow file",
++			              "unlocking-shadow-file",
+ 			              user_name, AUDIT_NO_ID,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -1398,7 +1358,7 @@ static void close_files (void)
+ 		SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_ADD_USER, Prog,
+-		              "unlocking passwd file",
++		              "unlocking-passwd-file",
+ 		              user_name, AUDIT_NO_ID,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -1410,7 +1370,7 @@ static void close_files (void)
+ 		SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_ADD_USER, Prog,
+-		              "unlocking group file",
++		              "unlocking-group-file",
+ 		              user_name, AUDIT_NO_ID,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -1424,7 +1384,7 @@ static void close_files (void)
+ 			SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
+ #ifdef WITH_AUDIT
+ 			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "unlocking gshadow file",
++			              "unlocking-gshadow-file",
+ 			              user_name, AUDIT_NO_ID,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -1584,7 +1544,7 @@ static void grp_add (void)
+ 		         Prog, gr_dbname (), grp.gr_name);
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_ADD_GROUP, Prog,
+-		              "adding group",
++		              "add-group",
+ 		              grp.gr_name, AUDIT_NO_ID,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -1600,7 +1560,7 @@ static void grp_add (void)
+ 		         Prog, sgr_dbname (), sgrp.sg_name);
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_ADD_GROUP, Prog,
+-		              "adding group",
++		              "add-group",
+ 		              grp.gr_name, AUDIT_NO_ID,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif
+@@ -1610,7 +1570,7 @@ static void grp_add (void)
+ 	SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u", user_name, user_gid));
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_ADD_GROUP, Prog,
+-	              "adding group",
++	              "add-group",
+ 	              grp.gr_name, AUDIT_NO_ID,
+ 	              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -1725,17 +1685,11 @@ static void usr_update (void)
+ 		fprintf (stderr,
+ 		         _("%s: failed to prepare the new %s entry '%s'\n"),
+ 		         Prog, spw_dbname (), spent.sp_namp);
+-#ifdef WITH_AUDIT
+-		audit_logger (AUDIT_ADD_USER, Prog,
+-		              "adding shadow password",
+-		              user_name, (unsigned int) user_id,
+-		              SHADOW_AUDIT_FAILURE);
+-#endif
+ 		fail_exit (E_PW_UPDATE);
+ 	}
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_ADD_USER, Prog,
+-	              "adding user",
++	              "add-user",
+ 	              user_name, (unsigned int) user_id,
+ 	              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -1771,12 +1725,6 @@ static void create_home (void)
+ 			fprintf (stderr,
+ 			         _("%s: cannot create directory %s\n"),
+ 			         Prog, user_home);
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding home directory",
+-			              user_name, (unsigned int) user_id,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			fail_exit (E_HOMEDIR);
+ 		}
+ 		chown (user_home, user_id, user_gid);
+@@ -1784,8 +1732,8 @@ static void create_home (void)
+ 		       0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));
+ 		home_added = true;
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_ADD_USER, Prog,
+-		              "adding home directory",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "add-home-dir",
+ 		              user_name, (unsigned int) user_id,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif
+@@ -1951,12 +1899,6 @@ int main (int argc, char **argv)
+ 	 */
+ 	if (getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */
+ 		fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name);
+-#ifdef WITH_AUDIT
+-		audit_logger (AUDIT_ADD_USER, Prog,
+-		              "adding user",
+-		              user_name, AUDIT_NO_ID,
+-		              SHADOW_AUDIT_FAILURE);
+-#endif
+ 		fail_exit (E_NAME_IN_USE);
+ 	}
+ 
+@@ -1972,12 +1914,6 @@ int main (int argc, char **argv)
+ 			fprintf (stderr,
+ 			         _("%s: group %s exists - if you want to add this user to that group, use -g.\n"),
+ 			         Prog, user_name);
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding group",
+-			              user_name, AUDIT_NO_ID,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif
+ 			fail_exit (E_NAME_IN_USE);
+ 		}
+ 	}
+@@ -2007,12 +1943,6 @@ int main (int argc, char **argv)
+ 				fprintf (stderr,
+ 				         _("%s: UID %lu is not unique\n"),
+ 				         Prog, (unsigned long) user_id);
+-#ifdef WITH_AUDIT
+-				audit_logger (AUDIT_ADD_USER, Prog,
+-				              "adding user",
+-				              user_name, (unsigned int) user_id,
+-				              SHADOW_AUDIT_FAILURE);
+-#endif
+ 				fail_exit (E_UID_IN_USE);
+ 			}
+ 		}
+@@ -2057,9 +1987,10 @@ int main (int argc, char **argv)
+ 			         _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
+ 			         Prog, user_name, user_selinux);
+ #ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "adding SELinux user mapping",
+-			              user_name, (unsigned int) user_id, 0);
++			audit_logger (AUDIT_ROLE_ASSIGN, Prog,
++			              "add-selinux-user-mapping",
++			              user_name, (unsigned int) user_id,
++			              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+ 			rv = E_SE_UPDATE;
+ 		    }
+diff -urp shadow-4.1.5.1.orig/src/userdel.c shadow-4.1.5.1/src/userdel.c
+--- shadow-4.1.5.1.orig/src/userdel.c	2014-09-13 15:45:55.001829558 -0400
++++ shadow-4.1.5.1/src/userdel.c	2014-10-14 08:44:52.714850149 -0400
+@@ -201,9 +201,9 @@ static void update_groups (void)
+ 		 * Update the DBM group file with the new entry as well.
+ 		 */
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "deleting user from group",
+-		              user_name, (unsigned int) user_id,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "deleting-user-from-group",
++		              user_name, (unsigned int) user_id, ngrp->gr_name,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif				/* WITH_AUDIT */
+ 		SYSLOG ((LOG_INFO, "delete '%s' from group '%s'\n",
+@@ -263,9 +263,9 @@ static void update_groups (void)
+ 			exit (E_GRP_UPDATE);
+ 		}
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "deleting user from shadow group",
+-		              user_name, (unsigned int) user_id,
++		audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++		              "deleting-user-from-shadow-group",
++		              user_name, (unsigned int) user_id, nsgrp->sg_name,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif				/* WITH_AUDIT */
+ 		SYSLOG ((LOG_INFO, "delete '%s' from shadow group '%s'\n",
+@@ -342,9 +342,9 @@ static void remove_usergroup (void)
+ 		}
+ 
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_GROUP, Prog,
+-		              "deleting group",
+-		              user_name, AUDIT_NO_ID,
++		audit_logger_with_group (AUDIT_DEL_GROUP, Prog,
++		              "delete-group",
++		              user_name, AUDIT_NO_ID, user_name,
+ 		              SHADOW_AUDIT_SUCCESS);
+ #endif				/* WITH_AUDIT */
+ 		SYSLOG ((LOG_INFO,
+@@ -360,9 +360,9 @@ static void remove_usergroup (void)
+ 				fail_exit (E_GRP_UPDATE);
+ 			}
+ #ifdef WITH_AUDIT
+-			audit_logger (AUDIT_DEL_GROUP, Prog,
+-			              "deleting shadow group",
+-			              user_name, AUDIT_NO_ID,
++			audit_logger_with_group (AUDIT_GRP_MGMT, Prog,
++			              "delete-shadow-group",
++			              user_name, AUDIT_NO_ID, user_name,
+ 			              SHADOW_AUDIT_SUCCESS);
+ #endif				/* WITH_AUDIT */
+ 			SYSLOG ((LOG_INFO,
+@@ -478,7 +478,7 @@ static void fail_exit (int code)
+ 
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_DEL_USER, Prog,
+-	              "deleting user",
++	              "delete-user",
+ 	              user_name, (unsigned int) user_id,
+ 	              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -498,24 +498,12 @@ static void open_files (void)
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+ 		         Prog, pw_dbname ());
+-#ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "locking password file",
+-		              user_name, (unsigned int) user_id,
+-		              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 		fail_exit (E_PW_UPDATE);
+ 	}
+ 	pw_locked = true;
+ 	if (pw_open (O_RDWR) == 0) {
+ 		fprintf (stderr,
+ 		         _("%s: cannot open %s\n"), Prog, pw_dbname ());
+-#ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "opening password file",
+-		              user_name, (unsigned int) user_id,
+-		              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 		fail_exit (E_PW_UPDATE);
+ 	}
+ 	if (is_shadow_pwd) {
+@@ -523,12 +511,6 @@ static void open_files (void)
+ 			fprintf (stderr,
+ 			         _("%s: cannot lock %s; try again later.\n"),
+ 			         Prog, spw_dbname ());
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "locking shadow password file",
+-			              user_name, (unsigned int) user_id,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 			fail_exit (E_PW_UPDATE);
+ 		}
+ 		spw_locked = true;
+@@ -536,12 +518,6 @@ static void open_files (void)
+ 			fprintf (stderr,
+ 			         _("%s: cannot open %s\n"),
+ 			         Prog, spw_dbname ());
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "opening shadow password file",
+-			              user_name, (unsigned int) user_id,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 			fail_exit (E_PW_UPDATE);
+ 		}
+ 	}
+@@ -549,23 +525,11 @@ static void open_files (void)
+ 		fprintf (stderr,
+ 		         _("%s: cannot lock %s; try again later.\n"),
+ 		         Prog, gr_dbname ());
+-#ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "locking group file",
+-		              user_name, (unsigned int) user_id,
+-		              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 		fail_exit (E_GRP_UPDATE);
+ 	}
+ 	gr_locked = true;
+ 	if (gr_open (O_RDWR) == 0) {
+ 		fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
+-#ifdef WITH_AUDIT
+-		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "opening group file",
+-		              user_name, (unsigned int) user_id,
+-		              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 		fail_exit (E_GRP_UPDATE);
+ 	}
+ #ifdef	SHADOWGRP
+@@ -574,24 +538,12 @@ static void open_files (void)
+ 			fprintf (stderr,
+ 			         _("%s: cannot lock %s; try again later.\n"),
+ 			         Prog, sgr_dbname ());
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "locking shadow group file",
+-			              user_name, (unsigned int) user_id,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 			fail_exit (E_GRP_UPDATE);
+ 		}
+ 		sgr_locked= true;
+ 		if (sgr_open (O_RDWR) == 0) {
+ 			fprintf (stderr, _("%s: cannot open %s\n"),
+ 			         Prog, sgr_dbname ());
+-#ifdef WITH_AUDIT
+-			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "opening shadow group file",
+-			              user_name, (unsigned int) user_id,
+-			              SHADOW_AUDIT_FAILURE);
+-#endif				/* WITH_AUDIT */
+ 			fail_exit (E_GRP_UPDATE);
+ 		}
+ 	}
+@@ -622,7 +574,7 @@ static void update_user (void)
+ 	}
+ #ifdef WITH_AUDIT
+ 	audit_logger (AUDIT_DEL_USER, Prog,
+-	              "deleting user entries",
++	              "delete-user",
+ 	              user_name, (unsigned int) user_id,
+ 	              SHADOW_AUDIT_SUCCESS);
+ #endif				/* WITH_AUDIT */
+@@ -716,7 +668,7 @@ static int remove_mailbox (void)
+ 			SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
+ #ifdef WITH_AUDIT
+ 			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "deleting mail file",
++			              "delete-mail-file",
+ 			              user_name, (unsigned int) user_id,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -732,7 +684,7 @@ static int remove_mailbox (void)
+ 			SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
+ #ifdef WITH_AUDIT
+ 			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "deleting mail file",
++			              "delete-mail-file",
+ 			              user_name, (unsigned int) user_id,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -742,8 +694,8 @@ static int remove_mailbox (void)
+ #ifdef WITH_AUDIT
+ 		else
+ 		{
+-			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "deleting mail file",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "delete-mail-file",
+ 			              user_name, (unsigned int) user_id,
+ 			              SHADOW_AUDIT_SUCCESS);
+ 		}
+@@ -760,7 +712,7 @@ static int remove_mailbox (void)
+ 		         mailfile, strerror (errno)));
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "deleting mail file",
++		              "delete-mail-file",
+ 		              user_name, (unsigned int) user_id,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -775,7 +727,7 @@ static int remove_mailbox (void)
+ 		SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "deleting mail file",
++		              "delete-mail-file",
+ 		              user_name, (unsigned int) user_id,
+ 		              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -785,8 +737,8 @@ static int remove_mailbox (void)
+ #ifdef WITH_AUDIT
+ 	else
+ 	{
+-		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "deleting mail file",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "delete-mail-file",
+ 		              user_name, (unsigned int) user_id,
+ 		              SHADOW_AUDIT_SUCCESS);
+ 	}
+@@ -980,7 +932,7 @@ int main (int argc, char **argv)
+ 				 Prog, user_name);
+ #ifdef WITH_AUDIT
+ 			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "deleting user not found",
++			              "deleting-user-not-found",
+ 			              user_name, AUDIT_NO_ID,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -1024,7 +976,7 @@ int main (int argc, char **argv)
+ 		if (!fflg) {
+ #ifdef WITH_AUDIT
+ 			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "deleting user logged in",
++			              "deleting-user-logged-in",
+ 			              user_name, AUDIT_NO_ID,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -1101,8 +1053,8 @@ int main (int argc, char **argv)
+ #ifdef WITH_AUDIT
+ 		else
+ 		{
+-			audit_logger (AUDIT_DEL_USER, Prog,
+-			              "deleting home directory",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "deleting-home-directory",
+ 			              user_name, (unsigned int) user_id,
+ 			              SHADOW_AUDIT_SUCCESS);
+ 		}
+@@ -1111,7 +1063,7 @@ int main (int argc, char **argv)
+ #ifdef WITH_AUDIT
+ 	if (0 != errors) {
+ 		audit_logger (AUDIT_DEL_USER, Prog,
+-		              "deleting home directory",
++		              "deleting-home-directory",
+ 		              user_name, AUDIT_NO_ID,
+ 		              SHADOW_AUDIT_FAILURE);
+ 	}
+@@ -1124,8 +1076,8 @@ int main (int argc, char **argv)
+ 			         _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"),
+ 			         Prog, user_name);
+ #ifdef WITH_AUDIT
+-			audit_logger (AUDIT_ADD_USER, Prog,
+-			              "removing SELinux user mapping",
++			audit_logger (AUDIT_ROLE_REMOVE, Prog,
++			              "delete-selinux-user-mapping",
+ 			              user_name, (unsigned int) user_id,
+ 			              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+diff -urp shadow-4.1.5.1.orig/src/usermod.c shadow-4.1.5.1/src/usermod.c
+--- shadow-4.1.5.1.orig/src/usermod.c	2014-09-13 15:45:55.013829557 -0400
++++ shadow-4.1.5.1/src/usermod.c	2014-10-14 08:50:05.817817855 -0400
+@@ -352,8 +352,8 @@ static char *new_pw_passwd (char *pw_pas
+ 
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "updating passwd",
+-		              user_newname, (unsigned int) user_newid, 0);
++		              "updating-password",
++		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO, "lock user '%s' password", user_newname));
+ 		strcpy (buf, "!");
+@@ -372,8 +372,8 @@ static char *new_pw_passwd (char *pw_pas
+ 
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "updating password",
+-		              user_newname, (unsigned int) user_newid, 0);
++		              "updating-password",
++		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname));
+ 		s = pw_pass;
+@@ -384,7 +384,7 @@ static char *new_pw_passwd (char *pw_pas
+ 	} else if (pflg) {
+ #ifdef WITH_AUDIT
+ 		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing password",
++		              "updating-password",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO, "change user '%s' password", user_newname));
+@@ -413,8 +413,8 @@ static void new_pwent (struct passwd *pw
+ 			fail_exit (E_NAME_IN_USE);
+ 		}
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing name",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-name",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -434,8 +434,8 @@ static void new_pwent (struct passwd *pw
+ 
+ 	if (uflg) {
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing uid",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-uid",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -445,8 +445,8 @@ static void new_pwent (struct passwd *pw
+ 	}
+ 	if (gflg) {
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing primary group",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-primary-group",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -456,8 +456,8 @@ static void new_pwent (struct passwd *pw
+ 	}
+ 	if (cflg) {
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing comment",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-comment",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		pwent->pw_gecos = user_newcomment;
+@@ -465,8 +465,8 @@ static void new_pwent (struct passwd *pw
+ 
+ 	if (dflg) {
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing home directory",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-home-dir",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -476,8 +476,8 @@ static void new_pwent (struct passwd *pw
+ 	}
+ 	if (sflg) {
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing user shell",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-shell",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -507,8 +507,8 @@ static void new_spent (struct spwd *spen
+ 
+ 	if (fflg) {
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing inactive days",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-inactive-days",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -524,8 +524,8 @@ static void new_spent (struct spwd *spen
+ 		date_to_str (old_exp, sizeof(old_exp),
+ 		             user_expire * DAY);
+ #ifdef WITH_AUDIT
+-		audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-		              "changing expiration date",
++		audit_logger (AUDIT_USER_MGMT, Prog,
++		              "changing-expiration-date",
+ 		              user_newname, (unsigned int) user_newid, 1);
+ #endif
+ 		SYSLOG ((LOG_INFO,
+@@ -592,9 +592,9 @@ static /*@noreturn@*/void fail_exit (int
+ 	}
+ 
+ #ifdef WITH_AUDIT
+-	audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-	              "modifying account",
+-	              user_name, AUDIT_NO_ID, 0);
++	audit_logger (AUDIT_USER_MGMT, Prog,
++	              "modify-account",
++	              user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE);
+ #endif
+ 	exit (code);
+ }
+@@ -648,9 +648,12 @@ static void update_group (void)
+ 					                         user_newname);
+ 					changed = true;
+ #ifdef WITH_AUDIT
+-					audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-					              "changing group member",
+-					              user_newname, AUDIT_NO_ID, 1);
++					audit_logger_with_group (
++					              AUDIT_USER_MGMT, Prog,
++					              "update-member-in-group",
++					              user_newname, AUDIT_NO_ID,
++					              ngrp->gr_name,
++					              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 					SYSLOG ((LOG_INFO,
+ 					         "change '%s' to '%s' in group '%s'",
+@@ -664,9 +667,11 @@ static void update_group (void)
+ 				ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
+ 				changed = true;
+ #ifdef WITH_AUDIT
+-				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-				              "removing group member",
+-				              user_name, AUDIT_NO_ID, 1);
++				audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++				              "delete-user-from-group",
++				              user_name, AUDIT_NO_ID,
++				              ngrp->gr_name,
++				              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 				SYSLOG ((LOG_INFO,
+ 				         "delete '%s' from group '%s'",
+@@ -679,9 +684,11 @@ static void update_group (void)
+ 			ngrp->gr_mem = add_list (ngrp->gr_mem, user_newname);
+ 			changed = true;
+ #ifdef WITH_AUDIT
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "adding user to group",
+-			              user_name, AUDIT_NO_ID, 1);
++			audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++			              "add-user-to-group",
++			              user_name, AUDIT_NO_ID,
++			              ngrp->gr_name,
++			              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 			SYSLOG ((LOG_INFO, "add '%s' to group '%s'",
+ 			         user_newname, ngrp->gr_name));
+@@ -756,9 +763,10 @@ static void update_gshadow (void)
+ 			nsgrp->sg_adm = add_list (nsgrp->sg_adm, user_newname);
+ 			changed = true;
+ #ifdef WITH_AUDIT
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "changing admin name in shadow group",
+-			              user_name, AUDIT_NO_ID, 1);
++			audit_logger_with_group (AUDIT_GRP_MGMT, Prog,
++			              "update-admin-name-in-group",
++			              user_name, AUDIT_NO_ID, nsgrp->sg_name,
++			              SHADOW_AUDIT_SUCCESS);
+ #endif
+ 			SYSLOG ((LOG_INFO,
+ 			         "change admin '%s' to '%s' in shadow group '%s'",
+@@ -778,9 +786,10 @@ static void update_gshadow (void)
+ 					                          user_newname);
+ 					changed = true;
+ #ifdef WITH_AUDIT
+-					audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-					              "changing member in shadow group",
+-					              user_name, AUDIT_NO_ID, 1);
++					audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++					              "update-member-in-group",
++					              user_name, AUDIT_NO_ID,
++					              nsgrp->sg_name, 1);
+ #endif
+ 					SYSLOG ((LOG_INFO,
+ 					         "change '%s' to '%s' in shadow group '%s'",
+@@ -794,9 +803,10 @@ static void update_gshadow (void)
+ 				nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
+ 				changed = true;
+ #ifdef WITH_AUDIT
+-				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-				              "removing user from shadow group",
+-				              user_name, AUDIT_NO_ID, 1);
++				audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++				              "delete-user-from-group",
++				              user_name, AUDIT_NO_ID,
++				              nsgrp->sg_name, 1);
+ #endif
+ 				SYSLOG ((LOG_INFO,
+ 				         "delete '%s' from shadow group '%s'",
+@@ -809,9 +819,10 @@ static void update_gshadow (void)
+ 			nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_newname);
+ 			changed = true;
+ #ifdef WITH_AUDIT
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "adding user to shadow group",
+-			              user_newname, AUDIT_NO_ID, 1);
++			audit_logger_with_group (AUDIT_USER_MGMT, Prog,
++			              "add-user-to-group",
++			              user_newname, AUDIT_NO_ID,
++			              nsgrp->sg_name, 1);
+ #endif
+ 			SYSLOG ((LOG_INFO, "add '%s' to shadow group '%s'",
+ 			         user_newname, nsgrp->sg_name));
+@@ -1515,8 +1526,8 @@ static void move_home (void)
+ 
+ #ifdef WITH_AUDIT
+ 		if (uflg || gflg) {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-				      "changing home directory owner",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++				      "updating-home-dir-owner",
+ 				      user_newname, (unsigned int) user_newid, 1);
+ 		}
+ #endif
+@@ -1534,8 +1545,8 @@ static void move_home (void)
+ 				fail_exit (E_HOMEDIR);
+ 			}
+ #ifdef WITH_AUDIT
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "moving home directory",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "moving-home-dir",
+ 			              user_newname, (unsigned int) user_newid,
+ 			              1);
+ #endif
+@@ -1554,9 +1565,9 @@ static void move_home (void)
+ 						         Prog, user_home);
+ 					}
+ #ifdef WITH_AUDIT
+-					audit_logger (AUDIT_USER_CHAUTHTOK,
++					audit_logger (AUDIT_USER_MGMT,
+ 					              Prog,
+-					              "moving home directory",
++					              "moving-home-dir",
+ 					              user_newname,
+ 					              (unsigned int) user_newid,
+ 					              1);
+@@ -1760,8 +1771,8 @@ static void move_mailbox (void)
+ 		}
+ #ifdef WITH_AUDIT
+ 		else {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "changing mail file owner",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "updating-mail-file-owner",
+ 			              user_newname, (unsigned int) user_newid, 1);
+ 		}
+ #endif
+@@ -1779,8 +1790,8 @@ static void move_mailbox (void)
+ 		}
+ #ifdef WITH_AUDIT
+ 		else {
+-			audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-			              "changing mail file name",
++			audit_logger (AUDIT_USER_MGMT, Prog,
++			              "updating-mail-file-name",
+ 			              user_newname, (unsigned int) user_newid, 1);
+ 		}
+ #endif
+@@ -1910,8 +1921,8 @@ int main (int argc, char **argv)
+ 				         _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
+ 				         Prog, user_name, user_selinux);
+ #ifdef WITH_AUDIT
+-				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-				              "modifying User mapping ",
++				audit_logger (AUDIT_ROLE_ASSIGN, Prog,
++				              "changing-selinux-user-mapping ",
+ 				              user_name, (unsigned int) user_id,
+ 				              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -1923,8 +1934,8 @@ int main (int argc, char **argv)
+ 				         _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"),
+ 				         Prog, user_name);
+ #ifdef WITH_AUDIT
+-				audit_logger (AUDIT_ADD_USER, Prog,
+-				              "removing SELinux user mapping",
++				audit_logger (AUDIT_ROLE_REMOVE, Prog,
++				              "delete-selinux-user-mapping",
+ 				              user_name, (unsigned int) user_id,
+ 				              SHADOW_AUDIT_FAILURE);
+ #endif				/* WITH_AUDIT */
+@@ -1962,8 +1973,8 @@ int main (int argc, char **argv)
+ 			 */
+ #ifdef WITH_AUDIT
+ 			if (uflg || gflg) {
+-				audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
+-					      "changing home directory owner",
++				audit_logger (AUDIT_USER_MGMT, Prog,
++					      "updating-home-dir-owner",
+ 					      user_newname, (unsigned int) user_newid, 1);
+ 			}
+ #endif
diff --git a/shadow-4.1.5.1-id-alloc.patch b/shadow-4.1.5.1-id-alloc.patch
new file mode 100644
index 0000000..df6f89f
--- /dev/null
+++ b/shadow-4.1.5.1-id-alloc.patch
@@ -0,0 +1,1219 @@
+Previously, this allocation was optimized for an outdated
+deployment style (that of /etc/group alongside nss_db). The issue
+here is that this results in extremely poor performance when using
+SSSD, Winbind or nss_ldap.
+
+There were actually three serious bugs here that have been addressed:
+
+1) Running getgrent() loops won't work in most SSSD or Winbind
+environments, as full group enumeration is disabled by default.
+This could easily result in auto-allocating a group that was
+already in use. (This might result in a security issue as well, if
+the shared GID is a privileged group).
+
+2) For system groups, the loop was always iterating through the
+complete SYS_GID_MIN->SYS_GID_MAX range. On SSSD and Winbind, this
+means hundreds of round-trips to LDAP (unless the GIDs were
+specifically configured to be ignored by the SSSD or winbindd).
+To a user with a slow connection to their LDAP server, this would
+appear as if groupadd -r was hung. (Though it would eventually
+complete).
+
+3) This patch also adds better error-handling for errno from
+getgrgid(), since if this function returns an unexpected error, we
+should not be treating it as "ID is available". This could result
+in assigning a GID that was already in use, with all the same
+issues as 1) above.
+
+This patch changes the algorithm to be more favorable for LDAP
+environments, at the expense of some performance when using nss_db.
+Given that the DB is a local service, this should have a negligible
+effect from a user's perspective.
+
+With the new algorithm, we simply first iterate through all entries
+in the local database with gr_next(), recording the IDs that are in
+use. We then start from the highest presumed-available entry and
+call getgrgid() to see if it is available. We continue this until
+we come to the first unused GID. We then select that and return it.
+
+If we make it through all the remaining IDs without finding a free
+one, we start over from the beginning of the range and try to find
+room in one of the gaps in the range.
+
+The patch was originally written by Stephen Gallagher and applied
+identically also to the user allocation by Tomáš Mráz.
+
+diff -up shadow-4.1.5.1/libmisc/find_new_gid.c.id-alloc shadow-4.1.5.1/libmisc/find_new_gid.c
+--- shadow-4.1.5.1/libmisc/find_new_gid.c.id-alloc	2014-09-10 10:25:41.165524986 +0200
++++ shadow-4.1.5.1/libmisc/find_new_gid.c	2014-09-10 10:25:41.195525677 +0200
+@@ -39,6 +39,118 @@
+ #include "getdef.h"
+ 
+ /*
++ * get_ranges - Get the minimum and maximum ID ranges for the search
++ *
++ * This function will return the minimum and maximum ranges for IDs
++ *
++ * 0: The function completed successfully
++ * EINVAL: The provided ranges are impossible (such as maximum < minimum)
++ *
++ * preferred_min: The special-case minimum value for a specifically-
++ * requested ID, which may be lower than the standard min_id
++ */
++static int get_ranges(bool sys_group, gid_t *min_id, gid_t *max_id,
++	gid_t *preferred_min)
++{
++	gid_t gid_def_max = 0;
++
++	if (sys_group) {
++		/* System groups */
++
++		/* A requested ID is allowed to be below the autoselect range */
++		*preferred_min = (gid_t) 1;
++
++		/* Get the minimum ID range from login.defs or default to 101 */
++		*min_id = (gid_t) getdef_ulong("SYS_GID_MIN", 101UL);
++
++		/*
++		 * If SYS_GID_MAX is unspecified, we should assume it to be one
++		 * less than the GID_MIN (which is reserved for non-system accounts)
++		 */
++		gid_def_max = (gid_t) getdef_ulong("GID_MIN", 1000UL) - 1;
++		*max_id = (gid_t) getdef_ulong("SYS_GID_MAX",
++				(unsigned long) gid_def_max);
++
++		/* Check that the ranges make sense */
++		if (*max_id < *min_id) {
++			(void) fprintf (stderr,
++                            _("%s: Invalid configuration: SYS_GID_MIN (%lu), "
++                              "GID_MIN (%lu), SYS_GID_MAX (%lu)\n"),
++                            Prog, (unsigned long) *min_id,
++                            getdef_ulong ("GID_MIN", 1000UL),
++                            (unsigned long) *max_id);
++			return EINVAL;
++		}
++	} else {
++		/* Non-system groups */
++
++		/* Get the values from login.defs or use reasonable defaults */
++		*min_id = (gid_t) getdef_ulong("GID_MIN", 1000UL);
++		*max_id = (gid_t) getdef_ulong("GID_MAX", 60000UL);
++
++		/*
++		 * The preferred minimum should match the standard ID minimum
++		 * for non-system groups.
++		 */
++		*preferred_min = *min_id;
++
++		/* Check that the ranges make sense */
++		if (*max_id < *min_id) {
++			(void) fprintf(stderr,
++					_("%s: Invalid configuration: GID_MIN (%lu), "
++					  "GID_MAX (%lu)\n"),
++					Prog, (unsigned long) *min_id,
++					(unsigned long) *max_id);
++			return EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++/*
++ * check_gid - See if the requested GID is available
++ *
++ * On success, return 0
++ * If the ID is in use, return EEXIST
++ * If the ID is outside the range, return ERANGE
++ * In other cases, return errno from getgrgid()
++ */
++static int check_gid(const gid_t gid,
++		             const gid_t gid_min,
++		             const gid_t gid_max,
++		             bool *used_gids)
++{
++	/* First test that the preferred ID is in the range */
++	if (gid < gid_min || gid > gid_max) {
++		return ERANGE;
++	}
++
++	/*
++	 * Check whether we already detected this GID
++	 * using the gr_next() loop
++	 */
++	if (used_gids != NULL && used_gids[gid]) {
++		return EEXIST;
++	}
++	/* Check if the GID exists according to NSS */
++	errno = 0;
++	if (getgrgid(gid) != NULL) {
++		return EEXIST;
++	} else {
++		/* getgrgid() was NULL, check whether this was
++		 * due to an error, so we can report it.
++		 */
++		/* ignore errors for now * if (errno != 0) {
++			return errno;
++		} */
++	}
++
++	/* If we've made it here, the GID must be available */
++	return 0;
++}
++
++/*
+  * find_new_gid - Find a new unused GID.
+  *
+  * If successful, find_new_gid provides an unused group ID in the
+@@ -48,166 +160,339 @@
+  * 
+  * Return 0 on success, -1 if no unused GIDs are available.
+  */
+-int find_new_gid (bool sys_group,
+-                  gid_t *gid,
+-                  /*@null@*/gid_t const *preferred_gid)
++int find_new_gid(bool sys_group,
++                 gid_t *gid,
++                 /*@null@*/gid_t const *preferred_gid)
+ {
+-	const struct group *grp;
+-	gid_t gid_min, gid_max, group_id;
+ 	bool *used_gids;
++	const struct group *grp;
++	gid_t gid_min, gid_max, preferred_min;
++	gid_t group_id, id;
++	gid_t lowest_found, highest_found;
++	int result;
++	int nospam = 0;
+ 
+-	assert (gid != NULL);
++	assert(gid != NULL);
+ 
+-	if (!sys_group) {
+-		gid_min = (gid_t) getdef_ulong ("GID_MIN", 1000UL);
+-		gid_max = (gid_t) getdef_ulong ("GID_MAX", 60000UL);
+-		if (gid_max < gid_min) {
+-			(void) fprintf (stderr,
+-			                _("%s: Invalid configuration: GID_MIN (%lu), GID_MAX (%lu)\n"),
+-			                Prog, (unsigned long) gid_min, (unsigned long) gid_max);
+-			return -1;
+-		}
+-	} else {
+-		gid_min = (gid_t) 1;
+-		gid_max = (gid_t) getdef_ulong ("GID_MIN", 1000UL) - 1;
+-		gid_max = (gid_t) getdef_ulong ("SYS_GID_MAX", (unsigned long) gid_max);
+-		if (gid_max < gid_min) {
+-			(void) fprintf (stderr,
+-			                _("%s: Invalid configuration: SYS_GID_MIN (%lu), GID_MIN (%lu), SYS_GID_MAX (%lu)\n"),
+-			                Prog, (unsigned long) gid_min, getdef_ulong ("GID_MIN", 1000UL), (unsigned long) gid_max);
++	/*
++	 * First, figure out what ID range is appropriate for
++	 * automatic assignment
++	 */
++	result = get_ranges(sys_group, &gid_min, &gid_max, &preferred_min);
++	if (result == EINVAL) {
++		return -1;
++	}
++
++	/* Check if the preferred GID is available */
++	if (preferred_gid) {
++		result = check_gid(*preferred_gid, preferred_min, gid_max, NULL);
++		if (result == 0) {
++			/*
++			 * Make sure the GID isn't queued for use already
++			 */
++			if (gr_locate_gid (*preferred_gid) == NULL) {
++				*gid = *preferred_gid;
++				return 0;
++			}
++			/*
++			 * gr_locate_gid() found the GID in an as-yet uncommitted
++			 * entry. We'll proceed below and auto-set a GID.
++			 */
++		} else if (result == EEXIST || result == ERANGE) {
++			/*
++			 * Continue on below. At this time, we won't
++			 * treat these two cases differently.
++			 */
++		} else {
++			/*
++			 * An unexpected error occurred. We should report
++			 * this and fail the group creation.
++			 * This differs from the automatic creation
++			 * behavior below, since if a specific GID was
++			 * requested and generated an error, the user is
++			 * more likely to want to stop and address the
++			 * issue.
++			 */
++			fprintf(stderr,
++					_("%s: Encountered error attempting to use "
++					  "preferred GID: %s\n"),
++					Prog, strerror(result));
+ 			return -1;
+ 		}
+ 	}
++
++	/*
++	 * Search the entire group file,
++	 * looking for the next unused value.
++	 *
++	 * We first check the local database with gr_rewind/gr_next to find
++	 * all local values that are in use.
++	 *
++	 * We then compare the next free value to all databases (local and
++	 * remote) and iterate until we find a free one. If there are free
++	 * values beyond the lowest (system groups) or highest (non-system
++	 * groups), we will prefer those and avoid potentially reclaiming a
++	 * deleted group (which can be a security issue, since it may grant
++	 * access to files belonging to that former group).
++	 *
++	 * If there are no GIDs available at the end of the search, we will
++	 * have no choice but to iterate through the range looking for gaps.
++	 *
++	 */
++
++	/* Create an array to hold all of the discovered GIDs */
+ 	used_gids = malloc (sizeof (bool) * (gid_max +1));
+ 	if (NULL == used_gids) {
+ 		fprintf (stderr,
+-		         _("%s: failed to allocate memory: %s\n"),
+-		         Prog, strerror (errno));
++				 _("%s: failed to allocate memory: %s\n"),
++				 Prog, strerror (errno));
+ 		return -1;
+ 	}
+ 	memset (used_gids, false, sizeof (bool) * (gid_max + 1));
+ 
+-	if (   (NULL != preferred_gid)
+-	    && (*preferred_gid >= gid_min)
+-	    && (*preferred_gid <= gid_max)
+-	    /* Check if the user exists according to NSS */
+-	    && (getgrgid (*preferred_gid) == NULL)
+-	    /* Check also the local database in case of uncommitted
+-	     * changes */
+-	    && (gr_locate_gid (*preferred_gid) == NULL)) {
+-		*gid = *preferred_gid;
+-		free (used_gids);
+-		return 0;
+-	}
+-
+-        /* if we did not find free preffered system gid, we start to look for
+-         * one in the range assigned to dynamic system IDs */
+-        if (sys_group)
+-                gid_min = (gid_t) getdef_ulong ("SYS_GID_MIN", 101UL);
++	/* First look for the lowest and highest value in the local database */
++	(void) gr_rewind ();
++	highest_found = gid_min;
++	lowest_found = gid_max;
++	while ((grp = gr_next ()) != NULL) {
++		/*
++		 * Does this entry have a lower GID than the lowest we've found
++		 * so far?
++		 */
++		if ((grp->gr_gid <= lowest_found) && (grp->gr_gid >= gid_min)) {
++			lowest_found = grp->gr_gid - 1;
++		}
++
++		/*
++		 * Does this entry have a higher GID than the highest we've found
++		 * so far?
++		 */
++		if ((grp->gr_gid >= highest_found) && (grp->gr_gid <= gid_max)) {
++			highest_found = grp->gr_gid + 1;
++		}
++
++		/* create index of used GIDs */
++		if (grp->gr_gid >= gid_min
++			&& grp->gr_gid <= gid_max) {
++
++			used_gids[grp->gr_gid] = true;
++		}
++	}
+ 
+-	/*
+-	 * Search the entire group file,
+-	 * looking for the largest unused value.
+-	 *
+-	 * We check the list of groups according to NSS (setgrent/getgrent),
+-	 * but we also check the local database (gr_rewind/gr_next) in case
+-	 * some groups were created but the changes were not committed yet.
+-	 */
+ 	if (sys_group) {
+-		gid_t id;
+-		/* setgrent / getgrent / endgrent can be very slow with
+-		 * LDAP configurations (and many accounts).
+-		 * Since there is a limited amount of IDs to be tested
+-		 * for system accounts, we just check the existence
+-		 * of IDs with getgrgid.
+-		 */
+-		group_id = gid_max;
+-		for (id = gid_max; id >= gid_min; id--) {
+-			if (getgrgid (id) != NULL) {
+-				group_id = id - 1;
+-				used_gids[id] = true;
+-			}
++		/*
++		 * For system groups, we want to start from the
++		 * top of the range and work downwards.
++		 */
++
++		/*
++		 * At the conclusion of the gr_next() search, we will either
++		 * have a presumed-free GID or we will be at GID_MIN - 1.
++		 */
++		if (lowest_found < gid_min) {
++			/*
++			 * In this case, a GID is in use at GID_MIN.
++			 *
++			 * We will reset the search to GID_MAX and proceed down
++			 * through all the GIDs (skipping those we detected with
++			 * used_gids) for a free one. It is a known issue that
++			 * this may result in reusing a previously-deleted GID,
++			 * so administrators should be instructed to use this
++			 * auto-detection with care (and prefer to assign GIDs
++			 * explicitly).
++			 */
++			lowest_found = gid_max;
+ 		}
+ 
+-		(void) gr_rewind ();
+-		while ((grp = gr_next ()) != NULL) {
+-			if ((grp->gr_gid <= group_id) && (grp->gr_gid >= gid_min)) {
+-				group_id = grp->gr_gid - 1;
+-			}
+-			/* create index of used GIDs */
+-			if (grp->gr_gid <= gid_max) {
+-				used_gids[grp->gr_gid] = true;
++		/* Search through all of the IDs in the range */
++		for (id = lowest_found; id >= gid_min; id--) {
++			result = check_gid(id, gid_min, gid_max, used_gids);
++			if (result == 0) {
++				/* This GID is available. Return it. */
++				*gid = id;
++				free(used_gids);
++				return 0;
++			} else if (result == EEXIST) {
++				/* This GID is in use, we'll continue to the next */
++			} else {
++				/*
++				 * An unexpected error occurred.
++				 *
++				 * Only report it the first time to avoid spamming
++				 * the logs
++				 *
++				 */
++				if (!nospam) {
++					fprintf(stderr,
++							_("%s: Can't get unique system GID (%s). "
++							  "Suppressing additional messages.\n"),
++							Prog, strerror(result));
++					SYSLOG((LOG_ERR,
++							"Error checking available GIDs: %s",
++							strerror(result)));
++					nospam = 1;
++				}
++				/*
++				 * We will continue anyway. Hopefully a later GID
++				 * will work properly.
++				 */
+ 			}
+ 		}
+-	} else {
+-		group_id = gid_min;
+-		setgrent ();
+-		while ((grp = getgrent ()) != NULL) {
+-			if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) {
+-				group_id = grp->gr_gid + 1;
+-			}
+-			/* create index of used GIDs */
+-			if (grp->gr_gid <= gid_max) {
+-				used_gids[grp->gr_gid] = true;
++
++		/*
++		 * If we get all the way through the loop, try again from GID_MAX,
++		 * unless that was where we previously started. (NOTE: the worst-case
++		 * scenario here is that we will run through (GID_MAX - GID_MIN - 1)
++		 * cycles *again* if we fall into this case with lowest_found as
++		 * GID_MAX - 1, all groups in the range in use and maintained by
++		 * network services such as LDAP.)
++		 */
++		if (lowest_found != gid_max) {
++			for (id = gid_max; id >= gid_min; id--) {
++				result = check_gid(id, gid_min, gid_max, used_gids);
++				if (result == 0) {
++					/* This GID is available. Return it. */
++					*gid = id;
++					free(used_gids);
++					return 0;
++				} else if (result == EEXIST) {
++					/* This GID is in use, we'll continue to the next */
++				} else {
++					/*
++					 * An unexpected error occurred.
++					 *
++					 * Only report it the first time to avoid spamming
++					 * the logs
++					 *
++					 */
++					if (!nospam) {
++						fprintf(stderr,
++								_("%s: Can't get unique system GID (%s). "
++								  "Suppressing additional messages.\n"),
++								Prog, strerror(result));
++						SYSLOG((LOG_ERR,
++								"Error checking available GIDs: %s",
++								strerror(result)));
++						nospam = 1;
++					}
++					/*
++					 * We will continue anyway. Hopefully a later GID
++					 * will work properly.
++					 */
++				}
+ 			}
+ 		}
+-		endgrent ();
++	} else { /* !sys_group */
++		/*
++		 * For non-system groups, we want to start from the
++		 * bottom of the range and work upwards.
++		 */
+ 
+-		(void) gr_rewind ();
+-		while ((grp = gr_next ()) != NULL) {
+-			if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) {
+-				group_id = grp->gr_gid + 1;
+-			}
+-			/* create index of used GIDs */
+-			if (grp->gr_gid <= gid_max) {
+-				used_gids[grp->gr_gid] = true;
+-			}
++		/*
++		 * At the conclusion of the gr_next() search, we will either
++		 * have a presumed-free GID or we will be at GID_MAX + 1.
++		 */
++		if (highest_found > gid_max) {
++			/*
++			 * In this case, a GID is in use at GID_MAX.
++			 *
++			 * We will reset the search to GID_MIN and proceed up
++			 * through all the GIDs (skipping those we detected with
++			 * used_gids) for a free one. It is a known issue that
++			 * this may result in reusing a previously-deleted GID,
++			 * so administrators should be instructed to use this
++			 * auto-detection with care (and prefer to assign GIDs
++			 * explicitly).
++			 */
++			highest_found = gid_min;
+ 		}
+-	}
+ 
+-	/*
+-	 * If a group (resp. system group) with GID equal to GID_MAX (resp.
+-	 * GID_MIN) exists, the above algorithm will give us GID_MAX+1
+-	 * (resp. GID_MIN-1) even if not unique. Search for the first free
+-	 * GID starting with GID_MIN (resp. GID_MAX).
+-	 */
+-	if (sys_group) {
+-		if (group_id < gid_min) {
+-			for (group_id = gid_max; group_id >= gid_min; group_id--) {
+-				if (false == used_gids[group_id]) {
+-					break;
++		/* Search through all of the IDs in the range */
++		for (id = highest_found; id <= gid_max; id++) {
++			result = check_gid(id, gid_min, gid_max, used_gids);
++			if (result == 0) {
++				/* This GID is available. Return it. */
++				*gid = id;
++				free(used_gids);
++				return 0;
++			} else if (result == EEXIST) {
++				/* This GID is in use, we'll continue to the next */
++			} else {
++				/*
++				 * An unexpected error occurred.
++				 *
++				 * Only report it the first time to avoid spamming
++				 * the logs
++				 *
++				 */
++				if (!nospam) {
++					fprintf(stderr,
++							_("%s: Can't get unique GID (%s). "
++							  "Suppressing additional messages.\n"),
++							Prog, strerror(result));
++					SYSLOG((LOG_ERR,
++							"Error checking available GIDs: %s",
++							strerror(result)));
++					nospam = 1;
+ 				}
+-			}
+-			if (group_id < gid_min) {
+-				fprintf (stderr,
+-				         _("%s: Can't get unique system GID (no more available GIDs)\n"),
+-				         Prog);
+-				SYSLOG ((LOG_WARN,
+-				         "no more available GID on the system"));
+-				free (used_gids);
+-				return -1;
++				/*
++				 * We will continue anyway. Hopefully a later GID
++				 * will work properly.
++				 */
+ 			}
+ 		}
+-	} else {
+-		if (group_id > gid_max) {
+-			for (group_id = gid_min; group_id <= gid_max; group_id++) {
+-				if (false == used_gids[group_id]) {
+-					break;
++
++		/*
++		 * If we get all the way through the loop, try again from GID_MIN,
++		 * unless that was where we previously started. (NOTE: the worst-case
++		 * scenario here is that we will run through (GID_MAX - GID_MIN - 1)
++		 * cycles *again* if we fall into this case with highest_found as
++		 * GID_MIN + 1, all groups in the range in use and maintained by
++		 * network services such as LDAP.)
++		 */
++		if (highest_found != gid_min) {
++			for (id = gid_min; id <= gid_max; id++) {
++				result = check_gid(id, gid_min, gid_max, used_gids);
++				if (result == 0) {
++					/* This GID is available. Return it. */
++					*gid = id;
++					free(used_gids);
++					return 0;
++				} else if (result == EEXIST) {
++					/* This GID is in use, we'll continue to the next */
++				} else {
++					/*
++					 * An unexpected error occurred.
++					 *
++					 * Only report it the first time to avoid spamming
++					 * the logs
++					 *
++					 */
++					if (!nospam) {
++						fprintf(stderr,
++								_("%s: Can't get unique GID (%s). "
++								  "Suppressing additional messages.\n"),
++								Prog, strerror(result));
++						SYSLOG((LOG_ERR,
++								"Error checking available GIDs: %s",
++								strerror(result)));
++						nospam = 1;
++					}
++					/*
++					 * We will continue anyway. Hopefully a later GID
++					 * will work properly.
++					 */
+ 				}
+ 			}
+-			if (group_id > gid_max) {
+-				fprintf (stderr,
+-				         _("%s: Can't get unique GID (no more available GIDs)\n"),
+-				         Prog);
+-				SYSLOG ((LOG_WARN, "no more available GID on the system"));
+-				free (used_gids);
+-				return -1;
+-			}
+ 		}
+ 	}
+ 
+-	free (used_gids);
+-	*gid = group_id;
+-	return 0;
++	/* The code reached here and found no available IDs in the range */
++	fprintf(stderr,
++			_("%s: Can't get unique GID (no more available GIDs)\n"),
++			Prog);
++	SYSLOG((LOG_WARN, "no more available GIDs on the system"));
++	free(used_gids);
++	return -1;
+ }
+ 
+diff -up shadow-4.1.5.1/libmisc/find_new_uid.c.id-alloc shadow-4.1.5.1/libmisc/find_new_uid.c
+--- shadow-4.1.5.1/libmisc/find_new_uid.c.id-alloc	2011-07-29 17:39:16.000000000 +0200
++++ shadow-4.1.5.1/libmisc/find_new_uid.c	2014-10-17 16:52:30.481217270 +0200
+@@ -39,6 +39,118 @@
+ #include "getdef.h"
+ 
+ /*
++ * get_ranges - Get the minimum and maximum ID ranges for the search
++ *
++ * This function will return the minimum and maximum ranges for IDs
++ *
++ * 0: The function completed successfully
++ * EINVAL: The provided ranges are impossible (such as maximum < minimum)
++ *
++ * preferred_min: The special-case minimum value for a specifically-
++ * requested ID, which may be lower than the standard min_id
++ */
++static int get_ranges(bool sys_user, uid_t *min_id, uid_t *max_id,
++	uid_t *preferred_min)
++{
++	uid_t uid_def_max = 0;
++
++	if (sys_user) {
++		/* System users */
++
++		/* A requested ID is allowed to be below the autoselect range */
++		*preferred_min = (uid_t) 1;
++
++		/* Get the minimum ID range from login.defs or default to 101 */
++		*min_id = (uid_t) getdef_ulong("SYS_UID_MIN", 101UL);
++
++		/*
++		 * If SYS_UID_MAX is unspecified, we should assume it to be one
++		 * less than the UID_MIN (which is reserved for non-system accounts)
++		 */
++		uid_def_max = (uid_t) getdef_ulong("UID_MIN", 1000UL) - 1;
++		*max_id = (uid_t) getdef_ulong("SYS_UID_MAX",
++				(unsigned long) uid_def_max);
++
++		/* Check that the ranges make sense */
++		if (*max_id < *min_id) {
++			(void) fprintf (stderr,
++                            _("%s: Invalid configuration: SYS_UID_MIN (%lu), "
++                              "UID_MIN (%lu), SYS_UID_MAX (%lu)\n"),
++                            Prog, (unsigned long) *min_id,
++                            getdef_ulong ("UID_MIN", 1000UL),
++                            (unsigned long) *max_id);
++			return EINVAL;
++		}
++	} else {
++		/* Non-system users */
++
++		/* Get the values from login.defs or use reasonable defaults */
++		*min_id = (uid_t) getdef_ulong("UID_MIN", 1000UL);
++		*max_id = (uid_t) getdef_ulong("UID_MAX", 60000UL);
++
++		/*
++		 * The preferred minimum should match the standard ID minimum
++		 * for non-system users.
++		 */
++		*preferred_min = *min_id;
++
++		/* Check that the ranges make sense */
++		if (*max_id < *min_id) {
++			(void) fprintf(stderr,
++					_("%s: Invalid configuration: UID_MIN (%lu), "
++					  "UID_MAX (%lu)\n"),
++					Prog, (unsigned long) *min_id,
++					(unsigned long) *max_id);
++			return EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++/*
++ * check_uid - See if the requested UID is available
++ *
++ * On success, return 0
++ * If the ID is in use, return EEXIST
++ * If the ID is outside the range, return ERANGE
++ * In other cases, return errno from getpwuid()
++ */
++static int check_uid(const uid_t uid,
++		             const uid_t uid_min,
++		             const uid_t uid_max,
++		             bool *used_uids)
++{
++	/* First test that the preferred ID is in the range */
++	if (uid < uid_min || uid > uid_max) {
++		return ERANGE;
++	}
++
++	/*
++	 * Check whether we already detected this UID
++	 * using the pw_next() loop
++	 */
++	if (used_uids != NULL && used_uids[uid]) {
++		return EEXIST;
++	}
++	/* Check if the UID exists according to NSS */
++	errno = 0;
++	if (getpwuid(uid) != NULL) {
++		return EEXIST;
++	} else {
++		/* getpwuid() was NULL, check whether this was
++		 * due to an error, so we can report it.
++		 */
++		/* ignore errors for now * if (errno != 0) {
++			return errno;
++		} */
++	}
++
++	/* If we've made it here, the UID must be available */
++	return 0;
++}
++
++/*
+  * find_new_uid - Find a new unused UID.
+  *
+  * If successful, find_new_uid provides an unused user ID in the
+@@ -48,162 +160,339 @@
+  * 
+  * Return 0 on success, -1 if no unused UIDs are available.
+  */
+-int find_new_uid (bool sys_user,
+-                  uid_t *uid,
+-                  /*@null@*/uid_t const *preferred_uid)
++int find_new_uid(bool sys_user,
++                 uid_t *uid,
++                 /*@null@*/uid_t const *preferred_uid)
+ {
+-	const struct passwd *pwd;
+-	uid_t uid_min, uid_max, user_id;
+ 	bool *used_uids;
++	const struct passwd *pwd;
++	uid_t uid_min, uid_max, preferred_min;
++	uid_t user_id, id;
++	uid_t lowest_found, highest_found;
++	int result;
++	int nospam = 0;
+ 
+ 	assert (uid != NULL);
+ 
+-	if (!sys_user) {
+-		uid_min = (uid_t) getdef_ulong ("UID_MIN", 1000UL);
+-		uid_max = (uid_t) getdef_ulong ("UID_MAX", 60000UL);
+-		if (uid_max < uid_min) {
+-			(void) fprintf (stderr,
+-			                _("%s: Invalid configuration: UID_MIN (%lu), UID_MAX (%lu)\n"),
+-			                Prog, (unsigned long) uid_min, (unsigned long) uid_max);
+-			return -1;
+-		}
+-	} else {
+-		uid_min = (uid_t) getdef_ulong ("SYS_UID_MIN", 101UL);
+-		uid_max = (uid_t) getdef_ulong ("UID_MIN", 1000UL) - 1;
+-		uid_max = (uid_t) getdef_ulong ("SYS_UID_MAX", (unsigned long) uid_max);
+-		if (uid_max < uid_min) {
+-			(void) fprintf (stderr,
+-			                _("%s: Invalid configuration: SYS_UID_MIN (%lu), UID_MIN (%lu), SYS_UID_MAX (%lu)\n"),
+-			                Prog, (unsigned long) uid_min, getdef_ulong ("UID_MIN", 1000UL), (unsigned long) uid_max);
++	/*
++	 * First, figure out what ID range is appropriate for
++	 * automatic assignment
++	 */
++	result = get_ranges(sys_user, &uid_min, &uid_max, &preferred_min);
++	if (result == EINVAL) {
++		return -1;
++	}
++
++	/* Check if the preferred UID is available */
++	if (preferred_uid) {
++		result = check_uid(*preferred_uid, preferred_min, uid_max, NULL);
++		if (result == 0) {
++			/*
++			 * Make sure the UID isn't queued for use already
++			 */
++			if (pw_locate_uid (*preferred_uid) == NULL) {
++				*uid = *preferred_uid;
++				return 0;
++			}
++			/*
++			 * pw_locate_uid() found the UID in an as-yet uncommitted
++			 * entry. We'll proceed below and auto-set an UID.
++			 */
++		} else if (result == EEXIST || result == ERANGE) {
++			/*
++			 * Continue on below. At this time, we won't
++			 * treat these two cases differently.
++			 */
++		} else {
++			/*
++			 * An unexpected error occurred. We should report
++			 * this and fail the user creation.
++			 * This differs from the automatic creation
++			 * behavior below, since if a specific UID was
++			 * requested and generated an error, the user is
++			 * more likely to want to stop and address the
++			 * issue.
++			 */
++			fprintf(stderr,
++					_("%s: Encountered error attempting to use "
++					  "preferred UID: %s\n"),
++					Prog, strerror(result));
+ 			return -1;
+ 		}
+ 	}
++
++	/*
++	 * Search the entire passwd file,
++	 * looking for the next unused value.
++	 *
++	 * We first check the local database with pw_rewind/pw_next to find
++	 * all local values that are in use.
++	 *
++	 * We then compare the next free value to all databases (local and
++	 * remote) and iterate until we find a free one. If there are free
++	 * values beyond the lowest (system users) or highest (non-system
++	 * users), we will prefer those and avoid potentially reclaiming a
++	 * deleted user (which can be a security issue, since it may grant
++	 * access to files belonging to that former user).
++	 *
++	 * If there are no UIDs available at the end of the search, we will
++	 * have no choice but to iterate through the range looking for gaps.
++	 *
++	 */
++
++	/* Create an array to hold all of the discovered UIDs */
+ 	used_uids = malloc (sizeof (bool) * (uid_max +1));
+ 	if (NULL == used_uids) {
+ 		fprintf (stderr,
+-		         _("%s: failed to allocate memory: %s\n"),
+-		         Prog, strerror (errno));
++				 _("%s: failed to allocate memory: %s\n"),
++				 Prog, strerror (errno));
+ 		return -1;
+ 	}
+ 	memset (used_uids, false, sizeof (bool) * (uid_max + 1));
+ 
+-	if (   (NULL != preferred_uid)
+-	    && (*preferred_uid >= uid_min)
+-	    && (*preferred_uid <= uid_max)
+-	    /* Check if the user exists according to NSS */
+-	    && (getpwuid (*preferred_uid) == NULL)
+-	    /* Check also the local database in case of uncommitted
+-	     * changes */
+-	    && (pw_locate_uid (*preferred_uid) == NULL)) {
+-		*uid = *preferred_uid;
+-		free (used_uids);
+-		return 0;
+-	}
++	/* First look for the lowest and highest value in the local database */
++	(void) pw_rewind ();
++	highest_found = uid_min;
++	lowest_found = uid_max;
++	while ((pwd = pw_next ()) != NULL) {
++		/*
++		 * Does this entry have a lower UID than the lowest we've found
++		 * so far?
++		 */
++		if ((pwd->pw_uid <= lowest_found) && (pwd->pw_uid >= uid_min)) {
++			lowest_found = pwd->pw_uid - 1;
++		}
+ 
++		/*
++		 * Does this entry have a higher UID than the highest we've found
++		 * so far?
++		 */
++		if ((pwd->pw_uid >= highest_found) && (pwd->pw_uid <= uid_max)) {
++			highest_found = pwd->pw_uid + 1;
++		}
++
++		/* create index of used UIDs */
++		if (pwd->pw_uid >= uid_min
++			&& pwd->pw_uid <= uid_max) {
++
++			used_uids[pwd->pw_uid] = true;
++		}
++	}
+ 
+-	/*
+-	 * Search the entire password file,
+-	 * looking for the largest unused value.
+-	 *
+-	 * We check the list of users according to NSS (setpwent/getpwent),
+-	 * but we also check the local database (pw_rewind/pw_next) in case
+-	 * some users were created but the changes were not committed yet.
+-	 */
+ 	if (sys_user) {
+-		uid_t id;
+-		/* setpwent / getpwent / endpwent can be very slow with
+-		 * LDAP configurations (and many accounts).
+-		 * Since there is a limited amount of IDs to be tested
+-		 * for system accounts, we just check the existence
+-		 * of IDs with getpwuid.
+-		 */
+-		user_id = uid_max;
+-		for (id = uid_max; id >= uid_min; id--) {
+-			if (getpwuid (id) != NULL) {
+-				user_id = id - 1;
+-				used_uids[id] = true;
+-			}
++		/*
++		 * For system users, we want to start from the
++		 * top of the range and work downwards.
++		 */
++
++		/*
++		 * At the conclusion of the pw_next() search, we will either
++		 * have a presumed-free UID or we will be at UID_MIN - 1.
++		 */
++		if (lowest_found < uid_min) {
++			/*
++			 * In this case, an UID is in use at UID_MIN.
++			 *
++			 * We will reset the search to UID_MAX and proceed down
++			 * through all the UIDs (skipping those we detected with
++			 * used_uids) for a free one. It is a known issue that
++			 * this may result in reusing a previously-deleted UID,
++			 * so administrators should be instructed to use this
++			 * auto-detection with care (and prefer to assign UIDs
++			 * explicitly).
++			 */
++			lowest_found = uid_max;
+ 		}
+ 
+-		(void) pw_rewind ();
+-		while ((pwd = pw_next ()) != NULL) {
+-			if ((pwd->pw_uid <= user_id) && (pwd->pw_uid >= uid_min)) {
+-				user_id = pwd->pw_uid - 1;
+-			}
+-			/* create index of used UIDs */
+-			if (pwd->pw_uid <= uid_max) {
+-				used_uids[pwd->pw_uid] = true;
++		/* Search through all of the IDs in the range */
++		for (id = lowest_found; id >= uid_min; id--) {
++			result = check_uid(id, uid_min, uid_max, used_uids);
++			if (result == 0) {
++				/* This UID is available. Return it. */
++				*uid = id;
++				free(used_uids);
++				return 0;
++			} else if (result == EEXIST) {
++				/* This UID is in use, we'll continue to the next */
++			} else {
++				/*
++				 * An unexpected error occurred.
++				 *
++				 * Only report it the first time to avoid spamming
++				 * the logs
++				 *
++				 */
++				if (!nospam) {
++					fprintf(stderr,
++							_("%s: Can't get unique system UID (%s). "
++							  "Suppressing additional messages.\n"),
++							Prog, strerror(result));
++					SYSLOG((LOG_ERR,
++							"Error checking available UIDs: %s",
++							strerror(result)));
++					nospam = 1;
++				}
++				/*
++				 * We will continue anyway. Hopefully a later UID
++				 * will work properly.
++				 */
+ 			}
+ 		}
+-	} else {
+-		user_id = uid_min;
+-		setpwent ();
+-		while ((pwd = getpwent ()) != NULL) {
+-			if ((pwd->pw_uid >= user_id) && (pwd->pw_uid <= uid_max)) {
+-				user_id = pwd->pw_uid + 1;
+-			}
+-			/* create index of used UIDs */
+-			if (pwd->pw_uid <= uid_max) {
+-				used_uids[pwd->pw_uid] = true;
++
++		/*
++		 * If we get all the way through the loop, try again from UID_MAX,
++		 * unless that was where we previously started. (NOTE: the worst-case
++		 * scenario here is that we will run through (UID_MAX - UID_MIN - 1)
++		 * cycles *again* if we fall into this case with lowest_found as
++		 * UID_MAX - 1, all users in the range in use and maintained by
++		 * network services such as LDAP.)
++		 */
++		if (lowest_found != uid_max) {
++			for (id = uid_max; id >= uid_min; id--) {
++				result = check_uid(id, uid_min, uid_max, used_uids);
++				if (result == 0) {
++					/* This UID is available. Return it. */
++					*uid = id;
++					free(used_uids);
++					return 0;
++				} else if (result == EEXIST) {
++					/* This UID is in use, we'll continue to the next */
++				} else {
++					/*
++					 * An unexpected error occurred.
++					 *
++					 * Only report it the first time to avoid spamming
++					 * the logs
++					 *
++					 */
++					if (!nospam) {
++						fprintf(stderr,
++								_("%s: Can't get unique system UID (%s). "
++								  "Suppressing additional messages.\n"),
++								Prog, strerror(result));
++						SYSLOG((LOG_ERR,
++								"Error checking available UIDs: %s",
++								strerror(result)));
++						nospam = 1;
++					}
++					/*
++					 * We will continue anyway. Hopefully a later UID
++					 * will work properly.
++					 */
++				}
+ 			}
+ 		}
+-		endpwent ();
++	} else { /* !sys_user */
++		/*
++		 * For non-system users, we want to start from the
++		 * bottom of the range and work upwards.
++		 */
+ 
+-		(void) pw_rewind ();
+-		while ((pwd = pw_next ()) != NULL) {
+-			if ((pwd->pw_uid >= user_id) && (pwd->pw_uid <= uid_max)) {
+-				user_id = pwd->pw_uid + 1;
+-			}
+-			/* create index of used UIDs */
+-			if (pwd->pw_uid <= uid_max) {
+-				used_uids[pwd->pw_uid] = true;
+-			}
++		/*
++		 * At the conclusion of the pw_next() search, we will either
++		 * have a presumed-free UID or we will be at UID_MAX + 1.
++		 */
++		if (highest_found > uid_max) {
++			/*
++			 * In this case, a UID is in use at UID_MAX.
++			 *
++			 * We will reset the search to UID_MIN and proceed up
++			 * through all the UIDs (skipping those we detected with
++			 * used_uids) for a free one. It is a known issue that
++			 * this may result in reusing a previously-deleted UID,
++			 * so administrators should be instructed to use this
++			 * auto-detection with care (and prefer to assign UIDs
++			 * explicitly).
++			 */
++			highest_found = uid_min;
+ 		}
+-	}
+ 
+-	/*
+-	 * If a user (resp. system user) with UID equal to UID_MAX (resp.
+-	 * UID_MIN) exists, the above algorithm will give us UID_MAX+1
+-	 * (resp. UID_MIN-1) even if not unique. Search for the first free
+-	 * UID starting with UID_MIN (resp. UID_MAX).
+-	 */
+-	if (sys_user) {
+-		if (user_id < uid_min) {
+-			for (user_id = uid_max; user_id >= uid_min; user_id--) {
+-				if (false == used_uids[user_id]) {
+-					break;
++		/* Search through all of the IDs in the range */
++		for (id = highest_found; id <= uid_max; id++) {
++			result = check_uid(id, uid_min, uid_max, used_uids);
++			if (result == 0) {
++				/* This UID is available. Return it. */
++				*uid = id;
++				free(used_uids);
++				return 0;
++			} else if (result == EEXIST) {
++				/* This UID is in use, we'll continue to the next */
++			} else {
++				/*
++				 * An unexpected error occurred.
++				 *
++				 * Only report it the first time to avoid spamming
++				 * the logs
++				 *
++				 */
++				if (!nospam) {
++					fprintf(stderr,
++							_("%s: Can't get unique UID (%s). "
++							  "Suppressing additional messages.\n"),
++							Prog, strerror(result));
++					SYSLOG((LOG_ERR,
++							"Error checking available UIDs: %s",
++							strerror(result)));
++					nospam = 1;
+ 				}
+-			}
+-			if (user_id < uid_min ) {
+-				fprintf (stderr,
+-				         _("%s: Can't get unique system UID (no more available UIDs)\n"),
+-				         Prog);
+-				SYSLOG ((LOG_WARN,
+-				         "no more available UID on the system"));
+-				free (used_uids);
+-				return -1;
++				/*
++				 * We will continue anyway. Hopefully a later UID
++				 * will work properly.
++				 */
+ 			}
+ 		}
+-	} else {
+-		if (user_id > uid_max) {
+-			for (user_id = uid_min; user_id <= uid_max; user_id++) {
+-				if (false == used_uids[user_id]) {
+-					break;
++
++		/*
++		 * If we get all the way through the loop, try again from UID_MIN,
++		 * unless that was where we previously started. (NOTE: the worst-case
++		 * scenario here is that we will run through (UID_MAX - UID_MIN - 1)
++		 * cycles *again* if we fall into this case with highest_found as
++		 * UID_MIN + 1, all users in the range in use and maintained by
++		 * network services such as LDAP.)
++		 */
++		if (highest_found != uid_min) {
++			for (id = uid_min; id <= uid_max; id++) {
++				result = check_uid(id, uid_min, uid_max, used_uids);
++				if (result == 0) {
++					/* This UID is available. Return it. */
++					*uid = id;
++					free(used_uids);
++					return 0;
++				} else if (result == EEXIST) {
++					/* This UID is in use, we'll continue to the next */
++				} else {
++					/*
++					 * An unexpected error occurred.
++					 *
++					 * Only report it the first time to avoid spamming
++					 * the logs
++					 *
++					 */
++					if (!nospam) {
++						fprintf(stderr,
++								_("%s: Can't get unique UID (%s). "
++								  "Suppressing additional messages.\n"),
++								Prog, strerror(result));
++						SYSLOG((LOG_ERR,
++								"Error checking available UIDs: %s",
++								strerror(result)));
++						nospam = 1;
++					}
++					/*
++					 * We will continue anyway. Hopefully a later UID
++					 * will work properly.
++					 */
+ 				}
+ 			}
+-			if (user_id > uid_max) {
+-				fprintf (stderr,
+-				         _("%s: Can't get unique UID (no more available UIDs)\n"),
+-				         Prog);
+-				SYSLOG ((LOG_WARN, "no more available UID on the system"));
+-				free (used_uids);
+-				return -1;
+-			}
+ 		}
+ 	}
+ 
+-	free (used_uids);
+-	*uid = user_id;
+-	return 0;
++	/* The code reached here and found no available IDs in the range */
++	fprintf(stderr,
++			_("%s: Can't get unique UID (no more available UIDs)\n"),
++			Prog);
++	SYSLOG((LOG_WARN, "no more available UIDs on the system"));
++	free(used_uids);
++	return -1;
+ }
+ 
diff --git a/shadow-utils.spec b/shadow-utils.spec
index e3d6b90..f1edde1 100644
--- a/shadow-utils.spec
+++ b/shadow-utils.spec
@@ -1,7 +1,7 @@
 Summary: Utilities for managing accounts and shadow password files
 Name: shadow-utils
 Version: 4.1.5.1
-Release: 20%{?dist}
+Release: 21%{?dist}
 Epoch: 2
 URL: http://pkg-shadow.alioth.debian.org/
 Source0: http://pkg-shadow.alioth.debian.org/releases/shadow-%{version}.tar.bz2
@@ -26,10 +26,11 @@ Patch14: shadow-4.1.5.1-default-range.patch
 Patch15: shadow-4.1.5.1-manfix.patch
 Patch16: shadow-4.1.5.1-crypt-null.patch
 Patch17: shadow-4.1.5.1-userdel-helpfix.patch
-Patch18: shadow-4.1.5.1-group-alloc.patch
+Patch18: shadow-4.1.5.1-id-alloc.patch
 Patch19: shadow-4.1.5.1-date-parsing.patch
 Patch20: shadow-4.1.5.1-ingroup.patch
 Patch21: shadow-4.1.5.1-move-home.patch
+Patch22: shadow-4.1.5.1-audit-update.patch
 
 License: BSD and GPLv2+
 Group: System Environment/Base
@@ -75,10 +76,11 @@ are used for managing group accounts.
 %patch15 -p1 -b .manfix
 %patch16 -p1 -b .crypt-null
 %patch17 -p1 -b .userdel
-%patch18 -p1 -b .group-alloc
+%patch18 -p1 -b .id-alloc
 %patch19 -p1 -b .date-parsing
 %patch20 -p1 -b .ingroup
 %patch21 -p1 -b .move-home
+%patch22 -p1 -b .audit-update
 
 iconv -f ISO88591 -t utf-8  doc/HOWTO > doc/HOWTO.utf8
 cp -f doc/HOWTO.utf8 doc/HOWTO
@@ -236,6 +238,11 @@ rm -rf $RPM_BUILD_ROOT
 %{_mandir}/man8/vigr.8*
 
 %changelog
+* Fri Oct 17 2014 Tomáš Mráz <tmraz at redhat.com> - 2:4.1.5.1-21
+- update auditing to cover more events and fix some incorrect audit
+  records - patch by Steve Grubb (#1151580)
+- apply the same new allocation algorithm to uids as for gids
+
 * Wed Sep 10 2014 Tomas Mraz <tmraz at redhat.com> - 2:4.1.5.1-20
 - discard obsolete matchpathcon cache after semanage_commit()
 


More information about the scm-commits mailing list