diff options
Diffstat (limited to 'pw/pw_group.c')
| -rw-r--r-- | pw/pw_group.c | 766 |
1 files changed, 248 insertions, 518 deletions
diff --git a/pw/pw_group.c b/pw/pw_group.c index 711ef68..b0db3cf 100644 --- a/pw/pw_group.c +++ b/pw/pw_group.c @@ -31,50 +31,46 @@ static const char rcsid[] = #include <ctype.h> #include <err.h> -#include <grp.h> -#include <libutil.h> -#include <paths.h> -#include <string.h> -#include <sysexits.h> #include <termios.h> +#include <stdbool.h> #include <unistd.h> +#include <grp.h> +#include <libutil.h> #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 int print_group(struct group * grp); +static gid_t gr_gidpolicy(struct userconf * cnf, long id); static void -grp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) +set_passwd(struct group *grp, bool update) { int b; int istty; struct termios t, n; char *p, line[256]; - if (fd == -1) - return; - - if (fd == '-') { + if (conf.fd == '-') { grp->gr_passwd = "*"; /* No access */ return; } - if ((istty = isatty(fd))) { + if ((istty = isatty(conf.fd))) { n = t; /* Disable echo */ n.c_lflag &= ~(ECHO); - tcsetattr(fd, TCSANOW, &n); + tcsetattr(conf.fd, TCSANOW, &n); printf("%sassword for group %s:", update ? "New p" : "P", grp->gr_name); fflush(stdout); } - b = read(fd, line, sizeof(line) - 1); + b = read(conf.fd, line, sizeof(line) - 1); if (istty) { /* Restore state */ - tcsetattr(fd, TCSANOW, &t); + tcsetattr(conf.fd, TCSANOW, &t); fputc('\n', stdout); fflush(stdout); } @@ -86,7 +82,7 @@ grp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) if (!*line) errx(EX_DATAERR, "empty password read on file descriptor %d", conf.fd); - if (precrypted) { + if (conf.precrypted) { if (strchr(line, ':') != 0) errx(EX_DATAERR, "wrong encrypted passwrd"); grp->gr_passwd = line; @@ -101,29 +97,198 @@ pw_groupnext(struct userconf *cnf, bool quiet) if (quiet) return (next); - printf("%ju\n", (uintmax_t)next); + printf("%u\n", next); return (EXIT_SUCCESS); } -static struct group * -getgroup(char *name, intmax_t id, bool fatal) +static int +pw_groupshow(const char *name, long id, struct group *fakegroup) { - struct group *grp; + struct group *grp = NULL; + + if (id < 0 && name == NULL && !conf.all) + errx(EX_DATAERR, "groupname or id or '-a' required"); + + if (conf.all) { + SETGRENT(); + while ((grp = GETGRENT()) != NULL) + print_group(grp); + ENDGRENT(); + + return (EXIT_SUCCESS); + } + + grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); + if (grp == NULL) { + if (conf.force) { + grp = fakegroup; + } else { + if (name == NULL) + errx(EX_DATAERR, "unknown gid `%ld'", id); + errx(EX_DATAERR, "unknown group `%s'", name); + } + } + + return (print_group(grp)); +} + +static int +pw_groupdel(const char *name, long id) +{ + struct group *grp = NULL; + int rc; - 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 gid `%ld'", id); errx(EX_DATAERR, "unknown group `%s'", name); } - return (grp); + + 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(conf.userconf, M_DELETE, W_GROUP, "%s(%ld) removed", name, id); + + return (EXIT_SUCCESS); +} + +int +pw_group(int mode, char *name, long id, struct cargs * args) +{ + int rc; + struct carg *arg; + struct group *grp = NULL; + struct userconf *cnf = conf.userconf; + + static struct group fakegroup = + { + "nogroup", + "*", + -1, + NULL + }; + + if (mode == M_NEXT) + return (pw_groupnext(cnf, conf.quiet)); + + if (mode == M_PRINT) + return (pw_groupshow(name, id, &fakegroup)); + + if (mode == M_DELETE) + return (pw_groupdel(name, id)); + + if (mode == M_LOCK || mode == M_UNLOCK) + errx(EX_USAGE, "'lock' command is not available for groups"); + + if (id < 0 && name == NULL) + errx(EX_DATAERR, "group name or id required"); + + grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); + + if (mode == M_UPDATE) { + if (name == NULL && grp == NULL) /* Try harder */ + grp = GETGRGID(id); + + if (grp == NULL) { + if (name == NULL) + errx(EX_DATAERR, "unknown group `%s'", name); + else + errx(EX_DATAERR, "unknown group `%ld'", id); + } + if (name == NULL) /* Needed later */ + name = grp->gr_name; + + if (id > 0) + grp->gr_gid = (gid_t) id; + + if (conf.newname != NULL) + grp->gr_name = pw_checkname(conf.newname, 0); + } else { + if (name == NULL) /* Required */ + errx(EX_DATAERR, "group name required"); + else if (grp != NULL) /* Exists */ + errx(EX_DATAERR, "group name `%s' already exists", name); + + 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. + */ + + if (conf.which == W_GROUP && conf.fd != -1) + set_passwd(grp, mode == M_UPDATE); + + if (((arg = getarg(args, 'M')) != NULL || + (arg = getarg(args, 'd')) != NULL || + (arg = getarg(args, 'm')) != NULL) && arg->val) { + char *p; + struct passwd *pwd; + + /* Make sure this is not stay NULL with -M "" */ + if (arg->ch == 'd') + delete_members(grp, arg->val); + else if (arg->ch == 'M') + grp->gr_mem = NULL; + + for (p = strtok(arg->val, ", \t"); arg->ch != 'd' && p != NULL; + p = strtok(NULL, ", \t")) { + int j; + + /* + * Check for duplicates + */ + pwd = lookup_pwent(p); + for (j = 0; grp->gr_mem != NULL && grp->gr_mem[j] != NULL; j++) { + if (strcmp(grp->gr_mem[j], pwd->pw_name) == 0) + break; + } + if (grp->gr_mem != NULL && grp->gr_mem[j] != NULL) + continue; + grp = gr_add(grp, pwd->pw_name); + } + } + + if (conf.dryrun) + return print_group(grp); + + if (mode == M_ADD && (rc = addgrent(grp)) != 0) { + if (rc == -1) + errx(EX_IOERR, "group '%s' already exists", + grp->gr_name); + else + err(EX_IOERR, "group update"); + } else if (mode == M_UPDATE && (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 (conf.newname != NULL) + name = conf.newname; + /* grp may have been invalidated */ + if ((grp = GETGRNAM(name)) == NULL) + errx(EX_SOFTWARE, "group disappeared during update"); + + pw_log(cnf, mode, W_GROUP, "%s(%u)", grp->gr_name, grp->gr_gid); + + return EXIT_SUCCESS; } + /* * Lookup a passwd entry using a name or UID. */ @@ -166,11 +331,11 @@ delete_members(struct group *grp, char *list) } } -static gid_t -gr_gidpolicy(struct userconf * cnf, intmax_t id) + +static gid_t +gr_gidpolicy(struct userconf * cnf, long id) { struct group *grp; - struct bitmap bm; gid_t gid = (gid_t) - 1; /* @@ -180,62 +345,67 @@ gr_gidpolicy(struct userconf * cnf, intmax_t id) 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); + errx(EX_DATAERR, "gid `%u' has already been allocated", grp->gr_gid); + } else { + struct bitmap bm; + + /* + * 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(); + /* + * 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 + /* + * 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); + /* + * 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) +print_group(struct group * grp) { - char *buf = NULL; - int i; + if (!conf.pretty) { + char *buf = NULL; + + buf = gr_make(grp); + printf("%s\n", buf); + free(buf); + } else { + int i; - if (pretty) { printf("Group Name: %-15s #%lu\n" " Members: ", grp->gr_name, (long) grp->gr_gid); @@ -244,446 +414,6 @@ print_group(struct group * grp, bool pretty) 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; - - 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 ((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); + return EXIT_SUCCESS; } |
