Here's a diff leaving -G alone and introducing -S for setting a user's
group. I am pretty unsure about my choice of F_SETSECGROUP, didn't knew
if I should choose 0x1200 or 0x1001 or even something completely
different.

Index: src/usr.sbin/user/user.c
===================================================================
RCS file: /cvs/src/usr.sbin/user/user.c,v
retrieving revision 1.79
diff -u -r1.79 user.c
--- src/usr.sbin/user/user.c    6 Apr 2011 11:36:26 -0000       1.79
+++ src/usr.sbin/user/user.c    12 Apr 2011 17:36:01 -0000
@@ -101,7 +101,8 @@
        F_SHELL         = 0x0200,
        F_UID           = 0x0400,
        F_USERNAME      = 0x0800,
-       F_CLASS         = 0x1000
+       F_CLASS         = 0x1000,
+       F_SETSECGROUP   = 0x1001
 };
 
 #define CONFFILE       "/etc/usermgmt.conf"
@@ -485,6 +486,138 @@
        return 1;
 }
 
+/* set group membership on all `groups' for `user' */
+static int
+set_group(char *user, int ngroups, const char **groups)
+{
+       struct group    *grp;
+       struct stat     st;
+       size_t          login_len;
+       FILE            *from;
+       FILE            *to;
+       char            buf[LINE_MAX];
+       char            f[MaxFileNameLen];
+       char            *colon;
+       char            *cp;
+       char            *ep;
+       int             fd;
+       int             cc;
+       int             i;
+       int             j;
+
+       login_len = strlen(user);
+       for (i = 0 ; i < ngroups ; i++) {
+               if ((grp = getgrnam(groups[i])) == NULL)
+                       warnx("can't append group `%s' for user `%s'",
+                           groups[i], user);
+       }
+       if ((from = fopen(_PATH_GROUP, "r")) == NULL) {
+               warn("can't append group for `%s': can't open `%s'", user,
+                   _PATH_GROUP);
+               return 0;
+       }
+       if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) {
+               warn("can't lock `%s'", _PATH_GROUP);
+       }
+       (void) fstat(fileno(from), &st);
+       (void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP);
+       if ((fd = mkstemp(f)) < 0) {
+               (void) fclose(from);
+               warn("can't append group: mkstemp failed");
+               return 0;
+       }
+       if ((to = fdopen(fd, "w")) == NULL) {
+               (void) fclose(from);
+               (void) close(fd);
+               (void) unlink(f);
+               warn("can't append group: fdopen `%s' failed", f);
+               return 0;
+       }
+       while (fgets(buf, sizeof(buf), from) != NULL) {
+               cc = strlen(buf);
+               if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) {
+                       while (fgetc(from) != '\n' && !feof(from))
+                               cc++;
+                       warnx("%s: line `%s' too long (%d bytes), skipping",
+                           _PATH_GROUP, buf, cc);
+                       continue;
+               }
+               if ((colon = strchr(buf, ':')) == NULL) {
+                       warnx("badly formed entry `%s'", buf);
+                       continue;
+               }
+
+               /* remove user from group */
+               for (cp = buf, cc = 0; *cp != '\0' && cc < 3; cp++) {
+                       if (*cp == ':')
+                           cc++;
+               }
+               if (cc != 3) {
+                       buf[strcspn(buf, "\n")] = '\0';
+                       warnx("Malformed entry `%s'. Skipping", buf);
+                       continue;
+               }
+               while ((cp = strstr(cp, user)) != NULL) {
+                       if ((cp[-1] == ':' || cp[-1] == ',') &&
+                           (cp[login_len] == ',' || cp[login_len] == '\n')) {
+                               ep = cp + login_len;
+                               if (cp[login_len] == ',')
+                                       ep++;
+                               else if (cp[-1] == ',')
+                                       cp--;
+                               memmove(cp, ep, strlen(ep) + 1);
+                       } else {
+                               if ((cp = strchr(cp, ',')) == NULL)
+                                       break;
+                               cp++;
+                       }
+               }
+               cc = strlen(buf);
+
+               /* add user to groups */
+               for (i = 0 ; i < ngroups ; i++) {
+                       j = (int)(colon - buf);
+                       if (strncmp(groups[i], buf, j) == 0 &&
+                           groups[i][j] == '\0') {
+                               while (isspace(buf[cc - 1]))
+                                       cc--;
+                               buf[(j = cc)] = '\0';
+                               if (buf[strlen(buf) - 1] != ':')
+                                       strlcat(buf, ",", sizeof(buf));
+                               cc = strlcat(buf, user, sizeof(buf)) + 1;
+                               if (cc >= sizeof(buf)) {
+                                       warnx("Warning: group `%s' would "
+                                           "become too long, not modifying",
+                                           groups[i]);
+                                       cc = j + 1;
+                               }
+                               buf[cc - 1] = '\n';
+                               buf[cc] = '\0';
+                       }
+               }
+               if (fwrite(buf, cc, 1, to) != 1) {
+                       (void) fclose(from);
+                       (void) fclose(to);
+                       (void) unlink(f);
+                       warn("can't append group: short write to `%s'", f);
+                       return 0;
+               }
+       }
+       (void) fclose(from);
+       if (fclose(to) == EOF) {
+               (void) unlink(f);
+               warn("can't append group: short write to `%s'", f);
+               return 0;
+       }
+       if (rename(f, _PATH_GROUP) < 0) {
+               (void) unlink(f);
+               warn("can't append group: can't rename `%s' to `%s'", f, 
_PATH_GROUP);
+               return 0;
+       }
+       (void) chmod(_PATH_GROUP, st.st_mode & 07777);
+       return 1;
+}
+
 /* modify the group entries for all `groups', by adding `user' */
 static int
 append_group(char *user, int ngroups, const char **groups)
