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