diff --git a/.fmf/version b/.fmf/version deleted file mode 100644 index d00491f..0000000 --- a/.fmf/version +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/.gitignore b/.gitignore index 3023995..3b04b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,47 +10,3 @@ shadow-4.1.4.2.tar.bz2 /shadow-4.5.tar.xz.asc /shadow-4.6.tar.xz /shadow-4.6.tar.xz.asc -/shadow-4.8.tar.xz -/shadow-4.8.tar.xz.asc -/shadow-4.8.1.tar.xz -/shadow-4.8.1.tar.xz.asc -/shadow-4.9.tar.xz -/shadow-4.9.tar.xz.asc -/shadow-4.11.1.tar.xz -/shadow-4.11.1.tar.xz.asc -/shadow-4.12.3.tar.xz -/shadow-4.12.3.tar.xz.asc -/shadow-4.13.tar.xz -/shadow-4.13.tar.xz.asc -/shadow-4.14.0.tar.xz -/shadow-4.14.0.tar.xz.asc -/shadow-4.15.0rc2.tar.xz -/shadow-4.15.0rc2.tar.xz.asc -/shadow-4.15.0rc3.tar.xz -/shadow-4.15.0rc3.tar.xz.asc -/shadow-4.15.0.tar.xz -/shadow-4.15.0.tar.xz.asc -/shadow-4.15.1.tar.xz -/shadow-4.15.1.tar.xz.asc -/shadow-4.16.0.tar.xz -/shadow-4.16.0.tar.xz.asc -/shadow-4.17.0-rc1.tar.xz -/shadow-4.17.0-rc1.tar.xz.asc -/shadow-4.17.0.tar.xz -/shadow-4.17.0.tar.xz.asc -/shadow-4.17.4.tar.xz -/shadow-4.17.4.tar.xz.asc -/shadow-4.18.0.tar.xz -/shadow-4.18.0.tar.xz.asc -/shadow-4.19.0.tar.xz -/shadow-4.19.0.tar.xz.asc -/shadow-4.19.2.tar.xz -/shadow-4.19.2.tar.xz.asc -/shadow-4.19.3.tar.xz -/shadow-4.19.3.tar.xz.asc -/shadow-4.20.0-rc2.tar.xz -/shadow-4.20.0-rc2.tar.xz.asc -/shadow-4.20.0-rc3.tar.xz -/shadow-4.20.0-rc3.tar.xz.asc -/shadow-4.20.0.tar.xz -/shadow-4.20.0.tar.xz.asc diff --git a/passwd.pamd b/passwd.pamd deleted file mode 100644 index fd03d03..0000000 --- a/passwd.pamd +++ /dev/null @@ -1,5 +0,0 @@ -#%PAM-1.0 -# This tool only uses the password stack. -password substack system-auth --password optional pam_gnome_keyring.so use_authtok -password substack postlogin diff --git a/plans/tier0-functional.fmf b/plans/tier0-functional.fmf deleted file mode 100644 index a9939b5..0000000 --- a/plans/tier0-functional.fmf +++ /dev/null @@ -1,51 +0,0 @@ -summary: Tier 0 functional tests for shadow-utils -description: | - Run comprehensive system tests for shadow-utils. These tests validate user and group - account management functionality by testing actual system operations including user - creation, password management, group operations, and verification of system files - (i.e. /etc/passwd, /etc/shadow). - -provision: - how: virtual - image: fedora - -prepare: - - name: Install general dependencies - how: install - package: - - expect - - gcc - - git - - libssh-devel - - python3-devel - - python3-pip - - - name: Setup SSH keys for localhost testing - how: shell - script: - - ssh-keygen -t rsa -f /root/.ssh/id_rsa -N "" -q - - cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys - - chmod 600 /root/.ssh/authorized_keys - - ssh-keyscan -H localhost >> /root/.ssh/known_hosts - - - name: Clone shadow repository - how: shell - script: - - git clone https://github.com/shadow-maint/shadow.git /tmp/shadow-test - - - name: Copy test topology for Fedora CI - how: shell - script: - - cp tests/mhc-fedora-ci.yaml /tmp/shadow-test/tests/system - - - name: Install test dependencies - how: shell - script: - - pip3 install -r /tmp/shadow-test/tests/system/requirements.txt - -execute: - how: tmt - duration: 30m - script: | - cd /tmp/shadow-test/tests/system - pytest --mh-config=mhc-fedora-ci.yaml --mh-lazy-ssh -v diff --git a/shadow-4.1.5.1-default-range.patch b/shadow-4.1.5.1-default-range.patch new file mode 100644 index 0000000..2a9d640 --- /dev/null +++ b/shadow-4.1.5.1-default-range.patch @@ -0,0 +1,36 @@ +Index: shadow-4.5/lib/semanage.c +=================================================================== +--- shadow-4.5.orig/lib/semanage.c ++++ shadow-4.5/lib/semanage.c +@@ -143,6 +143,7 @@ static int semanage_user_mod (semanage_h + goto done; + } + ++#if 0 + ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); + if (ret != 0) { + fprintf (stderr, +@@ -150,6 +151,7 @@ static int semanage_user_mod (semanage_h + ret = 1; + goto done; + } ++#endif + + ret = semanage_seuser_set_sename (handle, seuser, seuser_name); + if (ret != 0) { +@@ -200,6 +202,7 @@ static int semanage_user_add (semanage_h + goto done; + } + ++#if 0 + ret = semanage_seuser_set_mlsrange (handle, seuser, DEFAULT_SERANGE); + if (ret != 0) { + fprintf (stderr, +@@ -208,6 +211,7 @@ static int semanage_user_add (semanage_h + ret = 1; + goto done; + } ++#endif + + ret = semanage_seuser_set_sename (handle, seuser, seuser_name); + if (ret != 0) { diff --git a/shadow-4.1.5.1-info-parent-dir.patch b/shadow-4.1.5.1-info-parent-dir.patch new file mode 100644 index 0000000..b3a525b --- /dev/null +++ b/shadow-4.1.5.1-info-parent-dir.patch @@ -0,0 +1,21 @@ +Index: shadow-4.5/man/newusers.8.xml +=================================================================== +--- shadow-4.5.orig/man/newusers.8.xml ++++ shadow-4.5/man/newusers.8.xml +@@ -218,7 +218,15 @@ + + If this field does not specify an existing directory, the + specified directory is created, with ownership set to the +- user being created or updated and its primary group. ++ user being created or updated and its primary group. Note ++ that newusers does not create parent directories of the new ++ user's home directory. The newusers command will fail to ++ create the home directory if the parent directories do not ++ exist, and will send a message to stderr informing the user ++ of the failure. The newusers command will not halt or return ++ a failure to the calling shell if it fails to create the home ++ directory, it will continue to process the batch of new users ++ specified. + + + If the home directory of an existing user is changed, diff --git a/shadow-4.1.5.1-logmsg.patch b/shadow-4.1.5.1-logmsg.patch new file mode 100644 index 0000000..ca7e57b --- /dev/null +++ b/shadow-4.1.5.1-logmsg.patch @@ -0,0 +1,13 @@ +Index: shadow-4.5/src/useradd.c +=================================================================== +--- shadow-4.5.orig/src/useradd.c ++++ shadow-4.5/src/useradd.c +@@ -323,7 +323,7 @@ static void fail_exit (int code) + user_name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); + #endif +- SYSLOG ((LOG_INFO, "failed adding user '%s', data deleted", user_name)); ++ SYSLOG ((LOG_INFO, "failed adding user '%s', exit code: %d", user_name, code)); + exit (code); + } + diff --git a/shadow-4.1.5.1-userdel-helpfix.patch b/shadow-4.1.5.1-userdel-helpfix.patch new file mode 100644 index 0000000..075f482 --- /dev/null +++ b/shadow-4.1.5.1-userdel-helpfix.patch @@ -0,0 +1,16 @@ +Index: shadow-4.5/src/userdel.c +=================================================================== +--- shadow-4.5.orig/src/userdel.c ++++ shadow-4.5/src/userdel.c +@@ -143,8 +143,9 @@ static void usage (int status) + "\n" + "Options:\n"), + Prog); +- (void) fputs (_(" -f, --force force removal of files,\n" +- " even if not owned by user\n"), ++ (void) fputs (_(" -f, --force force some actions that would fail otherwise\n" ++ " e.g. removal of user still logged in\n" ++ " or files, even if not owned by the user\n"), + usageout); + (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); + (void) fputs (_(" -r, --remove remove home directory and mail spool\n"), usageout); diff --git a/shadow-4.2.1-date-parsing.patch b/shadow-4.2.1-date-parsing.patch new file mode 100644 index 0000000..2a798d0 --- /dev/null +++ b/shadow-4.2.1-date-parsing.patch @@ -0,0 +1,69 @@ +Index: shadow-4.5/libmisc/getdate.y +=================================================================== +--- shadow-4.5.orig/libmisc/getdate.y ++++ shadow-4.5/libmisc/getdate.y +@@ -152,6 +152,7 @@ static int yyHaveDay; + static int yyHaveRel; + static int yyHaveTime; + static int yyHaveZone; ++static int yyHaveYear; + static int yyTimezone; + static int yyDay; + static int yyHour; +@@ -293,18 +294,21 @@ date : tUNUMBER '/' tUNUMBER { + yyDay = $3; + yyYear = $5; + } ++ yyHaveYear++; + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; ++ yyHaveYear++; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; ++ yyHaveYear++; + } + | tMONTH tUNUMBER { + yyMonth = $1; +@@ -314,6 +318,7 @@ date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; ++ yyHaveYear++; + } + | tUNUMBER tMONTH { + yyMonth = $2; +@@ -323,6 +328,7 @@ date : tUNUMBER '/' tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; ++ yyHaveYear++; + } + ; + +@@ -395,7 +401,8 @@ relunit : tUNUMBER tYEAR_UNIT { + + number : tUNUMBER + { +- if ((yyHaveTime != 0) && (yyHaveDate != 0) && (yyHaveRel == 0)) ++ if ((yyHaveTime != 0 || $1 >= 100) && !yyHaveYear ++ && (yyHaveDate != 0) && (yyHaveRel == 0)) + yyYear = $1; + else + { +@@ -802,7 +809,7 @@ yylex (void) + return LookupWord (buff); + } + if (c != '(') +- return *yyInput++; ++ return (unsigned char)*yyInput++; + Count = 0; + do + { diff --git a/shadow-4.2.1-no-lock-dos.patch b/shadow-4.2.1-no-lock-dos.patch new file mode 100644 index 0000000..c6873e9 --- /dev/null +++ b/shadow-4.2.1-no-lock-dos.patch @@ -0,0 +1,16 @@ +Index: shadow-4.5/lib/commonio.c +=================================================================== +--- shadow-4.5.orig/lib/commonio.c ++++ shadow-4.5/lib/commonio.c +@@ -140,7 +140,10 @@ static int do_lock_file (const char *fil + int retval; + char buf[32]; + +- fd = open (file, O_CREAT | O_EXCL | O_WRONLY, 0600); ++ /* We depend here on the fact, that the file name is pid-specific. ++ * So no O_EXCL here and no DoS. ++ */ ++ fd = open (file, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (-1 == fd) { + if (log) { + (void) fprintf (stderr, diff --git a/shadow-4.2.1-null-tm.patch b/shadow-4.2.1-null-tm.patch new file mode 100644 index 0000000..b1dd1c4 --- /dev/null +++ b/shadow-4.2.1-null-tm.patch @@ -0,0 +1,91 @@ +Index: shadow-4.5/src/faillog.c +=================================================================== +--- shadow-4.5.orig/src/faillog.c ++++ shadow-4.5/src/faillog.c +@@ -163,10 +163,14 @@ static void print_one (/*@null@*/const s + } + + tm = localtime (&fl.fail_time); ++ if (tm == NULL) { ++ cp = "(unknown)"; ++ } else { + #ifdef HAVE_STRFTIME +- strftime (ptime, sizeof (ptime), "%D %H:%M:%S %z", tm); +- cp = ptime; ++ strftime (ptime, sizeof (ptime), "%D %H:%M:%S %z", tm); ++ cp = ptime; + #endif ++ } + printf ("%-9s %5d %5d ", + pw->pw_name, fl.fail_cnt, fl.fail_max); + /* FIXME: cp is not defined ifndef HAVE_STRFTIME */ +Index: shadow-4.5/src/chage.c +=================================================================== +--- shadow-4.5.orig/src/chage.c ++++ shadow-4.5/src/chage.c +@@ -168,6 +168,10 @@ static void date_to_str (char *buf, size + struct tm *tp; + + tp = gmtime (&date); ++ if (tp == NULL) { ++ (void) snprintf (buf, maxsize, "(unknown)"); ++ return; ++ } + #ifdef HAVE_STRFTIME + (void) strftime (buf, maxsize, "%Y-%m-%d", tp); + #else +Index: shadow-4.5/src/lastlog.c +=================================================================== +--- shadow-4.5.orig/src/lastlog.c ++++ shadow-4.5/src/lastlog.c +@@ -158,13 +158,17 @@ static void print_one (/*@null@*/const s + + ll_time = ll.ll_time; + tm = localtime (&ll_time); ++ if (tm == NULL) { ++ cp = "(unknown)"; ++ } else { + #ifdef HAVE_STRFTIME +- strftime (ptime, sizeof (ptime), "%a %b %e %H:%M:%S %z %Y", tm); +- cp = ptime; ++ strftime (ptime, sizeof (ptime), "%a %b %e %H:%M:%S %z %Y", tm); ++ cp = ptime; + #else +- cp = asctime (tm); +- cp[24] = '\0'; ++ cp = asctime (tm); ++ cp[24] = '\0'; + #endif ++ } + + if (ll.ll_time == (time_t) 0) { + cp = _("**Never logged in**\0"); +Index: shadow-4.5/src/passwd.c +=================================================================== +--- shadow-4.5.orig/src/passwd.c ++++ shadow-4.5/src/passwd.c +@@ -455,6 +455,9 @@ static /*@observer@*/const char *date_to + struct tm *tm; + + tm = gmtime (&t); ++ if (tm == NULL) { ++ return "(unknown)"; ++ } + #ifdef HAVE_STRFTIME + (void) strftime (buf, sizeof buf, "%m/%d/%Y", tm); + #else /* !HAVE_STRFTIME */ +Index: shadow-4.5/src/usermod.c +=================================================================== +--- shadow-4.5.orig/src/usermod.c ++++ shadow-4.5/src/usermod.c +@@ -210,6 +210,10 @@ static void date_to_str (/*@unique@*//*@ + } else { + time_t t = (time_t) date; + tp = gmtime (&t); ++ if (tp == NULL) { ++ strncpy (buf, "unknown", maxsize); ++ return; ++ } + #ifdef HAVE_STRFTIME + strftime (buf, maxsize, "%Y-%m-%d", tp); + #else diff --git a/shadow-4.3.1-manfix.patch b/shadow-4.3.1-manfix.patch new file mode 100644 index 0000000..bd1577e --- /dev/null +++ b/shadow-4.3.1-manfix.patch @@ -0,0 +1,349 @@ +Index: shadow-4.5/man/groupmems.8.xml +=================================================================== +--- shadow-4.5.orig/man/groupmems.8.xml ++++ shadow-4.5/man/groupmems.8.xml +@@ -179,20 +179,10 @@ + + SETUP + +- The groupmems executable should be in mode +- 2770 as user root and in group +- groups. The system administrator can add users to +- group groups to allow or disallow them using the +- groupmems utility to manage their own group +- membership list. ++ In this operating system the groupmems executable ++ is not setuid and regular users cannot use it to manipulate ++ the membership of their own group. + +- +- +- $ groupadd -r groups +- $ chmod 2770 groupmems +- $ chown root.groups groupmems +- $ groupmems -g groups -a gk4 +- + + + +Index: shadow-4.5/man/chage.1.xml +=================================================================== +--- shadow-4.5.orig/man/chage.1.xml ++++ shadow-4.5/man/chage.1.xml +@@ -102,6 +102,9 @@ + Set the number of days since January 1st, 1970 when the password + was last changed. The date may also be expressed in the format + YYYY-MM-DD (or the format more commonly used in your area). ++ If the LAST_DAY is set to ++ 0 the user is forced to change his password ++ on the next log on. + + + +@@ -119,6 +122,13 @@ + system again. + + ++ For example the following can be used to set an account to expire ++ in 180 days: ++ ++ ++ chage -E $(date -d +180days +%Y-%m-%d) ++ ++ + Passing the number -1 as the + EXPIRE_DATE will remove an account + expiration date. +@@ -233,6 +243,18 @@ + The chage program requires a shadow password file to + be available. + ++ ++ The chage program will report only the information from the shadow ++ password file. This implies that configuration from other sources ++ (e.g. LDAP or empty password hash field from the passwd file) that ++ affect the user's login will not be shown in the chage output. ++ ++ ++ The chage program will also not report any ++ inconsistency between the shadow and passwd files (e.g. missing x in ++ the passwd file). The pwck can be used to check ++ for this kind of inconsistencies. ++ + The chage command is restricted to the root + user, except for the option, which may be used by + an unprivileged user to determine when their password or account is due +Index: shadow-4.5/man/ja/man5/login.defs.5 +=================================================================== +--- shadow-4.5.orig/man/ja/man5/login.defs.5 ++++ shadow-4.5/man/ja/man5/login.defs.5 +@@ -147,10 +147,6 @@ PASS_MAX_DAYS, PASS_MIN_DAYS, PASS_WARN_ + shadow パスワード機能のどのプログラムが + どのパラメータを使用するかを示したものである。 + .na +-.IP chfn 12 +-CHFN_AUTH CHFN_RESTRICT +-.IP chsh 12 +-CHFN_AUTH + .IP groupadd 12 + GID_MAX GID_MIN + .IP newusers 12 +Index: shadow-4.5/man/login.defs.5.xml +=================================================================== +--- shadow-4.5.orig/man/login.defs.5.xml ++++ shadow-4.5/man/login.defs.5.xml +@@ -162,6 +162,17 @@ + long numeric parameters is machine-dependent. + + ++ ++ Please note that the parameters in this configuration file control the ++ behavior of the tools from the shadow-utils component. None of these ++ tools uses the PAM mechanism, and the utilities that use PAM (such as the ++ passwd command) should be configured elsewhere. The only values that ++ affect PAM modules are ENCRYPT_METHOD and SHA_CRYPT_MAX_ROUNDS ++ for pam_unix module, FAIL_DELAY for pam_faildelay module, ++ and UMASK for pam_umask module. Refer to ++ pam(8) for more information. ++ ++ + The following configuration items are provided: + + +@@ -252,16 +263,6 @@ + + + +- chfn +- +- +- CHFN_AUTH +- CHFN_RESTRICT +- LOGIN_STRING +- +- +- +- + chgpasswd + + +@@ -282,14 +283,6 @@ + + + +- +- chsh +- +- +- CHSH_AUTH LOGIN_STRING +- +- +- + + + +@@ -350,34 +343,6 @@ + + + +- +- login +- +- +- CONSOLE +- CONSOLE_GROUPS DEFAULT_HOME +- ENV_HZ ENV_PATH ENV_SUPATH +- ENV_TZ ENVIRON_FILE +- ERASECHAR FAIL_DELAY +- FAILLOG_ENAB +- FAKE_SHELL +- FTMP_FILE +- HUSHLOGIN_FILE +- ISSUE_FILE +- KILLCHAR +- LASTLOG_ENAB +- LOGIN_RETRIES +- LOGIN_STRING +- LOGIN_TIMEOUT LOG_OK_LOGINS LOG_UNKFAIL_ENAB +- MAIL_CHECK_ENAB MAIL_DIR MAIL_FILE +- MOTD_FILE NOLOGINS_FILE PORTTIME_CHECKS_ENAB +- QUOTAS_ENAB +- TTYGROUP TTYPERM TTYTYPE_FILE +- ULIMIT UMASK +- USERGROUPS_ENAB +- +- +- + + + newgrp / sg +@@ -405,17 +370,6 @@ + + + +- +- passwd +- +- +- ENCRYPT_METHOD MD5_CRYPT_ENAB OBSCURE_CHECKS_ENAB +- PASS_ALWAYS_WARN PASS_CHANGE_TRIES PASS_MAX_LEN PASS_MIN_LEN +- SHA_CRYPT_MAX_ROUNDS +- SHA_CRYPT_MIN_ROUNDS +- +- +- + + pwck + +@@ -442,32 +396,6 @@ + + + +- +- su +- +- +- CONSOLE +- CONSOLE_GROUPS DEFAULT_HOME +- ENV_HZ ENVIRON_FILE +- ENV_PATH ENV_SUPATH +- ENV_TZ LOGIN_STRING MAIL_CHECK_ENAB +- MAIL_DIR MAIL_FILE QUOTAS_ENAB +- SULOG_FILE SU_NAME +- SU_WHEEL_ONLY +- SYSLOG_SU_ENAB +- USERGROUPS_ENAB +- +- +- +- +- sulogin +- +- +- ENV_HZ +- ENV_TZ +- +- +- + + useradd + +Index: shadow-4.5/man/shadow.5.xml +=================================================================== +--- shadow-4.5.orig/man/shadow.5.xml ++++ shadow-4.5/man/shadow.5.xml +@@ -129,7 +129,7 @@ + + + The date of the last password change, expressed as the number +- of days since Jan 1, 1970. ++ of days since Jan 1, 1970 00:00 UTC. + + + The value 0 has a special meaning, which is that the user +@@ -208,8 +208,8 @@ + + + After expiration of the password and this expiration period is +- elapsed, no login is possible using the current user's +- password. The user should contact her administrator. ++ elapsed, no login is possible for the user. ++ The user should contact her administrator. + + + An empty field means that there are no enforcement of an +@@ -224,7 +224,7 @@ + + + The date of expiration of the account, expressed as the number +- of days since Jan 1, 1970. ++ of days since Jan 1, 1970 00:00 UTC. + + + Note that an account expiration differs from a password +Index: shadow-4.5/man/useradd.8.xml +=================================================================== +--- shadow-4.5.orig/man/useradd.8.xml ++++ shadow-4.5/man/useradd.8.xml +@@ -347,6 +347,11 @@ + is not enabled, no home + directories are created. + ++ ++ The directory where the user's home directory is created must ++ exist and have proper SELinux context and permissions. Otherwise ++ the user's home directory cannot be created or accessed. ++ + + + +Index: shadow-4.5/man/usermod.8.xml +=================================================================== +--- shadow-4.5.orig/man/usermod.8.xml ++++ shadow-4.5/man/usermod.8.xml +@@ -132,7 +132,8 @@ + If the + option is given, the contents of the current home directory will + be moved to the new home directory, which is created if it does +- not already exist. ++ not already exist. If the current home directory does not exist ++ the new home directory will not be created. + + + +@@ -256,7 +257,8 @@ + + + Move the content of the user's home directory to the new +- location. ++ location. If the current home directory does not exist ++ the new home directory will not be created. + + + This option is only valid in combination with the +diff --git a/man/login.defs.d/SUB_GID_COUNT.xml b/man/login.defs.d/SUB_GID_COUNT.xml +index 01ace007..93fe7421 100644 +--- a/man/login.defs.d/SUB_GID_COUNT.xml ++++ b/man/login.defs.d/SUB_GID_COUNT.xml +@@ -42,7 +42,7 @@ + + The default values for , + , +- are respectively 100000, 600100000 and 10000. ++ are respectively 100000, 600100000 and 65536. + + + +diff --git a/man/login.defs.d/SUB_UID_COUNT.xml b/man/login.defs.d/SUB_UID_COUNT.xml +index 5ad812f7..516417b7 100644 +--- a/man/login.defs.d/SUB_UID_COUNT.xml ++++ b/man/login.defs.d/SUB_UID_COUNT.xml +@@ -42,7 +42,7 @@ + + The default values for , + , +- are respectively 100000, 600100000 and 10000. ++ are respectively 100000, 600100000 and 65536. + + + +diff -up shadow-4.6/man/groupadd.8.xml.manfix shadow-4.6/man/groupadd.8.xml +--- shadow-4.6/man/groupadd.8.xml.manfix 2019-04-02 16:35:52.096637444 +0200 ++++ shadow-4.6/man/groupadd.8.xml 2019-06-07 14:23:57.477602106 +0200 +@@ -320,13 +320,13 @@ + + 4 + +- GID not unique (when not used) ++ GID is already used (when called without ) + + + + 9 + +- group name not unique ++ group name is already used + + + + diff --git a/shadow-4.3.1-selinux-perms.patch b/shadow-4.3.1-selinux-perms.patch new file mode 100644 index 0000000..a08660f --- /dev/null +++ b/shadow-4.3.1-selinux-perms.patch @@ -0,0 +1,290 @@ +Index: shadow-4.5/src/chgpasswd.c +=================================================================== +--- shadow-4.5.orig/src/chgpasswd.c ++++ shadow-4.5/src/chgpasswd.c +@@ -39,6 +39,13 @@ + #include + #include + #include ++#ifdef WITH_SELINUX ++#include ++#include ++#endif ++#ifdef WITH_LIBAUDIT ++#include ++#endif + #ifdef ACCT_TOOLS_SETUID + #ifdef USE_PAM + #include "pam_defs.h" +@@ -76,6 +83,9 @@ static bool sgr_locked = false; + #endif + static bool gr_locked = false; + ++/* The name of the caller */ ++static char *myname = NULL; ++ + /* local function prototypes */ + static void fail_exit (int code); + static /*@noreturn@*/void usage (int status); +@@ -300,6 +310,63 @@ static void check_perms (void) + #endif /* ACCT_TOOLS_SETUID */ + } + ++#ifdef WITH_SELINUX ++static int ++log_callback (int type, const char *fmt, ...) ++{ ++ int audit_fd; ++ va_list ap; ++ ++ va_start(ap, fmt); ++#ifdef WITH_AUDIT ++ audit_fd = audit_open(); ++ ++ if (audit_fd >= 0) { ++ char *buf; ++ ++ if (vasprintf (&buf, fmt, ap) < 0) ++ goto ret; ++ audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, ++ NULL, 0); ++ audit_close(audit_fd); ++ free(buf); ++ goto ret; ++ } ++ ++#endif ++ vsyslog (LOG_USER | LOG_INFO, fmt, ap); ++ret: ++ va_end(ap); ++ return 0; ++} ++ ++static void ++selinux_check_root (void) ++{ ++ int status = -1; ++ security_context_t user_context; ++ union selinux_callback old_callback; ++ ++ if (is_selinux_enabled() < 1) ++ return; ++ ++ old_callback = selinux_get_callback(SELINUX_CB_LOG); ++ /* setup callbacks */ ++ selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback); ++ if ((status = getprevcon(&user_context)) < 0) { ++ selinux_set_callback(SELINUX_CB_LOG, old_callback); ++ exit(1); ++ } ++ ++ status = selinux_check_access(user_context, user_context, "passwd", "passwd", NULL); ++ ++ selinux_set_callback(SELINUX_CB_LOG, old_callback); ++ freecon(user_context); ++ if (status != 0 && security_getenforce() != 0) ++ exit(1); ++} ++#endif ++ + /* + * open_files - lock and open the group databases + */ +@@ -393,6 +460,7 @@ int main (int argc, char **argv) + + const struct group *gr; + struct group newgr; ++ struct passwd *pw = NULL; + int errors = 0; + int line = 0; + +@@ -402,12 +470,37 @@ int main (int argc, char **argv) + (void) bindtextdomain (PACKAGE, LOCALEDIR); + (void) textdomain (PACKAGE); + ++#ifdef WITH_SELINUX ++ selinux_check_root (); ++#endif ++ + process_root_flag ("-R", argc, argv); + + process_flags (argc, argv); + + OPENLOG ("chgpasswd"); + ++#ifdef WITH_AUDIT ++ audit_help_open (); ++#endif ++ ++ /* ++ * Determine the name of the user that invoked this command. This ++ * is really hit or miss because there are so many ways that command ++ * can be executed and so many ways to trip up the routines that ++ * report the user name. ++ */ ++ pw = get_my_pwent (); ++ if (NULL == pw) { ++ fprintf (stderr, _("%s: Cannot determine your user name.\n"), ++ Prog); ++ SYSLOG ((LOG_WARN, ++ "Cannot determine the user name of the caller (UID %lu)", ++ (unsigned long) getuid ())); ++ exit (E_NOPERM); ++ } ++ myname = xstrdup (pw->pw_name); ++ + check_perms (); + + #ifdef SHADOWGRP + is_shadow_grp = sgr_file_present (); + #endif +@@ -536,6 +629,15 @@ int main (int argc, char **argv) + newgr.gr_passwd = cp; + } + ++#ifdef WITH_AUDIT ++ { ++ ++ audit_logger_with_group (AUDIT_GRP_CHAUTHTOK, Prog, ++ "change-password", ++ myname, AUDIT_NO_ID, gr->gr_name, ++ SHADOW_AUDIT_SUCCESS); ++ } ++#endif + /* + * The updated group file entry is then put back and will + * be written to the group file later, after all the +Index: shadow-4.5/src/chpasswd.c +=================================================================== +--- shadow-4.5.orig/src/chpasswd.c ++++ shadow-4.5/src/chpasswd.c +@@ -39,6 +39,13 @@ + #include + #include + #include ++#ifdef WITH_SELINUX ++#include ++#include ++#endif ++#ifdef WITH_LIBAUDIT ++#include ++#endif + #ifdef USE_PAM + #include "pam_defs.h" + #endif /* USE_PAM */ +@@ -297,6 +304,63 @@ static void check_perms (void) + #endif /* USE_PAM */ + } + ++#ifdef WITH_SELINUX ++static int ++log_callback (int type, const char *fmt, ...) ++{ ++ int audit_fd; ++ va_list ap; ++ ++ va_start(ap, fmt); ++#ifdef WITH_AUDIT ++ audit_fd = audit_open(); ++ ++ if (audit_fd >= 0) { ++ char *buf; ++ ++ if (vasprintf (&buf, fmt, ap) < 0) ++ goto ret; ++ audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, ++ NULL, 0); ++ audit_close(audit_fd); ++ free(buf); ++ goto ret; ++ } ++ ++#endif ++ vsyslog (LOG_USER | LOG_INFO, fmt, ap); ++ret: ++ va_end(ap); ++ return 0; ++} ++ ++static void ++selinux_check_root (void) ++{ ++ int status = -1; ++ security_context_t user_context; ++ union selinux_callback old_callback; ++ ++ if (is_selinux_enabled() < 1) ++ return; ++ ++ old_callback = selinux_get_callback(SELINUX_CB_LOG); ++ /* setup callbacks */ ++ selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback); ++ if ((status = getprevcon(&user_context)) < 0) { ++ selinux_set_callback(SELINUX_CB_LOG, old_callback); ++ exit(1); ++ } ++ ++ status = selinux_check_access(user_context, user_context, "passwd", "passwd", NULL); ++ ++ selinux_set_callback(SELINUX_CB_LOG, old_callback); ++ freecon(user_context); ++ if (status != 0 && security_getenforce() != 0) ++ exit(1); ++} ++#endif ++ + /* + * open_files - lock and open the password databases + */ +@@ -393,6 +457,10 @@ int main (int argc, char **argv) + (void) bindtextdomain (PACKAGE, LOCALEDIR); + (void) textdomain (PACKAGE); + ++#ifdef WITH_SELINUX ++ selinux_check_root (); ++#endif ++ + process_root_flag ("-R", argc, argv); + + process_flags (argc, argv); +@@ -405,6 +473,10 @@ int main (int argc, char **argv) + + OPENLOG ("chpasswd"); + ++#ifdef WITH_AUDIT ++ audit_help_open (); ++#endif ++ + check_perms (); + + #ifdef USE_PAM + if (!use_pam) + #endif /* USE_PAM */ +@@ -566,6 +638,11 @@ int main (int argc, char **argv) + newpw.pw_passwd = cp; + } + ++#ifdef WITH_AUDIT ++ audit_logger (AUDIT_USER_CHAUTHTOK, Prog, ++ "updating-password", ++ pw->pw_name, (unsigned int) pw->pw_uid, 1); ++#endif + /* + * The updated password file entry is then put back and will + * be written to the password file later, after all the +Index: shadow-4.5/src/Makefile.am +=================================================================== +--- shadow-4.5.orig/src/Makefile.am ++++ shadow-4.5/src/Makefile.am +@@ -87,9 +87,9 @@ chage_LDADD = $(LDADD) $(LIBPAM_SUID) + newuidmap_LDADD = $(LDADD) $(LIBSELINUX) + newgidmap_LDADD = $(LDADD) $(LIBSELINUX) + chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) +-chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT) ++chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT) + chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) +-chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) ++chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBAUDIT) $(LIBCRYPT) + gpasswd_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) + groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) + groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) diff --git a/shadow-4.5-crypt_h.patch b/shadow-4.5-crypt_h.patch new file mode 100644 index 0000000..c8d19e4 --- /dev/null +++ b/shadow-4.5-crypt_h.patch @@ -0,0 +1,41 @@ +Index: shadow-4.5/configure.ac +=================================================================== +--- shadow-4.5.orig/configure.ac ++++ shadow-4.5/configure.ac +@@ -32,9 +32,9 @@ AC_HEADER_STDC + AC_HEADER_SYS_WAIT + AC_HEADER_STDBOOL + +-AC_CHECK_HEADERS(errno.h fcntl.h limits.h unistd.h sys/time.h utmp.h \ +- utmpx.h termios.h termio.h sgtty.h sys/ioctl.h syslog.h paths.h \ +- utime.h ulimit.h sys/resource.h gshadow.h lastlog.h \ ++AC_CHECK_HEADERS(crypt.h errno.h fcntl.h limits.h unistd.h sys/time.h \ ++ utmp.h utmpx.h termios.h termio.h sgtty.h sys/ioctl.h syslog.h \ ++ paths.h utime.h ulimit.h sys/resource.h gshadow.h lastlog.h \ + locale.h rpc/key_prot.h netdb.h acl/libacl.h attr/libattr.h \ + attr/error_context.h) + +Index: shadow-4.5/lib/defines.h +=================================================================== +--- shadow-4.5.orig/lib/defines.h ++++ shadow-4.5/lib/defines.h +@@ -4,6 +4,8 @@ + #ifndef _DEFINES_H_ + #define _DEFINES_H_ + ++#include "config.h" ++ + #if HAVE_STDBOOL_H + # include + #else +@@ -94,6 +96,10 @@ char *strchr (), *strrchr (), *strtok () + # include + #endif + ++#if HAVE_CRYPT_H ++# include /* crypt(3) may be defined in here */ ++#endif ++ + #if TIME_WITH_SYS_TIME + # include + # include diff --git a/shadow-4.5-goodname.patch b/shadow-4.5-goodname.patch new file mode 100644 index 0000000..b107e3f --- /dev/null +++ b/shadow-4.5-goodname.patch @@ -0,0 +1,106 @@ +Index: shadow-4.5/libmisc/chkname.c +=================================================================== +--- shadow-4.5.orig/libmisc/chkname.c ++++ shadow-4.5/libmisc/chkname.c +@@ -47,27 +47,46 @@ + #include "chkname.h" + + static bool is_valid_name (const char *name) +-{ ++{ + /* +- * User/group names must match [a-z_][a-z0-9_-]*[$] +- */ +- if (('\0' == *name) || +- !((('a' <= *name) && ('z' >= *name)) || ('_' == *name))) { ++ * User/group names must match gnu e-regex: ++ * [a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,30}[a-zA-Z0-9_.$-]? ++ * ++ * as a non-POSIX, extension, allow "$" as the last char for ++ * sake of Samba 3.x "add machine script" ++ * ++ * Also do not allow fully numeric names or just "." or "..". ++ */ ++ int numeric; ++ ++ if ('\0' == *name || ++ ('.' == *name && (('.' == name[1] && '\0' == name[2]) || ++ '\0' == name[1])) || ++ !((*name >= 'a' && *name <= 'z') || ++ (*name >= 'A' && *name <= 'Z') || ++ (*name >= '0' && *name <= '9') || ++ *name == '_' || ++ *name == '.')) { + return false; + } + ++ numeric = isdigit(*name); ++ + while ('\0' != *++name) { +- if (!(( ('a' <= *name) && ('z' >= *name) ) || +- ( ('0' <= *name) && ('9' >= *name) ) || +- ('_' == *name) || +- ('-' == *name) || +- ( ('$' == *name) && ('\0' == *(name + 1)) ) ++ if (!((*name >= 'a' && *name <= 'z') || ++ (*name >= 'A' && *name <= 'Z') || ++ (*name >= '0' && *name <= '9') || ++ *name == '_' || ++ *name == '.' || ++ *name == '-' || ++ (*name == '$' && name[1] == '\0') + )) { + return false; + } ++ numeric &= isdigit(*name); + } + +- return true; ++ return !numeric; + } + + bool is_valid_user_name (const char *name) +Index: shadow-4.5/man/groupadd.8.xml +=================================================================== +--- shadow-4.5.orig/man/groupadd.8.xml ++++ shadow-4.5/man/groupadd.8.xml +@@ -256,10 +256,12 @@ + + CAVEATS + +- Groupnames must start with a lower case letter or an underscore, +- followed by lower case letters, digits, underscores, or dashes. +- They can end with a dollar sign. +- In regular expression terms: [a-z_][a-z0-9_-]*[$]? ++ Groupnames may contain only lower and upper case letters, digits, ++ underscores, or dashes. They can end with a dollar sign. ++ ++ Dashes are not allowed at the beginning of the groupname. ++ Fully numeric groupnames and groupnames . or .. are ++ also disallowed. + + + Groupnames may only be up to &GROUP_NAME_MAX_LENGTH; characters long. +Index: shadow-4.5/man/useradd.8.xml +=================================================================== +--- shadow-4.5.orig/man/useradd.8.xml ++++ shadow-4.5/man/useradd.8.xml +@@ -633,10 +633,14 @@ + + + +- Usernames must start with a lower case letter or an underscore, +- followed by lower case letters, digits, underscores, or dashes. +- They can end with a dollar sign. +- In regular expression terms: [a-z_][a-z0-9_-]*[$]? ++ Usernames may contain only lower and upper case letters, digits, ++ underscores, or dashes. They can end with a dollar sign. ++ ++ Dashes are not allowed at the beginning of the username. ++ Fully numeric usernames and usernames . or .. are ++ also disallowed. It is not recommended to use usernames beginning ++ with . character as their home directories will be hidden in ++ the ls output. + + + Usernames may only be up to 32 characters long. diff --git a/shadow-4.5-long-entry.patch b/shadow-4.5-long-entry.patch new file mode 100644 index 0000000..8670e75 --- /dev/null +++ b/shadow-4.5-long-entry.patch @@ -0,0 +1,84 @@ +diff -up shadow-4.5/lib/defines.h.long-entry shadow-4.5/lib/defines.h +--- shadow-4.5/lib/defines.h.long-entry 2014-09-01 16:36:40.000000000 +0200 ++++ shadow-4.5/lib/defines.h 2018-04-20 11:53:07.419308212 +0200 +@@ -382,4 +382,7 @@ extern char *strerror (); + # endif + #endif + ++/* Maximum length of passwd entry */ ++#define PASSWD_ENTRY_MAX_LENGTH 32768 ++ + #endif /* _DEFINES_H_ */ +diff -up shadow-4.5/lib/pwio.c.long-entry shadow-4.5/lib/pwio.c +--- shadow-4.5/lib/pwio.c.long-entry 2015-11-17 17:45:15.000000000 +0100 ++++ shadow-4.5/lib/pwio.c 2018-04-20 12:10:24.400837235 +0200 +@@ -79,7 +79,10 @@ static int passwd_put (const void *ent, + || (pw->pw_gid == (gid_t)-1) + || (valid_field (pw->pw_gecos, ":\n") == -1) + || (valid_field (pw->pw_dir, ":\n") == -1) +- || (valid_field (pw->pw_shell, ":\n") == -1)) { ++ || (valid_field (pw->pw_shell, ":\n") == -1) ++ || (strlen (pw->pw_name) + strlen (pw->pw_passwd) + ++ strlen (pw->pw_gecos) + strlen (pw->pw_dir) + ++ strlen (pw->pw_shell) + 100 > PASSWD_ENTRY_MAX_LENGTH)) { + return -1; + } + +diff -up shadow-4.5/lib/sgetpwent.c.long-entry shadow-4.5/lib/sgetpwent.c +--- shadow-4.5/lib/sgetpwent.c.long-entry 2014-09-01 16:36:40.000000000 +0200 ++++ shadow-4.5/lib/sgetpwent.c 2018-04-20 12:16:31.911513808 +0200 +@@ -57,7 +57,7 @@ + struct passwd *sgetpwent (const char *buf) + { + static struct passwd pwent; +- static char pwdbuf[1024]; ++ static char pwdbuf[PASSWD_ENTRY_MAX_LENGTH]; + register int i; + register char *cp; + char *fields[NFIELDS]; +@@ -67,8 +67,10 @@ struct passwd *sgetpwent (const char *bu + * the password structure remain valid. + */ + +- if (strlen (buf) >= sizeof pwdbuf) ++ if (strlen (buf) >= sizeof pwdbuf) { ++ fprintf (stderr, "Too long passwd entry encountered, file corruption?\n"); + return 0; /* fail if too long */ ++ } + strcpy (pwdbuf, buf); + + /* +diff -up shadow-4.5/lib/sgetspent.c.long-entry shadow-4.5/lib/sgetspent.c +--- shadow-4.5/lib/sgetspent.c.long-entry 2014-09-01 16:36:40.000000000 +0200 ++++ shadow-4.5/lib/sgetspent.c 2018-04-20 12:16:54.505056257 +0200 +@@ -48,7 +48,7 @@ + */ + struct spwd *sgetspent (const char *string) + { +- static char spwbuf[1024]; ++ static char spwbuf[PASSWD_ENTRY_MAX_LENGTH]; + static struct spwd spwd; + char *fields[FIELDS]; + char *cp; +@@ -61,6 +61,7 @@ struct spwd *sgetspent (const char *stri + */ + + if (strlen (string) >= sizeof spwbuf) { ++ fprintf (stderr, "Too long shadow entry encountered, file corruption?\n"); + return 0; /* fail if too long */ + } + strcpy (spwbuf, string); +diff -up shadow-4.5/lib/shadowio.c.long-entry shadow-4.5/lib/shadowio.c +--- shadow-4.5/lib/shadowio.c.long-entry 2016-12-07 06:30:41.000000001 +0100 ++++ shadow-4.5/lib/shadowio.c 2018-04-20 12:12:03.292171667 +0200 +@@ -79,7 +79,9 @@ static int shadow_put (const void *ent, + + if ( (NULL == sp) + || (valid_field (sp->sp_namp, ":\n") == -1) +- || (valid_field (sp->sp_pwdp, ":\n") == -1)) { ++ || (valid_field (sp->sp_pwdp, ":\n") == -1) ++ || (strlen (sp->sp_namp) + strlen (sp->sp_pwdp) + ++ 1000 > PASSWD_ENTRY_MAX_LENGTH)) { + return -1; + } + diff --git a/shadow-4.5-usermod-unlock.patch b/shadow-4.5-usermod-unlock.patch new file mode 100644 index 0000000..e2d70b5 --- /dev/null +++ b/shadow-4.5-usermod-unlock.patch @@ -0,0 +1,64 @@ +Index: shadow-4.5/src/usermod.c +=================================================================== +--- shadow-4.5.orig/src/usermod.c ++++ shadow-4.5/src/usermod.c +@@ -455,14 +455,17 @@ static char *new_pw_passwd (char *pw_pas + strcat (buf, pw_pass); + pw_pass = buf; + } else if (Uflg && pw_pass[0] == '!') { +- char *s; ++ char *s = pw_pass; + +- if (pw_pass[1] == '\0') { ++ while ('!' == *s) ++ ++s; ++ ++ if (*s == '\0') { + fprintf (stderr, + _("%s: unlocking the user's password would result in a passwordless account.\n" + "You should set a password with usermod -p to unlock this user's password.\n"), + Prog); +- return pw_pass; ++ return NULL; + } + + #ifdef WITH_AUDIT +@@ -471,12 +474,15 @@ static char *new_pw_passwd (char *pw_pas + user_newname, (unsigned int) user_newid, 1); + #endif + SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname)); +- s = pw_pass; +- while ('\0' != *s) { +- *s = *(s + 1); +- s++; +- } ++ memmove (pw_pass, s, strlen (s) + 1); + } else if (pflg) { ++ if (strchr (user_pass, ':') != NULL) { ++ fprintf (stderr, ++ _("%s: The password field cannot contain a colon character.\n"), ++ Prog); ++ return NULL; ++ ++ } + #ifdef WITH_AUDIT + audit_logger (AUDIT_USER_CHAUTHTOK, Prog, + "updating-password", +@@ -525,6 +531,8 @@ static void new_pwent (struct passwd *pw + if ( (!is_shadow_pwd) + || (strcmp (pwent->pw_passwd, SHADOW_PASSWD_STRING) != 0)) { + pwent->pw_passwd = new_pw_passwd (pwent->pw_passwd); ++ if (pwent->pw_passwd == NULL) ++ fail_exit (E_PW_UPDATE); + } + + if (uflg) { +@@ -639,6 +647,8 @@ static void new_spent (struct spwd *spen + * + aging has been requested + */ + spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp); ++ if (spent->sp_pwdp == NULL) ++ fail_exit(E_PW_UPDATE); + + if (pflg) { + spent->sp_lstchg = (long) gettime () / SCALE; diff --git a/shadow-4.6-audit-update.patch b/shadow-4.6-audit-update.patch new file mode 100644 index 0000000..4237739 --- /dev/null +++ b/shadow-4.6-audit-update.patch @@ -0,0 +1,2365 @@ +diff -up shadow-4.6/libmisc/audit_help.c.audit-update shadow-4.6/libmisc/audit_help.c +--- shadow-4.6/libmisc/audit_help.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/libmisc/audit_help.c 2018-05-28 15:01:09.913717564 +0200 +@@ -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 -up shadow-4.6/libmisc/cleanup_group.c.audit-update shadow-4.6/libmisc/cleanup_group.c +--- shadow-4.6/libmisc/cleanup_group.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/libmisc/cleanup_group.c 2018-05-28 15:01:09.913717564 +0200 +@@ -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 -up shadow-4.6/libmisc/cleanup_user.c.audit-update shadow-4.6/libmisc/cleanup_user.c +--- shadow-4.6/libmisc/cleanup_user.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/libmisc/cleanup_user.c 2018-05-28 15:01:09.913717564 +0200 +@@ -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 -up shadow-4.6/lib/prototypes.h.audit-update shadow-4.6/lib/prototypes.h +--- shadow-4.6/lib/prototypes.h.audit-update 2018-05-28 15:01:09.901717309 +0200 ++++ shadow-4.6/lib/prototypes.h 2018-05-28 15:01:09.913717564 +0200 +@@ -211,12 +211,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 -up shadow-4.6/src/gpasswd.c.audit-update shadow-4.6/src/gpasswd.c +--- shadow-4.6/src/gpasswd.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/src/gpasswd.c 2018-05-28 15:01:09.914717585 +0200 +@@ -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 +@@ -396,21 +396,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) { +@@ -418,13 +411,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) { +@@ -432,13 +421,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) { +@@ -446,13 +431,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) { +@@ -462,13 +443,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 + } +@@ -478,13 +455,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 + } +@@ -493,13 +466,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 + } +@@ -530,21 +499,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) { +@@ -552,13 +514,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) { +@@ -566,13 +524,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) { +@@ -580,13 +534,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) { +@@ -596,13 +546,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 + } +@@ -612,13 +558,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 + } +@@ -627,13 +569,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 -up shadow-4.6/src/groupadd.c.audit-update shadow-4.6/src/groupadd.c +--- shadow-4.6/src/groupadd.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/src/groupadd.c 2018-05-28 15:02:53.137910337 +0200 +@@ -130,6 +130,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 + * +@@ -213,7 +222,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 + /* +@@ -223,7 +232,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 */ + } +@@ -247,7 +256,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); + } + + /* +@@ -263,11 +272,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 +@@ -285,11 +294,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 +@@ -303,12 +312,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); +@@ -326,7 +329,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); + +@@ -336,7 +339,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); + } +@@ -352,7 +355,7 @@ static void open_files (void) + if (gr_open (O_CREAT | 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 +@@ -362,7 +365,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 */ +@@ -495,7 +498,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 && (prefix_getgrgid (group_id) != NULL)) { +@@ -514,7 +517,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); + } + } + } +@@ -542,7 +545,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); +@@ -562,7 +565,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 */ +@@ -595,7 +598,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: Cannot setup cleanup service.\n"), + Prog); +- exit (1); ++ fail_exit (1); + } + + /* +@@ -617,7 +620,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 -up shadow-4.6/src/groupdel.c.audit-update shadow-4.6/src/groupdel.c +--- shadow-4.6/src/groupdel.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/src/groupdel.c 2018-05-28 15:01:09.914717585 +0200 +@@ -105,6 +105,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 + * +@@ -131,7 +140,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 +@@ -143,7 +152,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 */ +@@ -162,12 +171,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 +@@ -187,12 +196,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 +@@ -206,13 +215,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); + } +@@ -229,7 +231,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 +@@ -238,7 +240,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); + } +@@ -256,7 +258,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) { +@@ -265,7 +267,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 */ +@@ -306,7 +308,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); + } + + /* +@@ -391,7 +393,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); +@@ -405,7 +407,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); +@@ -426,7 +428,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 */ +@@ -446,7 +448,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; +@@ -470,7 +472,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 -up shadow-4.6/src/groupmod.c.audit-update shadow-4.6/src/groupmod.c +--- shadow-4.6/src/groupmod.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/src/groupmod.c 2018-05-28 15:01:09.915717607 +0200 +@@ -449,7 +449,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); +@@ -472,7 +472,14 @@ static void close_files (void) + exit (E_GRP_UPDATE); + } + #ifdef WITH_AUDIT +- audit_logger (AUDIT_USER_ACCT, Prog, ++ /* If both happened, log password change as its more important */ ++ if (pflg) ++ audit_logger (AUDIT_GRP_CHAUTHTOK, Prog, ++ info_gshadow.audit_msg, ++ group_name, AUDIT_NO_ID, ++ SHADOW_AUDIT_SUCCESS); ++ else ++ audit_logger (AUDIT_GRP_MGMT, Prog, + info_gshadow.audit_msg, + group_name, AUDIT_NO_ID, + SHADOW_AUDIT_SUCCESS); +@@ -495,7 +502,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); +@@ -510,8 +517,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 +@@ -523,6 +530,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; +@@ -535,76 +544,109 @@ 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", (unsigned long)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) { ++ strncat(info_passwd.audit_msg, "op=change-password", ++ 511 - strlen (info_passwd.action)); ++ ++ /* 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", (unsigned long)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'; +@@ -612,6 +654,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 -up shadow-4.6/src/chage.c.audit-update shadow-4.6/src/chage.c +--- shadow-4.6/src/chage.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/src/chage.c 2018-05-28 15:01:09.915717607 +0200 +@@ -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 -up shadow-4.6/src/newgrp.c.audit-update shadow-4.6/src/newgrp.c +--- shadow-4.6/src/newgrp.c.audit-update 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/src/newgrp.c 2018-05-28 15:01:09.915717607 +0200 +@@ -206,11 +206,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'", +@@ -221,11 +222,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 + } + +@@ -236,19 +238,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); + } + +@@ -320,15 +309,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); +@@ -457,7 +458,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 ())); +@@ -573,15 +574,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); +@@ -738,10 +750,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); + } +@@ -750,10 +762,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); + } +@@ -767,10 +779,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); +@@ -834,11 +846,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 +@@ -862,15 +874,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 -up shadow-4.6/src/useradd.c.audit-update shadow-4.6/src/useradd.c +--- shadow-4.6/src/useradd.c.audit-update 2018-05-28 15:01:09.903717352 +0200 ++++ shadow-4.6/src/useradd.c 2018-05-28 15:06:36.824662074 +0200 +@@ -229,6 +229,8 @@ static void create_mail (void); + */ + static void fail_exit (int code) + { ++ int type; ++ + if (home_added) { + if (rmdir (prefix_user_home) != 0) { + fprintf (stderr, +@@ -242,12 +244,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 */ + } + } +@@ -255,12 +251,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 */ + } + } +@@ -268,12 +258,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 */ + } + } +@@ -282,12 +266,6 @@ 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 */ + } + } +@@ -297,12 +275,6 @@ static void fail_exit (int code) + if (sub_uid_unlock () == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); +-#ifdef WITH_AUDIT +- audit_logger (AUDIT_ADD_USER, Prog, +- "unlocking subordinate user file", +- user_name, AUDIT_NO_ID, +- SHADOW_AUDIT_FAILURE); +-#endif + /* continue */ + } + } +@@ -310,20 +282,19 @@ static void fail_exit (int code) + if (sub_gid_unlock () == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); +-#ifdef WITH_AUDIT +- audit_logger (AUDIT_ADD_USER, Prog, +- "unlocking subordinate group file", +- user_name, AUDIT_NO_ID, +- SHADOW_AUDIT_FAILURE); +-#endif + /* continue */ + } + } + #endif /* ENABLE_SUBIDS */ + + #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 +@@ -673,7 +644,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 +@@ -950,12 +921,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 */ + } + +@@ -969,18 +934,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, +@@ -1025,12 +984,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 */ + } + +@@ -1044,18 +997,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, +@@ -1407,7 +1355,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 +@@ -1522,7 +1470,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 +@@ -1535,7 +1483,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 +@@ -1547,7 +1495,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 +@@ -1561,7 +1509,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 +@@ -1577,7 +1525,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + #ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, +- "unlocking subordinate user file", ++ "unlocking-subordinate-user-file", + user_name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); + #endif +@@ -1591,7 +1539,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + #ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, +- "unlocking subordinate group file", ++ "unlocking-subordinate-group-file", + user_name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); + #endif +@@ -1783,7 +1731,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 +@@ -1799,7 +1747,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 +@@ -1809,7 +1757,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 +@@ -1970,12 +1918,6 @@ 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 ENABLE_SUBIDS +@@ -1996,9 +1938,14 @@ static void usr_update (void) + #endif /* ENABLE_SUBIDS */ + + #ifdef WITH_AUDIT ++ /* ++ * Even though we have the ID of the user, we won't send it now ++ * because its not written to disk yet. After close_files it is ++ * and we can use the real ID thereafter. ++ */ + audit_logger (AUDIT_ADD_USER, Prog, +- "adding user", +- user_name, (unsigned int) user_id, ++ "add-user", ++ user_name, AUDIT_NO_ID, + SHADOW_AUDIT_SUCCESS); + #endif + /* +@@ -2032,12 +1979,6 @@ static void create_home (void) + fprintf (stderr, + _("%s: cannot create directory %s\n"), + Prog, prefix_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); + } + (void) chown (prefix_user_home, user_id, user_gid); +@@ -2045,8 +1986,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 +@@ -2231,12 +2172,6 @@ int main (int argc, char **argv) + */ + if (prefix_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); + } + +@@ -2252,12 +2187,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); + } + } +@@ -2287,12 +2216,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); + } + } +@@ -2365,9 +2283,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 -up shadow-4.6/src/userdel.c.audit-update shadow-4.6/src/userdel.c +--- shadow-4.6/src/userdel.c.audit-update 2018-05-28 15:01:09.909717479 +0200 ++++ shadow-4.6/src/userdel.c 2018-05-28 15:01:09.916717628 +0200 +@@ -219,9 +219,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", +@@ -281,9 +281,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", +@@ -360,9 +360,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, +@@ -378,9 +378,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, +@@ -542,7 +542,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 */ +@@ -562,24 +562,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_CREAT | 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) { +@@ -587,12 +575,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; +@@ -600,12 +582,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); + } + } +@@ -613,23 +589,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_CREAT | 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 +@@ -638,24 +602,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_CREAT | 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); + } + } +@@ -666,24 +618,12 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_uid_dbname ()); +-#ifdef WITH_AUDIT +- audit_logger (AUDIT_DEL_USER, Prog, +- "locking subordinate user file", +- user_name, (unsigned int) user_id, +- SHADOW_AUDIT_FAILURE); +-#endif /* WITH_AUDIT */ + fail_exit (E_SUB_UID_UPDATE); + } + sub_uid_locked = true; + if (sub_uid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, sub_uid_dbname ()); +-#ifdef WITH_AUDIT +- audit_logger (AUDIT_DEL_USER, Prog, +- "opening subordinate user file", +- user_name, (unsigned int) user_id, +- SHADOW_AUDIT_FAILURE); +-#endif /* WITH_AUDIT */ + fail_exit (E_SUB_UID_UPDATE); + } + } +@@ -692,24 +632,12 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_gid_dbname ()); +-#ifdef WITH_AUDIT +- audit_logger (AUDIT_DEL_USER, Prog, +- "locking subordinate group file", +- user_name, (unsigned int) user_id, +- SHADOW_AUDIT_FAILURE); +-#endif /* WITH_AUDIT */ + fail_exit (E_SUB_GID_UPDATE); + } + sub_gid_locked = true; + if (sub_gid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, sub_gid_dbname ()); +-#ifdef WITH_AUDIT +- audit_logger (AUDIT_DEL_USER, Prog, +- "opening subordinate group file", +- user_name, (unsigned int) user_id, +- SHADOW_AUDIT_FAILURE); +-#endif /* WITH_AUDIT */ + fail_exit (E_SUB_GID_UPDATE); + } + } +@@ -754,7 +682,7 @@ static void update_user (void) + #endif /* ENABLE_SUBIDS */ + #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 */ +@@ -862,7 +790,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 */ +@@ -879,7 +807,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 */ +@@ -889,8 +817,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); + } +@@ -908,7 +836,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 */ +@@ -925,7 +853,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 */ +@@ -935,8 +863,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); + } +@@ -1149,7 +1077,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 */ +@@ -1205,7 +1133,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 */ +@@ -1282,8 +1210,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); + } +@@ -1292,7 +1220,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); + } +@@ -1305,8 +1233,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 -up shadow-4.6/src/usermod.c.audit-update shadow-4.6/src/usermod.c +--- shadow-4.6/src/usermod.c.audit-update 2018-05-28 15:01:09.912717543 +0200 ++++ shadow-4.6/src/usermod.c 2018-05-28 15:08:25.424969050 +0200 +@@ -453,8 +453,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, "!"); +@@ -473,8 +473,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; +@@ -485,7 +485,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)); +@@ -514,8 +514,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, +@@ -535,8 +535,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, +@@ -546,8 +546,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, +@@ -557,8 +557,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; +@@ -566,8 +566,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, +@@ -577,8 +577,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, +@@ -608,8 +608,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, +@@ -625,8 +625,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, +@@ -709,9 +709,9 @@ static /*@noreturn@*/void fail_exit (int + #endif /* ENABLE_SUBIDS */ + + #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); + } +@@ -765,9 +765,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'", +@@ -781,9 +784,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'", +@@ -796,9 +801,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)); +@@ -873,9 +880,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-shadow-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'", +@@ -895,9 +903,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-shadow-group", ++ user_name, AUDIT_NO_ID, ++ nsgrp->sg_name, 1); + #endif + SYSLOG ((LOG_INFO, + "change '%s' to '%s' in shadow group '%s'", +@@ -911,9 +920,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-shadow-group", ++ user_name, AUDIT_NO_ID, ++ nsgrp->sg_name, 1); + #endif + SYSLOG ((LOG_INFO, + "delete '%s' from shadow group '%s'", +@@ -926,9 +936,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-shadow-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)); +@@ -1789,8 +1800,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 +@@ -1808,8 +1819,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 +@@ -1828,9 +1839,9 @@ static void move_home (void) + Prog, prefix_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); +@@ -2045,8 +2056,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 +@@ -2072,8 +2083,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 +@@ -2267,8 +2278,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 */ +@@ -2280,8 +2291,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 */ +@@ -2319,8 +2330,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.6-chgrp-guard.patch b/shadow-4.6-chgrp-guard.patch new file mode 100644 index 0000000..2918684 --- /dev/null +++ b/shadow-4.6-chgrp-guard.patch @@ -0,0 +1,44 @@ +diff -up shadow-4.6/man/usermod.8.xml.chgrp-guard shadow-4.6/man/usermod.8.xml +--- shadow-4.6/man/usermod.8.xml.chgrp-guard 2018-11-06 09:08:54.170095358 +0100 ++++ shadow-4.6/man/usermod.8.xml 2018-12-18 15:24:12.283181180 +0100 +@@ -195,6 +195,12 @@ + The group ownership of files outside of the user's home directory + must be fixed manually. + ++ ++ The change of the group ownership of files inside of the user's ++ home directory is also not done if the home dir owner uid is ++ different from the current or new user id. This is safety measure ++ for special home directories such as /. ++ + + + +@@ -372,6 +378,12 @@ + must be fixed manually. + + ++ The change of the user ownership of files inside of the user's ++ home directory is also not done if the home dir owner uid is ++ different from the current or new user id. This is safety measure ++ for special home directories such as /. ++ ++ + No checks will be performed with regard to the + , , + , or +diff -up shadow-4.6/src/usermod.c.chgrp-guard shadow-4.6/src/usermod.c +--- shadow-4.6/src/usermod.c.chgrp-guard 2018-12-18 15:24:12.286181249 +0100 ++++ shadow-4.6/src/usermod.c 2018-12-18 15:26:51.227841435 +0100 +@@ -2336,7 +2336,10 @@ int main (int argc, char **argv) + } + + if (!mflg && (uflg || gflg)) { +- if (access (dflg ? prefix_user_newhome : prefix_user_home, F_OK) == 0) { ++ struct stat sb; ++ ++ if (stat (dflg ? prefix_user_newhome : prefix_user_home, &sb) == 0 && ++ ((uflg && sb.st_uid == user_newid) || sb.st_uid == user_id)) { + /* + * Change the UID on all of the files owned by + * `user_id' to `user_newid' in the user's home diff --git a/shadow-4.6-coverity.patch b/shadow-4.6-coverity.patch new file mode 100644 index 0000000..44533e2 --- /dev/null +++ b/shadow-4.6-coverity.patch @@ -0,0 +1,223 @@ +diff -up shadow-4.6/lib/commonio.c.coverity shadow-4.6/lib/commonio.c +--- shadow-4.6/lib/commonio.c.coverity 2018-10-10 09:50:59.307738194 +0200 ++++ shadow-4.6/lib/commonio.c 2018-10-10 09:55:32.919319048 +0200 +@@ -382,7 +382,7 @@ int commonio_lock_nowait (struct commoni + char* lock = NULL; + size_t lock_file_len; + size_t file_len; +- int err; ++ int err = 0; + + if (db->locked) { + return 1; +@@ -391,12 +391,10 @@ int commonio_lock_nowait (struct commoni + lock_file_len = strlen(db->filename) + 6; /* sizeof ".lock" */ + file = (char*)malloc(file_len); + if(file == NULL) { +- err = ENOMEM; + goto cleanup_ENOMEM; + } + lock = (char*)malloc(lock_file_len); + if(lock == NULL) { +- err = ENOMEM; + goto cleanup_ENOMEM; + } + snprintf (file, file_len, "%s.%lu", +diff -up shadow-4.6/libmisc/console.c.coverity shadow-4.6/libmisc/console.c +--- shadow-4.6/libmisc/console.c.coverity 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/libmisc/console.c 2018-10-10 11:56:51.368837533 +0200 +@@ -50,7 +50,7 @@ static bool is_listed (const char *cfgin + static bool is_listed (const char *cfgin, const char *tty, bool def) + { + FILE *fp; +- char buf[200], *s; ++ char buf[1024], *s; + const char *cons; + + /* +@@ -70,7 +70,8 @@ static bool is_listed (const char *cfgin + + if (*cons != '/') { + char *pbuf; +- strcpy (buf, cons); ++ strncpy (buf, cons, sizeof (buf)); ++ buf[sizeof (buf) - 1] = '\0'; + pbuf = &buf[0]; + while ((s = strtok (pbuf, ":")) != NULL) { + if (strcmp (s, tty) == 0) { +diff -up shadow-4.6/lib/spawn.c.coverity shadow-4.6/lib/spawn.c +--- shadow-4.6/lib/spawn.c.coverity 2018-04-29 18:42:37.000000001 +0200 ++++ shadow-4.6/lib/spawn.c 2018-10-10 11:36:49.035784609 +0200 +@@ -69,7 +69,7 @@ int run_command (const char *cmd, const + do { + wpid = waitpid (pid, status, 0); + } while ( ((pid_t)-1 == wpid && errno == EINTR) +- || (wpid != pid)); ++ || ((pid_t)-1 != wpid && wpid != pid)); + + if ((pid_t)-1 == wpid) { + fprintf (stderr, "%s: waitpid (status: %d): %s\n", +diff -up shadow-4.6/src/useradd.c.coverity shadow-4.6/src/useradd.c +--- shadow-4.6/src/useradd.c.coverity 2018-10-10 09:50:59.303738098 +0200 ++++ shadow-4.6/src/useradd.c 2018-10-12 13:51:54.480490257 +0200 +@@ -314,7 +314,7 @@ static void fail_exit (int code) + static void get_defaults (void) + { + FILE *fp; +- char* default_file = USER_DEFAULTS_FILE; ++ char *default_file = USER_DEFAULTS_FILE; + char buf[1024]; + char *cp; + +@@ -324,6 +324,8 @@ static void get_defaults (void) + + len = strlen(prefix) + strlen(USER_DEFAULTS_FILE) + 2; + default_file = malloc(len); ++ if (default_file == NULL) ++ return; + wlen = snprintf(default_file, len, "%s/%s", prefix, USER_DEFAULTS_FILE); + assert (wlen == (int) len -1); + } +@@ -334,7 +336,7 @@ static void get_defaults (void) + + fp = fopen (default_file, "r"); + if (NULL == fp) { +- return; ++ goto getdef_err; + } + + /* +@@ -445,7 +447,7 @@ static void get_defaults (void) + } + } + (void) fclose (fp); +- ++ getdef_err: + if(prefix[0]) { + free(default_file); + } +@@ -480,8 +482,8 @@ static int set_defaults (void) + FILE *ifp; + FILE *ofp; + char buf[1024]; +- char* new_file = NEW_USER_FILE; +- char* default_file = USER_DEFAULTS_FILE; ++ char *new_file = NULL; ++ char *default_file = USER_DEFAULTS_FILE; + char *cp; + int ofd; + int wlen; +@@ -492,17 +494,30 @@ static int set_defaults (void) + bool out_shell = false; + bool out_skel = false; + bool out_create_mail_spool = false; ++ size_t len; ++ int ret = -1; + +- if(prefix[0]) { +- size_t len; + +- len = strlen(prefix) + strlen(NEW_USER_FILE) + 2; +- new_file = malloc(len); +- wlen = snprintf(new_file, len, "%s/%s", prefix, NEW_USER_FILE); +- assert (wlen == (int) len -1); ++ len = strlen(prefix) + strlen(NEW_USER_FILE) + 2; ++ new_file = malloc(len); ++ if (new_file == NULL) { ++ fprintf (stderr, ++ _("%s: cannot create new defaults file: %s\n"), ++ Prog, strerror(errno)); ++ return -1; ++ } ++ wlen = snprintf(new_file, len, "%s%s%s", prefix, prefix[0]?"/":"", NEW_USER_FILE); ++ assert (wlen <= (int) len -1); + ++ if(prefix[0]) { + len = strlen(prefix) + strlen(USER_DEFAULTS_FILE) + 2; + default_file = malloc(len); ++ if (default_file == NULL) { ++ fprintf (stderr, ++ _("%s: cannot create new defaults file: %s\n"), ++ Prog, strerror(errno)); ++ goto setdef_err; ++ } + wlen = snprintf(default_file, len, "%s/%s", prefix, USER_DEFAULTS_FILE); + assert (wlen == (int) len -1); + } +@@ -515,7 +530,7 @@ static int set_defaults (void) + fprintf (stderr, + _("%s: cannot create new defaults file\n"), + Prog); +- return -1; ++ goto setdef_err; + } + + ofp = fdopen (ofd, "w"); +@@ -523,7 +538,7 @@ static int set_defaults (void) + fprintf (stderr, + _("%s: cannot open new defaults file\n"), + Prog); +- return -1; ++ goto setdef_err; + } + + /* +@@ -550,7 +565,7 @@ static int set_defaults (void) + _("%s: line too long in %s: %s..."), + Prog, default_file, buf); + (void) fclose (ifp); +- return -1; ++ goto setdef_err; + } + } + +@@ -614,7 +629,7 @@ static int set_defaults (void) + || (fsync (fileno (ofp)) != 0) + || (fclose (ofp) != 0)) { + unlink (new_file); +- return -1; ++ goto setdef_err; + } + + /* +@@ -629,7 +644,7 @@ static int set_defaults (void) + _("%s: Cannot create backup file (%s): %s\n"), + Prog, buf, strerror (err)); + unlink (new_file); +- return -1; ++ goto setdef_err; + } + + /* +@@ -640,11 +655,11 @@ static int set_defaults (void) + fprintf (stderr, + _("%s: rename: %s: %s\n"), + Prog, new_file, strerror (err)); +- return -1; ++ goto setdef_err; + } + #ifdef WITH_AUDIT + audit_logger (AUDIT_USYS_CONFIG, Prog, +- "changing-useradd-defaults", ++ "changing useradd defaults", + NULL, AUDIT_NO_ID, + SHADOW_AUDIT_SUCCESS); + #endif +@@ -654,13 +669,14 @@ static int set_defaults (void) + (unsigned int) def_group, def_home, def_shell, + def_inactive, def_expire, def_template, + def_create_mail_spool)); +- ++ ret = 0; ++ setdef_err: ++ free(new_file); + if(prefix[0]) { +- free(new_file); + free(default_file); + } + +- return 0; ++ return ret; + } + + /* diff --git a/shadow-4.6-getenforce.patch b/shadow-4.6-getenforce.patch new file mode 100644 index 0000000..8a55bf5 --- /dev/null +++ b/shadow-4.6-getenforce.patch @@ -0,0 +1,21 @@ +diff -up shadow-4.6/lib/selinux.c.getenforce shadow-4.6/lib/selinux.c +--- shadow-4.6/lib/selinux.c.getenforce 2018-05-28 15:10:15.870315221 +0200 ++++ shadow-4.6/lib/selinux.c 2018-05-28 15:10:15.894315731 +0200 +@@ -75,7 +75,7 @@ int set_selinux_file_context (const char + } + return 0; + error: +- if (security_getenforce () != 0) { ++ if (security_getenforce () > 0) { + return 1; + } + return 0; +@@ -95,7 +95,7 @@ int reset_selinux_file_context (void) + selinux_checked = true; + } + if (selinux_enabled) { +- if (setfscreatecon (NULL) != 0) { ++ if (setfscreatecon (NULL) != 0 && security_getenforce () > 0) { + return 1; + } + } diff --git a/shadow-4.6-ignore-login-prompt.patch b/shadow-4.6-ignore-login-prompt.patch new file mode 100644 index 0000000..faf50f7 --- /dev/null +++ b/shadow-4.6-ignore-login-prompt.patch @@ -0,0 +1,11 @@ +diff -up shadow-4.6/lib/getdef.c.login-prompt shadow-4.6/lib/getdef.c +--- shadow-4.6/lib/getdef.c.login-prompt 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/lib/getdef.c 2019-03-21 15:06:58.009280504 +0100 +@@ -94,6 +94,7 @@ static struct itemdef def_table[] = { + {"KILLCHAR", NULL}, + {"LOGIN_RETRIES", NULL}, + {"LOGIN_TIMEOUT", NULL}, ++ {"LOGIN_PLAIN_PROMPT", NULL}, + {"LOG_OK_LOGINS", NULL}, + {"LOG_UNKFAIL_ENAB", NULL}, + {"MAIL_DIR", NULL}, diff --git a/shadow-4.6-move-home.patch b/shadow-4.6-move-home.patch new file mode 100644 index 0000000..cff9561 --- /dev/null +++ b/shadow-4.6-move-home.patch @@ -0,0 +1,15 @@ +diff -up shadow-4.6/src/usermod.c.move-home shadow-4.6/src/usermod.c +--- shadow-4.6/src/usermod.c.move-home 2018-05-28 14:59:05.594076665 +0200 ++++ shadow-4.6/src/usermod.c 2018-05-28 15:00:28.479837392 +0200 +@@ -1845,6 +1845,11 @@ static void move_home (void) + Prog, prefix_user_home, prefix_user_newhome); + fail_exit (E_HOMEDIR); + } ++ } else { ++ fprintf (stderr, ++ _("%s: The previous home directory (%s) does " ++ "not exist or is inaccessible. Move cannot be completed.\n"), ++ Prog, prefix_user_home); + } + } + diff --git a/shadow-4.6-orig-context.patch b/shadow-4.6-orig-context.patch new file mode 100644 index 0000000..ea522e7 --- /dev/null +++ b/shadow-4.6-orig-context.patch @@ -0,0 +1,128 @@ +diff -up shadow-4.6/lib/commonio.c.orig-context shadow-4.6/lib/commonio.c +--- shadow-4.6/lib/commonio.c.orig-context 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/lib/commonio.c 2018-05-28 14:56:37.287929667 +0200 +@@ -961,7 +961,7 @@ int commonio_close (struct commonio_db * + snprintf (buf, sizeof buf, "%s-", db->filename); + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (buf) != 0) { ++ if (set_selinux_file_context (buf, db->filename) != 0) { + errors++; + } + #endif +@@ -994,7 +994,7 @@ int commonio_close (struct commonio_db * + snprintf (buf, sizeof buf, "%s+", db->filename); + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (buf) != 0) { ++ if (set_selinux_file_context (buf, db->filename) != 0) { + errors++; + } + #endif +diff -up shadow-4.6/libmisc/copydir.c.orig-context shadow-4.6/libmisc/copydir.c +--- shadow-4.6/libmisc/copydir.c.orig-context 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/libmisc/copydir.c 2018-05-28 14:56:37.287929667 +0200 +@@ -484,7 +484,7 @@ static int copy_dir (const char *src, co + */ + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst) != 0) { ++ if (set_selinux_file_context (dst, NULL) != 0) { + return -1; + } + #endif /* WITH_SELINUX */ +@@ -605,7 +605,7 @@ static int copy_symlink (const char *src + } + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst) != 0) { ++ if (set_selinux_file_context (dst, NULL) != 0) { + free (oldlink); + return -1; + } +@@ -684,7 +684,7 @@ static int copy_special (const char *src + int err = 0; + + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst) != 0) { ++ if (set_selinux_file_context (dst, NULL) != 0) { + return -1; + } + #endif /* WITH_SELINUX */ +@@ -744,7 +744,7 @@ static int copy_file (const char *src, c + return -1; + } + #ifdef WITH_SELINUX +- if (set_selinux_file_context (dst) != 0) { ++ if (set_selinux_file_context (dst, NULL) != 0) { + return -1; + } + #endif /* WITH_SELINUX */ +diff -up shadow-4.6/lib/prototypes.h.orig-context shadow-4.6/lib/prototypes.h +--- shadow-4.6/lib/prototypes.h.orig-context 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/lib/prototypes.h 2018-05-28 14:56:37.287929667 +0200 +@@ -326,7 +326,7 @@ extern /*@observer@*/const char *crypt_m + + /* selinux.c */ + #ifdef WITH_SELINUX +-extern int set_selinux_file_context (const char *dst_name); ++extern int set_selinux_file_context (const char *dst_name, const char *orig_name); + extern int reset_selinux_file_context (void); + #endif + +diff -up shadow-4.6/lib/selinux.c.orig-context shadow-4.6/lib/selinux.c +--- shadow-4.6/lib/selinux.c.orig-context 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/lib/selinux.c 2018-05-28 14:56:37.287929667 +0200 +@@ -50,7 +50,7 @@ static bool selinux_enabled; + * Callers may have to Reset SELinux to create files with default + * contexts with reset_selinux_file_context + */ +-int set_selinux_file_context (const char *dst_name) ++int set_selinux_file_context (const char *dst_name, const char *orig_name) + { + /*@null@*/security_context_t scontext = NULL; + +@@ -62,19 +62,23 @@ int set_selinux_file_context (const char + if (selinux_enabled) { + /* Get the default security context for this file */ + if (matchpathcon (dst_name, 0, &scontext) < 0) { +- if (security_getenforce () != 0) { +- return 1; +- } ++ /* We could not get the default, copy the original */ ++ if (orig_name == NULL) ++ goto error; ++ if (getfilecon (orig_name, &scontext) < 0) ++ goto error; + } + /* Set the security context for the next created file */ +- if (setfscreatecon (scontext) < 0) { +- if (security_getenforce () != 0) { +- return 1; +- } +- } ++ if (setfscreatecon (scontext) < 0) ++ goto error; + freecon (scontext); + } + return 0; ++ error: ++ if (security_getenforce () != 0) { ++ return 1; ++ } ++ return 0; + } + + /* +diff -up shadow-4.6/src/useradd.c.orig-context shadow-4.6/src/useradd.c +--- shadow-4.6/src/useradd.c.orig-context 2018-05-28 14:56:37.288929688 +0200 ++++ shadow-4.6/src/useradd.c 2018-05-28 14:58:02.242730903 +0200 +@@ -2020,7 +2020,7 @@ static void create_home (void) + { + if (access (prefix_user_home, F_OK) != 0) { + #ifdef WITH_SELINUX +- if (set_selinux_file_context (prefix_user_home) != 0) { ++ if (set_selinux_file_context (prefix_user_home, NULL) != 0) { + fprintf (stderr, + _("%s: cannot set SELinux context for home directory %s\n"), + Prog, user_home); diff --git a/shadow-4.6-redhat.patch b/shadow-4.6-redhat.patch new file mode 100644 index 0000000..7a8be2e --- /dev/null +++ b/shadow-4.6-redhat.patch @@ -0,0 +1,41 @@ +diff -up shadow-4.6/src/useradd.c.redhat shadow-4.6/src/useradd.c +--- shadow-4.6/src/useradd.c.redhat 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/src/useradd.c 2018-05-28 13:37:16.695651258 +0200 +@@ -98,7 +98,7 @@ const char *Prog; + static gid_t def_group = 100; + static const char *def_gname = "other"; + static const char *def_home = "/home"; +-static const char *def_shell = ""; ++static const char *def_shell = "/sbin/nologin"; + static const char *def_template = SKEL_DIR; + static const char *def_create_mail_spool = "no"; + +@@ -108,7 +108,7 @@ static const char *def_expire = ""; + #define VALID(s) (strcspn (s, ":\n") == strlen (s)) + + static const char *user_name = ""; +-static const char *user_pass = "!"; ++static const char *user_pass = "!!"; + static uid_t user_id; + static gid_t user_gid; + static const char *user_comment = ""; +@@ -1114,9 +1114,9 @@ static void process_flags (int argc, cha + }; + while ((c = getopt_long (argc, argv, + #ifdef WITH_SELINUX +- "b:c:d:De:f:g:G:hk:K:lmMNop:rR:P:s:u:UZ:", ++ "b:c:d:De:f:g:G:hk:K:lmMnNop:rR:P:s:u:UZ:", + #else /* !WITH_SELINUX */ +- "b:c:d:De:f:g:G:hk:K:lmMNop:rR:P:s:u:U", ++ "b:c:d:De:f:g:G:hk:K:lmMnNop:rR:P:s:u:U", + #endif /* !WITH_SELINUX */ + long_options, NULL)) != -1) { + switch (c) { +@@ -1267,6 +1267,7 @@ static void process_flags (int argc, cha + case 'M': + Mflg = true; + break; ++ case 'n': + case 'N': + Nflg = true; + break; diff --git a/shadow-4.6-selinux.patch b/shadow-4.6-selinux.patch new file mode 100644 index 0000000..dfd5140 --- /dev/null +++ b/shadow-4.6-selinux.patch @@ -0,0 +1,115 @@ +diff -up shadow-4.6/lib/semanage.c.selinux shadow-4.6/lib/semanage.c +--- shadow-4.6/lib/semanage.c.selinux 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/lib/semanage.c 2018-05-28 13:38:20.551008911 +0200 +@@ -294,6 +294,9 @@ int set_seuser (const char *login_name, + + ret = 0; + ++ /* drop obsolete matchpathcon cache */ ++ matchpathcon_fini(); ++ + done: + semanage_seuser_key_free (key); + semanage_handle_destroy (handle); +@@ -369,6 +372,10 @@ int del_seuser (const char *login_name) + } + + ret = 0; ++ ++ /* drop obsolete matchpathcon cache */ ++ matchpathcon_fini(); ++ + done: + semanage_handle_destroy (handle); + return ret; +diff -up shadow-4.6/src/useradd.c.selinux shadow-4.6/src/useradd.c +--- shadow-4.6/src/useradd.c.selinux 2018-05-28 13:43:30.996748997 +0200 ++++ shadow-4.6/src/useradd.c 2018-05-28 13:44:04.645486199 +0200 +@@ -2120,6 +2120,7 @@ static void create_mail (void) + */ + int main (int argc, char **argv) + { ++ int rv = E_SUCCESS; + #ifdef ACCT_TOOLS_SETUID + #ifdef USE_PAM + pam_handle_t *pamh = NULL; +@@ -2342,27 +2343,11 @@ int main (int argc, char **argv) + + usr_update (); + +- if (mflg) { +- create_home (); +- if (home_added) { +- copy_tree (def_template, prefix_user_home, false, false, +- (uid_t)-1, user_id, (gid_t)-1, user_gid); +- } else { +- fprintf (stderr, +- _("%s: warning: the home directory already exists.\n" +- "Not copying any file from skel directory into it.\n"), +- Prog); +- } +- +- } +- +- /* Do not create mail directory for system accounts */ +- if (!rflg) { +- create_mail (); +- } +- + close_files (); + ++ nscd_flush_cache ("passwd"); ++ nscd_flush_cache ("group"); ++ + /* + * tallylog_reset needs to be able to lookup + * a valid existing user name, +@@ -2373,8 +2358,9 @@ int main (int argc, char **argv) + } + + #ifdef WITH_SELINUX +- if (Zflg) { +- if (set_seuser (user_name, user_selinux) != 0) { ++ if (Zflg && *user_selinux) { ++ if (is_selinux_enabled () > 0) { ++ if (set_seuser (user_name, user_selinux) != 0) { + fprintf (stderr, + _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"), + Prog, user_name, user_selinux); +@@ -2383,14 +2369,31 @@ int main (int argc, char **argv) + "adding SELinux user mapping", + user_name, (unsigned int) user_id, 0); + #endif /* WITH_AUDIT */ +- fail_exit (E_SE_UPDATE); ++ rv = E_SE_UPDATE; ++ } + } + } +-#endif /* WITH_SELINUX */ ++#endif + +- nscd_flush_cache ("passwd"); +- nscd_flush_cache ("group"); ++ if (mflg) { ++ create_home (); ++ if (home_added) { ++ copy_tree (def_template, prefix_user_home, false, true, ++ (uid_t)-1, user_id, (gid_t)-1, user_gid); ++ } else { ++ fprintf (stderr, ++ _("%s: warning: the home directory already exists.\n" ++ "Not copying any file from skel directory into it.\n"), ++ Prog); ++ } ++ ++ } ++ ++ /* Do not create mail directory for system accounts */ ++ if (!rflg) { ++ create_mail (); ++ } + +- return E_SUCCESS; ++ return rv; + } + diff --git a/shadow-4.6-sssd-flush.patch b/shadow-4.6-sssd-flush.patch new file mode 100644 index 0000000..025c1d3 --- /dev/null +++ b/shadow-4.6-sssd-flush.patch @@ -0,0 +1,641 @@ +From 4aaf05d72e9d6daf348cefb8a6ad35d2966cbe9b Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Wed, 12 Sep 2018 14:22:11 +0200 +Subject: [PATCH] Flush sssd caches in addition to nscd caches + +Some distributions, notably Fedora, have the following order of nsswitch +modules by default: + passwd: sss files + group: sss files + +The advantage of serving local users through SSSD is that the nss_sss +module has a fast mmapped-cache that speeds up NSS lookups compared to +accessing the disk an opening the files on each NSS request. + +Traditionally, this has been done with the help of nscd, but using nscd +in parallel with sssd is cumbersome, as both SSSD and nscd use their own +independent caching, so using nscd in setups where sssd is also serving +users from some remote domain (LDAP, AD, ...) can result in a bit of +unpredictability. + +More details about why Fedora chose to use sss before files can be found +on e.g.: + https://fedoraproject.org//wiki/Changes/SSSDCacheForLocalUsers +or: + https://docs.pagure.org/SSSD.sssd/design_pages/files_provider.html + +Now, even though sssd watches the passwd and group files with the help +of inotify, there can still be a small window where someone requests a +user or a group, finds that it doesn't exist, adds the entry and checks +again. Without some support in shadow-utils that would explicitly drop +the sssd caches, the inotify watch can fire a little late, so a +combination of commands like this: + getent passwd user || useradd user; getent passwd user +can result in the second getent passwd not finding the newly added user +as the racy behaviour might still return the cached negative hit from +the first getent passwd. + +This patch more or less copies the already existing support that +shadow-utils had for dropping nscd caches, except using the "sss_cache" +tool that sssd ships. +--- + configure.ac | 10 +++++++ + lib/Makefile.am | 2 ++ + lib/commonio.c | 2 ++ + lib/sssd.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ + lib/sssd.h | 17 +++++++++++ + src/chfn.c | 2 ++ + src/chgpasswd.c | 2 ++ + src/chpasswd.c | 2 ++ + src/chsh.c | 2 ++ + src/gpasswd.c | 2 ++ + src/groupadd.c | 2 ++ + src/groupdel.c | 2 ++ + src/groupmod.c | 2 ++ + src/grpck.c | 2 ++ + src/grpconv.c | 2 ++ + src/grpunconv.c | 2 ++ + src/newusers.c | 2 ++ + src/passwd.c | 2 ++ + src/pwck.c | 2 ++ + src/pwconv.c | 2 ++ + src/pwunconv.c | 2 ++ + src/useradd.c | 2 ++ + src/userdel.c | 2 ++ + src/usermod.c | 2 ++ + src/vipw.c | 2 ++ + 25 files changed, 146 insertions(+) + create mode 100644 lib/sssd.c + create mode 100644 lib/sssd.h + +diff --git a/configure.ac b/configure.ac +index 41068a5d..10ad70cf 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -280,6 +280,9 @@ AC_ARG_WITH(sha-crypt, + AC_ARG_WITH(nscd, + [AC_HELP_STRING([--with-nscd], [enable support for nscd @<:@default=yes@:>@])], + [with_nscd=$withval], [with_nscd=yes]) ++AC_ARG_WITH(sssd, ++ [AC_HELP_STRING([--with-sssd], [enable support for flushing sssd caches @<:@default=yes@:>@])], ++ [with_sssd=$withval], [with_sssd=yes]) + AC_ARG_WITH(group-name-max-length, + [AC_HELP_STRING([--with-group-name-max-length], [set max group name length @<:@default=16@:>@])], + [with_group_name_max_length=$withval], [with_group_name_max_length=yes]) +@@ -304,6 +307,12 @@ if test "$with_nscd" = "yes"; then + [AC_MSG_ERROR([posix_spawn is needed for nscd support])]) + fi + ++if test "$with_sssd" = "yes"; then ++ AC_CHECK_FUNC(posix_spawn, ++ [AC_DEFINE(USE_SSSD, 1, [Define to support flushing of sssd caches])], ++ [AC_MSG_ERROR([posix_spawn is needed for sssd support])]) ++fi ++ + dnl Check for some functions in libc first, only if not found check for + dnl other libraries. This should prevent linking libnsl if not really + dnl needed (Linux glibc, Irix), but still link it if needed (Solaris). +@@ -679,5 +688,6 @@ echo " shadow group support: $enable_shadowgrp" + echo " S/Key support: $with_skey" + echo " SHA passwords encryption: $with_sha_crypt" + echo " nscd support: $with_nscd" ++echo " sssd support: $with_sssd" + echo " subordinate IDs support: $enable_subids" + echo +diff --git a/lib/Makefile.am b/lib/Makefile.am +index 6db86cd6..fd634542 100644 +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -30,6 +30,8 @@ libshadow_la_SOURCES = \ + lockpw.c \ + nscd.c \ + nscd.h \ ++ sssd.c \ ++ sssd.h \ + pam_defs.h \ + port.c \ + port.h \ +diff --git a/lib/commonio.c b/lib/commonio.c +index d06b8e7d..96f2d5f7 100644 +--- a/lib/commonio.c ++++ b/lib/commonio.c +@@ -45,6 +45,7 @@ + #include + #include + #include "nscd.h" ++#include "sssd.h" + #ifdef WITH_TCB + #include + #endif /* WITH_TCB */ +@@ -485,6 +486,7 @@ static void dec_lock_count (void) + if (nscd_need_reload) { + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + nscd_need_reload = false; + } + #ifdef HAVE_LCKPWDF +diff --git a/lib/sssd.c b/lib/sssd.c +new file mode 100644 +index 00000000..80e49e55 +--- /dev/null ++++ b/lib/sssd.c +@@ -0,0 +1,75 @@ ++/* Author: Peter Vrabec */ ++ ++#include ++#ifdef USE_SSSD ++ ++#include ++#include ++#include ++#include "exitcodes.h" ++#include "defines.h" ++#include "prototypes.h" ++#include "sssd.h" ++ ++#define MSG_SSSD_FLUSH_CACHE_FAILED "%s: Failed to flush the sssd cache.\n" ++ ++int sssd_flush_cache (int dbflags) ++{ ++ int status, code, rv; ++ const char *cmd = "/usr/sbin/sss_cache"; ++ char *sss_cache_args = NULL; ++ const char *spawnedArgs[] = {"sss_cache", NULL, NULL}; ++ const char *spawnedEnv[] = {NULL}; ++ int i = 0; ++ ++ sss_cache_args = malloc(4); ++ if (sss_cache_args == NULL) { ++ return -1; ++ } ++ ++ sss_cache_args[i++] = '-'; ++ if (dbflags & SSSD_DB_PASSWD) { ++ sss_cache_args[i++] = 'U'; ++ } ++ if (dbflags & SSSD_DB_GROUP) { ++ sss_cache_args[i++] = 'G'; ++ } ++ sss_cache_args[i++] = '\0'; ++ if (i == 2) { ++ /* Neither passwd nor group, nothing to do */ ++ free(sss_cache_args); ++ return 0; ++ } ++ spawnedArgs[1] = sss_cache_args; ++ ++ rv = run_command (cmd, spawnedArgs, spawnedEnv, &status); ++ free(sss_cache_args); ++ if (rv != 0) { ++ /* run_command writes its own more detailed message. */ ++ (void) fprintf (stderr, _(MSG_SSSD_FLUSH_CACHE_FAILED), Prog); ++ return -1; ++ } ++ ++ code = WEXITSTATUS (status); ++ if (!WIFEXITED (status)) { ++ (void) fprintf (stderr, ++ _("%s: sss_cache did not terminate normally (signal %d)\n"), ++ Prog, WTERMSIG (status)); ++ return -1; ++ } else if (code == E_CMD_NOTFOUND) { ++ /* sss_cache is not installed, or it is installed but uses an ++ interpreter that is missing. Probably the former. */ ++ return 0; ++ } else if (code != 0) { ++ (void) fprintf (stderr, _("%s: sss_cache exited with status %d\n"), ++ Prog, code); ++ (void) fprintf (stderr, _(MSG_SSSD_FLUSH_CACHE_FAILED), Prog); ++ return -1; ++ } ++ ++ return 0; ++} ++#else /* USE_SSSD */ ++extern int errno; /* warning: ANSI C forbids an empty source file */ ++#endif /* USE_SSSD */ ++ +diff --git a/lib/sssd.h b/lib/sssd.h +new file mode 100644 +index 00000000..00ff2a8a +--- /dev/null ++++ b/lib/sssd.h +@@ -0,0 +1,17 @@ ++#ifndef _SSSD_H_ ++#define _SSSD_H_ ++ ++#define SSSD_DB_PASSWD 0x001 ++#define SSSD_DB_GROUP 0x002 ++ ++/* ++ * sssd_flush_cache - flush specified service buffer in sssd cache ++ */ ++#ifdef USE_SSSD ++extern int sssd_flush_cache (int dbflags); ++#else ++#define sssd_flush_cache(service) (0) ++#endif ++ ++#endif ++ +diff --git a/src/chfn.c b/src/chfn.c +index 18aa3de7..0725e1c7 100644 +--- a/src/chfn.c ++++ b/src/chfn.c +@@ -47,6 +47,7 @@ + #include "defines.h" + #include "getdef.h" + #include "nscd.h" ++#include "sssd.h" + #ifdef USE_PAM + #include "pam_defs.h" + #endif +@@ -746,6 +747,7 @@ int main (int argc, char **argv) + SYSLOG ((LOG_INFO, "changed user '%s' information", user)); + + nscd_flush_cache ("passwd"); ++ sssd_flush_cache (SSSD_DB_PASSWD); + + closelog (); + exit (E_SUCCESS); +diff --git a/src/chgpasswd.c b/src/chgpasswd.c +index 13203a46..e5f2eb7e 100644 +--- a/src/chgpasswd.c ++++ b/src/chgpasswd.c +@@ -46,6 +46,7 @@ + #endif /* ACCT_TOOLS_SETUID */ + #include "defines.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "groupio.h" + #ifdef SHADOWGRP +@@ -581,6 +582,7 @@ int main (int argc, char **argv) + close_files (); + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + return (0); + } +diff --git a/src/chpasswd.c b/src/chpasswd.c +index 918b27ee..49e79cdb 100644 +--- a/src/chpasswd.c ++++ b/src/chpasswd.c +@@ -44,6 +44,7 @@ + #endif /* USE_PAM */ + #include "defines.h" + #include "nscd.h" ++#include "sssd.h" + #include "getdef.h" + #include "prototypes.h" + #include "pwio.h" +@@ -624,6 +625,7 @@ int main (int argc, char **argv) + } + + nscd_flush_cache ("passwd"); ++ sssd_flush_cache (SSSD_DB_PASSWD); + + return (0); + } +diff --git a/src/chsh.c b/src/chsh.c +index c89708b9..910e3dd4 100644 +--- a/src/chsh.c ++++ b/src/chsh.c +@@ -46,6 +46,7 @@ + #include "defines.h" + #include "getdef.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "pwauth.h" + #include "pwio.h" +@@ -557,6 +558,7 @@ int main (int argc, char **argv) + SYSLOG ((LOG_INFO, "changed user '%s' shell to '%s'", user, loginsh)); + + nscd_flush_cache ("passwd"); ++ sssd_flush_cache (SSSD_DB_PASSWD); + + closelog (); + exit (E_SUCCESS); +diff --git a/src/gpasswd.c b/src/gpasswd.c +index c4a492b1..4d75af96 100644 +--- a/src/gpasswd.c ++++ b/src/gpasswd.c +@@ -45,6 +45,7 @@ + #include "defines.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #ifdef SHADOWGRP + #include "sgroupio.h" +@@ -1201,6 +1202,7 @@ int main (int argc, char **argv) + close_files (); + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + exit (E_SUCCESS); + } +diff --git a/src/groupadd.c b/src/groupadd.c +index b57006c5..2dd8eec9 100644 +--- a/src/groupadd.c ++++ b/src/groupadd.c +@@ -51,6 +51,7 @@ + #include "getdef.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #ifdef SHADOWGRP + #include "sgroupio.h" +@@ -625,6 +626,7 @@ int main (int argc, char **argv) + close_files (); + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + return E_SUCCESS; + } +diff --git a/src/groupdel.c b/src/groupdel.c +index 70bed010..f941a84a 100644 +--- a/src/groupdel.c ++++ b/src/groupdel.c +@@ -49,6 +49,7 @@ + #include "defines.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #ifdef SHADOWGRP + #include "sgroupio.h" +@@ -492,6 +493,7 @@ int main (int argc, char **argv) + close_files (); + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + return E_SUCCESS; + } +diff --git a/src/groupmod.c b/src/groupmod.c +index b293b98f..1dca5fc9 100644 +--- a/src/groupmod.c ++++ b/src/groupmod.c +@@ -51,6 +51,7 @@ + #include "groupio.h" + #include "pwio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #ifdef SHADOWGRP + #include "sgroupio.h" +@@ -877,6 +878,7 @@ int main (int argc, char **argv) + close_files (); + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + return E_SUCCESS; + } +diff --git a/src/grpck.c b/src/grpck.c +index ea5d3b39..6140b10d 100644 +--- a/src/grpck.c ++++ b/src/grpck.c +@@ -45,6 +45,7 @@ + #include "defines.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + + #ifdef SHADOWGRP +@@ -870,6 +871,7 @@ int main (int argc, char **argv) + close_files (changed); + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + /* + * Tell the user what we did and exit. +diff --git a/src/grpconv.c b/src/grpconv.c +index f95f4960..5e5eaaca 100644 +--- a/src/grpconv.c ++++ b/src/grpconv.c +@@ -48,6 +48,7 @@ + #include + #include + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + /*@-exitarg@*/ + #include "exitcodes.h" +@@ -273,6 +274,7 @@ int main (int argc, char **argv) + } + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + return 0; + } +diff --git a/src/grpunconv.c b/src/grpunconv.c +index 253f06f5..e4105c26 100644 +--- a/src/grpunconv.c ++++ b/src/grpunconv.c +@@ -48,6 +48,7 @@ + #include + #include + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + /*@-exitarg@*/ + #include "exitcodes.h" +@@ -236,6 +237,7 @@ int main (int argc, char **argv) + } + + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_GROUP); + + return 0; + } +diff --git a/src/newusers.c b/src/newusers.c +index 8e4bef97..7c3bb1c2 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -62,6 +62,7 @@ + #include "getdef.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "pwio.h" + #include "sgroupio.h" + #include "shadowio.h" +@@ -1233,6 +1234,7 @@ int main (int argc, char **argv) + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + + #ifdef USE_PAM + unsigned int i; +diff --git a/src/passwd.c b/src/passwd.c +index 3af3e651..5bea2765 100644 +--- a/src/passwd.c ++++ b/src/passwd.c +@@ -51,6 +51,7 @@ + #include "defines.h" + #include "getdef.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "pwauth.h" + #include "pwio.h" +@@ -1150,6 +1151,7 @@ int main (int argc, char **argv) + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + + SYSLOG ((LOG_INFO, "password for '%s' changed by '%s'", name, myname)); + closelog (); +diff --git a/src/pwck.c b/src/pwck.c +index 05df68ec..0ffb711e 100644 +--- a/src/pwck.c ++++ b/src/pwck.c +@@ -48,6 +48,7 @@ + #include "shadowio.h" + #include "getdef.h" + #include "nscd.h" ++#include "sssd.h" + #ifdef WITH_TCB + #include "tcbfuncs.h" + #endif /* WITH_TCB */ +@@ -877,6 +878,7 @@ int main (int argc, char **argv) + close_files (changed); + + nscd_flush_cache ("passwd"); ++ sssd_flush_cache (SSSD_DB_PASSWD); + + /* + * Tell the user what we did and exit. +diff --git a/src/pwconv.c b/src/pwconv.c +index d6ee31a8..9c69fa13 100644 +--- a/src/pwconv.c ++++ b/src/pwconv.c +@@ -72,6 +72,7 @@ + #include "pwio.h" + #include "shadowio.h" + #include "nscd.h" ++#include "sssd.h" + + /* + * exit status values +@@ -328,6 +329,7 @@ int main (int argc, char **argv) + } + + nscd_flush_cache ("passwd"); ++ sssd_flush_cache (SSSD_DB_PASSWD); + + return E_SUCCESS; + } +diff --git a/src/pwunconv.c b/src/pwunconv.c +index fabf0237..e11ea494 100644 +--- a/src/pwunconv.c ++++ b/src/pwunconv.c +@@ -42,6 +42,7 @@ + #include + #include "defines.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "pwio.h" + #include "shadowio.h" +@@ -250,6 +251,7 @@ int main (int argc, char **argv) + } + + nscd_flush_cache ("passwd"); ++ sssd_flush_cache (SSSD_DB_PASSWD); + + return 0; + } +diff --git a/src/useradd.c b/src/useradd.c +index ca90f076..b0c2224d 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -60,6 +60,7 @@ + #include "getdef.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "pwauth.h" + #include "pwio.h" +@@ -2425,6 +2426,7 @@ int main (int argc, char **argv) + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + + /* + * tallylog_reset needs to be able to lookup +diff --git a/src/userdel.c b/src/userdel.c +index c8de1d31..0715e4fe 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -53,6 +53,7 @@ + #include "getdef.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "pwauth.h" + #include "pwio.h" +@@ -1328,6 +1329,7 @@ int main (int argc, char **argv) + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + + return ((0 != errors) ? E_HOMEDIR : E_SUCCESS); + } +diff --git a/src/usermod.c b/src/usermod.c +index 7355ad31..fd9a98a6 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -57,6 +57,7 @@ + #include "getdef.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "pwauth.h" + #include "pwio.h" +@@ -2255,6 +2256,7 @@ int main (int argc, char **argv) + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + + #ifdef WITH_SELINUX + if (Zflg) { +diff --git a/src/vipw.c b/src/vipw.c +index 6d730f65..2cfac6b4 100644 +--- a/src/vipw.c ++++ b/src/vipw.c +@@ -42,6 +42,7 @@ + #include "defines.h" + #include "groupio.h" + #include "nscd.h" ++#include "sssd.h" + #include "prototypes.h" + #include "pwio.h" + #include "sgroupio.h" +@@ -556,6 +557,7 @@ int main (int argc, char **argv) + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); ++ sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); + + return E_SUCCESS; + } diff --git a/shadow-4.6-sysugid-min-limit.patch b/shadow-4.6-sysugid-min-limit.patch new file mode 100644 index 0000000..4cea6ef --- /dev/null +++ b/shadow-4.6-sysugid-min-limit.patch @@ -0,0 +1,34 @@ +diff -up shadow-4.6/libmisc/find_new_gid.c.min-limit shadow-4.6/libmisc/find_new_gid.c +--- shadow-4.6/libmisc/find_new_gid.c.min-limit 2018-04-29 18:42:37.000000001 +0200 ++++ shadow-4.6/libmisc/find_new_gid.c 2018-11-06 10:51:20.554963292 +0100 +@@ -82,6 +82,13 @@ static int get_ranges (bool sys_group, g + (unsigned long) *max_id); + return EINVAL; + } ++ /* ++ * Zero is reserved for root and the allocation algorithm does not ++ * work right with it. ++ */ ++ if (*min_id == 0) { ++ *min_id = (gid_t) 1; ++ } + } else { + /* Non-system groups */ + +diff -up shadow-4.6/libmisc/find_new_uid.c.min-limit shadow-4.6/libmisc/find_new_uid.c +--- shadow-4.6/libmisc/find_new_uid.c.min-limit 2018-04-29 18:42:37.000000001 +0200 ++++ shadow-4.6/libmisc/find_new_uid.c 2018-11-06 10:51:39.341399569 +0100 +@@ -82,6 +82,13 @@ static int get_ranges (bool sys_user, ui + (unsigned long) *max_id); + return EINVAL; + } ++ /* ++ * Zero is reserved for root and the allocation algorithm does not ++ * work right with it. ++ */ ++ if (*min_id == 0) { ++ *min_id = (uid_t) 1; ++ } + } else { + /* Non-system users */ + diff --git a/shadow-4.6-use-itstool.patch b/shadow-4.6-use-itstool.patch new file mode 100644 index 0000000..760b5b3 --- /dev/null +++ b/shadow-4.6-use-itstool.patch @@ -0,0 +1,31 @@ +diff -up shadow-4.6/man/generate_translations.mak.use-itstool shadow-4.6/man/generate_translations.mak +--- shadow-4.6/man/generate_translations.mak.use-itstool 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/man/generate_translations.mak 2018-07-31 16:42:21.623990969 +0200 +@@ -5,8 +5,19 @@ config.xml: ../config.xml.in + $(MAKE) -C .. config.xml + cp ../config.xml $@ + +-%.xml: ../%.xml ../po/$(LANG).po +- xml2po --expand-all-entities -l $(LANG) -p ../po/$(LANG).po -o $@ ../$@ ++messages.mo: ../po/$(LANG).po ++ msgfmt ../po/$(LANG).po -o messages.mo ++ ++login.defs.d: ++ ln -sf ../login.defs.d login.defs.d ++ ++%.xml: ../%.xml messages.mo login.defs.d ++ if grep -q SHADOW-CONFIG-HERE $< ; then \ ++ sed -e 's/^/%config;/' $< > $@; \ ++ else \ ++ sed -e 's/^\(/\1 [%config;]>/' $< > $@; \ ++ fi ++ itstool -d -l $(LANG) -m messages.mo -o . $@ + sed -i 's:\(^:\1 lang="$(LANG)">:' $@ + + include ../generate_mans.mak +@@ -16,4 +27,4 @@ $(man_MANS): + @echo you need to run configure with --enable-man to generate man pages + endif + +-CLEANFILES = .xml2po.mo $(EXTRA_DIST) $(addsuffix .xml,$(EXTRA_DIST)) config.xml ++CLEANFILES = messages.mo login.defs.d $(EXTRA_DIST) $(addsuffix .xml,$(EXTRA_DIST)) config.xml diff --git a/shadow-4.6-use-lckpwdf.patch b/shadow-4.6-use-lckpwdf.patch new file mode 100644 index 0000000..363579a --- /dev/null +++ b/shadow-4.6-use-lckpwdf.patch @@ -0,0 +1,190 @@ +commit 408b8a548243aebaa6d773beeae8ddf4bb6100f0 +Author: Tomas Mraz +Date: Thu May 2 14:33:06 2019 +0200 + + Use the lckpwdf() again if prefix is not set + + The implementation of prefix option dropped the use of lckpwdf(). + However that is incorrect as other tools manipulating the shadow passwords + such as PAM use lckpwdf() and do not know anything about the + shadow's own locking mechanism. + + This reverts the implementation to use lckpwdf() if prefix option + is not used. + +diff --git a/lib/commonio.c b/lib/commonio.c +index 26e518f2..94dda779 100644 +--- a/lib/commonio.c ++++ b/lib/commonio.c +@@ -364,6 +364,7 @@ static void free_linked_list (struct commonio_db *db) + int commonio_setname (struct commonio_db *db, const char *name) + { + snprintf (db->filename, sizeof (db->filename), "%s", name); ++ db->setname = true; + return 1; + } + +@@ -414,37 +415,39 @@ cleanup_ENOMEM: + + int commonio_lock (struct commonio_db *db) + { +-/*#ifdef HAVE_LCKPWDF*/ /* not compatible with prefix option*/ +-#if 0 +- /* +- * only if the system libc has a real lckpwdf() - the one from +- * lockpw.c calls us and would cause infinite recursion! +- */ ++ int i; + ++#ifdef HAVE_LCKPWDF + /* +- * Call lckpwdf() on the first lock. +- * If it succeeds, call *_lock() only once +- * (no retries, it should always succeed). ++ * Only if the system libc has a real lckpwdf() - the one from ++ * lockpw.c calls us and would cause infinite recursion! ++ * It is also not used with the prefix option. + */ +- if (0 == lock_count) { +- if (lckpwdf () == -1) { +- if (geteuid () != 0) { +- (void) fprintf (stderr, +- "%s: Permission denied.\n", +- Prog); ++ if (!db->setname) { ++ /* ++ * Call lckpwdf() on the first lock. ++ * If it succeeds, call *_lock() only once ++ * (no retries, it should always succeed). ++ */ ++ if (0 == lock_count) { ++ if (lckpwdf () == -1) { ++ if (geteuid () != 0) { ++ (void) fprintf (stderr, ++ "%s: Permission denied.\n", ++ Prog); ++ } ++ return 0; /* failure */ + } +- return 0; /* failure */ + } +- } + +- if (commonio_lock_nowait (db, true) != 0) { +- return 1; /* success */ +- } ++ if (commonio_lock_nowait (db, true) != 0) { ++ return 1; /* success */ ++ } + +- ulckpwdf (); +- return 0; /* failure */ +-#else /* !HAVE_LCKPWDF */ +- int i; ++ ulckpwdf (); ++ return 0; /* failure */ ++ } ++#endif /* !HAVE_LCKPWDF */ + + /* + * lckpwdf() not used - do it the old way. +@@ -471,7 +474,6 @@ int commonio_lock (struct commonio_db *db) + } + } + return 0; /* failure */ +-#endif /* !HAVE_LCKPWDF */ + } + + static void dec_lock_count (void) +diff --git a/lib/commonio.h b/lib/commonio.h +index 40e5708f..64e83073 100644 +--- a/lib/commonio.h ++++ b/lib/commonio.h +@@ -143,6 +143,7 @@ struct commonio_db { + bool isopen:1; + bool locked:1; + bool readonly:1; ++ bool setname:1; + }; + + extern int commonio_setname (struct commonio_db *, const char *); +diff --git a/lib/groupio.c b/lib/groupio.c +index ae2302b5..bffb06e0 100644 +--- a/lib/groupio.c ++++ b/lib/groupio.c +@@ -139,7 +139,8 @@ static /*@owned@*/struct commonio_db group_db = { + false, /* changed */ + false, /* isopen */ + false, /* locked */ +- false /* readonly */ ++ false, /* readonly */ ++ false /* setname */ + }; + + int gr_setdbname (const char *filename) +diff --git a/lib/pwio.c b/lib/pwio.c +index 7ee85377..127719cb 100644 +--- a/lib/pwio.c ++++ b/lib/pwio.c +@@ -114,7 +114,8 @@ static struct commonio_db passwd_db = { + false, /* changed */ + false, /* isopen */ + false, /* locked */ +- false /* readonly */ ++ false, /* readonly */ ++ false /* setname */ + }; + + int pw_setdbname (const char *filename) +diff --git a/lib/sgroupio.c b/lib/sgroupio.c +index 5423626a..ffbdb263 100644 +--- a/lib/sgroupio.c ++++ b/lib/sgroupio.c +@@ -238,7 +238,8 @@ static struct commonio_db gshadow_db = { + false, /* changed */ + false, /* isopen */ + false, /* locked */ +- false /* readonly */ ++ false, /* readonly */ ++ false /* setname */ + }; + + int sgr_setdbname (const char *filename) +diff --git a/lib/shadowio.c b/lib/shadowio.c +index 5fa3d312..676b1f1a 100644 +--- a/lib/shadowio.c ++++ b/lib/shadowio.c +@@ -114,7 +114,8 @@ static struct commonio_db shadow_db = { + false, /* changed */ + false, /* isopen */ + false, /* locked */ +- false /* readonly */ ++ false, /* readonly */ ++ false /* setname */ + }; + + int spw_setdbname (const char *filename) +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index a662e67e..dd779c59 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -550,7 +550,8 @@ static struct commonio_db subordinate_uid_db = { + false, /* changed */ + false, /* isopen */ + false, /* locked */ +- false /* readonly */ ++ false, /* readonly */ ++ false /* setname */ + }; + + int sub_uid_setdbname (const char *filename) +@@ -631,7 +632,8 @@ static struct commonio_db subordinate_gid_db = { + false, /* changed */ + false, /* isopen */ + false, /* locked */ +- false /* readonly */ ++ false, /* readonly */ ++ false /* setname */ + }; + + int sub_gid_setdbname (const char *filename) diff --git a/shadow-4.6-useradd-man-clarification.patch b/shadow-4.6-useradd-man-clarification.patch new file mode 100644 index 0000000..9fdb652 --- /dev/null +++ b/shadow-4.6-useradd-man-clarification.patch @@ -0,0 +1,35 @@ +From 6543c600d841e4f7779269412d470e50eae25b13 Mon Sep 17 00:00:00 2001 +From: ikerexxe +Date: Wed, 4 Mar 2020 14:50:04 +0100 +Subject: [PATCH] useradd: clarify the useradd -d parameter behavior in man + page + +Explanation: clarify the useradd -d parameter as it does create directory HOME_DIR if it doesn't exit. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1677005 + +Changelog: [serge] minor tweak to the text +--- + man/useradd.8.xml | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/man/useradd.8.xml b/man/useradd.8.xml +index 03612ce8..023c0d69 100644 +--- a/man/useradd.8.xml ++++ b/man/useradd.8.xml +@@ -181,8 +181,10 @@ + login directory. The default is to append the + LOGIN name to + BASE_DIR and use that as the login +- directory name. The directory HOME_DIR +- does not have to exist but will not be created if it is missing. ++ directory name. If the directory ++ HOME_DIR does not exist, then it ++ will be created unless the option is ++ specified. + + + +-- +2.25.1 + diff --git a/shadow-4.6-useradd-selinux-mail.patch b/shadow-4.6-useradd-selinux-mail.patch new file mode 100644 index 0000000..1777f2d --- /dev/null +++ b/shadow-4.6-useradd-selinux-mail.patch @@ -0,0 +1,61 @@ +From 4dc62ebcf37d7568be1d4ca54367215eba8b8a28 Mon Sep 17 00:00:00 2001 +From: ikerexxe +Date: Wed, 5 Feb 2020 15:04:39 +0100 +Subject: [PATCH] useradd: doesn't generate /var/spool/mail/$USER with the + proper SELinux user identity + +Explanation: use set_selinux_file_context() and reset_selinux_file_context() for create_mail() just as is done for create_home() + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1690527 +--- + src/useradd.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/src/useradd.c b/src/useradd.c +index a679392d..645d4a40 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -190,6 +190,7 @@ static bool home_added = false; + #define E_NAME_IN_USE 9 /* username already in use */ + #define E_GRP_UPDATE 10 /* can't update group file */ + #define E_HOMEDIR 12 /* can't create home directory */ ++#define E_MAILBOXFILE 13 /* can't create mailbox file */ + #define E_SE_UPDATE 14 /* can't update SELinux user mapping */ + #ifdef ENABLE_SUBIDS + #define E_SUB_UID_UPDATE 16 /* can't update the subordinate uid file */ +@@ -2210,6 +2211,16 @@ static void create_mail (void) + sprintf (file, "%s/%s/%s", prefix, spool, user_name); + else + sprintf (file, "%s/%s", spool, user_name); ++ ++#ifdef WITH_SELINUX ++ if (set_selinux_file_context (file, NULL) != 0) { ++ fprintf (stderr, ++ _("%s: cannot set SELinux context for mailbox file %s\n"), ++ Prog, file); ++ fail_exit (E_MAILBOXFILE); ++ } ++#endif ++ + fd = open (file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0); + if (fd < 0) { + perror (_("Creating mailbox file")); +@@ -2234,6 +2245,15 @@ static void create_mail (void) + + fsync (fd); + close (fd); ++#ifdef WITH_SELINUX ++ /* Reset SELinux to create files with default contexts */ ++ if (reset_selinux_file_context () != 0) { ++ fprintf (stderr, ++ _("%s: cannot reset SELinux file creation context\n"), ++ Prog); ++ fail_exit (E_MAILBOXFILE); ++ } ++#endif + } + } + +-- +2.24.1 + diff --git a/shadow-4.6-usermod-crash.patch b/shadow-4.6-usermod-crash.patch new file mode 100644 index 0000000..d2861b3 --- /dev/null +++ b/shadow-4.6-usermod-crash.patch @@ -0,0 +1,42 @@ +diff -up shadow-4.6/libmisc/prefix_flag.c.usermod-crash shadow-4.6/libmisc/prefix_flag.c +--- shadow-4.6/libmisc/prefix_flag.c.usermod-crash 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6/libmisc/prefix_flag.c 2018-05-28 15:14:10.642302440 +0200 +@@ -319,6 +319,7 @@ extern struct group *prefix_getgr_nam_gi + { + long long int gid; + char *endptr; ++ struct group *g; + + if (NULL == grname) { + return NULL; +@@ -333,7 +334,8 @@ extern struct group *prefix_getgr_nam_gi + && (gid == (gid_t)gid)) { + return prefix_getgrgid ((gid_t) gid); + } +- return prefix_getgrnam (grname); ++ g = prefix_getgrnam (grname); ++ return g ? __gr_dup(g) : NULL; + } + else + return getgr_nam_gid(grname); +diff -up shadow-4.6/src/usermod.c.usermod-crash shadow-4.6/src/usermod.c +--- shadow-4.6/src/usermod.c.usermod-crash 2018-05-28 15:12:37.920332763 +0200 ++++ shadow-4.6/src/usermod.c 2018-05-28 15:15:50.337422470 +0200 +@@ -1276,11 +1276,13 @@ static void process_flags (int argc, cha + prefix_user_home = xmalloc(len); + wlen = snprintf(prefix_user_home, len, "%s/%s", prefix, user_home); + assert (wlen == (int) len -1); ++ if (user_newhome) { ++ len = strlen(prefix) + strlen(user_newhome) + 2; ++ prefix_user_newhome = xmalloc(len); ++ wlen = snprintf(prefix_user_newhome, len, "%s/%s", prefix, user_newhome); ++ assert (wlen == (int) len -1); ++ } + +- len = strlen(prefix) + strlen(user_newhome) + 2; +- prefix_user_newhome = xmalloc(len); +- wlen = snprintf(prefix_user_newhome, len, "%s/%s", prefix, user_newhome); +- assert (wlen == (int) len -1); + } + else { + prefix_user_home = user_home; diff --git a/shadow-utils.HOME_MODE.xml b/shadow-utils.HOME_MODE.xml deleted file mode 100644 index 21aa55f..0000000 --- a/shadow-utils.HOME_MODE.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - (number) - - - The mode for new home directories. If not specified, - the is used to create the mode. - - - useradd and newusers use this - to set the mode of the home directory they create. - - - diff --git a/shadow-utils.login.defs b/shadow-utils.login.defs index e84c7ab..0adfb66 100644 --- a/shadow-utils.login.defs +++ b/shadow-utils.login.defs @@ -6,121 +6,15 @@ # /etc/pam.d/system-auth for more information. # -# -# Delay in seconds before being allowed another attempt after a login failure -# Note: When PAM is used, some modules may enforce a minimum delay (e.g. -# pam_unix(8) enforces a 2s delay) -# -#FAIL_DELAY 3 - -# Currently FAILLOG_ENAB is not supported - -# -# Enable display of unknown usernames when login(1) failures are recorded. -# -#LOG_UNKFAIL_ENAB no - -# Currently LOG_OK_LOGINS is not supported - -# Currently LASTLOG_ENAB is not supported - -# -# Limit the highest user ID number for which the lastlog entries should -# be updated. -# -# No LASTLOG_UID_MAX means that there is no user ID limit for writing -# lastlog entries. -# -#LASTLOG_UID_MAX - -# Currently MAIL_CHECK_ENAB is not supported - -# Currently OBSCURE_CHECKS_ENAB is not supported - -# Currently PORTTIME_CHECKS_ENAB is not supported - -# Currently QUOTAS_ENAB is not supported - -# Currently SYSLOG_SU_ENAB is not supported - -# -# Enable "syslog" logging of newgrp(1) and sg(1) activity. -# -#SYSLOG_SG_ENAB yes - -# Currently CONSOLE is not supported - -# Currently SULOG_FILE is not supported - -# Currently MOTD_FILE is not supported - -# Currently ISSUE_FILE is not supported - -# Currently TTYTYPE_FILE is not supported - -# Currently FTMP_FILE is not supported - -# Currently NOLOGINS_FILE is not supported - -# Currently SU_NAME is not supported - # *REQUIRED* # Directory where mailboxes reside, _or_ name of file, relative to the # home directory. If you _do_ define both, MAIL_DIR takes precedence. +# QMAIL_DIR is for Qmail # +#QMAIL_DIR Maildir MAIL_DIR /var/spool/mail #MAIL_FILE .mail -# -# If defined, file which inhibits all the usual chatter during the login -# sequence. If a full pathname, then hushed mode will be enabled if the -# user's name or shell are found in the file. If not a full pathname, then -# hushed mode will be enabled if the file exists in the user's home directory. -# -#HUSHLOGIN_FILE .hushlogin -#HUSHLOGIN_FILE /etc/hushlogins - -# Currently ENV_TZ is not supported - -# Currently ENV_HZ is not supported - -# -# The default PATH settings, for superuser and normal users. -# -# (they are minimal, add the rest in the shell startup files) -#ENV_SUPATH PATH=/sbin:/bin:/usr/sbin:/usr/bin -#ENV_PATH PATH=/bin:/usr/bin - -# -# Terminal permissions -# -# TTYGROUP Login tty will be assigned this group ownership. -# TTYPERM Login tty will be set to this permission. -# -# If you have a write(1) program which is "setgid" to a special group -# which owns the terminals, define TTYGROUP as the number of such group -# and TTYPERM as 0620. Otherwise leave TTYGROUP commented out and -# set TTYPERM to either 622 or 600. -# -#TTYGROUP tty -#TTYPERM 0600 - -# Currently ERASECHAR, KILLCHAR and ULIMIT are not supported - -# Default initial "umask" value used by login(1) on non-PAM enabled systems. -# Default "umask" value for pam_umask(8) on PAM enabled systems. -# UMASK is also used by useradd(8) and newusers(8) to set the mode for new -# home directories if HOME_MODE is not set. -# 022 is the default value, but 027, or even 077, could be considered -# for increased privacy. There is no One True Answer here: each sysadmin -# must make up their mind. -UMASK 022 - -# HOME_MODE is used by useradd(8) and newusers(8) to set the mode for new -# home directories. -# If HOME_MODE is not set, the value of UMASK is used to create the mode. -HOME_MODE 0700 - # Password aging controls: # # PASS_MAX_DAYS Maximum number of days a password may be used. @@ -130,142 +24,26 @@ HOME_MODE 0700 # PASS_MAX_DAYS 99999 PASS_MIN_DAYS 0 -PASS_MIN_LEN 8 +PASS_MIN_LEN 5 PASS_WARN_AGE 7 -# Currently SU_WHEEL_ONLY is not supported - -# Currently CRACKLIB_DICTPATH is not supported - # -# Min/max values for automatic uid selection in useradd(8) +# Min/max values for automatic uid selection in useradd # UID_MIN 1000 UID_MAX 60000 # System accounts SYS_UID_MIN 201 SYS_UID_MAX 999 -# Extra per user uids -SUB_UID_MIN 524288 -SUB_UID_MAX 600100000 -SUB_UID_COUNT 65536 # -# Min/max values for automatic gid selection in groupadd(8) +# Min/max values for automatic gid selection in groupadd # GID_MIN 1000 GID_MAX 60000 # System accounts SYS_GID_MIN 201 SYS_GID_MAX 999 -# Extra per user group ids -SUB_GID_MIN 524288 -SUB_GID_MAX 600100000 -SUB_GID_COUNT 65536 - -# -# Max number of login(1) retries if password is bad -# -#LOGIN_RETRIES 3 - -# -# Max time in seconds for login(1) -# -#LOGIN_TIMEOUT 60 - -# -# Maximum number of attempts to change password if rejected (too easy) -# -PASS_CHANGE_TRIES 5 - -# -# Warn about weak passwords (but still allow them) if you are root. -# -PASS_ALWAYS_WARN yes - -# -# Number of significant characters in the password for crypt(). -# Default is 8, don't change unless your crypt() is better. -# Ignored if MD5_CRYPT_ENAB set to "yes". -# -#PASS_MAX_LEN 8 - -# Currently CHFN_AUTH is not supported - -# -# Which fields may be changed by regular users using chfn(1) - use -# any combination of letters "frwh" (full name, room number, work -# phone, home phone). If not defined, no changes are allowed. -# For backward compatibility, "yes" = "rwh" and "no" = "frwh". -# -#CHFN_RESTRICT rwh - -# Currently LOGIN_STRING is not supported - -# Currently MD5_CRYPT_ENAB is not supported - -# -# If set to MD5, MD5-based algorithm will be used for encrypting password -# If set to SHA256, SHA256-based algorithm will be used for encrypting password -# If set to SHA512, SHA512-based algorithm will be used for encrypting password -# If set to BCRYPT, BCRYPT-based algorithm will be used for encrypting password -# If set to YESCRYPT, YESCRYPT-based algorithm will be used for encrypting password -# If set to DES, DES-based algorithm will be used for encrypting password (default) -# -ENCRYPT_METHOD YESCRYPT - -# -# Only works if ENCRYPT_METHOD is set to SHA256 or SHA512. -# -# Define the number of SHA rounds. -# With a lot of rounds, it is more difficult to brute-force the password. -# However, more CPU resources will be needed to authenticate users if -# this value is increased. -# -# If not specified, the libc will choose the default number of rounds (5000). -# The values must be within the 1000-999999999 range. -# -#SHA_CRYPT_MAX_ROUNDS 5000 - -# Currently SHA_CRYPT_MIN_ROUNDS is not supported - -# -# Only works if ENCRYPT_METHOD is set to BCRYPT. -# -# Define the number of BCRYPT rounds. -# With a lot of rounds, it is more difficult to brute-force the password. -# However, more CPU resources will be needed to authenticate users if -# this value is increased. -# -# If not specified, 13 rounds will be attempted. -# If only one of the MIN or MAX values is set, then this value will be used. -# If MIN > MAX, the highest value will be used. -# -#BCRYPT_MIN_ROUNDS 13 -#BCRYPT_MAX_ROUNDS 31 - -# -# Only works if ENCRYPT_METHOD is set to YESCRYPT. -# -# Define the YESCRYPT cost factor. -# With a higher cost factor, it is more difficult to brute-force the password. -# However, more CPU time and more memory will be needed to authenticate users -# if this value is increased. -# -# If not specified, a cost factor of 5 will be used. -# The value must be within the 1-11 range. -# -#YESCRYPT_COST_FACTOR 5 - -# Currently CONSOLE_GROUPS is not supported - -# -# Should login be allowed if we can't cd to the home directory? -# Default is yes. -# -#DEFAULT_HOME yes - -# Currently ENVIRON_FILE is not supported # # If defined, this command is run when removing a user. @@ -275,41 +53,20 @@ ENCRYPT_METHOD YESCRYPT #USERDEL_CMD /usr/sbin/userdel_local # -# Enables userdel(8) to remove user groups if no members exist. -# -USERGROUPS_ENAB yes - -# -# If set to a non-zero number, the shadow utilities will make sure that -# groups never have more than this number of users on one line. -# This permits to support split groups (groups split into multiple lines, -# with the same group ID, to avoid limitation of the line length in the -# group file). -# -# 0 is the default value and disables this feature. -# -#MAX_MEMBERS_PER_GROUP 0 - -# -# If useradd(8) should create home directories for users by default (non -# system users only). -# This option is overridden with the -M or -m flags on the useradd(8) -# command-line. +# If useradd should create home directories for users by default +# On RH systems, we do. This option is overridden with the -m flag on +# useradd command line. # CREATE_HOME yes -# -# Force use shadow, even if shadow passwd & shadow group files are -# missing. -# -#FORCE_SHADOW yes +# The permission mask is initialized to this value. If not specified, +# the permission mask will be initialized to 022. +UMASK 077 +# This enables userdel to remove user groups if no members exist. # -# Select the HMAC cryptography algorithm. -# Used in pam_timestamp module to calculate the keyed-hash message -# authentication code. -# -# Note: It is recommended to check hmac(3) to see the possible algorithms -# that are available in your system. -# -HMAC_CRYPTO_ALGO SHA512 +USERGROUPS_ENAB yes + +# Use SHA512 to encrypt password. +ENCRYPT_METHOD SHA512 + diff --git a/shadow-utils.spec b/shadow-utils.spec index 7bc7e8b..7a01bb4 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,74 +1,59 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils -Version: 4.20.0 -Release: 1%{?dist} +Version: 4.6 +Release: 18%{?dist} Epoch: 2 -License: BSD-3-Clause AND GPL-2.0-or-later -URL: https://github.com/shadow-maint/shadow -Source0: https://github.com/shadow-maint/shadow/releases/download/4.20.0/shadow-4.20.0.tar.xz -Source1: https://github.com/shadow-maint/shadow/releases/download/4.20.0/shadow-4.20.0.tar.xz.asc +URL: http://pkg-shadow.alioth.debian.org/ +Source0: https://github.com/shadow-maint/shadow/releases/download/%{version}/shadow-%{version}.tar.xz +Source1: https://github.com/shadow-maint/shadow/releases/download/%{version}/shadow-%{version}.tar.xz.asc Source2: shadow-utils.useradd Source3: shadow-utils.login.defs Source4: shadow-bsd.txt Source5: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt -Source6: shadow-utils.HOME_MODE.xml -Source7: passwd.pamd +Patch0: shadow-4.6-redhat.patch +Patch1: shadow-4.5-goodname.patch +Patch2: shadow-4.1.5.1-info-parent-dir.patch +Patch6: shadow-4.6-selinux.patch +Patch10: shadow-4.6-orig-context.patch +Patch11: shadow-4.1.5.1-logmsg.patch +Patch14: shadow-4.1.5.1-default-range.patch +Patch15: shadow-4.3.1-manfix.patch +Patch17: shadow-4.1.5.1-userdel-helpfix.patch +Patch19: shadow-4.2.1-date-parsing.patch +Patch21: shadow-4.6-move-home.patch +Patch22: shadow-4.6-audit-update.patch +Patch23: shadow-4.5-usermod-unlock.patch +Patch24: shadow-4.2.1-no-lock-dos.patch +Patch28: shadow-4.3.1-selinux-perms.patch +Patch29: shadow-4.2.1-null-tm.patch +Patch31: shadow-4.6-getenforce.patch +Patch32: shadow-4.5-crypt_h.patch +Patch33: shadow-4.5-long-entry.patch +Patch34: shadow-4.6-usermod-crash.patch +Patch35: shadow-4.6-coverity.patch +Patch36: shadow-4.6-use-itstool.patch +Patch37: shadow-4.6-sssd-flush.patch +Patch38: shadow-4.6-sysugid-min-limit.patch +Patch39: shadow-4.6-chgrp-guard.patch +Patch40: shadow-4.6-ignore-login-prompt.patch +Patch41: shadow-4.6-use-lckpwdf.patch +# Generate /var/spool/mail/$USER with the proper SELinux user identity - already upstreamed +Patch42: shadow-4.6-useradd-selinux-mail.patch +# Clarify useradd man regarding "-d" parameter - already upstreamed +Patch43: shadow-4.6-useradd-man-clarification.patch -### Globals ### -%global includesubiddir %{_includedir}/shadow -# Fail linking if there are undefined symbols. -%global _ld_strict_symbol_defs 1 - -### Patches ### - -### Dependencies ### -Requires: audit-libs >= 1.6.5 -Requires: libselinux >= 1.25.2-1 -Requires: pam-libs -Requires: setup - -### Build Dependencies ### -BuildRequires: audit-libs-devel >= 1.6.5 -BuildRequires: autoconf -BuildRequires: automake -BuildRequires: bison -BuildRequires: docbook-dtds -BuildRequires: docbook-style-xsl -BuildRequires: flex +License: BSD and GPLv2+ BuildRequires: gcc -BuildRequires: gettext-devel -BuildRequires: git -BuildRequires: itstool -BuildRequires: libacl-devel -BuildRequires: libattr-devel -BuildRequires: libcmocka-devel -BuildRequires: libeconf-devel BuildRequires: libselinux-devel >= 1.25.2-1 +BuildRequires: audit-libs-devel >= 1.6.5 BuildRequires: libsemanage-devel -BuildRequires: libtool -BuildRequires: libxcrypt-devel -BuildRequires: libxslt -BuildRequires: make -BuildRequires: pam -BuildRequires: pam-devel - -### Provides ### -Provides: shadow = %{epoch}:%{version}-%{release} -Provides: passwd = 0.80-18 -Obsoletes: passwd <= 0.80-19 - -%if "%{_sbindir}" == "%{_bindir}" -# Compat symlinks for Requires in other packages. -# We rely on filesystem to create the symlinks for us. -Requires: filesystem(unmerged-sbin-symlinks) -Provides: /usr/sbin/adduser -Provides: /usr/sbin/useradd -Provides: /usr/sbin/userdel -Provides: /usr/sbin/usermod -Provides: /usr/sbin/groupadd -Provides: /usr/sbin/groupdel -Provides: /usr/sbin/groupmod -%endif +BuildRequires: libacl-devel, libattr-devel +BuildRequires: bison, flex, docbook-style-xsl, docbook-dtds +BuildRequires: autoconf, automake, libtool, gettext-devel +BuildRequires: /usr/bin/xsltproc, /usr/bin/itstool +Requires: libselinux >= 1.25.2-1 +Requires: audit-libs >= 1.6.5 +Requires: setup %description The shadow-utils package includes the necessary programs for @@ -77,112 +62,135 @@ programs for managing user and group accounts. The pwconv command converts passwords to the shadow password format. The pwunconv command unconverts shadow passwords and generates a passwd file (a standard UNIX password file). The pwck command checks the integrity of password -and shadow files. The useradd, userdel, and usermod commands are used -for managing user accounts. The groupadd, groupdel, and groupmod -commands are used for managing group accounts. - - -### Subpackages ### -%package subid -Summary: A library to manage subordinate uid and gid ranges - -%description subid -Utility library that provides a way to manage subid ranges. - - -%package subid-devel -Summary: Development package for shadow-utils-subid -Requires: shadow-utils-subid = %{epoch}:%{version}-%{release} - -%description subid-devel -Development files for shadow-utils-subid. +and shadow files. The lastlog command prints out the last login times +for all users. The useradd, userdel, and usermod commands are used for +managing user accounts. The groupadd, groupdel, and groupmod commands +are used for managing group accounts. %prep -%autosetup -p 1 -S git -n shadow-4.20.0 +%setup -q -n shadow-%{version} +%patch0 -p1 -b .redhat +%patch1 -p1 -b .goodname +%patch2 -p1 -b .info-parent-dir +%patch6 -p1 -b .selinux +%patch10 -p1 -b .orig-context +%patch11 -p1 -b .logmsg +%patch14 -p1 -b .default-range +%patch15 -p1 -b .manfix +%patch17 -p1 -b .userdel +%patch19 -p1 -b .date-parsing +%patch21 -p1 -b .move-home +%patch22 -p1 -b .audit-update +%patch23 -p1 -b .unlock +%patch24 -p1 -b .no-lock-dos +%patch28 -p1 -b .selinux-perms +%patch29 -p1 -b .null-tm +%patch31 -p1 -b .getenforce +%patch32 -p1 -b .crypt_h +%patch33 -p1 -b .long-entry +%patch34 -p1 -b .usermod-crash +%patch35 -p1 -b .coverity +%patch36 -p1 -b .use-itstool +%patch37 -p1 -b .sssd-flush +%patch38 -p1 -b .sysugid-min-limit +%patch39 -p1 -b .chgrp-guard +%patch40 -p1 -b .login-prompt +%patch41 -p1 -b .use-lckpwdf +%patch42 -p1 -b .useradd-selinux-mail +%patch43 -p1 -b .useradd-man-clarification iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO cp -a %{SOURCE4} %{SOURCE5} . -cp -a %{SOURCE6} man/login.defs.d/HOME_MODE.xml + +# Force regeneration of getdate.c +rm libmisc/getdate.c %build +%ifarch sparc64 +#sparc64 need big PIE +export CFLAGS="$RPM_OPT_FLAGS -fPIE" +export LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now" +%else +export CFLAGS="$RPM_OPT_FLAGS -fpie" +export LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now" +%endif + autoreconf %configure \ - --disable-account-tools-setuid \ - --enable-logind=no \ - --enable-man \ --enable-shadowgrp \ - --enable-shared \ + --enable-man \ --with-audit \ - --with-bcrypt \ - --with-group-name-max-length=32 \ - --with-libpam \ - --with-selinux \ --with-sha-crypt \ - --with-yescrypt \ - --without-libbsd \ + --with-selinux \ --without-libcrack \ - --without-nscd \ - --without-sssd + --without-libpam \ + --disable-shared \ + --with-group-name-max-length=32 %make_build -%check -make check - %install -%make_install gnulocaledir=$RPM_BUILD_ROOT%{_datadir}/locale MKINSTALLDIRS=`pwd`/mkinstalldirs -install -d -m 755 $RPM_BUILD_ROOT%{_sysconfdir}/default -install -p -c -m 0644 %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/login.defs -install -p -c -m 0600 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/default/useradd -install -d -m 755 $RPM_BUILD_ROOT%{_pam_confdir} -install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/chpasswd -install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/newusers -install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/passwd +rm -rf $RPM_BUILD_ROOT +%make_install gnulocaledir=$RPM_BUILD_ROOT/%{_datadir}/locale MKINSTALLDIRS=`pwd`/mkinstalldirs +install -d -m 755 $RPM_BUILD_ROOT/%{_sysconfdir}/default +install -p -c -m 0644 %{SOURCE3} $RPM_BUILD_ROOT/%{_sysconfdir}/login.defs +install -p -c -m 0600 %{SOURCE2} $RPM_BUILD_ROOT/%{_sysconfdir}/default/useradd ln -s useradd $RPM_BUILD_ROOT%{_sbindir}/adduser -ln -s useradd.8 $RPM_BUILD_ROOT%{_mandir}/man8/adduser.8 -for subdir in $RPM_BUILD_ROOT%{_mandir}/{??,??_??,??_??.*}/man* ; do +ln -s useradd.8 $RPM_BUILD_ROOT/%{_mandir}/man8/adduser.8 +for subdir in $RPM_BUILD_ROOT/%{_mandir}/{??,??_??,??_??.*}/man* ; do test -d $subdir && test -e $subdir/useradd.8 && echo ".so man8/useradd.8" > $subdir/adduser.8 done -%if "%{_sbindir}" == "%{_bindir}" -# The installation script doesn't actually use the configured paths :( -mv -v $RPM_BUILD_ROOT/usr/sbin/* $RPM_BUILD_ROOT%{_bindir}/ -%endif - # Remove binaries we don't use. -rm $RPM_BUILD_ROOT%{_bindir}/chfn -rm $RPM_BUILD_ROOT%{_bindir}/chsh -rm $RPM_BUILD_ROOT%{_bindir}/login -rm $RPM_BUILD_ROOT%{_bindir}/su -rm $RPM_BUILD_ROOT%{_bindir}/faillog -rm $RPM_BUILD_ROOT%{_sbindir}/nologin -rm $RPM_BUILD_ROOT%{_mandir}/man1/chfn.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man1/chfn.* -rm $RPM_BUILD_ROOT%{_mandir}/man1/chsh.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man1/chsh.* -rm $RPM_BUILD_ROOT%{_mandir}/man1/login.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man1/login.* -rm $RPM_BUILD_ROOT%{_mandir}/man1/su.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man1/su.* -rm $RPM_BUILD_ROOT%{_mandir}/man5/passwd.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man5/passwd.* -rm $RPM_BUILD_ROOT%{_mandir}/man8/nologin.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man8/nologin.* -rm $RPM_BUILD_ROOT%{_mandir}/man3/getspnam.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man3/getspnam.* -rm $RPM_BUILD_ROOT%{_mandir}/man5/faillog.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man5/faillog.* -rm $RPM_BUILD_ROOT%{_mandir}/man8/faillog.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man8/faillog.* - -# Remove PAM service files we don't use. -rm $RPM_BUILD_ROOT%{_pam_confdir}/chfn -rm $RPM_BUILD_ROOT%{_pam_confdir}/chsh -rm $RPM_BUILD_ROOT%{_pam_confdir}/login -rm $RPM_BUILD_ROOT%{_pam_confdir}/su +rm $RPM_BUILD_ROOT/%{_bindir}/chfn +rm $RPM_BUILD_ROOT/%{_bindir}/chsh +rm $RPM_BUILD_ROOT/%{_bindir}/expiry +rm $RPM_BUILD_ROOT/%{_bindir}/groups +rm $RPM_BUILD_ROOT/%{_bindir}/login +rm $RPM_BUILD_ROOT/%{_bindir}/passwd +rm $RPM_BUILD_ROOT/%{_bindir}/su +rm $RPM_BUILD_ROOT/%{_bindir}/faillog +rm $RPM_BUILD_ROOT/%{_sysconfdir}/login.access +rm $RPM_BUILD_ROOT/%{_sysconfdir}/limits +rm $RPM_BUILD_ROOT/%{_sbindir}/logoutd +rm $RPM_BUILD_ROOT/%{_sbindir}/nologin +rm $RPM_BUILD_ROOT/%{_mandir}/man1/chfn.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man1/chfn.* +rm $RPM_BUILD_ROOT/%{_mandir}/man1/chsh.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man1/chsh.* +rm $RPM_BUILD_ROOT/%{_mandir}/man1/expiry.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man1/expiry.* +rm $RPM_BUILD_ROOT/%{_mandir}/man1/groups.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man1/groups.* +rm $RPM_BUILD_ROOT/%{_mandir}/man1/login.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man1/login.* +rm $RPM_BUILD_ROOT/%{_mandir}/man1/passwd.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man1/passwd.* +rm $RPM_BUILD_ROOT/%{_mandir}/man1/su.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man1/su.* +rm $RPM_BUILD_ROOT/%{_mandir}/man5/limits.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man5/limits.* +rm $RPM_BUILD_ROOT/%{_mandir}/man5/login.access.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man5/login.access.* +rm $RPM_BUILD_ROOT/%{_mandir}/man5/passwd.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man5/passwd.* +rm $RPM_BUILD_ROOT/%{_mandir}/man5/porttime.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man5/porttime.* +rm $RPM_BUILD_ROOT/%{_mandir}/man5/suauth.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man5/suauth.* +rm $RPM_BUILD_ROOT/%{_mandir}/man8/logoutd.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man8/logoutd.* +rm $RPM_BUILD_ROOT/%{_mandir}/man8/nologin.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man8/nologin.* +rm $RPM_BUILD_ROOT/%{_mandir}/man3/getspnam.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man3/getspnam.* +rm $RPM_BUILD_ROOT/%{_mandir}/man5/faillog.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man5/faillog.* +rm $RPM_BUILD_ROOT/%{_mandir}/man8/faillog.* +rm $RPM_BUILD_ROOT/%{_mandir}/*/man8/faillog.* find $RPM_BUILD_ROOT%{_mandir} -depth -type d -empty -delete %find_lang shadow @@ -194,30 +202,19 @@ for dir in $(ls -1d $RPM_BUILD_ROOT%{_mandir}/{??,??_??}) ; do echo "%%lang($lang) $dir/man*/*" >> shadow.lang done -# Move header files to its own folder -echo $(ls) -mkdir -p $RPM_BUILD_ROOT/%{includesubiddir} -install -m 644 libsubid/subid.h $RPM_BUILD_ROOT/%{includesubiddir}/ - -# Remove .la and .a files created by libsubid -rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.la -rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a - %files -f shadow.lang %doc NEWS doc/HOWTO README +%{!?_licensedir:%global license %%doc} %license gpl-2.0.txt shadow-bsd.txt %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/login.defs %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/default/useradd -%config(noreplace) %{_pam_confdir}/chpasswd -%config(noreplace) %{_pam_confdir}/newusers -%config(noreplace) %{_pam_confdir}/passwd %{_bindir}/sg %attr(4755,root,root) %{_bindir}/chage %attr(4755,root,root) %{_bindir}/gpasswd +%{_bindir}/lastlog %attr(4755,root,root) %{_bindir}/newgrp %attr(0755,root,root) %caps(cap_setgid=ep) %{_bindir}/newgidmap %attr(0755,root,root) %caps(cap_setuid=ep) %{_bindir}/newuidmap -%attr(4755,root,root) %{_bindir}/passwd %{_sbindir}/adduser %attr(0755,root,root) %{_sbindir}/user* %attr(0755,root,root) %{_sbindir}/group* @@ -235,7 +232,6 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_mandir}/man1/newgrp.1* %{_mandir}/man1/newgidmap.1* %{_mandir}/man1/newuidmap.1* -%{_mandir}/man1/passwd.* %{_mandir}/man3/shadow.3* %{_mandir}/man5/shadow.5* %{_mandir}/man5/login.defs.5* @@ -251,365 +247,18 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_mandir}/man8/chgpasswd.8* %{_mandir}/man8/newusers.8* %{_mandir}/man8/*conv.8* +%{_mandir}/man8/lastlog.8* %{_mandir}/man8/vipw.8* %{_mandir}/man8/vigr.8* -%files subid -%{_libdir}/libsubid.so.* -%{_bindir}/getsubids -%{_mandir}/man1/getsubids.1* - -%files subid-devel -%{includesubiddir}/subid.h -%{_libdir}/libsubid.so - %changelog -* Wed Jul 29 2026 Iker Pedrosa - 2:4.20.0-1 -- Rebase to version 4.20.0 - -* Wed Jul 22 2026 Iker Pedrosa - 2:4.20.0-rc3-1 -- Rebase to version 4.20.0-rc3 - -* Fri Jul 17 2026 Fedora Release Engineering - 2:4.19.3-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild - -* Wed May 27 2026 Iker Pedrosa - 2:4.19.3-3 -- Enable use of PAM for chpasswd and newusers - Resolves: #2461179 and #2283963 - -* Thu Apr 23 2026 Iker Pedrosa - 2:4.19.3-2 -- btrfs: simplify checks improve useradd behavior for non-btrfs - -* Wed Feb 11 2026 Debarshi Ray - 2:4.19.3-1 -- Rebase to version 4.19.3 - Resolves: #2426288 - -* Tue Jan 27 2026 Adam Williamson - 2:4.19.0-6 -- chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 regexes - -* Mon Jan 26 2026 Iker Pedrosa - 2:4.19.0-5 -- chkhash.c: fix support for ! and * in hashes -- usermod.c: add back optimizations - -* Sat Jan 17 2026 Fedora Release Engineering - 2:4.19.0-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild - -* Mon Jan 12 2026 Iker Pedrosa - 2:4.19.0-3 -- useradd: Support config for creating home dirs as Btrfs subvolumes - -* Fri Jan 9 2026 Iker Pedrosa - 2:4.19.0-2 -- Enable unit-tests - -* Fri Jan 9 2026 Iker Pedrosa - 2:4.19.0-1 -- Rebase to version 4.19.0 - Resolves: #2426288 and #2249524 - -* Tue Nov 25 2025 Adam Williamson - 2:4.18.0-7 -- Also revert changes from -4 (last known good was -3) - -* Tue Nov 25 2025 Adam Williamson - 2:4.18.0-6 -- Revert changes from -5 (they were only meant for testing) - -* Tue Nov 25 2025 Iker Pedrosa - 2:4.18.0-5 -- Test CI - -* Fri Oct 31 2025 Iker Pedrosa - 2:4.18.0-4 -- Stop setting SELinux labels in chroot and prefix environments - Resolves: #2249524 - -* Tue Jul 29 2025 Alexey Tikhonov - 2:4.18.0-3 -- Revert "Stop assigning subids by default" - Resolves: #2382662 - -* Fri Jul 25 2025 Fedora Release Engineering - 2:4.18.0-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild - -* Tue Jul 22 2025 Iker Pedrosa - 2:4.18.0-1 -- Rebase to version 4.18.0. Resolves: #2374710 - -* Fri Jul 18 2025 Iker Pedrosa - 2:4.17.4-5 -- Stop assigning subids by default - Resolves: CVE-2024-56433 and #2334168 - -* Tue Jul 15 2025 Iker Pedrosa - 2:4.17.4-4 -- FSWC: Migrate to lastlog2 - Link: - Resolves: #2361588 - -* Tue Mar 25 2025 Iker Pedrosa - 2:4.17.4-2 -- Add pam dependency for _pam_confdir missing macro. Resolves: #2354806 - -* Thu Mar 20 2025 Iker Pedrosa - 2:4.17.4-1 -- Rebase to version 4.17.4. Resolves: #2353491 -- Fixes problems with expiration dates - -* Sat Feb 01 2025 Björn Esser - 2:4.17.0-5 -- Add explicit BR: libxcrypt-devel - -* Sun Jan 19 2025 Fedora Release Engineering - 2:4.17.0-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild - -* Sun Jan 12 2025 Zbigniew Jędrzejewski-Szmek - 2:4.17.0-3 -- Rebuilt for the bin-sbin merge (2nd attempt) - -* Thu Dec 26 2024 Iker Pedrosa - 2:4.17.0 -- Rebase to version 4.17.0. Resolves: #2293678 - -* Sun Dec 22 2024 Björn Esser - 2:4.17.0~rc1-2 -- Remove potentially dangerous {C,LD}FLAGS shenanigans -- Fail linking if there are undefined symbols at link-time - -* Mon Dec 9 2024 Iker Pedrosa - 2:4.17.0~rc1-1 -- Rebase to version 4.17.0-rc1 - -* Tue Nov 12 2024 Iker Pedrosa - 2:4.16.0-7 -- SPDX license migration for subpackages - -* Wed Oct 16 2024 Iker Pedrosa - 2:4.16.0-6 -- Rebuilt for libeconf soname bump - -* Mon Oct 7 2024 Iker Pedrosa - 2:4.16.0-5 -- useradd: fix write_full() return value. Resolves: #2313559 - -* Fri Sep 13 2024 Iker Pedrosa - 2:4.16.0-3 -- Disable nscd - -* Wed Aug 28 2024 Iker Pedrosa - 2:4.16.0-2 -- Rebase to version 4.16.0 (#2293678) - -* Tue Jul 23 2024 Kevin Fenzi - 2:4.15.1-9 -- Revert chpasswd: use PAM again for now. - -* Sat Jul 20 2024 Fedora Release Engineering - 2:4.15.1-8 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild - -* Tue Jul 09 2024 Zbigniew Jędrzejewski-Szmek - 2:4.15.1-7 -- Rebuilt for the bin-sbin merge - -* Thu May 30 2024 Iker Pedrosa - 2:4.15.1-6 -- chpasswd: use PAM again (#2283963) - -* Mon May 20 2024 Iker Pedrosa - 2:4.15.1-5 -- Several fixes for issues detected by static analyzer tools - -* Thu Apr 11 2024 Zbigniew Jedrzejewski-Szmek - 2:4.15.1-4 -- Fix build with %%_bindir==%%_sbindir -- Add virtual Provides for old file paths used by other packages - -* Thu Mar 21 2024 Iker Pedrosa - 2:4.15.1-2 -- getdef: avoid spurious error messages about unknown configuration options. - Resolves: #2265291 - -* Tue Mar 12 2024 Iker Pedrosa - 2:4.15.1-1 -- Wrong versioning for 4.15.0 RC releases makes the oficial release look lower than them. - Fix it by bumping the minor release to 4.15.1. Resolves: #2268690 - -* Mon Mar 11 2024 Iker Pedrosa - 2:4.15.0-1 -- Rebase to version 4.15.0 - -* Mon Feb 12 2024 Iker Pedrosa - 2:4.14.0-6 -- Build linking `libpam` - -* Thu Feb 1 2024 Iker Pedrosa - 2:4.14.0-5 -- passwd: Provide binary from this package. Enable libpam and - disable account-tools-setuid. Provide passwd PAM service file. - Resolves: #2233275 -- passwd: provide --stdin option - -* Mon Jan 29 2024 Iker Pedrosa - 2:4.14.0-4 -- Disable SSSD support. Resolves: #2253182 - -* Sat Jan 27 2024 Fedora Release Engineering - 2:4.14.0-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Tue Oct 3 2023 Iker Pedrosa - 2:4.14.0-2 -- useradd: Set proper SELinux labels for def_usrtemplate - -* Wed Aug 16 2023 Iker Pedrosa - 2:4.14.0-1 -- Rebase to version 4.14.0. Resolves: #2229000 - -* Sat Jul 22 2023 Fedora Release Engineering - 2:4.13-8 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild - -* Tue Jun 06 2023 Yaakov Selkowitz - 2:4.13-7 -- Remove unused libbsd-devel dependency - -* Mon Mar 6 2023 Iker Pedrosa - 2:4.13-6 -- Add libbsd-devel and libeconf-devel as build dependencies - -* Thu Mar 2 2023 Iker Pedrosa - 2:4.13-5 -- newuidmap and newgidmap: support passing pid as fd. Resolves: #2174752 - -* Sat Jan 21 2023 Fedora Release Engineering - 2:4.13-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild - -* Wed Nov 23 2022 Iker Pedrosa - 2:4.13-3 -- Change SUB_UID_MIN and SUB_GID_MIN to 524288. Resolves: #2144558 - -* Mon Nov 21 2022 Florian Weimer - 2:4.13-2 -- Fix gshadow configure check (switching to glibc implementation) - -* Wed Nov 9 2022 Iker Pedrosa - 2:4.13-1 -- Rebase to version 4.13 -- SPDX license migration - -* Wed Oct 5 2022 Iker Pedrosa - 2:4.12.3-3 -- chage: Fix regression in print_date. Resolves: #2129336 - -* Fri Sep 9 2022 Iker Pedrosa - 2:4.12.3-2 -- useradd: Do not reset non-existent data in {last,fail}log - -* Mon Aug 22 2022 Iker Pedrosa - 2:4.12.3-1 -- Rebase to version 4.12.3. Resolves: #2117809 - -* Mon Aug 1 2022 Iker Pedrosa - 2:4.11.1-4 -- useradd: modify check ID range for system users. Resolves: #2093692 - -* Sat Jul 23 2022 Fedora Release Engineering - 2:4.11.1-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild - -* Thu Feb 10 2022 Iker Pedrosa - 2:4.11.1-2 -- Fix explicit subid requirement for subid-devel - -* Tue Jan 25 2022 Iker Pedrosa - 2:4.11.1-1 -- Rebase to version 4.11.1 (#2034038) -- Fix release sources -- Add explicit subid requirement for subid-devel - -* Sat Jan 22 2022 Fedora Release Engineering - 2:4.9-10 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild - -* Mon Jan 17 2022 Iker Pedrosa - 2:4.9-9 -- nss: get shadow_logfd with log_get_logfd() (#2038811) -- lib: make shadow_logfd and Prog not extern -- lib: rename Prog to shadow_progname -- lib: provide default values for shadow_progname -- libsubid: use log_set_progname in subid_init - -* Fri Nov 19 2021 Iker Pedrosa - 2:4.9-8 -- getsubids: provide system binary and man page (#1980780) -- pwck: fix segfault when calling fprintf() (#2021339) -- newgrp: fix segmentation fault (#2019553) -- groupdel: fix SIGSEGV when passwd does not exist (#1986111) - -* Fri Nov 12 2021 Iker Pedrosa - 2:4.9-7 -- useradd: change SELinux labels for home files (#2022658) - -* Thu Nov 4 2021 Iker Pedrosa - 2:4.9-6 -- useradd: revert fix memleak of grp (#2018697) - -* Wed Oct 27 2021 Iker Pedrosa - 2:4.9-5 -- useradd: generate home and mail directories with selinux user attribute - -* Thu Sep 23 2021 Iker Pedrosa - 2:4.9-4 -- login.defs: include HMAC_CRYPTO_ALGO key -- Clean spec file: organize dependencies and move License location - -* Tue Aug 17 2021 Iker Pedrosa - 2:4.9-3 -- libmisc: fix default value in SHA_get_salt_rounds() - -* Mon Aug 9 2021 Iker Pedrosa - 2:4.9-2 -- useradd: avoid generating an empty subid range (#1990653) - -* Wed Aug 4 2021 Iker Pedrosa - 2:4.9-1 -- Rebase to version 4.9 -- usermod: allow all group types with -G option (#1975327) -- Clean spec file - -* Fri Jul 23 2021 Fedora Release Engineering - 2:4.8.1-20 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild - -* Wed Jul 14 2021 Björn Esser - 2:4.8.1-19 -- Add patch to fix 'fread returns element count, not element size' - -* Wed Jul 14 2021 Iker Pedrosa - 2:4.8.1-18 -- Fix regression issues detected in rhbz#667593 and rhbz#672510 - -* Mon Jul 12 2021 Björn Esser - 2:4.8.1-17 -- Enable bcrypt support, as libxcrypt supports it well - -* Sun Jul 04 2021 Björn Esser - 2:4.8.1-16 -- Add a patch to obtain random bytes using getentropy() -- Update shadow-4.8-crypt_h.patch with the upstreamed version -- Add a patch to make use of crypt_gensalt() from libxcrypt - -* Tue Jun 29 2021 Iker Pedrosa - 2:4.8.1-15 -- useradd: free correct pointer (#1976809) - -* Mon Jun 28 2021 Björn Esser - 2:4.8.1-14 -- Add a patch to fix the used prefix for the bcrypt hash method -- Add a patch to cleanup the code in libmisc/salt.c -- Add a patch adding some clarifying comments in libmisc/salt.c -- Add a patch to obtain random bytes from /dev/urandom - -* Mon Jun 28 2021 Iker Pedrosa - 2:4.8.1-13 -- Covscan fixes - -* Mon Jun 21 2021 Björn Esser - 2:4.8.1-12 -- Backport support for yescrypt hash method -- Add a patch to fix the parameter type of YESCRYPT_salt_cost() - -* Mon Jun 21 2021 Iker Pedrosa - 2:4.8.1-11 -- libsubid: don't print error messages on stderr by default -- libsubid: libsubid_init return false if out of memory -- useradd: fix SUB_UID_COUNT=0 -- libsubid: don't return owner in list_owner_ranges API call -- libsubid: libsubid_init don't print messages on error -- libsubid: fix newusers when nss provides subids -- man: clarify subid delegation -- libsubid: make shadow_logfd not extern - -* Thu May 6 2021 Iker Pedrosa - 2:4.8.1-10 -- man: mention NSS in new[ug]idmap manpages -- libsubid: move development header to shadow folder - -* Fri Apr 16 2021 Iker Pedrosa - 2:4.8.1-9 -- libsubid: creation and nsswitch support -- Creation of subid and subid-devel subpackages - -* Mon Mar 29 2021 Iker Pedrosa - 2:4.8.1-8 -- man: include lastlog file caveat (#951564) -- Upstream links to several patches -- Spec file cleanup by Robert Scheck -- Add BuildRequires: make by Tom Stellard - -* Wed Jan 27 2021 Fedora Release Engineering - 2:4.8.1-7 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild - -* Mon Nov 9 2020 Iker Pedrosa - 2:4.8.1-6 -- commonio: force lock file sync (#1862056) - -* Tue Nov 3 2020 Petr Lautrbach - 2:4.8.1-5 -- Rebuild with libsemanage.so.2 - -* Wed Jul 29 2020 Fedora Release Engineering - 2:4.8.1-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild - -* Thu May 14 2020 Iker Pedrosa - 2:4.8.1-3 -- check only local groups when adding new supplementary groups to a user (#1727236) - -* Tue Mar 24 2020 Iker Pedrosa - 2:4.8.1-2 +* Tue Mar 24 2020 Iker Pedrosa - 2:4.6-18 - useradd: clarify the useradd -d parameter behavior in man page -* Tue Mar 17 2020 Iker Pedrosa - 2:4.8.1-1 -- updated upstream to 4.8.1 - -* Tue Mar 17 2020 Iker Pedrosa - 2:4.8-5 -- synchronized login.defs with upstream file (#1261099 and #1807957) - -* Mon Feb 24 2020 Iker Pedrosa - 2:4.8-4 +* Mon Feb 24 2020 Iker Pedrosa - 2:4.6-17 - fix useradd: doesn't generate spool mail with the proper SELinux user identity (#1690527) -* Thu Jan 30 2020 Fedora Release Engineering - 2:4.8-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild - -* Thu Jan 16 2020 Tomáš Mráz - 2:4.8-2 -- make the invalid shell check into warning - -* Mon Jan 13 2020 Tomáš Mráz - 2:4.8-1 -- update to current upstream release 4.8 - * Mon Sep 2 2019 Tomáš Mráz - 2:4.6-16 - fix SELinux related problem in chpasswd/chgpasswd when run with -R (patch by Petr Lautrbach) (#1747215) diff --git a/sources b/sources index 7669083..2093465 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (shadow-4.20.0.tar.xz) = 0b8afded372e4d37a78f38cb972c0ab877870ef6356cdd1c45be3c708af3d1496c6f87de6bf6a5b1d217d4d52d7e2d15c28b26530949cc6de0231a4028930406 -SHA512 (shadow-4.20.0.tar.xz.asc) = e4b134543768f323df30a1e450c22086d013020928d62bd3207db7f633beea3587b67802da5b575d5a794e8c2bebd7a0abeba6ed69d44bf08adc37df3bf352ac +SHA512 (shadow-4.6.tar.xz) = e8eee52c649d9973f724bc2d5aeee71fa2e6a2e41ec3487cd6cf6d47af70c32e0cdf304df29b32eae2b6eb6f9066866b5f2c891add0ec87ba583bea3207b3631 +SHA512 (shadow-4.6.tar.xz.asc) = 8728bff5544db6ea123f758cce5bd5c2d346489570c33092e4e97db35c274d7aba01580018f120e4ad80b8f79cfe296a33bccbe9bf68df51bf9b2004c6bfffed diff --git a/tests/mhc-fedora-ci.yaml b/tests/mhc-fedora-ci.yaml deleted file mode 100644 index 8747e67..0000000 --- a/tests/mhc-fedora-ci.yaml +++ /dev/null @@ -1,13 +0,0 @@ -provisioned_topologies: -- shadow -domains: -- id: shadow - hosts: - - hostname: localhost - role: shadow - conn: - type: ssh - host: localhost - user: root - artifacts: - - /var/log/* diff --git a/tests/sanity/Makefile b/tests/sanity/Makefile new file mode 100644 index 0000000..386221b --- /dev/null +++ b/tests/sanity/Makefile @@ -0,0 +1,77 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Author: Jakub Hrozek + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# +# Example Makefile for RHTS # +# This example is geared towards a test for a specific package # +# It does most of the work for you, but may require further coding # +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# + +# The toplevel namespace within which the test lives. +TOPLEVEL_NAMESPACE=CoreOS + +# The name of the package under test: +PACKAGE_NAME=shadow-utils + +# The path of the test below the package: +RELATIVE_PATH=sanity + +# Version of the Test. Used with make tag. +export TESTVERSION=1.1 + +# The combined namespace of the test. +export TEST=/$(TOPLEVEL_NAMESPACE)/$(PACKAGE_NAME)/$(RELATIVE_PATH) + +# A phony target is one that is not really the name of a file. +# It is just a name for some commands to be executed when you +# make an explicit request. There are two reasons to use a +# phony target: to avoid a conflict with a file of the same +# name, and to improve performance. +.PHONY: all install download clean + +# Executables to be built should be added here, they will be generated on the system under test. +BUILT_FILES= + +# Data files, .c files, scripts anything needed to either compile the test and/or run it. +FILES=$(METADATA) Makefile PURPOSE sanity_test.py runtest.sh + +run: $(FILES) build + ./runtest.sh + +build: $(BUILT_FILES) + chmod a+x ./sanity_test.py + chmod a+x ./runtest.sh + +clean: + rm -f *~ *.rpm $(BUILT_FILES) + +# Include Common Makefile +include /usr/share/rhts/lib/rhts-make.include + +# Generate the testinfo.desc here: +$(METADATA): Makefile + @touch $(METADATA) + @echo "Owner: Jakub Hrozek " > $(METADATA) + @echo "Name: $(TEST)" >> $(METADATA) + @echo "Path: $(TEST_DIR)" >> $(METADATA) + @echo "TestVersion: $(TESTVERSION)" >> $(METADATA) + @echo "License: GNU GPL" >> $(METADATA) + @echo "Description: Basic sanity test for shadow-utils" >> $(METADATA) + @echo "TestTime: 5m" >> $(METADATA) + @echo "RunFor: $(PACKAGE_NAME)" >> $(METADATA) + @echo "Requires: $(PACKAGE_NAME)" >> $(METADATA) + @echo "Requires: python" >> $(METADATA) + rhts-lint $(METADATA) + diff --git a/tests/sanity/PURPOSE b/tests/sanity/PURPOSE new file mode 100644 index 0000000..27062e1 --- /dev/null +++ b/tests/sanity/PURPOSE @@ -0,0 +1,10 @@ +This is a basic sanity test for the shadow-utils package. It is implemented +in python on top of the unittesting.py module. + +Its purpose is to ensure that the binaries in the shadow-utils package behave +as expected and its switches/options work correctly. + +For the most part, every binary in the shadow-utils package is represented by +a single class named Test, i.e. TestUsermod etc. There are some +exceptions, like TestUseraddWeirdNameTest though. + diff --git a/tests/sanity/runtest.sh b/tests/sanity/runtest.sh new file mode 100755 index 0000000..cb2a2b5 --- /dev/null +++ b/tests/sanity/runtest.sh @@ -0,0 +1,24 @@ +#!/bin/bash +. /usr/bin/rhts-environment.sh +. /usr/share/beakerlib/beakerlib.sh || exit 1 + +rlJournalStart +rlFileBackup --clean /etc/default/useradd- /etc/default/useradd +setenforce 0 +python sanity_test.py -v +setenforce 1 +rlFileRestore + +EXIT=$? +if [[ $EXIT -eq 0 ]]; then + RESULT="PASS" +else + RESULT="FAIL" +fi + + +rlJournalEnd + +echo "Result: $RESULT" +echo "Exit: $EXIT" +report_result $TEST $RESULT $EXIT diff --git a/tests/sanity/sanity_test.py b/tests/sanity/sanity_test.py new file mode 100755 index 0000000..e9c45c2 --- /dev/null +++ b/tests/sanity/sanity_test.py @@ -0,0 +1,1013 @@ +#!/usr/bin/env python +""" +A script that tests functionality of the shadow-utils package. + +Author: Jakub Hrozek, +License: GNU GPL v2 +Date: 2007 + +TODO: + * tests for password aging + * if something fails, print out the command issued for easier debugging + * test long options variants along with the short ones +""" + +import unittest +import pwd +import grp +import commands +import os +import os.path +import sys +import copy +import tempfile +import rpm +import shutil + +from UserDict import UserDict + +class RedHatVersion(object): + def __init__(self, type=None, version=None, release=None): + self.type = type + self.version = version + self.release = release + self.rhel = False + + def __eq__( self, other): + """ + Don't compare if either of the values is None + so we can do comparisons like 'is it fedora?' or 'is it rhel4?' + """ + ok = (self.type == other.type) + if ok == False: return False + + if self.version and other.version: + ok = (self.version == other.version) + if ok == False: return False + + if (self.release == other.release): + ok = (self.release == other.release) + + return ok + + def __ne__( self, other): + return not self.__eq__(other) + + def __get_fedora_info(self, mi): + return [ (h['version'],h['release']) for h in mi ][0] + + def __get_rhel_info(self, mi): + # The rules for RHEL versions are braindead..releases even more + ver_rpm, rel_rpm = [ (h['version'],h['release']) for h in mi ][0] + rhel_versions = { '3AS' : 3, '4AS' : 4, '5Server' : 5, '5Client' : 5, '6' : 6 } + if ver_rpm[:3] == '5.9' or ver_rpm[:1] == '6': # rhel6 prerelease and release hack + rhel_versions[ver_rpm] = 6 + if ver_rpm in rhel_versions.keys(): + return (rhel_versions[ver_rpm], rel_rpm) + + def is_rhel(self): + return self.rhel + + def get_info(self): + """ + Returns a tuple containing (type, version, release) of RHEL or Fedora. + Type is either RHEL or Fedora. + Returns None if it cannot parse the info + """ + + ts = rpm.TransactionSet() + mi = ts.dbMatch() + mi.pattern('name', rpm.RPMMIRE_GLOB, 'redhat-release*') + + if mi: + self.rhel = True + return ('RHEL',) + self.__get_rhel_info(mi) + else: + mi = ts.dbMatch('name','fedora-release') + self.rhel = False + if mi.count() != 0: + return ('Fedora',) + self.__get_fedora_info(mi) + + return None + + +class UserInfo(UserDict): + fields = { "pw_name" : 0, "pw_passwd" : 1, "pw_uid" : 2, "pw_gid" : 3, + "pw_gecos" : 4, "pw_dir" : 5, "pw_shell" : 6 } + + def __init__(self): + UserDict.__init__(self) + for f in UserInfo.fields: self[f] = None + + def __getitem__(self, key): + return UserDict.__getitem__(self, key) + + def __setitem__(self, key, value): + UserDict.__setitem__(self, key, value) + + def __cmp__(self, other): + return UserDict.__cmp__(self, other) + + def __repr__(self): + return " ; ".join( [ "%s => %s" % (k, v) for k, v in self.data.items() ] ) + + def __parse_info(self, struct): + for f in UserInfo.fields: + self[f] = struct[UserInfo.fields[f]] + + def get_info_uid(self, uid): + self.__parse_info(pwd.getpwuid(uid)) + + def get_info_name(self, name): + try: + self.__parse_info(pwd.getpwnam(name)) + except KeyError: + return None + + def lazy_compare(self, pattern): + """ Compare pattern against self. If any field in pattern is set + to None, it is automatically considered equal with the corresponding + field in self. """ + for field in UserInfo.fields: + if pattern[field] and pattern[field] != self[field]: + return False + + return True + +class GroupInfo(UserDict): + fields = { "gr_name" : 0, "gr_passwd" : 1, + "gr_gid" : 2, "gr_mem" : 3} + + def __init__(self): + UserDict.__init__(self) + for f in GroupInfo.fields: self[f] = None + + def __getitem__(self, key): + return UserDict.__getitem__(self, key) + + def __setitem__(self, key, value): + UserDict.__setitem__(self, key, value) + + def __cmp__(self, other): + return UserDict.__cmp__(self, other) + + def __repr__(self): + return " ; ".join( [ "%s => %s" % (k, v) for k, v in self.data.items() ] ) + + def __parse_info(self, struct): + for f in GroupInfo.fields: + self[f] = struct[GroupInfo.fields[f]] + + def get_info_gid(self, gid): + self.__parse_info(grp.getgrgid(gid)) + + def get_info_name(self, name): + self.__parse_info(grp.getgrnam(name)) + + def lazy_compare(self, pattern): + """ Compare pattern against self. If any field in pattern is set + to None, it is automatically considered equal with the corresponding + field in self. """ + for field in GroupInfo.fields: + if pattern[field] and pattern[field] != self[field]: + return False + + return True + +class LoginDefsParser(UserDict): + "A quick-n-dirty way how to fetch the defaults from /etc/login.defs into a dictionary" + + def __getitem__(self, key): + try: + return UserDict.__getitem__(self, key) + except KeyError: + # if a name-value is not defined in the config file, return defaults + if key == "CREATE_MAIL_SPOOL": + return "yes" + if key == "UMASK": + return "077" + + def __init__(self, path="/etc/login.defs",split=None): + self.path = path + UserDict.__init__(self) + try: + defs = open(path) + except IOError: + print "Could not open the config file %s" % (path) + + for line in defs: + if line.startswith('#'): continue + fields = line.split(split) + if len(fields) != 2: continue # yeah, we're dirty + self.data[fields[0]] = fields[1] + + def serialize(self): + output = open(self.path, "w+") + for k,v in self.data.items(): + output.write("%s=%s" % (k, v)) + + output.write("\n") + output.close() + +class TestUserInfo(unittest.TestCase): + def testLazyCompare(self): + """ (test sanity): Test comparing two UserInfo records """ + a = UserInfo() + a["pw_name"] = "foo" + a["pw_uid"] = 555 + b = copy.deepcopy(a) + c = UserInfo() + + self.assertEqual(a.lazy_compare(b), True) + self.assertEqual(a.lazy_compare(c), True) + + c["pw_name"] = "foo" + c["pw_uid"] = None + self.assertEqual(a.lazy_compare(c), True) + self.assertEqual(c.lazy_compare(a), False) + + c["pw_name"] = "bar" + self.assertNotEqual(a.lazy_compare(c), True) + + def testGetInfoUid(self): + """ (test sanity): Test getting user info based on his UID """ + a = UserInfo() + a.get_info_uid(0) + self.assertEqual(a["pw_name"], "root") + + def testGetInfoName(self): + """ (test sanity): Test getting user info based on his name """ + a = UserInfo() + a.get_info_name("root") + self.assertEqual(a["pw_uid"], 0) + +class ShadowUtilsTestBase: + """ Handy routines """ + def getDefaults(self): + # get the default values for so we can compare against that + (status, defaults_str) = commands.getstatusoutput('useradd -D') + if status != 0: + raise RuntimeError("Could not get the default values for useradd") + return dict([ rec.split("=") for rec in defaults_str.split("\n") ]) + + def getDefaultUserInfo(self, username): + expected = UserInfo() + defaults = self.getDefaults() + + expected["pw_name"] = username + expected["pw_dir"] = defaults["HOME"] + "/" + username + expected["pw_shell"] = defaults["SHELL"] + + return expected + +class TestUseradd(ShadowUtilsTestBase, unittest.TestCase): + def setUp(self): + self.username = "test-shadow-utils-useradd" + + def tearDown(self): + commands.getstatusoutput("userdel -r %s" % (self.username)) + + def testBasicAdd(self): + """ useradd: Tests basic adding of a user """ + expected = self.getDefaultUserInfo(self.username) + + runme = "useradd %s" % (self.username) + (status, output) = commands.getstatusoutput(runme) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user\nIssued command: %s" % (runme)) + + def testExistingUser(self): + """ useradd: Test that user with an existing name cannot be added """ + (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + self.assertNotEqual(commands.getstatusoutput("useradd %s" % (self.username))[0], 0, "FAIL: User that already exists added") + + def testCustomUID(self): + """ useradd: Adding an user with a specific UID """ + UID = 23456 # FIXME - test for a free UID slot first + + expected = self.getDefaultUserInfo(self.username) + expected["pw_uid"] = UID + + runme = "useradd %s -u %d" % (self.username, UID) + (status, output) = commands.getstatusoutput(runme) + self.failUnlessEqual(status, 0, "Issued command: %s\n" % (runme) + "Got from useradd: %s\n" % (output)) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific UID\nIssued command: %s" % (runme)) + + def testNegativeUID(self): + """ useradd: Tests that user cannot have a negative UID assigned """ + self.assertNotEqual(commands.getstatusoutput("useradd %s --uid -5" % (self.username))[0], 0, "FAIL: User with UID < 0 added") + + def testCustomExistingUID(self): + """ useradd: Adding a user with a specific existing UID """ + UID = 32112 + + expected = self.getDefaultUserInfo(self.username) + expected["pw_uid"] = UID + + (status_u, output_u) = commands.getstatusoutput("useradd %s -u %d" % (self.username, UID)) + + # must fail without -o flag + (status_u_no_o, output_u_no_o) = commands.getstatusoutput("useradd foo -u %d" % (UID)) + + # must pass with -o flag + (status_o, output_o) = commands.getstatusoutput("useradd foo -u %d -o" % (UID)) + + # clean up + (status, output) = commands.getstatusoutput("userdel -r foo") + + self.failUnlessEqual(status_u, 0, "FAIL: cannot add an user with a specified UID\n"+output_u) + self.assertEqual(status_o, 0, "FAIL: cannot add an user with an existing UID using the -o flag\n"+output_o) + self.failUnlessEqual(status, 0, output) + self.assertNotEqual(status_u_no_o, 0, "FAIL: user with an existing UID added\n"+output_u_no_o) + + def testCustomGID(self): + """ useradd: Adding an user with a specific GID """ + GID = 100 # users group should be everywhere - should we test before? + expected = self.getDefaultUserInfo(self.username) + expected["pw_gid"] = GID + + (status, output) = commands.getstatusoutput("useradd %s -g %d" % (self.username, GID)) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific GID") + + def testCustomShell(self): + """ useradd: Adding an user with a specific login shell """ + shell = "/bin/ksh" + expected = self.getDefaultUserInfo(self.username) + expected["pw_shell"] = shell + + (status, output) = commands.getstatusoutput("useradd %s -s %s" % (self.username, shell)) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific shell") + + def testCustomHome(self): + """ useradd: Adding an user with a specific home directory """ + home = "/tmp/useradd-test" + os.mkdir(home) + expected = self.getDefaultUserInfo(self.username) + expected["pw_dir"] = home + + (status, output) = commands.getstatusoutput("useradd %s -d %s" % (self.username, home)) + shutil.rmtree(home) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific home") + + def testSystemAccount(self): + """ useradd: Adding a system user (UID < UID_MIN from /etc/login.defs) """ + defaults = LoginDefsParser() + + # system account with no home dir + expected = self.getDefaultUserInfo(self.username) + + (status, output) = commands.getstatusoutput("useradd -r %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(os.path.exists(created["pw_dir"]), False, "FAIL: System user has a home dir created") + self.assertEqual(created["pw_uid"] < defaults['UID_MIN'], True, "FAIL: System user has UID > UID_MIN") + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a system user") + + def testAddToMoreGroups(self): + """ useradd: Creating an user that belongs to more than one group """ + (status, output) = commands.getstatusoutput("useradd -G bin %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + gr_bin = GroupInfo() + gr_bin.get_info_name("bin") + self.assertEqual(self.username in gr_bin["gr_mem"], True, "FAIL: User not in supplementary group after usermod -G -a") + + + def testAddWithCommonName(self): + """ useradd: Specifying a comment (user for account name) """ + comment = "zzzzzz" + (status, output) = commands.getstatusoutput("useradd -c %s %s" % (comment, self.username)) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(created["pw_gecos"], comment, "FAIL: failed to create a user with a GECOS comment") + + def testHomePermissions(self): + """ useradd: Check if permissions on newly created home dir match the umask """ + defaults = LoginDefsParser() + + (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + + import stat + perm = os.stat(created["pw_dir"])[stat.ST_MODE] + mode = int(oct(perm & 0777)) + + self.assertEqual(defaults["UMASK"], "077", "FAIL: umask setting is not sane - is %s, should be 077" % (defaults["UMASK"])) + self.assertEqual(int(defaults["UMASK"]) + mode , 777, "FAIL: newly-created home dir does not match the umask") + + def testCreateMailSpool(self): + """ useradd: Check whether the mail spool gets created when told to""" + # set up creating of mail spool + defaults = LoginDefsParser("/etc/default/useradd", split="=") + + create_mail = defaults["CREATE_MAIL_SPOOL"] + defaults["CREATE_MAIL_SPOOL"] = "yes" + defaults.serialize() + + login_defs = LoginDefsParser() + + (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + # clean up + defaults["CREATE_MAIL_SPOOL"] = create_mail + defaults.serialize() + self.assertEqual(os.path.exists(login_defs["MAIL_DIR"] + "/" + self.username), True, "FAIL: useradd did not create mail spool") + + def testDefaultMailSettings(self): + """ useradd: Check whether the mail spool is on by default""" + defaults = LoginDefsParser("/etc/default/useradd", split="=") + self.assertEqual(defaults["CREATE_MAIL_SPOOL"], "yes\n") + + def testNoLastlog(self): + """ useradd: Check if the -l option prevents from being added to the lastlog """ + pass # FIXME - add some code here + + +class TestUseraddWeirdNameTest(unittest.TestCase, ShadowUtilsTestBase): + """ Tests addition/removal of usernames that have proven to be problematic in the past. + The reason to separate these from the main useradd test suite is to not run the setUp + and tearDown methods """ + + def addAndRemove(self, username, success=True): + expected = self.getDefaultUserInfo(username) + expected["pw_name"] = username + + (status, output) = commands.getstatusoutput("useradd %s" % (username)) + if success: + self.failUnlessEqual(status, 0, output) + else: + self.failIfEqual(status, 0, output) + return True + + created = UserInfo() + created.get_info_name(username) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: TestUseraddWeirdName::addAndRemove - could not add a user") + + # the cleanup method won't help this time + (status, output) = commands.getstatusoutput("userdel -r %s" % (username)) + self.failUnlessEqual(status, 0, output) + + def testNumericName(self): + """ useradd: Test if an user with a purely numerical name can be added (123) """ + return self.addAndRemove("123") + + def testSambaName(self): + """ useradd: Test if an user with a name with a dollar at the end can be added (joepublic$ ) """ + return self.addAndRemove("joepublic$") + + def testDotInName(self): + """ useradd: Test if an user with a name with a dot in it can be added (joe.public ) """ + return self.addAndRemove("joe.public") + + def testAtInName(self): + """ useradd: Test if an user with an '@' in name can be added (joe@public.com) - should fail """ + return self.addAndRemove("joe@public.com", False) + + def testUppercase(self): + """ useradd: Test if an user with UPPERCASE or Uppercase name can be added """ + return self.addAndRemove("JOEPUBLIC") + return self.addAndRemove("Joepublic") + +class TestUseraddDefaultsChange(unittest.TestCase, ShadowUtilsTestBase): + def testDefaultsChange(self): + """ useradd: Test overriding default settings (shell, home dir, group) with a -D option """ + save = self.getDefaults() + + new_defs = dict() + new_defs["SHELL"] = "/bin/ksh" + new_defs["GROUP"] = "1" + new_defs["HOME"] = "/tmp" + + command = "useradd -D -s%s -g%s -b%s" % (new_defs["SHELL"], new_defs["GROUP"], new_defs["HOME"]) + (status, output) = commands.getstatusoutput(command) + self.failUnlessEqual(status, 0, output) + + overriden = self.getDefaults() + [ self.assertEqual(overriden[k], new_defs[k]) for k in new_defs.keys() ] + + command = "useradd -D -s%s -g%s -b%s" % (save["SHELL"], save["GROUP"], save["HOME"]) + (status, output) = commands.getstatusoutput(command) + self.failUnlessEqual(status, 0, output) + + +class TestUserdel(unittest.TestCase, ShadowUtilsTestBase): + def setUp(self): + self.username = "test-shadow-utils-userdel" + (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + def testRemoveUserGroup(self): + """ userdel: test if userdel removes user's group when he's deleted - regression test for #201379 """ + (status, output) = commands.getstatusoutput("userdel -r %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + # This would fail if we did not have the group removed + (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + (status, output) = commands.getstatusoutput("userdel -r %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + +class TestUsermod(unittest.TestCase, ShadowUtilsTestBase): + def setUp(self): + self.username = "test-shadow-utils-usermod" + (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + def tearDown(self): + (status, output) = commands.getstatusoutput("userdel -r %s" % (self.username)) + self.failUnlessEqual(status, 0, output) + + def testAppendToSupplementaryGroup(self): + """ usermod: Test if a user can be added to a supplementary group """ + add_group = "additional_group" + (status, output) = commands.getstatusoutput("groupadd %s" % (add_group)) + self.failUnlessEqual(status, 0, output) + + (status_mod, output_mod) = commands.getstatusoutput("usermod -a -G %s %s" % (add_group, self.username)) + add_group_info = GroupInfo() + add_group_info.get_info_name(add_group) + (status, output) = commands.getstatusoutput("groupdel %s" % (add_group)) + + self.failUnlessEqual(status, 0, output) + self.failUnlessEqual(status_mod, 0, output_mod) + self.assertEqual(self.username in add_group_info["gr_mem"], True, "User not in supplementary group after usermod -G --append") + + + def testAppendToSupplementaryGroupLongOption(self): + """ usermod: Test if a user can be added to a supplementary group via --append rather that -a (regression test for 222540) """ + # this is known to not work on older RHELs - test what we are running + rhv = RedHatVersion() + runs = rhv.get_info() + if rhv.is_rhel(): + if runs[1] < 5: + print "This test makes sense for RHEL5+" + return + else: + if runs[1] < 6: + print "This test makes sense for Fedora 6+" + return + + type, release, version = RedHatVersion().get_info() + if RedHatVersion().is_rhel(): + if release < 5 or (release == 5 and version < 2): + print "This test makes sense for RHEL 5.2+" + return + + add_group = "additional_group" + (status, output) = commands.getstatusoutput("groupadd %s" % (add_group)) + self.failUnlessEqual(status, 0, output) + + (status_mod, output_mod) = commands.getstatusoutput("usermod --append -G %s %s" % (add_group, self.username)) + add_group_info = GroupInfo() + add_group_info.get_info_name(add_group) + (status, output) = commands.getstatusoutput("groupdel %s" % (add_group)) + + self.failUnlessEqual(status, 0, output) + self.failUnlessEqual(status_mod, 0, output_mod) + self.assertEqual(self.username in add_group_info["gr_mem"], True, "User not in supplementary group after usermod -G --append") + + + def testNameChange(self): + """ usermod: Test if the comment field (used as the Common Name) can be changed """ + new_comment = "zzzzzz" + + (status, output) = commands.getstatusoutput("usermod -c %s %s" % (new_comment, self.username)) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + + self.assertEqual(created["pw_gecos"], new_comment) + + def testHomeChange(self): + """ usermod: Test if user's home directory can be changed """ + new_home = "/tmp" + created = UserInfo() + created.get_info_name(self.username) + old_home = created["pw_dir"] + + (status, output) = commands.getstatusoutput("usermod -d %s %s" % (new_home, self.username)) + self.failUnlessEqual(status, 0, output) + + created.get_info_name(self.username) + self.assertEqual(created["pw_dir"], new_home) + + # revert to old home so we can userdel -r in tearDown + (status, output) = commands.getstatusoutput("usermod -d %s %s" % (old_home, self.username)) + self.failUnlessEqual(status, 0, output) + + # FIXME - test if contents of /home directories are transferred with the -m option + # FIXME - test if new home is created if does not exist before + + def testGIDChange(self): + """ usermod: Test if user's gid can be changed. """ + new_group = "root" + # test non-existing group + (status_fail, output_fail) = commands.getstatusoutput("usermod -g no-such-group %s" % (self.username)) + (status, output) = commands.getstatusoutput("usermod -g %s %s" % (new_group, self.username)) + + created = UserInfo() + created.get_info_name(self.username) + + left = GroupInfo() + if left.get_info_name(self.username) == None: + (status_del, output_del) = commands.getstatusoutput("groupdel %s" % (self.username)) + self.failUnlessEqual(status_del, 0, output_del) + + self.failIfEqual(status_fail, 0, output_fail) + self.failUnlessEqual(status, 0, output) + self.assertEqual(created["pw_gid"], 0) #0 is root group + + def testLoginChange(self): + """ usermod: Test if user's login can be changed """ + new_login = "usermod-login-change" + user = UserInfo() + user.get_info_name(self.username) + uid = user["pw_uid"] # UID won't change even when login does + + # test changing to an existing user name + (status, output) = commands.getstatusoutput("usermod -l root %s" % (self.username)) + self.failIfEqual(status, 0, output) + + (status, output) = commands.getstatusoutput("usermod -l %s %s" % (new_login, self.username)) + self.failUnlessEqual(status, 0, output) + user.get_info_name(new_login) + self.assertEqual(user["pw_uid"], uid) + + # revert so we can userdel -r on tearDown + (status, output) = commands.getstatusoutput("usermod -l %s %s" % (self.username, new_login)) + self.failUnlessEqual(status, 0, output) + + def testShellChange(self): + """ usermod: Test if user's shell can be changed """ + new_shell = "/bin/sh" + + (status, output) = commands.getstatusoutput("usermod -s %s %s" % (new_shell, self.username)) + self.failUnlessEqual(status, 0, output) + + created = UserInfo() + created.get_info_name(self.username) + self.assertEqual(created["pw_shell"], new_shell) + +class TestGroupadd(unittest.TestCase, ShadowUtilsTestBase): + def setUp(self): + self.groupname = "test-shadow-utils-groups" + + def tearDown(self): + commands.getstatusoutput("groupdel %s" % (self.groupname)) + + def testAddGroup(self): + """ groupadd: Basic adding of a group """ + + expected = GroupInfo() + expected["gr_name"] = self.groupname + + (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) + self.failUnlessEqual(status, 0, output) + + created = GroupInfo() + created.get_info_name(self.groupname) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a group") + + def testAddSystemGroup(self): + """ groupadd: Adding a system group with gid < MIN_GID """ + + expected = GroupInfo() + expected["gr_name"] = self.groupname + defaults = LoginDefsParser() + + (status, output) = commands.getstatusoutput("groupadd -r %s" % (self.groupname)) + self.failUnlessEqual(status, 0, output) + + created = GroupInfo() + created.get_info_name(self.groupname) + self.assertEqual(created["gr_gid"] < defaults["GID_MIN"], True, "FAIL: System group has gid >= GID_MIN") + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a system group") + + def testAddExistingGid(self): + """ groupadd: Test if we group with an existing GID can be added """ + (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) + self.failUnlessEqual(status, 0, output) + + gname = "%s-2" % (self.groupname) + + created = GroupInfo() + created.get_info_name(self.groupname) + + # no -o option -> this should fail + (status, output) = commands.getstatusoutput("groupadd -g%s %s" % (created["gr_gid"], gname)) + self.failIfEqual(status, 0, output) + + # override with -o option, should pass now + (status, output) = commands.getstatusoutput("groupadd -g%s -o %s" % (created["gr_gid"], gname)) + self.failUnlessEqual(status, 0, output) + + # test if the new GID is really the same + same_gid = GroupInfo() + same_gid.get_info_name(gname) + self.assertEqual(same_gid["gr_gid"], created["gr_gid"]) + + # clean up + (status, output) = commands.getstatusoutput("groupdel %s" % (gname)) + self.failUnlessEqual(status, 0, output) + + + def testOverrideDefaults(self): + """ groupadd: Test if the defaults can be overriden with the -K option """ + # this is known to not work on older RHELs - test what we are running + rhv = RedHatVersion() + runs = rhv.get_info() + if rhv.is_rhel(): + if runs[1] < 5: + print "This test makes sense for RHEL5+" + return + else: + if runs[1] < 6: + print "This test makes sense for Fedora 6+" + return + + + GID_MIN = 600 + GID_MAX = 625 + + (status, output) = commands.getstatusoutput("groupadd -K GID_MIN=%d -K GID_MAX=%d %s" % + (GID_MIN, GID_MAX, self.groupname)) + self.failUnlessEqual(status, 0, output) + + created = GroupInfo() + created.get_info_name(self.groupname) + self.assertEqual(GID_MIN <= created["gr_gid"] <= GID_MAX, True, "FAIL: created an user with UID of %d" % (created["gr_gid"])) + + + def testFOption(self): + """ groupadd: Tests the -f option of groupadd """ + (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) + self.failUnlessEqual(status, 0, output) + + (status, output) = commands.getstatusoutput("groupadd -f %s" % (self.groupname)) + self.assertEqual(status, 0, output) + +class TestGroupaddInvalidName(unittest.TestCase, ShadowUtilsTestBase): + def testGroupaddInvalidName(self): + """ groupadd: Test adding of a group with an invalid name """ + (status, output) = commands.getstatusoutput("groupadd foo?") + self.assertNotEqual(status, 0, output) + (status, output) = commands.getstatusoutput("groupadd aaaaabbbbbcccccdddddeeeeefffffggg") #33 chars + self.assertNotEqual(status, 0, output) + +class TestGroupaddValidName(unittest.TestCase, ShadowUtilsTestBase): + def testGroupaddValidName(self): + """ groupadd: Test adding and removing of groups with maximal valid name and name ending with $ """ + (status, output) = commands.getstatusoutput("groupadd aaaaabbbbbcccccdddddeeeeefffffgg") #32 chars + self.assertEqual(status, 0, output) + (status, output) = commands.getstatusoutput("groupadd aaaaabbbbbcccccdddddeeeeefffffg\$") #32 chars + self.assertEqual(status, 0, output) + (status, output) = commands.getstatusoutput("groupdel aaaaabbbbbcccccdddddeeeeefffffgg") #32 chars + self.assertEqual(status, 0, output) + (status, output) = commands.getstatusoutput("groupdel aaaaabbbbbcccccdddddeeeeefffffg\$") #32 chars + self.assertEqual(status, 0, output) + + +class TestGroupmod(unittest.TestCase, ShadowUtilsTestBase): + def setUp(self): + self.groupname = "test-shadow-utils-groups" + (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) + self.failUnlessEqual(status, 0, output) + + def tearDown(self): + commands.getstatusoutput("groupdel %s" % (self.groupname)) + + def testChangeGID(self): + """ groupmod: Test changing a gid of a group """ + expected = GroupInfo() + expected["gr_name"] = self.groupname + expected["gr_gid"] = 54321 + + (status, output) = commands.getstatusoutput("groupmod -g%d %s" % (expected["gr_gid"], self.groupname)) + self.failUnlessEqual(status, 0, output) + + created = GroupInfo() + created.get_info_name(self.groupname) + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not change GID of an existing group") + + def testChangeGIDToExistingValue(self): + """ groupmod: Test changing GID to an existing value """ + second_name = "%s-2" % (self.groupname) + + created = GroupInfo() + created.get_info_name(self.groupname) + + expected = GroupInfo() + expected["gr_name"] = self.groupname + expected["gr_gid"] = created["gr_gid"] + + (status, output) = commands.getstatusoutput("groupadd %s" % (second_name)) + self.failUnlessEqual(status, 0, output) + + # try to assingn GID of the first group to the second - this should fail without the -o option + (status, output) = commands.getstatusoutput("groupmod -g%d %s" % (created["gr_gid"], second_name)) + self.failIfEqual(status, 0, output) + + # should pass with the -o option + (status, output) = commands.getstatusoutput("groupmod -g%d -o %s" % (created["gr_gid"], second_name)) + self.failUnlessEqual(status, 0, output) + + self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not change GID of an existing group to an existing one") + + # clean up + commands.getstatusoutput("groupdel %s" % (second_name)) + self.failUnlessEqual(status, 0, output) + + def testChangeGroupName(self): + """ groupmod: Test changing a group's name """ + second_name = "%s-2" % (self.groupname) + + created = GroupInfo() + created.get_info_name(self.groupname) + + (status, output) = commands.getstatusoutput("groupmod -n%s %s" % (second_name, self.groupname)) + self.failUnlessEqual(status, 0, output) + + changed = GroupInfo() + changed.get_info_gid(created["gr_gid"]) + self.assertEqual(changed["gr_name"], second_name) + self.assertEqual(changed["gr_gid"], created["gr_gid"]) + + # change back, so the group could be deleted by tearDown + (status, output) = commands.getstatusoutput("groupmod -n%s %s" % (self.groupname, second_name)) + self.failUnlessEqual(status, 0, output) + + def testChangeGroupNameExisting(self): + """ groupmod: Test changing a group's name to an existing one """ + existing = "bin" + (status, output) = commands.getstatusoutput("groupmod -n%s %s" % (existing, self.groupname)) + self.assertNotEqual(status, 0, output) # man groupmod -> 9: group name already in use + + def testChangeNonExistingGroup(self): + """ groupmod: Test properties of a non-existing group """ + nonexistent = "foobar" + (status, output) = commands.getstatusoutput("groupmod -nspameggs %s" % (nonexistent)) + self.assertNotEqual(status, 0, status) # man groupmod -> 6: specified group doesn't exist + +class TestGroupdel(unittest.TestCase, ShadowUtilsTestBase): + def testCorrectGroupdel(self): + """ groupdel: Basic usage of groupdel """ + self.groupname = "test-shadow-utils-groups" + (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) + self.failUnlessEqual(status, 0, output) + (status, output) = commands.getstatusoutput("groupdel %s" % (self.groupname)) + self.assertEqual(status, 0, output) + + def testGroupdelNoSuchGroup(self): + """ groupdel: Remove non-existing group """ + (status, output) = commands.getstatusoutput("groupdel foobar") + self.assertNotEqual(status, 0, output) + + def testRemovePrimaryGroup(self): + """ groupdel: Remove a primary group of an user """ + username = "test-groupdel-primary" + (status, output) = commands.getstatusoutput("useradd %s" % (username)) + self.failUnlessEqual(status, 0, output) + + (status, output) = commands.getstatusoutput("groupdel %s" % (username)) + self.assertNotEqual(status, 0, output) + + # clean up + (status, output) = commands.getstatusoutput("userdel -r %s" % (username)) + self.failUnlessEqual(status, 0, output) + +class TestPwckGrpck(unittest.TestCase): + def setUp(self): + self.passwd_path = tempfile.mktemp(suffix="test-pwck-passwd") + self.passwd_file = open(self.passwd_path, "w") + self.group_path = tempfile.mktemp(suffix="test-pwck-grp") + self.group_file = open(self.group_path, "w") + self.gshadow_path = tempfile.mktemp(suffix="test-pwck-gshadow") + self.gshadow_file = open(self.gshadow_path, "w") + + def tearDown(self): + self.passwd_file.close() + self.group_file.close() + self.gshadow_file.close() + + os.remove(self.passwd_path) + os.remove(self.group_path) + os.remove(self.gshadow_path) + + def runPwckCheck(self, passwd, group): + self.passwd_file.truncate() + self.group_file.truncate() + + self.passwd_file.write(passwd) + self.passwd_file.flush() + self.group_file.write(group) + self.group_file.flush() + + command = "pwck -r %s %s" % (self.passwd_path, self.group_path) + return commands.getstatusoutput(command) + + def runGrpCheck(self, group, gshadow): + self.group_file.truncate() + self.gshadow_file.truncate() + + self.gshadow_file.write(gshadow) + self.gshadow_file.flush() + + self.group_file.write(group) + self.group_file.flush() + + command = "grpck -r %s %s" % (self.group_path, self.gshadow_path) + return commands.getstatusoutput(command) + + + def testValidEntries(self): + """ pwck: a valid entry """ + status, output = self.runPwckCheck("foo:x:685:0::/dev/null:/bin/bash", "") + rhv = RedHatVersion() + runs = rhv.get_info() + if rhv.is_rhel(): + if runs[1] < 6: + self.assertEqual(status, 0, output) + else: + self.assertNotEqual(status, 0, output) + + def testNumberOfFields(self): + """ pwck: invalid number of fields in the record """ + not_enough = "foo:x:685:685::/dev/null" + too_many = "foo:x:685:685::/dev/null:/bin/bash:comment" + status, output = self.runPwckCheck(not_enough, "") + self.assertNotEqual(status, 0, output) + + status, output = self.runPwckCheck(too_many, "") + self.assertNotEqual(status, 0, output) + + def testUniqueUserName(self): + """ pwck: unique user name in the record """ + duplicate_username = "foo:x:685:685::/dev/null:/bin/bash\nfoo:x:686:686::/dev/null:/bin/bash" + status, output = self.runPwckCheck(duplicate_username, "") + self.assertNotEqual(status, 0, output) + + def testValidID(self): + """ pwck: invalid UID in the records """ + invalid_ids = [ "foo:x:-1:685::/dev/null:/bin/bash", "foo:x:blah:685::/dev/null:/bin/bash", "foo:x:1234567890:685::/dev/null:/bin/bash" ] + for record in invalid_ids: + status, output = self.runPwckCheck(record, "") + self.assertNotEqual(status, 0, record) + + + def testValidPrimaryGroup(self): + """ pwck: invalid primary group """ + invalid_groups = [ "foo:x:685:-1::/dev/null:/bin/bash", "foo:x:685:blah::/dev/null:/bin/bash", "foo:x:685:1234567890::/dev/null:/bin/bash" ] + for record in invalid_groups: + status, output = self.runPwckCheck("", record) + self.assertNotEqual(status, 0, output) + + def testValidHomeDir(self): + """ pwck: invalid home dir """ + for record in [ "foo:x:685:685::123:/bin/bash", "foo:x:685:685::/path/to/nowhere:/bin/bash", "foo:x:685:1234567890::!:/bin/bash" ]: + status, output = self.runPwckCheck(record, "") + self.assertNotEqual(status, 0, output) + + def testBZ164954(self): + """ grpck: regression test for BZ164954 """ + record = "root:x:0:root\nbin:x:1:root,bin,daemon\ndaemon:x:2:root,bin,daemon\nsys:x:3:root,bin,adm\nadm:x:4:root,adm,daemon" + status, output = self.runGrpCheck("", record) + self.assertNotEqual(status, 0, output) + +if __name__ == "__main__": + broken_on_rhel4 = { "TestUseradd" : [ "testCustomUID", "testCustomGID" ] } + + if os.getuid() != 0: + print "This test must be run as root" + sys.exit(1) + + unittest.main() + diff --git a/tests/tests.yml b/tests/tests.yml new file mode 100644 index 0000000..09f4769 --- /dev/null +++ b/tests/tests.yml @@ -0,0 +1,13 @@ +--- +# This first play always runs on the local staging system +- hosts: localhost + roles: + - role: standard-test-beakerlib + tags: + - classic + - atomic + tests: + - sanity + required_packages: + - shadow-utils # sanity test needs shadow-utils + - python # sanity test needs python