From 342c934a35906362b89aaa015b265a66ce011d45 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Sun, 9 Aug 2020 16:44:30 -0500 Subject: [PATCH] add -U option to groupadd and groupmod Add a -U option which adds new usernames as members. For groupmod, also add -a (append), without which existing members are removed. Closes #265 --- lib/groupmem.c | 49 +++++++++++++++++++++++++++++++++++++++------- lib/prototypes.h | 2 ++ man/groupadd.8.xml | 16 +++++++++++++++ man/groupmod.8.xml | 25 +++++++++++++++++++++++ src/groupadd.c | 23 ++++++++++++++++++++-- src/groupmod.c | 42 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 147 insertions(+), 10 deletions(-) diff --git a/lib/groupmem.c b/lib/groupmem.c index 1fd1c135..2060d03b 100644 --- a/lib/groupmem.c +++ b/lib/groupmem.c @@ -87,6 +87,18 @@ return gr; } +void gr_free_members (struct group *grent) +{ + if (NULL != grent->gr_mem) { + size_t i; + for (i = 0; NULL != grent->gr_mem[i]; i++) { + free (grent->gr_mem[i]); + } + free (grent->gr_mem); + grent->gr_mem = NULL; + } +} + void gr_free (/*@out@*/ /*@only@*/struct group *grent) { free (grent->gr_name); @@ -94,13 +106,36 @@ void gr_free (/*@out@*/ /*@only@*/struct group *grent) memzero (grent->gr_passwd, strlen (grent->gr_passwd)); free (grent->gr_passwd); } - if (NULL != grent->gr_mem) { - size_t i; - for (i = 0; NULL != grent->gr_mem[i]; i++) { - free (grent->gr_mem[i]); - } - free (grent->gr_mem); - } + gr_free_members(grent); free (grent); } +bool gr_append_member(struct group *grp, char *member) +{ + int i; + + if (NULL == grp->gr_mem || grp->gr_mem[0] == NULL) { + grp->gr_mem = (char **)malloc(2 * sizeof(char *)); + if (!grp->gr_mem) { + return false; + } + grp->gr_mem[0] = strdup(member); + if (!grp->gr_mem[0]) { + return false; + } + grp->gr_mem[1] = NULL; + return true; + } + + for (i = 0; grp->gr_mem[i]; i++) ; + grp->gr_mem = realloc(grp->gr_mem, (i + 2) * sizeof(char *)); + if (NULL == grp->gr_mem) { + return false; + } + grp->gr_mem[i] = strdup(member); + if (NULL == grp->gr_mem[i]) { + return false; + } + grp->gr_mem[i + 1] = NULL; + return true; +} diff --git a/lib/prototypes.h b/lib/prototypes.h index 90651fb9..53d991fe 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -206,7 +206,9 @@ extern void __gr_set_changed (void); /* groupmem.c */ extern /*@null@*/ /*@only@*/struct group *__gr_dup (const struct group *grent); +extern void gr_free_members (struct group *grent); extern void gr_free (/*@out@*/ /*@only@*/struct group *grent); +extern bool gr_append_member (struct group *grp, char *member); /* hushed.c */ extern bool hushed (const char *username); diff --git a/man/groupadd.8.xml b/man/groupadd.8.xml index 1e58f093..f838c91e 100644 --- a/man/groupadd.8.xml +++ b/man/groupadd.8.xml @@ -229,6 +229,22 @@ + + + , + + + + A list of usernames to add as members of the group. + + + The default behavior (if the , + , and options are not + specified) is defined by the + variable in /etc/login.defs. + + + diff --git a/man/groupmod.8.xml b/man/groupmod.8.xml index b381c33a..79233273 100644 --- a/man/groupmod.8.xml +++ b/man/groupmod.8.xml @@ -93,6 +93,15 @@ + + ,  GID + + + If group members are specified with -U, append them to the existing + member list, rather than replacing it. + + + ,  GID @@ -203,6 +212,22 @@ + + + , + + + + A list of usernames to add as members of the group. + + + The default behavior (if the , + , and options are not + specified) is defined by the + variable in /etc/login.defs. + + + diff --git a/src/groupadd.c b/src/groupadd.c index 2dd8eec9..fa75c7f7 100644 --- a/src/groupadd.c +++ b/src/groupadd.c @@ -79,6 +79,7 @@ static /*@null@*/char *group_passwd; static /*@null@*/char *empty_list = NULL; static const char *prefix = ""; +static char *user_list; static bool oflg = false; /* permit non-unique group ID to be specified with -g */ static bool gflg = false; /* ID value for the new group */ @@ -126,7 +127,8 @@ static /*@noreturn@*/void usage (int status) (void) fputs (_(" -p, --password PASSWORD use this encrypted password for the new group\n"), usageout); (void) fputs (_(" -r, --system create a system account\n"), usageout); (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); - (void) fputs (_(" -P, --prefix PREFIX_DIR directory prefix\n"), usageout); + (void) fputs (_(" -P, --prefix PREFIX_DI directory prefix\n"), usageout); + (void) fputs (_(" -U, --users USERS list of user members of this group\n"), usageout); (void) fputs ("\n", usageout); exit (status); } @@ -207,6 +209,19 @@ static void grp_update (void) } #endif /* SHADOWGRP */ + if (user_list) { + char *token; + token = strtok(user_list, ","); + while (token) { + if (prefix_getpwnam (token) == NULL) { + fprintf (stderr, _("Invalid member username %s\n"), token); + exit (E_GRP_UPDATE); + } + grp.gr_mem = add_list(grp.gr_mem, token); + token = strtok(NULL, ","); + } + } + /* * Write out the new group file entry. */ @@ -391,10 +406,11 @@ static void process_flags (int argc, char **argv) {"system", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, {"prefix", required_argument, NULL, 'P'}, + {"users", required_argument, NULL, 'U'}, {NULL, 0, NULL, '\0'} }; - while ((c = getopt_long (argc, argv, "fg:hK:op:rR:P:", + while ((c = getopt_long (argc, argv, "fg:hK:op:rR:P:U:", long_options, NULL)) != -1) { switch (c) { case 'f': @@ -453,6 +469,9 @@ static void process_flags (int argc, char **argv) break; case 'P': /* no-op, handled in process_prefix_flag () */ break; + case 'U': + user_list = optarg; + break; default: usage (E_USAGE); } diff --git a/src/groupmod.c b/src/groupmod.c index 1dca5fc9..e64ae3e5 100644 --- a/src/groupmod.c +++ b/src/groupmod.c @@ -87,6 +87,7 @@ static gid_t group_id; static gid_t group_newid; static const char* prefix = ""; +static char *user_list; static struct cleanup_info_mod info_passwd; static struct cleanup_info_mod info_group; @@ -95,6 +96,7 @@ static struct cleanup_info_mod info_gshadow; #endif static bool + aflg = false, /* append -U members rather than replace them */ oflg = false, /* permit non-unique group ID to be specified with -g */ gflg = false, /* new ID value for the group */ nflg = false, /* a new name has been specified for the group */ @@ -117,6 +119,7 @@ static void open_files (void); static void close_files (void); static void update_primary_groups (gid_t ogid, gid_t ngid); + /* * usage - display usage message and exit */ @@ -129,6 +132,8 @@ static void usage (int status) "\n" "Options:\n"), Prog); + (void) fputs (_(" -a, --append append the users mentioned by -U option to the group \n" + " without removing existing user members\n"), usageout); (void) fputs (_(" -g, --gid GID change the group ID to GID\n"), usageout); (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); (void) fputs (_(" -n, --new-name NEW_GROUP change the name to NEW_GROUP\n"), usageout); @@ -137,6 +142,7 @@ static void usage (int status) " PASSWORD\n"), usageout); (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); (void) fputs (_(" -P, --prefix PREFIX_DIR prefix directory where are located the /etc/* files\n"), usageout); + (void) fputs (_(" -U, --users USERS list of user members of this group\n"), usageout); (void) fputs ("\n", usageout); exit (status); } @@ -255,6 +261,32 @@ static void grp_update (void) update_primary_groups (ogrp->gr_gid, group_newid); } + if (user_list) { + char *token; + + if (!aflg) { + // requested to replace the existing groups + if (NULL != grp.gr_mem[0]) + gr_free_members(&grp); + grp.gr_mem = (char **)xmalloc(sizeof(char *)); + grp.gr_mem[0] = (char *)0; + } else { + // append to existing groups + if (NULL != grp.gr_mem[0]) + grp.gr_mem = dup_list (grp.gr_mem); + } + + token = strtok(user_list, ","); + while (token) { + if (prefix_getpwnam (token) == NULL) { + fprintf (stderr, _("Invalid member username %s\n"), token); + exit (E_GRP_UPDATE); + } + grp.gr_mem = add_list(grp.gr_mem, token); + token = strtok(NULL, ","); + } + } + /* * Write out the new group file entry. */ @@ -379,6 +411,7 @@ static void process_flags (int argc, char **argv) { int c; static struct option long_options[] = { + {"append", no_argument, NULL, 'a'}, {"gid", required_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, {"new-name", required_argument, NULL, 'n'}, @@ -386,11 +419,15 @@ static void process_flags (int argc, char **argv) {"password", required_argument, NULL, 'p'}, {"root", required_argument, NULL, 'R'}, {"prefix", required_argument, NULL, 'P'}, + {"users", required_argument, NULL, 'U'}, {NULL, 0, NULL, '\0'} }; - while ((c = getopt_long (argc, argv, "g:hn:op:R:P:", + while ((c = getopt_long (argc, argv, "ag:hn:op:R:P:U:", long_options, NULL)) != -1) { switch (c) { + case 'a': + aflg = true; + break; case 'g': gflg = true; if ( (get_gid (optarg, &group_newid) == 0) @@ -419,6 +456,9 @@ static void process_flags (int argc, char **argv) break; case 'P': /* no-op, handled in process_prefix_flag () */ break; + case 'U': + user_list = optarg; + break; default: usage (E_USAGE); }