@@ -1537,12 +1670,18 @@
                        err(EXIT_FAILURE, "can't move `%s' to `%s'",
                            homedir, pwp->pw_dir);
                }
-               if (up->u_groupc > 0 &&
+               if (up->u_groupc > 0 && (up->u_flags & F_SECGROUP) && 
                    !append_group(newlogin, up->u_groupc, up->u_groupv)) {
                        (void) close(ptmpfd);
                        pw_abort();
                        errx(EXIT_FAILURE, "can't append `%s' to new groups",
                            newlogin);
+               } else if (up->u_groupc > 0 && (up->u_flags & F_SETSECGROUP) &&
+                   !set_group(newlogin, up->u_groupc, up->u_groupv)) {
+                       (void) close(ptmpfd);
+                       pw_abort();
+                       errx(EXIT_FAILURE, "can't append `%s' to new groups",
+                           newlogin);
                }
        }
        (void) close(ptmpfd);
@@ -1621,8 +1760,8 @@
                    "               [-s shell] [-u uid] user\n", prog);
        } else if (strcmp(prog, "usermod") == 0) {
                (void) fprintf(stderr, "usage: %s [-mov] "
-                   "[-G secondary-group[,group,...]] [-c comment]\n"
-                   "               [-d home-directory] [-e expiry-time] "
+                   "[-G secondary-group[,group,...] | -S 
secondary-group[,group,...]]\n"
+                   "               [-c comment] [-d home-directory] [-e 
expiry-time] "
                    "[-f inactive-time]\n"
                    "               [-g gid | name | =uid] [-L login-class] "
                    "[-l new-login]\n"
@@ -1820,7 +1959,7 @@
        free(u.u_primgrp);
        u.u_primgrp = NULL;
        have_new_user = 0;
-       while ((c = getopt(argc, argv, "G:c:d:e:f:g:l:mos:u:" 
MOD_OPT_EXTENSIONS)) != -1) {
+       while ((c = getopt(argc, argv, "G:S:c:d:e:f:g:l:mos:u:" 
MOD_OPT_EXTENSIONS)) != -1) {
                switch(c) {
                case 'G':
                        while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) 
!= NULL &&
@@ -1834,6 +1973,18 @@
                        }
                        u.u_flags |= F_SECGROUP;
                        break;
+               case 'S':
+                       while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) 
!= NULL &&
+                           u.u_groupc < NGROUPS_MAX - 2) {
+                               if (u.u_groupv[u.u_groupc][0] != 0) {
+                                       u.u_groupc++;
+                               }
+                       }
+                       if (optarg != NULL) {
+                               warnx("Truncated list of secondary groups to %d 
entries", NGROUPS_MAX - 2);
+                       }
+                       u.u_flags |= F_SETSECGROUP;
+                       break;
                case 'c':
                        memsave(&u.u_comment, optarg, strlen(optarg));
                        u.u_flags |= F_COMMENT;
@@ -1907,6 +2058,8 @@
                warnx("option 'm' useless without 'd' or 'l' -- ignored");
                u.u_flags &= ~F_MKDIR;
        }
+       if ((u.u_flags & F_SECGROUP) && (u.u_flags & F_SETSECGROUP))
+               errx(EXIT_FAILURE, "options 'G' and 'S' are mutually 
exclusive");
        argc -= optind;
        argv += optind;
        if (argc != 1) {
Index: src/usr.sbin/user/usermod.8
===================================================================
RCS file: /cvs/src/usr.sbin/user/usermod.8,v
retrieving revision 1.24
diff -u -r1.24 usermod.8
--- src/usr.sbin/user/usermod.8 3 Sep 2010 11:22:36 -0000       1.24
+++ src/usr.sbin/user/usermod.8 12 Apr 2011 17:36:01 -0000
@@ -41,10 +41,10 @@
 .Nm usermod
 .Bk -words
 .Op Fl mov
-.Op Fl G Ar secondary-group[,group,...]
 .Op Fl c Ar comment
 .Op Fl d Ar home-directory
 .Op Fl e Ar expiry-time
+.Op Fl G Ar secondary-group[,group,...] | Fl S Ar secondary-group[,group,...]
 .Op Fl f Ar inactive-time
 .Oo
 .Fl g Ar gid | name | Li =uid
@@ -103,11 +103,21 @@
 .Fl e
 option.
 .It Fl G Ar secondary-group[,group,...]
+Adds the user to the given groups.
+.Fl G
+and
+.Fl S
+are mutually exclusive.
+.It Xo
+.It Fl S Ar secondary-group[,group,...]
 Sets the secondary groups the user will be a member of in the
 .Pa /etc/group
 file.
-.It Xo
-.Fl g Ar gid | name | Li =uid
+.Fl S
+and
+.Fl G
+are mutually exclusive.
+.It Fl g Ar gid | name | Li =uid
 .Xc
 Gives the group name or identifier to be used for the user's primary group.
 If this is

Reply via email to