Index: stable/10/usr.sbin/pw/pw_conf.c =================================================================== --- stable/10/usr.sbin/pw/pw_conf.c (revision 292964) +++ stable/10/usr.sbin/pw/pw_conf.c (revision 292965) @@ -1,524 +1,524 @@ /*- * Copyright (C) 1996 * David L. Nugent. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include "pw.h" #define debugging 0 enum { _UC_NONE, _UC_DEFAULTPWD, _UC_REUSEUID, _UC_REUSEGID, _UC_NISPASSWD, _UC_DOTDIR, _UC_NEWMAIL, _UC_LOGFILE, _UC_HOMEROOT, _UC_HOMEMODE, _UC_SHELLPATH, _UC_SHELLS, _UC_DEFAULTSHELL, _UC_DEFAULTGROUP, _UC_EXTRAGROUPS, _UC_DEFAULTCLASS, _UC_MINUID, _UC_MAXUID, _UC_MINGID, _UC_MAXGID, _UC_EXPIRE, _UC_PASSWORD, _UC_FIELDS }; static char bourne_shell[] = "sh"; static char *system_shells[_UC_MAXSHELLS] = { bourne_shell, "csh", "tcsh" }; static char const *booltrue[] = { "yes", "true", "1", "on", NULL }; static char const *boolfalse[] = { "no", "false", "0", "off", NULL }; static struct userconf config = { 0, /* Default password for new users? (nologin) */ 0, /* Reuse uids? */ 0, /* Reuse gids? */ NULL, /* NIS version of the passwd file */ "/usr/share/skel", /* Where to obtain skeleton files */ NULL, /* Mail to send to new accounts */ "/var/log/userlog", /* Where to log changes */ "/home", /* Where to create home directory */ _DEF_DIRMODE, /* Home directory perms, modified by umask */ "/bin", /* Where shells are located */ system_shells, /* List of shells (first is default) */ bourne_shell, /* Default shell */ NULL, /* Default group name */ NULL, /* Default (additional) groups */ NULL, /* Default login class */ 1000, 32000, /* Allowed range of uids */ 1000, 32000, /* Allowed range of gids */ 0, /* Days until account expires */ 0 /* Days until password expires */ }; static char const *comments[_UC_FIELDS] = { "#\n# pw.conf - user/group configuration defaults\n#\n", "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", "\n# Reuse gaps in uid sequence? (yes or no)\n", "\n# Reuse gaps in gid sequence? (yes or no)\n", "\n# Path to the NIS passwd file (blank or 'no' for none)\n", "\n# Obtain default dotfiles from this directory\n", "\n# Mail this file to new user (/etc/newuser.msg or no)\n", "\n# Log add/change/remove information in this file\n", "\n# Root directory in which $HOME directory is created\n", "\n# Mode for the new $HOME directory, will be modified by umask\n", "\n# Colon separated list of directories containing valid shells\n", "\n# Comma separated list of available shells (without paths)\n", "\n# Default shell (without path)\n", "\n# Default group (leave blank for new group per user)\n", "\n# Extra groups for new users\n", "\n# Default login class for new users\n", "\n# Range of valid default user ids\n", NULL, "\n# Range of valid default group ids\n", NULL, "\n# Days after which account expires (0=disabled)\n", "\n# Days after which password expires (0=disabled)\n" }; static char const *kwds[] = { "", "defaultpasswd", "reuseuids", "reusegids", "nispasswd", "skeleton", "newmail", "logfile", "home", "homemode", "shellpath", "shells", "defaultshell", "defaultgroup", "extragroups", "defaultclass", "minuid", "maxuid", "mingid", "maxgid", "expire_days", "password_days", NULL }; static char * unquote(char const * str) { if (str && (*str == '"' || *str == '\'')) { char *p = strchr(str + 1, *str); if (p != NULL) *p = '\0'; return (char *) (*++str ? str : NULL); } return (char *) str; } int boolean_val(char const * str, int dflt) { if ((str = unquote(str)) != NULL) { int i; for (i = 0; booltrue[i]; i++) if (strcmp(str, booltrue[i]) == 0) return 1; for (i = 0; boolfalse[i]; i++) if (strcmp(str, boolfalse[i]) == 0) return 0; /* * Special cases for defaultpassword */ if (strcmp(str, "random") == 0) return -1; if (strcmp(str, "none") == 0) return -2; } return dflt; } char const * boolean_str(int val) { if (val == -1) return "random"; else if (val == -2) return "none"; else return val ? booltrue[0] : boolfalse[0]; } char * newstr(char const * p) { char *q; if ((p = unquote(p)) == NULL) return (NULL); if ((q = strdup(p)) == NULL) err(1, "strdup()"); return (q); } struct userconf * read_userconfig(char const * file) { FILE *fp; char *buf, *p; const char *errstr; size_t linecap; ssize_t linelen; buf = NULL; linecap = 0; if (file == NULL) file = _PATH_PW_CONF; if ((fp = fopen(file, "r")) == NULL) return (&config); while ((linelen = getline(&buf, &linecap, fp)) > 0) { if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { static char const toks[] = " \t\r\n,="; char *q = strtok(NULL, toks); int i = 0; mode_t *modeset; while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) ++i; #if debugging if (i == _UC_FIELDS) printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); else printf("Got kwd[%s]=%s\n", p, q); #endif switch (i) { case _UC_DEFAULTPWD: config.default_password = boolean_val(q, 1); break; case _UC_REUSEUID: config.reuse_uids = boolean_val(q, 0); break; case _UC_REUSEGID: config.reuse_gids = boolean_val(q, 0); break; case _UC_NISPASSWD: config.nispasswd = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_DOTDIR: config.dotdir = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_NEWMAIL: config.newmail = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_LOGFILE: config.logfile = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_HOMEROOT: config.home = (q == NULL || !boolean_val(q, 1)) ? "/home" : newstr(q); break; case _UC_HOMEMODE: modeset = setmode(q); config.homemode = (q == NULL || !boolean_val(q, 1)) ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE); free(modeset); break; case _UC_SHELLPATH: config.shelldir = (q == NULL || !boolean_val(q, 1)) ? "/bin" : newstr(q); break; case _UC_SHELLS: for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) system_shells[i] = newstr(q); if (i > 0) while (i < _UC_MAXSHELLS) system_shells[i++] = NULL; break; case _UC_DEFAULTSHELL: config.shell_default = (q == NULL || !boolean_val(q, 1)) ? (char *) bourne_shell : newstr(q); break; case _UC_DEFAULTGROUP: q = unquote(q); config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) ? NULL : newstr(q); break; case _UC_EXTRAGROUPS: - for (i = 0; q != NULL; q = strtok(NULL, toks)) { + while ((q = strtok(NULL, toks)) != NULL) { if (config.groups == NULL) config.groups = sl_init(); sl_add(config.groups, newstr(q)); } break; case _UC_DEFAULTCLASS: config.default_class = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_MINUID: if ((q = unquote(q)) != NULL) { config.min_uid = strtounum(q, 0, UID_MAX, &errstr); if (errstr) warnx("Invalid min_uid: '%s';" " ignoring", q); } break; case _UC_MAXUID: if ((q = unquote(q)) != NULL) { config.max_uid = strtounum(q, 0, UID_MAX, &errstr); if (errstr) warnx("Invalid max_uid: '%s';" " ignoring", q); } break; case _UC_MINGID: if ((q = unquote(q)) != NULL) { config.min_gid = strtounum(q, 0, GID_MAX, &errstr); if (errstr) warnx("Invalid min_gid: '%s';" " ignoring", q); } break; case _UC_MAXGID: if ((q = unquote(q)) != NULL) { config.max_gid = strtounum(q, 0, GID_MAX, &errstr); if (errstr) warnx("Invalid max_gid: '%s';" " ignoring", q); } break; case _UC_EXPIRE: if ((q = unquote(q)) != NULL) { config.expire_days = strtonum(q, 0, INT_MAX, &errstr); if (errstr) warnx("Invalid expire days:" " '%s'; ignoring", q); } break; case _UC_PASSWORD: if ((q = unquote(q)) != NULL) { config.password_days = strtonum(q, 0, INT_MAX, &errstr); if (errstr) warnx("Invalid password days:" " '%s'; ignoring", q); } break; case _UC_FIELDS: case _UC_NONE: break; } } } free(buf); fclose(fp); return (&config); } int write_userconfig(struct userconf *cnf, const char *file) { int fd; int i, j; struct sbuf *buf; FILE *fp; if (file == NULL) file = _PATH_PW_CONF; if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1) return (0); if ((fp = fdopen(fd, "w")) == NULL) { close(fd); return (0); } buf = sbuf_new_auto(); for (i = _UC_NONE; i < _UC_FIELDS; i++) { int quote = 1; sbuf_clear(buf); switch (i) { case _UC_DEFAULTPWD: sbuf_cat(buf, boolean_str(cnf->default_password)); break; case _UC_REUSEUID: sbuf_cat(buf, boolean_str(cnf->reuse_uids)); break; case _UC_REUSEGID: sbuf_cat(buf, boolean_str(cnf->reuse_gids)); break; case _UC_NISPASSWD: sbuf_cat(buf, cnf->nispasswd ? cnf->nispasswd : ""); quote = 0; break; case _UC_DOTDIR: sbuf_cat(buf, cnf->dotdir ? cnf->dotdir : boolean_str(0)); break; case _UC_NEWMAIL: sbuf_cat(buf, cnf->newmail ? cnf->newmail : boolean_str(0)); break; case _UC_LOGFILE: sbuf_cat(buf, cnf->logfile ? cnf->logfile : boolean_str(0)); break; case _UC_HOMEROOT: sbuf_cat(buf, cnf->home); break; case _UC_HOMEMODE: sbuf_printf(buf, "%04o", cnf->homemode); quote = 0; break; case _UC_SHELLPATH: sbuf_cat(buf, cnf->shelldir); break; case _UC_SHELLS: for (j = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) sbuf_printf(buf, "%s\"%s\"", j ? "," : "", system_shells[j]); quote = 0; break; case _UC_DEFAULTSHELL: sbuf_cat(buf, cnf->shell_default ? cnf->shell_default : bourne_shell); break; case _UC_DEFAULTGROUP: sbuf_cat(buf, cnf->default_group ? cnf->default_group : ""); break; case _UC_EXTRAGROUPS: for (j = 0; cnf->groups != NULL && j < (int)cnf->groups->sl_cur; j++) sbuf_printf(buf, "%s\"%s\"", j ? "," : "", cnf->groups->sl_str[j]); quote = 0; break; case _UC_DEFAULTCLASS: sbuf_cat(buf, cnf->default_class ? cnf->default_class : ""); break; case _UC_MINUID: sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_uid); quote = 0; break; case _UC_MAXUID: sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_uid); quote = 0; break; case _UC_MINGID: sbuf_printf(buf, "%ju", (uintmax_t)cnf->min_gid); quote = 0; break; case _UC_MAXGID: sbuf_printf(buf, "%ju", (uintmax_t)cnf->max_gid); quote = 0; break; case _UC_EXPIRE: sbuf_printf(buf, "%jd", (intmax_t)cnf->expire_days); quote = 0; break; case _UC_PASSWORD: sbuf_printf(buf, "%jd", (intmax_t)cnf->password_days); quote = 0; break; case _UC_NONE: break; } sbuf_finish(buf); if (comments[i]) fputs(comments[i], fp); if (*kwds[i]) { if (quote) fprintf(fp, "%s = \"%s\"\n", kwds[i], sbuf_data(buf)); else fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf)); #if debugging printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf)); #endif } } sbuf_delete(buf); return (fclose(fp) != EOF); } Index: stable/10/usr.sbin/pw/pw_group.c =================================================================== --- stable/10/usr.sbin/pw/pw_group.c (revision 292964) +++ stable/10/usr.sbin/pw/pw_group.c (revision 292965) @@ -1,689 +1,694 @@ /*- * Copyright (C) 1996 * David L. Nugent. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include "pw.h" #include "bitmap.h" static struct passwd *lookup_pwent(const char *user); static void delete_members(struct group *grp, char *list); static int print_group(struct group * grp, bool pretty); static gid_t gr_gidpolicy(struct userconf * cnf, intmax_t id); static void grp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) { int b; int istty; struct termios t, n; char *p, line[256]; if (fd == -1) return; if (fd == '-') { grp->gr_passwd = "*"; /* No access */ return; } if ((istty = isatty(fd))) { n = t; /* Disable echo */ n.c_lflag &= ~(ECHO); tcsetattr(fd, TCSANOW, &n); printf("%sassword for group %s:", update ? "New p" : "P", grp->gr_name); fflush(stdout); } b = read(fd, line, sizeof(line) - 1); if (istty) { /* Restore state */ tcsetattr(fd, TCSANOW, &t); fputc('\n', stdout); fflush(stdout); } if (b < 0) err(EX_OSERR, "-h file descriptor"); line[b] = '\0'; if ((p = strpbrk(line, " \t\r\n")) != NULL) *p = '\0'; if (!*line) errx(EX_DATAERR, "empty password read on file descriptor %d", conf.fd); if (precrypted) { if (strchr(line, ':') != 0) errx(EX_DATAERR, "wrong encrypted passwrd"); grp->gr_passwd = line; } else grp->gr_passwd = pw_pwcrypt(line); } int pw_groupnext(struct userconf *cnf, bool quiet) { gid_t next = gr_gidpolicy(cnf, -1); if (quiet) return (next); printf("%ju\n", (uintmax_t)next); return (EXIT_SUCCESS); } static struct group * getgroup(char *name, intmax_t id, bool fatal) { struct group *grp; if (id < 0 && name == NULL) errx(EX_DATAERR, "groupname or id required"); grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); if (grp == NULL) { if (!fatal) return (NULL); if (name == NULL) errx(EX_DATAERR, "unknown gid `%ju'", id); errx(EX_DATAERR, "unknown group `%s'", name); } return (grp); } /* * Lookup a passwd entry using a name or UID. */ static struct passwd * lookup_pwent(const char *user) { struct passwd *pwd; if ((pwd = GETPWNAM(user)) == NULL && (!isdigit((unsigned char)*user) || (pwd = getpwuid((uid_t) atoi(user))) == NULL)) errx(EX_NOUSER, "user `%s' does not exist", user); return (pwd); } /* * Delete requested members from a group. */ static void delete_members(struct group *grp, char *list) { char *p; int k; if (grp->gr_mem == NULL) return; for (p = strtok(list, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { for (k = 0; grp->gr_mem[k] != NULL; k++) { if (strcmp(grp->gr_mem[k], p) == 0) break; } if (grp->gr_mem[k] == NULL) /* No match */ continue; for (; grp->gr_mem[k] != NULL; k++) grp->gr_mem[k] = grp->gr_mem[k+1]; } } static gid_t gr_gidpolicy(struct userconf * cnf, intmax_t id) { struct group *grp; struct bitmap bm; gid_t gid = (gid_t) - 1; /* * Check the given gid, if any */ if (id > 0) { gid = (gid_t) id; if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) errx(EX_DATAERR, "gid `%ju' has already been allocated", (uintmax_t)grp->gr_gid); return (gid); } /* * We need to allocate the next available gid under one of * two policies a) Grab the first unused gid b) Grab the * highest possible unused gid */ if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ cnf->min_gid = 1000; cnf->max_gid = 32000; } bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); /* * Now, let's fill the bitmap from the password file */ SETGRENT(); while ((grp = GETGRENT()) != NULL) if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) bm_setbit(&bm, grp->gr_gid - cnf->min_gid); ENDGRENT(); /* * Then apply the policy, with fallback to reuse if necessary */ if (cnf->reuse_gids) gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); else { gid = (gid_t) (bm_lastset(&bm) + 1); if (!bm_isset(&bm, gid)) gid += cnf->min_gid; else gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); } /* * Another sanity check */ if (gid < cnf->min_gid || gid > cnf->max_gid) errx(EX_SOFTWARE, "unable to allocate a new gid - range fully " "used"); bm_dealloc(&bm); return (gid); } static int print_group(struct group * grp, bool pretty) { char *buf = NULL; int i; if (pretty) { printf("Group Name: %-15s #%lu\n" " Members: ", grp->gr_name, (long) grp->gr_gid); if (grp->gr_mem != NULL) { for (i = 0; grp->gr_mem[i]; i++) printf("%s%s", i ? "," : "", grp->gr_mem[i]); } fputs("\n\n", stdout); return (EXIT_SUCCESS); } buf = gr_make(grp); printf("%s\n", buf); free(buf); return (EXIT_SUCCESS); } int pw_group_next(int argc, char **argv, char *arg1 __unused) { struct userconf *cnf; const char *cfg = NULL; int ch; bool quiet = false; while ((ch = getopt(argc, argv, "Cq")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); cnf = get_userconfig(cfg); return (pw_groupnext(cnf, quiet)); } int pw_group_show(int argc, char **argv, char *arg1) { struct group *grp = NULL; char *name; intmax_t id = -1; int ch; bool all, force, quiet, pretty; all = force = quiet = pretty = false; struct group fakegroup = { "nogroup", "*", -1, NULL }; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:g:FPa")) != -1) { switch (ch) { case 'C': /* ignore compatibility */ break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'F': force = true; break; case 'P': pretty = true; break; case 'a': all = true; break; } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); if (all) { SETGRENT(); while ((grp = GETGRENT()) != NULL) print_group(grp, pretty); ENDGRENT(); return (EXIT_SUCCESS); } grp = getgroup(name, id, !force); if (grp == NULL) grp = &fakegroup; return (print_group(grp, pretty)); } int pw_group_del(int argc, char **argv, char *arg1) { struct userconf *cnf = NULL; struct group *grp = NULL; char *name; const char *cfg = NULL; intmax_t id = -1; int ch, rc; bool quiet = false; bool nis = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:g:Y")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'Y': nis = true; break; } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); grp = getgroup(name, id, true); cnf = get_userconfig(cfg); rc = delgrent(grp); if (rc == -1) err(EX_IOERR, "group '%s' not available (NIS?)", name); else if (rc != 0) err(EX_IOERR, "group update"); pw_log(cnf, M_DELETE, W_GROUP, "%s(%ju) removed", name, (uintmax_t)id); if (nis && nis_update() == 0) pw_log(cnf, M_DELETE, W_GROUP, "NIS maps updated"); return (EXIT_SUCCESS); } static bool grp_has_member(struct group *grp, const char *name) { int j; for (j = 0; grp->gr_mem != NULL && grp->gr_mem[j] != NULL; j++) if (strcmp(grp->gr_mem[j], name) == 0) return (true); return (false); } static void grp_add_members(struct group **grp, char *members) { struct passwd *pwd; char *p; char tok[] = ", \t"; if (members == NULL) return; for (p = strtok(members, tok); p != NULL; p = strtok(NULL, tok)) { pwd = lookup_pwent(p); if (grp_has_member(*grp, pwd->pw_name)) continue; *grp = gr_add(*grp, pwd->pw_name); } } int groupadd(struct userconf *cnf, char *name, gid_t id, char *members, int fd, bool dryrun, bool pretty, bool precrypted) { struct group *grp; int rc; struct group fakegroup = { "nogroup", "*", -1, NULL }; grp = &fakegroup; grp->gr_name = pw_checkname(name, 0); grp->gr_passwd = "*"; grp->gr_gid = gr_gidpolicy(cnf, id); grp->gr_mem = NULL; /* * This allows us to set a group password Group passwords is an * antique idea, rarely used and insecure (no secure database) Should * be discouraged, but it is apparently still supported by some * software. */ grp_set_passwd(grp, false, fd, precrypted); grp_add_members(&grp, members); if (dryrun) return (print_group(grp, pretty)); if ((rc = addgrent(grp)) != 0) { if (rc == -1) errx(EX_IOERR, "group '%s' already exists", grp->gr_name); else err(EX_IOERR, "group update"); } pw_log(cnf, M_ADD, W_GROUP, "%s(%ju)", grp->gr_name, (uintmax_t)grp->gr_gid); return (EXIT_SUCCESS); } int pw_group_add(int argc, char **argv, char *arg1) { struct userconf *cnf = NULL; char *name = NULL; char *members = NULL; const char *cfg = NULL; intmax_t id = -1; int ch, rc, fd = -1; bool quiet, precrypted, dryrun, pretty, nis; quiet = precrypted = dryrun = pretty = nis = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:g:h:H:M:oNPY")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'H': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; if (fd == '-') errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); break; case 'M': members = optarg; break; case 'o': conf.checkduplicate = false; break; case 'N': dryrun = true; break; case 'P': pretty = true; break; case 'Y': nis = true; break; } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); if (name == NULL) errx(EX_DATAERR, "group name required"); if (GETGRNAM(name) != NULL) errx(EX_DATAERR, "group name `%s' already exists", name); cnf = get_userconfig(cfg); rc = groupadd(cnf, name, gr_gidpolicy(cnf, id), members, fd, dryrun, pretty, precrypted); if (nis && rc == EXIT_SUCCESS && nis_update() == 0) pw_log(cnf, M_ADD, W_GROUP, "NIS maps updated"); return (rc); } int pw_group_mod(int argc, char **argv, char *arg1) { struct userconf *cnf; struct group *grp = NULL; const char *cfg = NULL; char *oldmembers = NULL; char *members = NULL; char *newmembers = NULL; char *newname = NULL; char *name = NULL; intmax_t id = -1; int ch, rc, fd = -1; bool quiet, pretty, dryrun, nis, precrypted; quiet = pretty = dryrun = nis = precrypted = false; if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, GID_MAX); else name = arg1; } while ((ch = getopt(argc, argv, "C:qn:d:g:l:h:H:M:m:NPY")) != -1) { switch (ch) { case 'C': cfg = optarg; break; case 'q': quiet = true; break; case 'n': name = optarg; break; case 'g': id = pw_checkid(optarg, GID_MAX); break; case 'd': oldmembers = optarg; break; case 'l': newname = optarg; break; case 'H': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); precrypted = true; if (fd == '-') errx(EX_USAGE, "-H expects a file descriptor"); break; case 'h': if (fd != -1) errx(EX_USAGE, "'-h' and '-H' are mutually " "exclusive options"); fd = pw_checkfd(optarg); break; case 'M': members = optarg; break; case 'm': newmembers = optarg; break; case 'N': dryrun = true; break; case 'P': pretty = true; break; case 'Y': nis = true; break; } } if (quiet) freopen(_PATH_DEVNULL, "w", stderr); cnf = get_userconfig(cfg); grp = getgroup(name, id, true); if (name == NULL) name = grp->gr_name; if (id > 0) grp->gr_gid = id; if (newname != NULL) grp->gr_name = pw_checkname(newname, 0); grp_set_passwd(grp, true, fd, precrypted); /* * Keep the same logic as old code for now: * if -M is passed, -d and -m are ignored * then id -d, -m is ignored * last is -m */ if (members) { grp->gr_mem = NULL; grp_add_members(&grp, members); } else if (oldmembers) { delete_members(grp, oldmembers); } else if (newmembers) { grp_add_members(&grp, newmembers); } + if (dryrun) { + print_group(grp, pretty); + return (EXIT_SUCCESS); + } + if ((rc = chggrent(name, grp)) != 0) { if (rc == -1) errx(EX_IOERR, "group '%s' not available (NIS?)", grp->gr_name); else err(EX_IOERR, "group update"); } if (newname) name = newname; /* grp may have been invalidated */ if ((grp = GETGRNAM(name)) == NULL) errx(EX_SOFTWARE, "group disappeared during update"); pw_log(cnf, M_UPDATE, W_GROUP, "%s(%ju)", grp->gr_name, (uintmax_t)grp->gr_gid); if (nis && nis_update() == 0) pw_log(cnf, M_UPDATE, W_GROUP, "NIS maps updated"); return (EXIT_SUCCESS); } Index: stable/10/usr.sbin/pw/pw_vpw.c =================================================================== --- stable/10/usr.sbin/pw/pw_vpw.c (revision 292964) +++ stable/10/usr.sbin/pw/pw_vpw.c (revision 292965) @@ -1,207 +1,205 @@ /*- * Copyright (C) 1996 * David L. Nugent. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #define _WITH_GETLINE #include #include #include #include #include #include "pwupd.h" static FILE * pwd_fp = NULL; void vendpwent(void) { if (pwd_fp != NULL) { fclose(pwd_fp); pwd_fp = NULL; } } void vsetpwent(void) { vendpwent(); } static struct passwd * vnextpwent(char const *nam, uid_t uid, int doclose) { struct passwd *pw; char *line; size_t linecap; ssize_t linelen; pw = NULL; line = NULL; linecap = 0; - linelen = 0; if (pwd_fp != NULL || (pwd_fp = fopen(getpwpath(_MASTERPASSWD), "r")) != NULL) { while ((linelen = getline(&line, &linecap, pwd_fp)) > 0) { /* Skip comments and empty lines */ if (*line == '\n' || *line == '#') continue; /* trim latest \n */ if (line[linelen - 1 ] == '\n') line[linelen - 1] = '\0'; pw = pw_scan(line, PWSCAN_MASTER); if (pw == NULL) errx(EXIT_FAILURE, "Invalid user entry in '%s':" " '%s'", getpwpath(_MASTERPASSWD), line); if (uid != (uid_t)-1) { if (uid == pw->pw_uid) break; } else if (nam != NULL) { if (strcmp(nam, pw->pw_name) == 0) break; } else break; free(pw); pw = NULL; } if (doclose) vendpwent(); } free(line); return (pw); } struct passwd * vgetpwent(void) { return vnextpwent(NULL, -1, 0); } struct passwd * vgetpwuid(uid_t uid) { return vnextpwent(NULL, uid, 1); } struct passwd * vgetpwnam(const char * nam) { return vnextpwent(nam, -1, 1); } static FILE * grp_fp = NULL; void vendgrent(void) { if (grp_fp != NULL) { fclose(grp_fp); grp_fp = NULL; } } RET_SETGRENT vsetgrent(void) { vendgrent(); #if defined(__FreeBSD__) return 0; #endif } static struct group * vnextgrent(char const *nam, gid_t gid, int doclose) { struct group *gr; char *line; size_t linecap; ssize_t linelen; gr = NULL; line = NULL; linecap = 0; - linelen = 0; if (grp_fp != NULL || (grp_fp = fopen(getgrpath(_GROUP), "r")) != NULL) { while ((linelen = getline(&line, &linecap, grp_fp)) > 0) { /* Skip comments and empty lines */ if (*line == '\n' || *line == '#') continue; /* trim latest \n */ if (line[linelen - 1 ] == '\n') line[linelen - 1] = '\0'; gr = gr_scan(line); if (gr == NULL) errx(EXIT_FAILURE, "Invalid group entry in '%s':" " '%s'", getgrpath(_GROUP), line); if (gid != (gid_t)-1) { if (gid == gr->gr_gid) break; } else if (nam != NULL) { if (strcmp(nam, gr->gr_name) == 0) break; } else break; free(gr); gr = NULL; } if (doclose) vendgrent(); } free(line); return (gr); } struct group * vgetgrent(void) { return vnextgrent(NULL, -1, 0); } struct group * vgetgrgid(gid_t gid) { return vnextgrent(NULL, gid, 1); } struct group * vgetgrnam(const char * nam) { return vnextgrent(nam, -1, 1); } Index: stable/10 =================================================================== --- stable/10 (revision 292964) +++ stable/10 (revision 292965) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r292846-292847,292849