pw_group.c revision 286203
120253Sjoerg/*- 220302Sjoerg * Copyright (C) 1996 320302Sjoerg * David L. Nugent. All rights reserved. 420253Sjoerg * 520253Sjoerg * Redistribution and use in source and binary forms, with or without 620253Sjoerg * modification, are permitted provided that the following conditions 720253Sjoerg * are met: 820253Sjoerg * 1. Redistributions of source code must retain the above copyright 920302Sjoerg * notice, this list of conditions and the following disclaimer. 1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1120253Sjoerg * notice, this list of conditions and the following disclaimer in the 1220253Sjoerg * documentation and/or other materials provided with the distribution. 1320253Sjoerg * 1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1720302Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2420253Sjoerg * SUCH DAMAGE. 2520253Sjoerg */ 2620253Sjoerg 2730259Scharnier#ifndef lint 2830259Scharnierstatic const char rcsid[] = 2950479Speter "$FreeBSD: head/usr.sbin/pw/pw_group.c 286203 2015-08-02 13:33:17Z bapt $"; 3030259Scharnier#endif /* not lint */ 3130259Scharnier 3220253Sjoerg#include <ctype.h> 3330259Scharnier#include <err.h> 34286196Sbapt#include <grp.h> 35286196Sbapt#include <libutil.h> 36286196Sbapt#include <paths.h> 37286201Sbapt#include <string.h> 38286201Sbapt#include <sysexits.h> 3920253Sjoerg#include <termios.h> 4030259Scharnier#include <unistd.h> 4120253Sjoerg 4220253Sjoerg#include "pw.h" 4320253Sjoerg#include "bitmap.h" 4420253Sjoerg 45176474Sscfstatic struct passwd *lookup_pwent(const char *user); 46285411Sbaptstatic void delete_members(struct group *grp, char *list); 47286196Sbaptstatic int print_group(struct group * grp, bool pretty); 48286196Sbaptstatic gid_t gr_gidpolicy(struct userconf * cnf, intmax_t id); 4920253Sjoerg 50285136Sbaptstatic void 51286196Sbaptgrp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) 52285136Sbapt{ 53285136Sbapt int b; 54285136Sbapt int istty; 55285136Sbapt struct termios t, n; 56285136Sbapt char *p, line[256]; 57285136Sbapt 58286196Sbapt if (fd == -1) 59286196Sbapt return; 60286196Sbapt 61286196Sbapt if (fd == '-') { 62285136Sbapt grp->gr_passwd = "*"; /* No access */ 63285136Sbapt return; 64285136Sbapt } 65285136Sbapt 66286196Sbapt if ((istty = isatty(fd))) { 67285136Sbapt n = t; 68285136Sbapt /* Disable echo */ 69285136Sbapt n.c_lflag &= ~(ECHO); 70286196Sbapt tcsetattr(fd, TCSANOW, &n); 71285136Sbapt printf("%sassword for group %s:", update ? "New p" : "P", 72285136Sbapt grp->gr_name); 73285136Sbapt fflush(stdout); 74285136Sbapt } 75286196Sbapt b = read(fd, line, sizeof(line) - 1); 76285136Sbapt if (istty) { /* Restore state */ 77286196Sbapt tcsetattr(fd, TCSANOW, &t); 78285136Sbapt fputc('\n', stdout); 79285136Sbapt fflush(stdout); 80285136Sbapt } 81285136Sbapt if (b < 0) 82285136Sbapt err(EX_OSERR, "-h file descriptor"); 83285136Sbapt line[b] = '\0'; 84285136Sbapt if ((p = strpbrk(line, " \t\r\n")) != NULL) 85285136Sbapt *p = '\0'; 86285136Sbapt if (!*line) 87285136Sbapt errx(EX_DATAERR, "empty password read on file descriptor %d", 88285136Sbapt conf.fd); 89286196Sbapt if (precrypted) { 90285136Sbapt if (strchr(line, ':') != 0) 91285136Sbapt errx(EX_DATAERR, "wrong encrypted passwrd"); 92285136Sbapt grp->gr_passwd = line; 93285136Sbapt } else 94285136Sbapt grp->gr_passwd = pw_pwcrypt(line); 95285136Sbapt} 96285136Sbapt 9720253Sjoergint 98285395Sbaptpw_groupnext(struct userconf *cnf, bool quiet) 99285395Sbapt{ 100285395Sbapt gid_t next = gr_gidpolicy(cnf, -1); 101285395Sbapt 102285395Sbapt if (quiet) 103285395Sbapt return (next); 104286150Sbapt printf("%ju\n", (uintmax_t)next); 105285395Sbapt 106285395Sbapt return (EXIT_SUCCESS); 107285395Sbapt} 108285395Sbapt 109286196Sbaptstatic struct group * 110286196Sbaptgetgroup(char *name, intmax_t id, bool fatal) 111285398Sbapt{ 112286196Sbapt struct group *grp; 113285398Sbapt 114286196Sbapt if (id < 0 && name == NULL) 115286196Sbapt errx(EX_DATAERR, "groupname or id required"); 116285398Sbapt grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 117285398Sbapt if (grp == NULL) { 118286196Sbapt if (!fatal) 119286196Sbapt return (NULL); 120285401Sbapt if (name == NULL) 121286196Sbapt errx(EX_DATAERR, "unknown gid `%ju'", id); 122285401Sbapt errx(EX_DATAERR, "unknown group `%s'", name); 123285401Sbapt } 124286196Sbapt return (grp); 125285401Sbapt} 126285401Sbapt 127176474Sscf/* 128176474Sscf * Lookup a passwd entry using a name or UID. 129176474Sscf */ 130176474Sscfstatic struct passwd * 131176474Sscflookup_pwent(const char *user) 132176474Sscf{ 133176474Sscf struct passwd *pwd; 134176474Sscf 135176474Sscf if ((pwd = GETPWNAM(user)) == NULL && 136176474Sscf (!isdigit((unsigned char)*user) || 137176474Sscf (pwd = getpwuid((uid_t) atoi(user))) == NULL)) 138176474Sscf errx(EX_NOUSER, "user `%s' does not exist", user); 139176474Sscf 140176474Sscf return (pwd); 141176474Sscf} 142176474Sscf 143176474Sscf 144176474Sscf/* 145176474Sscf * Delete requested members from a group. 146176474Sscf */ 147176474Sscfstatic void 148285411Sbaptdelete_members(struct group *grp, char *list) 149176474Sscf{ 150285411Sbapt char *p; 151176474Sscf int k; 152176474Sscf 153262864Sjulian if (grp->gr_mem == NULL) 154262864Sjulian return; 155262864Sjulian 156285411Sbapt for (p = strtok(list, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { 157285411Sbapt for (k = 0; grp->gr_mem[k] != NULL; k++) { 158285411Sbapt if (strcmp(grp->gr_mem[k], p) == 0) 159176474Sscf break; 160176474Sscf } 161285411Sbapt if (grp->gr_mem[k] == NULL) /* No match */ 162285411Sbapt continue; 163176474Sscf 164285411Sbapt for (; grp->gr_mem[k] != NULL; k++) 165285411Sbapt grp->gr_mem[k] = grp->gr_mem[k+1]; 166176474Sscf } 167176474Sscf} 168176474Sscf 169286196Sbaptstatic gid_t 170286196Sbaptgr_gidpolicy(struct userconf * cnf, intmax_t id) 17120253Sjoerg{ 17220253Sjoerg struct group *grp; 173286196Sbapt struct bitmap bm; 17420253Sjoerg gid_t gid = (gid_t) - 1; 17520253Sjoerg 17620253Sjoerg /* 17720253Sjoerg * Check the given gid, if any 17820253Sjoerg */ 179284133Sbapt if (id > 0) { 180284133Sbapt gid = (gid_t) id; 18120253Sjoerg 182284133Sbapt if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) 183286203Sbapt errx(EX_DATAERR, "gid `%ju' has already been allocated", 184286203Sbapt (uintmax_t)grp->gr_gid); 185286196Sbapt return (gid); 186286196Sbapt } 18720253Sjoerg 188286196Sbapt /* 189286196Sbapt * We need to allocate the next available gid under one of 190286196Sbapt * two policies a) Grab the first unused gid b) Grab the 191286196Sbapt * highest possible unused gid 192286196Sbapt */ 193286196Sbapt if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 194286196Sbapt cnf->min_gid = 1000; 195286196Sbapt cnf->max_gid = 32000; 196286196Sbapt } 197286196Sbapt bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 19820253Sjoerg 199286196Sbapt /* 200286196Sbapt * Now, let's fill the bitmap from the password file 201286196Sbapt */ 202286196Sbapt SETGRENT(); 203286196Sbapt while ((grp = GETGRENT()) != NULL) 204286196Sbapt if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && 205286196Sbapt (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) 206286196Sbapt bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 207286196Sbapt ENDGRENT(); 20820253Sjoerg 209286196Sbapt /* 210286196Sbapt * Then apply the policy, with fallback to reuse if necessary 211286196Sbapt */ 212286196Sbapt if (cnf->reuse_gids) 213286196Sbapt gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 214286196Sbapt else { 215286196Sbapt gid = (gid_t) (bm_lastset(&bm) + 1); 216286196Sbapt if (!bm_isset(&bm, gid)) 217286196Sbapt gid += cnf->min_gid; 218286196Sbapt else 21920253Sjoerg gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 220286196Sbapt } 22120253Sjoerg 222286196Sbapt /* 223286196Sbapt * Another sanity check 224286196Sbapt */ 225286196Sbapt if (gid < cnf->min_gid || gid > cnf->max_gid) 226286203Sbapt errx(EX_SOFTWARE, "unable to allocate a new gid - range fully " 227286203Sbapt "used"); 228286196Sbapt bm_dealloc(&bm); 229286196Sbapt return (gid); 23020253Sjoerg} 23120253Sjoerg 23220253Sjoergstatic int 233286196Sbaptprint_group(struct group * grp, bool pretty) 23420253Sjoerg{ 235286196Sbapt char *buf = NULL; 236286196Sbapt int i; 23720253Sjoerg 238286196Sbapt if (pretty) { 23922398Sdavidn printf("Group Name: %-15s #%lu\n" 24020747Sdavidn " Members: ", 24120253Sjoerg grp->gr_name, (long) grp->gr_gid); 242262864Sjulian if (grp->gr_mem != NULL) { 243262864Sjulian for (i = 0; grp->gr_mem[i]; i++) 244262864Sjulian printf("%s%s", i ? "," : "", grp->gr_mem[i]); 245262864Sjulian } 24620253Sjoerg fputs("\n\n", stdout); 247286196Sbapt return (EXIT_SUCCESS); 24820253Sjoerg } 249286196Sbapt 250286196Sbapt buf = gr_make(grp); 251286196Sbapt printf("%s\n", buf); 252286196Sbapt free(buf); 253286196Sbapt return (EXIT_SUCCESS); 25420253Sjoerg} 255286196Sbapt 256286196Sbaptint 257286196Sbaptpw_group_next(int argc, char **argv, char *arg1 __unused) 258286196Sbapt{ 259286196Sbapt struct userconf *cnf; 260286196Sbapt const char *cfg = NULL; 261286196Sbapt int ch; 262286196Sbapt bool quiet; 263286196Sbapt 264286196Sbapt while ((ch = getopt(argc, argv, "Cq")) != -1) { 265286196Sbapt switch (ch) { 266286196Sbapt case 'C': 267286196Sbapt cfg = optarg; 268286196Sbapt break; 269286196Sbapt case 'q': 270286196Sbapt quiet = true; 271286196Sbapt break; 272286196Sbapt } 273286196Sbapt } 274286196Sbapt 275286196Sbapt if (quiet) 276286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 277286196Sbapt cnf = get_userconfig(cfg); 278286196Sbapt return (pw_groupnext(cnf, quiet)); 279286196Sbapt} 280286196Sbapt 281286196Sbaptint 282286196Sbaptpw_group_show(int argc, char **argv, char *arg1) 283286196Sbapt{ 284286196Sbapt struct group *grp = NULL; 285286196Sbapt char *name; 286286196Sbapt intmax_t id = -1; 287286196Sbapt int ch; 288286196Sbapt bool all, force, quiet, pretty; 289286196Sbapt 290286196Sbapt all = force = quiet = pretty = false; 291286196Sbapt 292286196Sbapt struct group fakegroup = { 293286196Sbapt "nogroup", 294286196Sbapt "*", 295286196Sbapt -1, 296286196Sbapt NULL 297286196Sbapt }; 298286196Sbapt 299286196Sbapt if (arg1 != NULL) { 300286196Sbapt if (strspn(arg1, "0123456789") == strlen(arg1)) 301286196Sbapt id = pw_checkid(arg1, GID_MAX); 302286196Sbapt else 303286196Sbapt name = arg1; 304286196Sbapt } 305286196Sbapt 306286196Sbapt while ((ch = getopt(argc, argv, "C:qn:g:FPa")) != -1) { 307286196Sbapt switch (ch) { 308286196Sbapt case 'C': 309286196Sbapt /* ignore compatibility */ 310286196Sbapt break; 311286196Sbapt case 'q': 312286196Sbapt quiet = true; 313286196Sbapt break; 314286196Sbapt case 'n': 315286196Sbapt name = optarg; 316286196Sbapt break; 317286196Sbapt case 'g': 318286196Sbapt id = pw_checkid(optarg, GID_MAX); 319286196Sbapt break; 320286196Sbapt case 'F': 321286196Sbapt force = true; 322286196Sbapt break; 323286196Sbapt case 'P': 324286196Sbapt pretty = true; 325286196Sbapt break; 326286196Sbapt case 'a': 327286196Sbapt all = true; 328286196Sbapt break; 329286196Sbapt } 330286196Sbapt } 331286196Sbapt 332286196Sbapt if (quiet) 333286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 334286196Sbapt 335286196Sbapt if (all) { 336286196Sbapt SETGRENT(); 337286196Sbapt while ((grp = GETGRENT()) != NULL) 338286196Sbapt print_group(grp, pretty); 339286196Sbapt ENDGRENT(); 340286196Sbapt return (EXIT_SUCCESS); 341286196Sbapt } 342286196Sbapt 343286196Sbapt grp = getgroup(name, id, !force); 344286196Sbapt if (grp == NULL) 345286196Sbapt grp = &fakegroup; 346286196Sbapt 347286196Sbapt return (print_group(grp, pretty)); 348286196Sbapt} 349286196Sbapt 350286196Sbaptint 351286196Sbaptpw_group_del(int argc, char **argv, char *arg1) 352286196Sbapt{ 353286196Sbapt struct userconf *cnf = NULL; 354286196Sbapt struct group *grp = NULL; 355286196Sbapt char *name; 356286196Sbapt const char *cfg = NULL; 357286196Sbapt intmax_t id = -1; 358286196Sbapt int ch, rc; 359286196Sbapt bool quiet = false; 360286196Sbapt bool nis = false; 361286196Sbapt 362286196Sbapt if (arg1 != NULL) { 363286196Sbapt if (strspn(arg1, "0123456789") == strlen(arg1)) 364286196Sbapt id = pw_checkid(arg1, GID_MAX); 365286196Sbapt else 366286196Sbapt name = arg1; 367286196Sbapt } 368286196Sbapt 369286196Sbapt while ((ch = getopt(argc, argv, "C:qn:g:Y")) != -1) { 370286196Sbapt switch (ch) { 371286196Sbapt case 'C': 372286196Sbapt cfg = optarg; 373286196Sbapt break; 374286196Sbapt case 'q': 375286196Sbapt quiet = true; 376286196Sbapt break; 377286196Sbapt case 'n': 378286196Sbapt name = optarg; 379286196Sbapt break; 380286196Sbapt case 'g': 381286196Sbapt id = pw_checkid(optarg, GID_MAX); 382286196Sbapt break; 383286196Sbapt case 'Y': 384286196Sbapt nis = true; 385286196Sbapt break; 386286196Sbapt } 387286196Sbapt } 388286196Sbapt 389286196Sbapt if (quiet) 390286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 391286196Sbapt grp = getgroup(name, id, true); 392286196Sbapt cnf = get_userconfig(cfg); 393286196Sbapt rc = delgrent(grp); 394286196Sbapt if (rc == -1) 395286196Sbapt err(EX_IOERR, "group '%s' not available (NIS?)", name); 396286196Sbapt else if (rc != 0) 397286196Sbapt err(EX_IOERR, "group update"); 398286196Sbapt pw_log(cnf, M_DELETE, W_GROUP, "%s(%ju) removed", name, 399286196Sbapt (uintmax_t)id); 400286196Sbapt 401286196Sbapt if (nis && nis_update() == 0) 402286196Sbapt pw_log(cnf, M_DELETE, W_GROUP, "NIS maps updated"); 403286196Sbapt 404286196Sbapt return (EXIT_SUCCESS); 405286196Sbapt} 406286196Sbapt 407286196Sbaptstatic bool 408286196Sbaptgrp_has_member(struct group *grp, const char *name) 409286196Sbapt{ 410286196Sbapt int j; 411286196Sbapt 412286196Sbapt for (j = 0; grp->gr_mem != NULL && grp->gr_mem[j] != NULL; j++) 413286196Sbapt if (strcmp(grp->gr_mem[j], name) == 0) 414286196Sbapt return (true); 415286196Sbapt return (false); 416286196Sbapt} 417286196Sbapt 418286196Sbaptstatic void 419286196Sbaptgrp_add_members(struct group **grp, char *members) 420286196Sbapt{ 421286196Sbapt struct passwd *pwd; 422286196Sbapt char *p; 423286196Sbapt char tok[] = ", \t"; 424286196Sbapt 425286196Sbapt if (members == NULL) 426286196Sbapt return; 427286196Sbapt for (p = strtok(members, tok); p != NULL; p = strtok(NULL, tok)) { 428286196Sbapt pwd = lookup_pwent(p); 429286196Sbapt if (grp_has_member(*grp, pwd->pw_name)) 430286196Sbapt continue; 431286196Sbapt *grp = gr_add(*grp, pwd->pw_name); 432286196Sbapt } 433286196Sbapt} 434286196Sbapt 435286196Sbaptint 436286196Sbaptgroupadd(struct userconf *cnf, char *name, gid_t id, char *members, int fd, 437286196Sbapt bool dryrun, bool pretty, bool precrypted) 438286196Sbapt{ 439286196Sbapt struct group *grp; 440286196Sbapt int rc; 441286196Sbapt 442286196Sbapt struct group fakegroup = { 443286196Sbapt "nogroup", 444286196Sbapt "*", 445286196Sbapt -1, 446286196Sbapt NULL 447286196Sbapt }; 448286196Sbapt 449286196Sbapt grp = &fakegroup; 450286196Sbapt grp->gr_name = pw_checkname(name, 0); 451286196Sbapt grp->gr_passwd = "*"; 452286196Sbapt grp->gr_gid = gr_gidpolicy(cnf, id); 453286196Sbapt grp->gr_mem = NULL; 454286196Sbapt 455286196Sbapt /* 456286196Sbapt * This allows us to set a group password Group passwords is an 457286196Sbapt * antique idea, rarely used and insecure (no secure database) Should 458286196Sbapt * be discouraged, but it is apparently still supported by some 459286196Sbapt * software. 460286196Sbapt */ 461286196Sbapt grp_set_passwd(grp, false, fd, precrypted); 462286196Sbapt grp_add_members(&grp, members); 463286196Sbapt if (dryrun) 464286196Sbapt return (print_group(grp, pretty)); 465286196Sbapt 466286196Sbapt if ((rc = addgrent(grp)) != 0) { 467286196Sbapt if (rc == -1) 468286196Sbapt errx(EX_IOERR, "group '%s' already exists", 469286196Sbapt grp->gr_name); 470286196Sbapt else 471286196Sbapt err(EX_IOERR, "group update"); 472286196Sbapt } 473286196Sbapt 474286196Sbapt pw_log(cnf, M_ADD, W_GROUP, "%s(%ju)", grp->gr_name, 475286196Sbapt (uintmax_t)grp->gr_gid); 476286196Sbapt 477286196Sbapt return (EXIT_SUCCESS); 478286196Sbapt} 479286196Sbapt 480286196Sbaptint 481286196Sbaptpw_group_add(int argc, char **argv, char *arg1) 482286196Sbapt{ 483286196Sbapt struct userconf *cnf = NULL; 484286196Sbapt char *name = NULL; 485286196Sbapt char *members = NULL; 486286196Sbapt const char *cfg = NULL; 487286196Sbapt intmax_t id = -1; 488286196Sbapt int ch, rc, fd = -1; 489286196Sbapt bool quiet, precrypted, dryrun, pretty, nis; 490286196Sbapt 491286196Sbapt quiet = precrypted = dryrun = pretty = nis = false; 492286196Sbapt 493286196Sbapt if (arg1 != NULL) { 494286196Sbapt if (strspn(arg1, "0123456789") == strlen(arg1)) 495286196Sbapt id = pw_checkid(arg1, GID_MAX); 496286196Sbapt else 497286196Sbapt name = arg1; 498286196Sbapt } 499286196Sbapt 500286196Sbapt while ((ch = getopt(argc, argv, "C:qn:g:h:H:M:oNPY")) != -1) { 501286196Sbapt switch (ch) { 502286196Sbapt case 'C': 503286196Sbapt cfg = optarg; 504286196Sbapt break; 505286196Sbapt case 'q': 506286196Sbapt quiet = true; 507286196Sbapt break; 508286196Sbapt case 'n': 509286196Sbapt name = optarg; 510286196Sbapt break; 511286196Sbapt case 'g': 512286196Sbapt id = pw_checkid(optarg, GID_MAX); 513286196Sbapt break; 514286196Sbapt case 'H': 515286196Sbapt if (fd != -1) 516286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 517286196Sbapt "exclusive options"); 518286196Sbapt fd = pw_checkfd(optarg); 519286196Sbapt precrypted = true; 520286196Sbapt if (fd == '-') 521286196Sbapt errx(EX_USAGE, "-H expects a file descriptor"); 522286196Sbapt break; 523286196Sbapt case 'h': 524286196Sbapt if (fd != -1) 525286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 526286196Sbapt "exclusive options"); 527286196Sbapt fd = pw_checkfd(optarg); 528286196Sbapt break; 529286196Sbapt case 'M': 530286196Sbapt members = optarg; 531286196Sbapt break; 532286196Sbapt case 'o': 533286196Sbapt conf.checkduplicate = false; 534286196Sbapt break; 535286196Sbapt case 'N': 536286196Sbapt dryrun = true; 537286196Sbapt break; 538286196Sbapt case 'P': 539286196Sbapt pretty = true; 540286196Sbapt break; 541286196Sbapt case 'Y': 542286196Sbapt nis = true; 543286196Sbapt break; 544286196Sbapt } 545286196Sbapt } 546286196Sbapt 547286196Sbapt if (quiet) 548286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 549286196Sbapt if (name == NULL) 550286196Sbapt errx(EX_DATAERR, "group name required"); 551286199Sbapt if (GETGRNAM(name) != NULL) 552286199Sbapt errx(EX_DATAERR, "group name `%s' already exists", name); 553286196Sbapt cnf = get_userconfig(cfg); 554286196Sbapt rc = groupadd(cnf, name, gr_gidpolicy(cnf, id), members, fd, dryrun, 555286196Sbapt pretty, precrypted); 556286196Sbapt if (nis && rc == EXIT_SUCCESS && nis_update() == 0) 557286196Sbapt pw_log(cnf, M_ADD, W_GROUP, "NIS maps updated"); 558286196Sbapt 559286196Sbapt return (rc); 560286196Sbapt} 561286196Sbapt 562286196Sbaptint 563286196Sbaptpw_group_mod(int argc, char **argv, char *arg1) 564286196Sbapt{ 565286196Sbapt struct userconf *cnf; 566286196Sbapt struct group *grp = NULL; 567286196Sbapt const char *cfg = NULL; 568286196Sbapt char *oldmembers = NULL; 569286196Sbapt char *members = NULL; 570286196Sbapt char *newmembers = NULL; 571286196Sbapt char *newname = NULL; 572286196Sbapt char *name = NULL; 573286196Sbapt intmax_t id = -1; 574286196Sbapt int ch, rc, fd = -1; 575286196Sbapt bool quiet, pretty, dryrun, nis, precrypted; 576286196Sbapt 577286196Sbapt quiet = pretty = dryrun = nis = precrypted = false; 578286196Sbapt 579286196Sbapt if (arg1 != NULL) { 580286196Sbapt if (strspn(arg1, "0123456789") == strlen(arg1)) 581286196Sbapt id = pw_checkid(arg1, GID_MAX); 582286196Sbapt else 583286196Sbapt name = arg1; 584286196Sbapt } 585286196Sbapt 586286196Sbapt while ((ch = getopt(argc, argv, "C:qn:d:g:l:h:H:M:m:NPY")) != -1) { 587286196Sbapt switch (ch) { 588286196Sbapt case 'C': 589286196Sbapt cfg = optarg; 590286196Sbapt break; 591286196Sbapt case 'q': 592286196Sbapt quiet = true; 593286196Sbapt break; 594286196Sbapt case 'n': 595286196Sbapt name = optarg; 596286196Sbapt break; 597286196Sbapt case 'g': 598286196Sbapt id = pw_checkid(optarg, GID_MAX); 599286196Sbapt break; 600286196Sbapt case 'd': 601286196Sbapt oldmembers = optarg; 602286196Sbapt break; 603286196Sbapt case 'l': 604286196Sbapt newname = optarg; 605286196Sbapt break; 606286196Sbapt case 'H': 607286196Sbapt if (fd != -1) 608286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 609286196Sbapt "exclusive options"); 610286196Sbapt fd = pw_checkfd(optarg); 611286196Sbapt precrypted = true; 612286196Sbapt if (fd == '-') 613286196Sbapt errx(EX_USAGE, "-H expects a file descriptor"); 614286196Sbapt break; 615286196Sbapt case 'h': 616286196Sbapt if (fd != -1) 617286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 618286196Sbapt "exclusive options"); 619286196Sbapt fd = pw_checkfd(optarg); 620286196Sbapt break; 621286196Sbapt case 'M': 622286196Sbapt members = optarg; 623286196Sbapt break; 624286196Sbapt case 'm': 625286196Sbapt newmembers = optarg; 626286196Sbapt break; 627286196Sbapt case 'N': 628286196Sbapt dryrun = true; 629286196Sbapt break; 630286196Sbapt case 'P': 631286196Sbapt pretty = true; 632286196Sbapt break; 633286196Sbapt case 'Y': 634286196Sbapt nis = true; 635286196Sbapt break; 636286196Sbapt } 637286196Sbapt } 638286196Sbapt if (quiet) 639286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 640286196Sbapt cnf = get_userconfig(cfg); 641286196Sbapt grp = getgroup(name, id, true); 642286196Sbapt if (name == NULL) 643286196Sbapt name = grp->gr_name; 644286196Sbapt if (id > 0) 645286196Sbapt grp->gr_gid = id; 646286196Sbapt 647286196Sbapt if (newname != NULL) 648286196Sbapt grp->gr_name = pw_checkname(newname, 0); 649286196Sbapt 650286196Sbapt grp_set_passwd(grp, true, fd, precrypted); 651286196Sbapt /* 652286196Sbapt * Keep the same logic as old code for now: 653286196Sbapt * if -M is passed, -d and -m are ignored 654286196Sbapt * then id -d, -m is ignored 655286196Sbapt * last is -m 656286196Sbapt */ 657286196Sbapt 658286196Sbapt if (members) { 659286196Sbapt grp->gr_mem = NULL; 660286196Sbapt grp_add_members(&grp, members); 661286196Sbapt } else if (oldmembers) { 662286196Sbapt delete_members(grp, oldmembers); 663286196Sbapt } else if (newmembers) { 664286196Sbapt grp_add_members(&grp, newmembers); 665286196Sbapt } 666286196Sbapt 667286196Sbapt if ((rc = chggrent(name, grp)) != 0) { 668286196Sbapt if (rc == -1) 669286196Sbapt errx(EX_IOERR, "group '%s' not available (NIS?)", 670286196Sbapt grp->gr_name); 671286196Sbapt else 672286196Sbapt err(EX_IOERR, "group update"); 673286196Sbapt } 674286196Sbapt 675286196Sbapt if (newname) 676286196Sbapt name = newname; 677286196Sbapt 678286196Sbapt /* grp may have been invalidated */ 679286196Sbapt if ((grp = GETGRNAM(name)) == NULL) 680286196Sbapt errx(EX_SOFTWARE, "group disappeared during update"); 681286196Sbapt 682286196Sbapt pw_log(cnf, M_UPDATE, W_GROUP, "%s(%ju)", grp->gr_name, 683286196Sbapt (uintmax_t)grp->gr_gid); 684286196Sbapt 685286196Sbapt if (nis && nis_update() == 0) 686286196Sbapt pw_log(cnf, M_UPDATE, W_GROUP, "NIS maps updated"); 687286196Sbapt 688286196Sbapt return (EXIT_SUCCESS); 689286196Sbapt} 690