120253Sjoerg/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 420302Sjoerg * Copyright (C) 1996 520302Sjoerg * David L. Nugent. All rights reserved. 620253Sjoerg * 720253Sjoerg * Redistribution and use in source and binary forms, with or without 820253Sjoerg * modification, are permitted provided that the following conditions 920253Sjoerg * are met: 1020253Sjoerg * 1. Redistributions of source code must retain the above copyright 1120302Sjoerg * notice, this list of conditions and the following disclaimer. 1220253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1320253Sjoerg * notice, this list of conditions and the following disclaimer in the 1420253Sjoerg * documentation and/or other materials provided with the distribution. 1520253Sjoerg * 1620302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 1720253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1820253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1920302Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 2020253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2120253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2220253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2320253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2420253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2520253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2620253Sjoerg * SUCH DAMAGE. 2720253Sjoerg */ 2820253Sjoerg 2930259Scharnier#ifndef lint 3030259Scharnierstatic const char rcsid[] = 3150479Speter "$FreeBSD: stable/11/usr.sbin/pw/pw_group.c 330449 2018-03-05 07:26:05Z eadler $"; 3230259Scharnier#endif /* not lint */ 3330259Scharnier 3420253Sjoerg#include <ctype.h> 3530259Scharnier#include <err.h> 36286196Sbapt#include <grp.h> 37286196Sbapt#include <libutil.h> 38286196Sbapt#include <paths.h> 39286201Sbapt#include <string.h> 40286201Sbapt#include <sysexits.h> 4120253Sjoerg#include <termios.h> 4230259Scharnier#include <unistd.h> 4320253Sjoerg 4420253Sjoerg#include "pw.h" 4520253Sjoerg#include "bitmap.h" 4620253Sjoerg 47176474Sscfstatic struct passwd *lookup_pwent(const char *user); 48285411Sbaptstatic void delete_members(struct group *grp, char *list); 49286196Sbaptstatic int print_group(struct group * grp, bool pretty); 50286196Sbaptstatic gid_t gr_gidpolicy(struct userconf * cnf, intmax_t id); 5120253Sjoerg 52285136Sbaptstatic void 53286196Sbaptgrp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) 54285136Sbapt{ 55285136Sbapt int b; 56285136Sbapt int istty; 57285136Sbapt struct termios t, n; 58285136Sbapt char *p, line[256]; 59285136Sbapt 60286196Sbapt if (fd == -1) 61286196Sbapt return; 62286196Sbapt 63286196Sbapt if (fd == '-') { 64285136Sbapt grp->gr_passwd = "*"; /* No access */ 65285136Sbapt return; 66285136Sbapt } 67285136Sbapt 68286196Sbapt if ((istty = isatty(fd))) { 69285136Sbapt n = t; 70285136Sbapt /* Disable echo */ 71285136Sbapt n.c_lflag &= ~(ECHO); 72286196Sbapt tcsetattr(fd, TCSANOW, &n); 73285136Sbapt printf("%sassword for group %s:", update ? "New p" : "P", 74285136Sbapt grp->gr_name); 75285136Sbapt fflush(stdout); 76285136Sbapt } 77286196Sbapt b = read(fd, line, sizeof(line) - 1); 78285136Sbapt if (istty) { /* Restore state */ 79286196Sbapt tcsetattr(fd, TCSANOW, &t); 80285136Sbapt fputc('\n', stdout); 81285136Sbapt fflush(stdout); 82285136Sbapt } 83285136Sbapt if (b < 0) 84285136Sbapt err(EX_OSERR, "-h file descriptor"); 85285136Sbapt line[b] = '\0'; 86285136Sbapt if ((p = strpbrk(line, " \t\r\n")) != NULL) 87285136Sbapt *p = '\0'; 88285136Sbapt if (!*line) 89285136Sbapt errx(EX_DATAERR, "empty password read on file descriptor %d", 90285136Sbapt conf.fd); 91286196Sbapt if (precrypted) { 92285136Sbapt if (strchr(line, ':') != 0) 93285136Sbapt errx(EX_DATAERR, "wrong encrypted passwrd"); 94285136Sbapt grp->gr_passwd = line; 95285136Sbapt } else 96285136Sbapt grp->gr_passwd = pw_pwcrypt(line); 97285136Sbapt} 98285136Sbapt 9920253Sjoergint 100285395Sbaptpw_groupnext(struct userconf *cnf, bool quiet) 101285395Sbapt{ 102285395Sbapt gid_t next = gr_gidpolicy(cnf, -1); 103285395Sbapt 104285395Sbapt if (quiet) 105285395Sbapt return (next); 106286150Sbapt printf("%ju\n", (uintmax_t)next); 107285395Sbapt 108285395Sbapt return (EXIT_SUCCESS); 109285395Sbapt} 110285395Sbapt 111286196Sbaptstatic struct group * 112286196Sbaptgetgroup(char *name, intmax_t id, bool fatal) 113285398Sbapt{ 114286196Sbapt struct group *grp; 115285398Sbapt 116286196Sbapt if (id < 0 && name == NULL) 117286196Sbapt errx(EX_DATAERR, "groupname or id required"); 118285398Sbapt grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 119285398Sbapt if (grp == NULL) { 120286196Sbapt if (!fatal) 121286196Sbapt return (NULL); 122285401Sbapt if (name == NULL) 123286196Sbapt errx(EX_DATAERR, "unknown gid `%ju'", id); 124285401Sbapt errx(EX_DATAERR, "unknown group `%s'", name); 125285401Sbapt } 126286196Sbapt return (grp); 127285401Sbapt} 128285401Sbapt 129176474Sscf/* 130176474Sscf * Lookup a passwd entry using a name or UID. 131176474Sscf */ 132176474Sscfstatic struct passwd * 133176474Sscflookup_pwent(const char *user) 134176474Sscf{ 135176474Sscf struct passwd *pwd; 136176474Sscf 137176474Sscf if ((pwd = GETPWNAM(user)) == NULL && 138176474Sscf (!isdigit((unsigned char)*user) || 139176474Sscf (pwd = getpwuid((uid_t) atoi(user))) == NULL)) 140176474Sscf errx(EX_NOUSER, "user `%s' does not exist", user); 141176474Sscf 142176474Sscf return (pwd); 143176474Sscf} 144176474Sscf 145176474Sscf 146176474Sscf/* 147176474Sscf * Delete requested members from a group. 148176474Sscf */ 149176474Sscfstatic void 150285411Sbaptdelete_members(struct group *grp, char *list) 151176474Sscf{ 152285411Sbapt char *p; 153176474Sscf int k; 154176474Sscf 155262864Sjulian if (grp->gr_mem == NULL) 156262864Sjulian return; 157262864Sjulian 158285411Sbapt for (p = strtok(list, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { 159285411Sbapt for (k = 0; grp->gr_mem[k] != NULL; k++) { 160285411Sbapt if (strcmp(grp->gr_mem[k], p) == 0) 161176474Sscf break; 162176474Sscf } 163285411Sbapt if (grp->gr_mem[k] == NULL) /* No match */ 164285411Sbapt continue; 165176474Sscf 166285411Sbapt for (; grp->gr_mem[k] != NULL; k++) 167285411Sbapt grp->gr_mem[k] = grp->gr_mem[k+1]; 168176474Sscf } 169176474Sscf} 170176474Sscf 171286196Sbaptstatic gid_t 172286196Sbaptgr_gidpolicy(struct userconf * cnf, intmax_t id) 17320253Sjoerg{ 17420253Sjoerg struct group *grp; 175286196Sbapt struct bitmap bm; 17620253Sjoerg gid_t gid = (gid_t) - 1; 17720253Sjoerg 17820253Sjoerg /* 17920253Sjoerg * Check the given gid, if any 18020253Sjoerg */ 181284133Sbapt if (id > 0) { 182284133Sbapt gid = (gid_t) id; 18320253Sjoerg 184284133Sbapt if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) 185286203Sbapt errx(EX_DATAERR, "gid `%ju' has already been allocated", 186286203Sbapt (uintmax_t)grp->gr_gid); 187286196Sbapt return (gid); 188286196Sbapt } 18920253Sjoerg 190286196Sbapt /* 191286196Sbapt * We need to allocate the next available gid under one of 192286196Sbapt * two policies a) Grab the first unused gid b) Grab the 193286196Sbapt * highest possible unused gid 194286196Sbapt */ 195286196Sbapt if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 196286196Sbapt cnf->min_gid = 1000; 197286196Sbapt cnf->max_gid = 32000; 198286196Sbapt } 199286196Sbapt bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 20020253Sjoerg 201286196Sbapt /* 202286196Sbapt * Now, let's fill the bitmap from the password file 203286196Sbapt */ 204286196Sbapt SETGRENT(); 205286196Sbapt while ((grp = GETGRENT()) != NULL) 206286196Sbapt if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && 207286196Sbapt (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) 208286196Sbapt bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 209286196Sbapt ENDGRENT(); 21020253Sjoerg 211286196Sbapt /* 212286196Sbapt * Then apply the policy, with fallback to reuse if necessary 213286196Sbapt */ 214286196Sbapt if (cnf->reuse_gids) 215286196Sbapt gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 216286196Sbapt else { 217286196Sbapt gid = (gid_t) (bm_lastset(&bm) + 1); 218286196Sbapt if (!bm_isset(&bm, gid)) 219286196Sbapt gid += cnf->min_gid; 220286196Sbapt else 22120253Sjoerg gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 222286196Sbapt } 22320253Sjoerg 224286196Sbapt /* 225286196Sbapt * Another sanity check 226286196Sbapt */ 227286196Sbapt if (gid < cnf->min_gid || gid > cnf->max_gid) 228286203Sbapt errx(EX_SOFTWARE, "unable to allocate a new gid - range fully " 229286203Sbapt "used"); 230286196Sbapt bm_dealloc(&bm); 231286196Sbapt return (gid); 23220253Sjoerg} 23320253Sjoerg 23420253Sjoergstatic int 235286196Sbaptprint_group(struct group * grp, bool pretty) 23620253Sjoerg{ 237286196Sbapt char *buf = NULL; 238286196Sbapt int i; 23920253Sjoerg 240286196Sbapt if (pretty) { 24122398Sdavidn printf("Group Name: %-15s #%lu\n" 24220747Sdavidn " Members: ", 24320253Sjoerg grp->gr_name, (long) grp->gr_gid); 244262864Sjulian if (grp->gr_mem != NULL) { 245262864Sjulian for (i = 0; grp->gr_mem[i]; i++) 246262864Sjulian printf("%s%s", i ? "," : "", grp->gr_mem[i]); 247262864Sjulian } 24820253Sjoerg fputs("\n\n", stdout); 249286196Sbapt return (EXIT_SUCCESS); 25020253Sjoerg } 251286196Sbapt 252286196Sbapt buf = gr_make(grp); 253286196Sbapt printf("%s\n", buf); 254286196Sbapt free(buf); 255286196Sbapt return (EXIT_SUCCESS); 25620253Sjoerg} 257286196Sbapt 258286196Sbaptint 259286196Sbaptpw_group_next(int argc, char **argv, char *arg1 __unused) 260286196Sbapt{ 261286196Sbapt struct userconf *cnf; 262286196Sbapt const char *cfg = NULL; 263286196Sbapt int ch; 264289600Sngie bool quiet = false; 265286196Sbapt 266301367Sbapt while ((ch = getopt(argc, argv, "C:q")) != -1) { 267286196Sbapt switch (ch) { 268286196Sbapt case 'C': 269286196Sbapt cfg = optarg; 270286196Sbapt break; 271286196Sbapt case 'q': 272286196Sbapt quiet = true; 273286196Sbapt break; 274286196Sbapt } 275286196Sbapt } 276286196Sbapt 277286196Sbapt if (quiet) 278286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 279286196Sbapt cnf = get_userconfig(cfg); 280286196Sbapt return (pw_groupnext(cnf, quiet)); 281286196Sbapt} 282286196Sbapt 283286196Sbaptint 284286196Sbaptpw_group_show(int argc, char **argv, char *arg1) 285286196Sbapt{ 286286196Sbapt struct group *grp = NULL; 287309880Sbapt char *name = NULL; 288286196Sbapt intmax_t id = -1; 289286196Sbapt int ch; 290286196Sbapt bool all, force, quiet, pretty; 291286196Sbapt 292286196Sbapt all = force = quiet = pretty = false; 293286196Sbapt 294286196Sbapt struct group fakegroup = { 295286196Sbapt "nogroup", 296286196Sbapt "*", 297286196Sbapt -1, 298286196Sbapt NULL 299286196Sbapt }; 300286196Sbapt 301286196Sbapt if (arg1 != NULL) { 302286259Sed if (arg1[strspn(arg1, "0123456789")] == '\0') 303286196Sbapt id = pw_checkid(arg1, GID_MAX); 304286196Sbapt else 305286196Sbapt name = arg1; 306286196Sbapt } 307286196Sbapt 308286196Sbapt while ((ch = getopt(argc, argv, "C:qn:g:FPa")) != -1) { 309286196Sbapt switch (ch) { 310286196Sbapt case 'C': 311286196Sbapt /* ignore compatibility */ 312286196Sbapt break; 313286196Sbapt case 'q': 314286196Sbapt quiet = true; 315286196Sbapt break; 316286196Sbapt case 'n': 317286196Sbapt name = optarg; 318286196Sbapt break; 319286196Sbapt case 'g': 320286196Sbapt id = pw_checkid(optarg, GID_MAX); 321286196Sbapt break; 322286196Sbapt case 'F': 323286196Sbapt force = true; 324286196Sbapt break; 325286196Sbapt case 'P': 326286196Sbapt pretty = true; 327286196Sbapt break; 328286196Sbapt case 'a': 329286196Sbapt all = true; 330286196Sbapt break; 331286196Sbapt } 332286196Sbapt } 333286196Sbapt 334286196Sbapt if (quiet) 335286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 336286196Sbapt 337286196Sbapt if (all) { 338286196Sbapt SETGRENT(); 339286196Sbapt while ((grp = GETGRENT()) != NULL) 340286196Sbapt print_group(grp, pretty); 341286196Sbapt ENDGRENT(); 342286196Sbapt return (EXIT_SUCCESS); 343286196Sbapt } 344286196Sbapt 345286196Sbapt grp = getgroup(name, id, !force); 346286196Sbapt if (grp == NULL) 347286196Sbapt grp = &fakegroup; 348286196Sbapt 349286196Sbapt return (print_group(grp, pretty)); 350286196Sbapt} 351286196Sbapt 352286196Sbaptint 353286196Sbaptpw_group_del(int argc, char **argv, char *arg1) 354286196Sbapt{ 355286196Sbapt struct userconf *cnf = NULL; 356286196Sbapt struct group *grp = NULL; 357286196Sbapt char *name; 358286196Sbapt const char *cfg = NULL; 359286196Sbapt intmax_t id = -1; 360286196Sbapt int ch, rc; 361286196Sbapt bool quiet = false; 362286196Sbapt bool nis = false; 363286196Sbapt 364286196Sbapt if (arg1 != NULL) { 365286259Sed if (arg1[strspn(arg1, "0123456789")] == '\0') 366286196Sbapt id = pw_checkid(arg1, GID_MAX); 367286196Sbapt else 368286196Sbapt name = arg1; 369286196Sbapt } 370286196Sbapt 371286196Sbapt while ((ch = getopt(argc, argv, "C:qn:g:Y")) != -1) { 372286196Sbapt switch (ch) { 373286196Sbapt case 'C': 374286196Sbapt cfg = optarg; 375286196Sbapt break; 376286196Sbapt case 'q': 377286196Sbapt quiet = true; 378286196Sbapt break; 379286196Sbapt case 'n': 380286196Sbapt name = optarg; 381286196Sbapt break; 382286196Sbapt case 'g': 383286196Sbapt id = pw_checkid(optarg, GID_MAX); 384286196Sbapt break; 385286196Sbapt case 'Y': 386286196Sbapt nis = true; 387286196Sbapt break; 388286196Sbapt } 389286196Sbapt } 390286196Sbapt 391286196Sbapt if (quiet) 392286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 393286196Sbapt grp = getgroup(name, id, true); 394286196Sbapt cnf = get_userconfig(cfg); 395286196Sbapt rc = delgrent(grp); 396286196Sbapt if (rc == -1) 397286196Sbapt err(EX_IOERR, "group '%s' not available (NIS?)", name); 398286196Sbapt else if (rc != 0) 399286196Sbapt err(EX_IOERR, "group update"); 400286196Sbapt pw_log(cnf, M_DELETE, W_GROUP, "%s(%ju) removed", name, 401286196Sbapt (uintmax_t)id); 402286196Sbapt 403286196Sbapt if (nis && nis_update() == 0) 404286196Sbapt pw_log(cnf, M_DELETE, W_GROUP, "NIS maps updated"); 405286196Sbapt 406286196Sbapt return (EXIT_SUCCESS); 407286196Sbapt} 408286196Sbapt 409286196Sbaptstatic bool 410286196Sbaptgrp_has_member(struct group *grp, const char *name) 411286196Sbapt{ 412286196Sbapt int j; 413286196Sbapt 414286196Sbapt for (j = 0; grp->gr_mem != NULL && grp->gr_mem[j] != NULL; j++) 415286196Sbapt if (strcmp(grp->gr_mem[j], name) == 0) 416286196Sbapt return (true); 417286196Sbapt return (false); 418286196Sbapt} 419286196Sbapt 420286196Sbaptstatic void 421286196Sbaptgrp_add_members(struct group **grp, char *members) 422286196Sbapt{ 423286196Sbapt struct passwd *pwd; 424286196Sbapt char *p; 425286196Sbapt char tok[] = ", \t"; 426286196Sbapt 427286196Sbapt if (members == NULL) 428286196Sbapt return; 429286196Sbapt for (p = strtok(members, tok); p != NULL; p = strtok(NULL, tok)) { 430286196Sbapt pwd = lookup_pwent(p); 431286196Sbapt if (grp_has_member(*grp, pwd->pw_name)) 432286196Sbapt continue; 433286196Sbapt *grp = gr_add(*grp, pwd->pw_name); 434286196Sbapt } 435286196Sbapt} 436286196Sbapt 437286196Sbaptint 438286196Sbaptgroupadd(struct userconf *cnf, char *name, gid_t id, char *members, int fd, 439286196Sbapt bool dryrun, bool pretty, bool precrypted) 440286196Sbapt{ 441286196Sbapt struct group *grp; 442286196Sbapt int rc; 443286196Sbapt 444286196Sbapt struct group fakegroup = { 445286196Sbapt "nogroup", 446286196Sbapt "*", 447286196Sbapt -1, 448286196Sbapt NULL 449286196Sbapt }; 450286196Sbapt 451286196Sbapt grp = &fakegroup; 452286196Sbapt grp->gr_name = pw_checkname(name, 0); 453286196Sbapt grp->gr_passwd = "*"; 454286196Sbapt grp->gr_gid = gr_gidpolicy(cnf, id); 455286196Sbapt grp->gr_mem = NULL; 456286196Sbapt 457286196Sbapt /* 458286196Sbapt * This allows us to set a group password Group passwords is an 459286196Sbapt * antique idea, rarely used and insecure (no secure database) Should 460286196Sbapt * be discouraged, but it is apparently still supported by some 461286196Sbapt * software. 462286196Sbapt */ 463286196Sbapt grp_set_passwd(grp, false, fd, precrypted); 464286196Sbapt grp_add_members(&grp, members); 465286196Sbapt if (dryrun) 466286196Sbapt return (print_group(grp, pretty)); 467286196Sbapt 468286196Sbapt if ((rc = addgrent(grp)) != 0) { 469286196Sbapt if (rc == -1) 470286196Sbapt errx(EX_IOERR, "group '%s' already exists", 471286196Sbapt grp->gr_name); 472286196Sbapt else 473286196Sbapt err(EX_IOERR, "group update"); 474286196Sbapt } 475286196Sbapt 476286196Sbapt pw_log(cnf, M_ADD, W_GROUP, "%s(%ju)", grp->gr_name, 477286196Sbapt (uintmax_t)grp->gr_gid); 478286196Sbapt 479286196Sbapt return (EXIT_SUCCESS); 480286196Sbapt} 481286196Sbapt 482286196Sbaptint 483286196Sbaptpw_group_add(int argc, char **argv, char *arg1) 484286196Sbapt{ 485286196Sbapt struct userconf *cnf = NULL; 486286196Sbapt char *name = NULL; 487286196Sbapt char *members = NULL; 488286196Sbapt const char *cfg = NULL; 489286196Sbapt intmax_t id = -1; 490286196Sbapt int ch, rc, fd = -1; 491286196Sbapt bool quiet, precrypted, dryrun, pretty, nis; 492286196Sbapt 493286196Sbapt quiet = precrypted = dryrun = pretty = nis = false; 494286196Sbapt 495286196Sbapt if (arg1 != NULL) { 496286259Sed if (arg1[strspn(arg1, "0123456789")] == '\0') 497286196Sbapt id = pw_checkid(arg1, GID_MAX); 498286196Sbapt else 499286196Sbapt name = arg1; 500286196Sbapt } 501286196Sbapt 502286196Sbapt while ((ch = getopt(argc, argv, "C:qn:g:h:H:M:oNPY")) != -1) { 503286196Sbapt switch (ch) { 504286196Sbapt case 'C': 505286196Sbapt cfg = optarg; 506286196Sbapt break; 507286196Sbapt case 'q': 508286196Sbapt quiet = true; 509286196Sbapt break; 510286196Sbapt case 'n': 511286196Sbapt name = optarg; 512286196Sbapt break; 513286196Sbapt case 'g': 514286196Sbapt id = pw_checkid(optarg, GID_MAX); 515286196Sbapt break; 516286196Sbapt case 'H': 517286196Sbapt if (fd != -1) 518286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 519286196Sbapt "exclusive options"); 520286196Sbapt fd = pw_checkfd(optarg); 521286196Sbapt precrypted = true; 522286196Sbapt if (fd == '-') 523286196Sbapt errx(EX_USAGE, "-H expects a file descriptor"); 524286196Sbapt break; 525286196Sbapt case 'h': 526286196Sbapt if (fd != -1) 527286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 528286196Sbapt "exclusive options"); 529286196Sbapt fd = pw_checkfd(optarg); 530286196Sbapt break; 531286196Sbapt case 'M': 532286196Sbapt members = optarg; 533286196Sbapt break; 534286196Sbapt case 'o': 535286196Sbapt conf.checkduplicate = false; 536286196Sbapt break; 537286196Sbapt case 'N': 538286196Sbapt dryrun = true; 539286196Sbapt break; 540286196Sbapt case 'P': 541286196Sbapt pretty = true; 542286196Sbapt break; 543286196Sbapt case 'Y': 544286196Sbapt nis = true; 545286196Sbapt break; 546286196Sbapt } 547286196Sbapt } 548286196Sbapt 549286196Sbapt if (quiet) 550286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 551286196Sbapt if (name == NULL) 552286196Sbapt errx(EX_DATAERR, "group name required"); 553286199Sbapt if (GETGRNAM(name) != NULL) 554286199Sbapt errx(EX_DATAERR, "group name `%s' already exists", name); 555286196Sbapt cnf = get_userconfig(cfg); 556286196Sbapt rc = groupadd(cnf, name, gr_gidpolicy(cnf, id), members, fd, dryrun, 557286196Sbapt pretty, precrypted); 558286196Sbapt if (nis && rc == EXIT_SUCCESS && nis_update() == 0) 559286196Sbapt pw_log(cnf, M_ADD, W_GROUP, "NIS maps updated"); 560286196Sbapt 561286196Sbapt return (rc); 562286196Sbapt} 563286196Sbapt 564286196Sbaptint 565286196Sbaptpw_group_mod(int argc, char **argv, char *arg1) 566286196Sbapt{ 567286196Sbapt struct userconf *cnf; 568286196Sbapt struct group *grp = NULL; 569286196Sbapt const char *cfg = NULL; 570286196Sbapt char *oldmembers = NULL; 571286196Sbapt char *members = NULL; 572286196Sbapt char *newmembers = NULL; 573286196Sbapt char *newname = NULL; 574286196Sbapt char *name = NULL; 575286196Sbapt intmax_t id = -1; 576286196Sbapt int ch, rc, fd = -1; 577286196Sbapt bool quiet, pretty, dryrun, nis, precrypted; 578286196Sbapt 579286196Sbapt quiet = pretty = dryrun = nis = precrypted = false; 580286196Sbapt 581286196Sbapt if (arg1 != NULL) { 582286259Sed if (arg1[strspn(arg1, "0123456789")] == '\0') 583286196Sbapt id = pw_checkid(arg1, GID_MAX); 584286196Sbapt else 585286196Sbapt name = arg1; 586286196Sbapt } 587286196Sbapt 588286196Sbapt while ((ch = getopt(argc, argv, "C:qn:d:g:l:h:H:M:m:NPY")) != -1) { 589286196Sbapt switch (ch) { 590286196Sbapt case 'C': 591286196Sbapt cfg = optarg; 592286196Sbapt break; 593286196Sbapt case 'q': 594286196Sbapt quiet = true; 595286196Sbapt break; 596286196Sbapt case 'n': 597286196Sbapt name = optarg; 598286196Sbapt break; 599286196Sbapt case 'g': 600286196Sbapt id = pw_checkid(optarg, GID_MAX); 601286196Sbapt break; 602286196Sbapt case 'd': 603286196Sbapt oldmembers = optarg; 604286196Sbapt break; 605286196Sbapt case 'l': 606286196Sbapt newname = optarg; 607286196Sbapt break; 608286196Sbapt case 'H': 609286196Sbapt if (fd != -1) 610286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 611286196Sbapt "exclusive options"); 612286196Sbapt fd = pw_checkfd(optarg); 613286196Sbapt precrypted = true; 614286196Sbapt if (fd == '-') 615286196Sbapt errx(EX_USAGE, "-H expects a file descriptor"); 616286196Sbapt break; 617286196Sbapt case 'h': 618286196Sbapt if (fd != -1) 619286196Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 620286196Sbapt "exclusive options"); 621286196Sbapt fd = pw_checkfd(optarg); 622286196Sbapt break; 623286196Sbapt case 'M': 624286196Sbapt members = optarg; 625286196Sbapt break; 626286196Sbapt case 'm': 627286196Sbapt newmembers = optarg; 628286196Sbapt break; 629286196Sbapt case 'N': 630286196Sbapt dryrun = true; 631286196Sbapt break; 632286196Sbapt case 'P': 633286196Sbapt pretty = true; 634286196Sbapt break; 635286196Sbapt case 'Y': 636286196Sbapt nis = true; 637286196Sbapt break; 638286196Sbapt } 639286196Sbapt } 640286196Sbapt if (quiet) 641286196Sbapt freopen(_PATH_DEVNULL, "w", stderr); 642286196Sbapt cnf = get_userconfig(cfg); 643286196Sbapt grp = getgroup(name, id, true); 644286196Sbapt if (name == NULL) 645286196Sbapt name = grp->gr_name; 646286196Sbapt if (id > 0) 647286196Sbapt grp->gr_gid = id; 648286196Sbapt 649286196Sbapt if (newname != NULL) 650286196Sbapt grp->gr_name = pw_checkname(newname, 0); 651286196Sbapt 652286196Sbapt grp_set_passwd(grp, true, fd, precrypted); 653286196Sbapt /* 654286196Sbapt * Keep the same logic as old code for now: 655286196Sbapt * if -M is passed, -d and -m are ignored 656286196Sbapt * then id -d, -m is ignored 657286196Sbapt * last is -m 658286196Sbapt */ 659286196Sbapt 660286196Sbapt if (members) { 661286196Sbapt grp->gr_mem = NULL; 662286196Sbapt grp_add_members(&grp, members); 663286196Sbapt } else if (oldmembers) { 664286196Sbapt delete_members(grp, oldmembers); 665286196Sbapt } else if (newmembers) { 666286196Sbapt grp_add_members(&grp, newmembers); 667286196Sbapt } 668286196Sbapt 669292846Sbapt if (dryrun) { 670292846Sbapt print_group(grp, pretty); 671292846Sbapt return (EXIT_SUCCESS); 672292846Sbapt } 673292846Sbapt 674286196Sbapt if ((rc = chggrent(name, grp)) != 0) { 675286196Sbapt if (rc == -1) 676286196Sbapt errx(EX_IOERR, "group '%s' not available (NIS?)", 677286196Sbapt grp->gr_name); 678286196Sbapt else 679286196Sbapt err(EX_IOERR, "group update"); 680286196Sbapt } 681286196Sbapt 682286196Sbapt if (newname) 683286196Sbapt name = newname; 684286196Sbapt 685286196Sbapt /* grp may have been invalidated */ 686286196Sbapt if ((grp = GETGRNAM(name)) == NULL) 687286196Sbapt errx(EX_SOFTWARE, "group disappeared during update"); 688286196Sbapt 689286196Sbapt pw_log(cnf, M_UPDATE, W_GROUP, "%s(%ju)", grp->gr_name, 690286196Sbapt (uintmax_t)grp->gr_gid); 691286196Sbapt 692286196Sbapt if (nis && nis_update() == 0) 693286196Sbapt pw_log(cnf, M_UPDATE, W_GROUP, "NIS maps updated"); 694286196Sbapt 695286196Sbapt return (EXIT_SUCCESS); 696286196Sbapt} 697