Adding new option -rG to usermod

Adding a new switch -rG, which provides a similar feature set to
-aG, allowing a person to list exactly what groups to remove a
user from.

https://github.com/shadow-maint/shadow/issues/337
This commit is contained in:
Andy Zaugg 2021-09-22 20:29:09 -07:00
parent 9a9c9231db
commit aaaaf21b6f
3 changed files with 51 additions and 1 deletions

View File

@ -15,6 +15,7 @@ Aleksa Sarai <cyphar@cyphar.com>
Alexander O. Yuriev <alex@bach.cis.temple.edu> Alexander O. Yuriev <alex@bach.cis.temple.edu>
Algis Rudys <arudys@rice.edu> Algis Rudys <arudys@rice.edu>
Andreas Jaeger <aj@arthur.rhein-neckar.de> Andreas Jaeger <aj@arthur.rhein-neckar.de>
Andy Zaugg <andy.zaugg@gmail.com>
Aniello Del Sorbo <anidel@edu-gw.dia.unisa.it> Aniello Del Sorbo <anidel@edu-gw.dia.unisa.it>
Anton Gluck <gluc@midway.uchicago.edu> Anton Gluck <gluc@midway.uchicago.edu>
Arkadiusz Miskiewicz <misiek@pld.org.pl> Arkadiusz Miskiewicz <misiek@pld.org.pl>

View File

@ -326,6 +326,17 @@
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<option>-r</option>, <option>--remove</option>
</term>
<listitem>
<para>
Remove the user from named supplementary group(s). Use only with the
<option>-G</option> option.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<option>-R</option>, <option>--root</option>&nbsp;<replaceable>CHROOT_DIR</replaceable> <option>-R</option>, <option>--root</option>&nbsp;<replaceable>CHROOT_DIR</replaceable>

View File

@ -147,6 +147,7 @@ static bool
mflg = false, /* create user's home directory if it doesn't exist */ mflg = false, /* create user's home directory if it doesn't exist */
oflg = false, /* permit non-unique user ID to be specified with -u */ oflg = false, /* permit non-unique user ID to be specified with -u */
pflg = false, /* new encrypted password */ pflg = false, /* new encrypted password */
rflg = false, /* remove a user from a single group */
sflg = false, /* new shell program */ sflg = false, /* new shell program */
#ifdef WITH_SELINUX #ifdef WITH_SELINUX
Zflg = false, /* new selinux user */ Zflg = false, /* new selinux user */
@ -424,6 +425,9 @@ static /*@noreturn@*/void usage (int status)
(void) fputs (_(" -a, --append append the user to the supplemental GROUPS\n" (void) fputs (_(" -a, --append append the user to the supplemental GROUPS\n"
" mentioned by the -G option without removing\n" " mentioned by the -G option without removing\n"
" the user from other groups\n"), usageout); " the user from other groups\n"), usageout);
(void) fputs (_(" -r, --remove remove the user from only the supplemental GROUPS\n"
" mentioned by the -G option without removing\n"
" the user from other groups\n"), usageout);
(void) fputs (_(" -h, --help display this help message and exit\n"), usageout); (void) fputs (_(" -h, --help display this help message and exit\n"), usageout);
(void) fputs (_(" -l, --login NEW_LOGIN new value of the login name\n"), usageout); (void) fputs (_(" -l, --login NEW_LOGIN new value of the login name\n"), usageout);
(void) fputs (_(" -L, --lock lock the user account\n"), usageout); (void) fputs (_(" -L, --lock lock the user account\n"), usageout);
@ -751,6 +755,14 @@ static void update_group (void)
continue; continue;
} }
/*
* If rflg+Gflg is passed in AKA -rG invert is_member flag, which removes
* mentioned groups while leaving the others.
*/
if (Gflg && rflg && was_member) {
is_member = !is_member;
}
ngrp = __gr_dup (grp); ngrp = __gr_dup (grp);
if (NULL == ngrp) { if (NULL == ngrp) {
fprintf (stderr, fprintf (stderr,
@ -866,6 +878,14 @@ static void update_gshadow (void)
continue; continue;
} }
/*
* If rflg+Gflg is passed in AKA -rG invert is_member, to remove targeted
* groups while leaving the user apart of groups not mentioned
*/
if (Gflg && rflg && was_member) {
is_member = !is_member;
}
nsgrp = __sgr_dup (sgrp); nsgrp = __sgr_dup (sgrp);
if (NULL == nsgrp) { if (NULL == nsgrp) {
fprintf (stderr, fprintf (stderr,
@ -1014,6 +1034,7 @@ static void process_flags (int argc, char **argv)
{"move-home", no_argument, NULL, 'm'}, {"move-home", no_argument, NULL, 'm'},
{"non-unique", no_argument, NULL, 'o'}, {"non-unique", no_argument, NULL, 'o'},
{"password", required_argument, NULL, 'p'}, {"password", required_argument, NULL, 'p'},
{"remove", no_argument, NULL, 'r'},
{"root", required_argument, NULL, 'R'}, {"root", required_argument, NULL, 'R'},
{"prefix", required_argument, NULL, 'P'}, {"prefix", required_argument, NULL, 'P'},
{"shell", required_argument, NULL, 's'}, {"shell", required_argument, NULL, 's'},
@ -1031,7 +1052,7 @@ static void process_flags (int argc, char **argv)
{NULL, 0, NULL, '\0'} {NULL, 0, NULL, '\0'}
}; };
while ((c = getopt_long (argc, argv, while ((c = getopt_long (argc, argv,
"abc:d:e:f:g:G:hl:Lmop:R:s:u:UP:" "abc:d:e:f:g:G:hl:Lmop:rR:s:u:UP:"
#ifdef ENABLE_SUBIDS #ifdef ENABLE_SUBIDS
"v:w:V:W:" "v:w:V:W:"
#endif /* ENABLE_SUBIDS */ #endif /* ENABLE_SUBIDS */
@ -1141,6 +1162,9 @@ static void process_flags (int argc, char **argv)
user_pass = optarg; user_pass = optarg;
pflg = true; pflg = true;
break; break;
case 'r':
rflg = true;
break;
case 'R': /* no-op, handled in process_root_flag () */ case 'R': /* no-op, handled in process_root_flag () */
break; break;
case 'P': /* no-op, handled in process_prefix_flag () */ case 'P': /* no-op, handled in process_prefix_flag () */
@ -1342,6 +1366,20 @@ static void process_flags (int argc, char **argv)
usage (E_USAGE); usage (E_USAGE);
} }
if (rflg && (!Gflg)) {
fprintf (stderr,
_("%s: %s flag is only allowed with the %s flag\n"),
Prog, "-r", "-G");
usage (E_USAGE);
}
if (rflg && aflg) {
fprintf (stderr,
_("%s: %s and %s are mutually exclusive flags\n"),
Prog, "-r", "-a");
usage (E_USAGE);
}
if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) { if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) {
fprintf (stderr, fprintf (stderr,
_("%s: the -L, -p, and -U flags are exclusive\n"), _("%s: the -L, -p, and -U flags are exclusive\n"),