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: stable/10/usr.sbin/pw/pw_group.c 309881 2016-12-12 07:03:10Z bapt $"; 3030259Scharnier#endif /* not lint */ 3130259Scharnier 3220253Sjoerg#include <ctype.h> 3330259Scharnier#include <err.h> 34287084Sbapt#include <grp.h> 35287084Sbapt#include <libutil.h> 36287084Sbapt#include <paths.h> 37287084Sbapt#include <string.h> 38287084Sbapt#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); 46287084Sbaptstatic void delete_members(struct group *grp, char *list); 47287084Sbaptstatic int print_group(struct group * grp, bool pretty); 48287084Sbaptstatic gid_t gr_gidpolicy(struct userconf * cnf, intmax_t id); 4920253Sjoerg 50287084Sbaptstatic void 51287084Sbaptgrp_set_passwd(struct group *grp, bool update, int fd, bool precrypted) 5220253Sjoerg{ 53287084Sbapt int b; 54287084Sbapt int istty; 55287084Sbapt struct termios t, n; 56287084Sbapt char *p, line[256]; 5720253Sjoerg 58287084Sbapt if (fd == -1) 59287084Sbapt return; 6020253Sjoerg 61287084Sbapt if (fd == '-') { 62287084Sbapt grp->gr_passwd = "*"; /* No access */ 63287084Sbapt return; 6420267Sjoerg } 65287084Sbapt 66287084Sbapt if ((istty = isatty(fd))) { 67287084Sbapt n = t; 68287084Sbapt /* Disable echo */ 69287084Sbapt n.c_lflag &= ~(ECHO); 70287084Sbapt tcsetattr(fd, TCSANOW, &n); 71287084Sbapt printf("%sassword for group %s:", update ? "New p" : "P", 72287084Sbapt grp->gr_name); 73287084Sbapt fflush(stdout); 7420253Sjoerg } 75287084Sbapt b = read(fd, line, sizeof(line) - 1); 76287084Sbapt if (istty) { /* Restore state */ 77287084Sbapt tcsetattr(fd, TCSANOW, &t); 78287084Sbapt fputc('\n', stdout); 79287084Sbapt fflush(stdout); 8020253Sjoerg } 81287084Sbapt if (b < 0) 82287084Sbapt err(EX_OSERR, "-h file descriptor"); 83287084Sbapt line[b] = '\0'; 84287084Sbapt if ((p = strpbrk(line, " \t\r\n")) != NULL) 85287084Sbapt *p = '\0'; 86287084Sbapt if (!*line) 87287084Sbapt errx(EX_DATAERR, "empty password read on file descriptor %d", 88287084Sbapt conf.fd); 89287084Sbapt if (precrypted) { 90287084Sbapt if (strchr(line, ':') != 0) 91287084Sbapt errx(EX_DATAERR, "wrong encrypted passwrd"); 92287084Sbapt grp->gr_passwd = line; 93287084Sbapt } else 94287084Sbapt grp->gr_passwd = pw_pwcrypt(line); 95287084Sbapt} 9620253Sjoerg 97287084Sbaptint 98287084Sbaptpw_groupnext(struct userconf *cnf, bool quiet) 99287084Sbapt{ 100287084Sbapt gid_t next = gr_gidpolicy(cnf, -1); 10120253Sjoerg 102287084Sbapt if (quiet) 103287084Sbapt return (next); 104287084Sbapt printf("%ju\n", (uintmax_t)next); 10520253Sjoerg 106287084Sbapt return (EXIT_SUCCESS); 107287084Sbapt} 10820253Sjoerg 109287084Sbaptstatic struct group * 110287084Sbaptgetgroup(char *name, intmax_t id, bool fatal) 111287084Sbapt{ 112287084Sbapt struct group *grp; 11320267Sjoerg 114287084Sbapt if (id < 0 && name == NULL) 115287084Sbapt errx(EX_DATAERR, "groupname or id required"); 116287084Sbapt grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 117287084Sbapt if (grp == NULL) { 118287084Sbapt if (!fatal) 119287084Sbapt return (NULL); 120287084Sbapt if (name == NULL) 121287084Sbapt errx(EX_DATAERR, "unknown gid `%ju'", id); 122287084Sbapt errx(EX_DATAERR, "unknown group `%s'", name); 12320267Sjoerg } 124287084Sbapt return (grp); 12520253Sjoerg} 12620253Sjoerg 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 148287084Sbaptdelete_members(struct group *grp, char *list) 149176474Sscf{ 150287084Sbapt char *p; 151176474Sscf int k; 152176474Sscf 153272192Sdteske if (grp->gr_mem == NULL) 154272192Sdteske return; 155272192Sdteske 156287084Sbapt for (p = strtok(list, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { 157287084Sbapt for (k = 0; grp->gr_mem[k] != NULL; k++) { 158287084Sbapt if (strcmp(grp->gr_mem[k], p) == 0) 159176474Sscf break; 160176474Sscf } 161287084Sbapt if (grp->gr_mem[k] == NULL) /* No match */ 162287084Sbapt continue; 163176474Sscf 164287084Sbapt for (; grp->gr_mem[k] != NULL; k++) 165287084Sbapt grp->gr_mem[k] = grp->gr_mem[k+1]; 166176474Sscf } 167176474Sscf} 168176474Sscf 169287084Sbaptstatic gid_t 170287084Sbaptgr_gidpolicy(struct userconf * cnf, intmax_t id) 17120253Sjoerg{ 17220253Sjoerg struct group *grp; 173287084Sbapt struct bitmap bm; 17420253Sjoerg gid_t gid = (gid_t) - 1; 17520253Sjoerg 17620253Sjoerg /* 17720253Sjoerg * Check the given gid, if any 17820253Sjoerg */ 179285092Sbapt if (id > 0) { 180285092Sbapt gid = (gid_t) id; 18120253Sjoerg 182285092Sbapt if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) 183287084Sbapt errx(EX_DATAERR, "gid `%ju' has already been allocated", 184287084Sbapt (uintmax_t)grp->gr_gid); 185287084Sbapt return (gid); 186287084Sbapt } 18720253Sjoerg 188287084Sbapt /* 189287084Sbapt * We need to allocate the next available gid under one of 190287084Sbapt * two policies a) Grab the first unused gid b) Grab the 191287084Sbapt * highest possible unused gid 192287084Sbapt */ 193287084Sbapt if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 194287084Sbapt cnf->min_gid = 1000; 195287084Sbapt cnf->max_gid = 32000; 196287084Sbapt } 197287084Sbapt bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 19820253Sjoerg 199287084Sbapt /* 200287084Sbapt * Now, let's fill the bitmap from the password file 201287084Sbapt */ 202287084Sbapt SETGRENT(); 203287084Sbapt while ((grp = GETGRENT()) != NULL) 204287084Sbapt if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && 205287084Sbapt (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) 206287084Sbapt bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 207287084Sbapt ENDGRENT(); 20820253Sjoerg 209287084Sbapt /* 210287084Sbapt * Then apply the policy, with fallback to reuse if necessary 211287084Sbapt */ 212287084Sbapt if (cnf->reuse_gids) 213287084Sbapt gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 214287084Sbapt else { 215287084Sbapt gid = (gid_t) (bm_lastset(&bm) + 1); 216287084Sbapt if (!bm_isset(&bm, gid)) 217287084Sbapt gid += cnf->min_gid; 218287084Sbapt else 21920253Sjoerg gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 220287084Sbapt } 22120253Sjoerg 222287084Sbapt /* 223287084Sbapt * Another sanity check 224287084Sbapt */ 225287084Sbapt if (gid < cnf->min_gid || gid > cnf->max_gid) 226287084Sbapt errx(EX_SOFTWARE, "unable to allocate a new gid - range fully " 227287084Sbapt "used"); 228287084Sbapt bm_dealloc(&bm); 229287084Sbapt return (gid); 23020253Sjoerg} 23120253Sjoerg 23220253Sjoergstatic int 233287084Sbaptprint_group(struct group * grp, bool pretty) 23420253Sjoerg{ 235287084Sbapt char *buf = NULL; 236287084Sbapt int i; 23720253Sjoerg 238287084Sbapt if (pretty) { 23922398Sdavidn printf("Group Name: %-15s #%lu\n" 24020747Sdavidn " Members: ", 24120253Sjoerg grp->gr_name, (long) grp->gr_gid); 242272192Sdteske if (grp->gr_mem != NULL) { 243272192Sdteske for (i = 0; grp->gr_mem[i]; i++) 244272192Sdteske printf("%s%s", i ? "," : "", grp->gr_mem[i]); 245272192Sdteske } 24620253Sjoerg fputs("\n\n", stdout); 247287084Sbapt return (EXIT_SUCCESS); 24820253Sjoerg } 249287084Sbapt 250287084Sbapt buf = gr_make(grp); 251287084Sbapt printf("%s\n", buf); 252287084Sbapt free(buf); 253287084Sbapt return (EXIT_SUCCESS); 25420253Sjoerg} 255287084Sbapt 256287084Sbaptint 257287084Sbaptpw_group_next(int argc, char **argv, char *arg1 __unused) 258287084Sbapt{ 259287084Sbapt struct userconf *cnf; 260287084Sbapt const char *cfg = NULL; 261287084Sbapt int ch; 262289978Sngie bool quiet = false; 263287084Sbapt 264287084Sbapt while ((ch = getopt(argc, argv, "Cq")) != -1) { 265287084Sbapt switch (ch) { 266287084Sbapt case 'C': 267287084Sbapt cfg = optarg; 268287084Sbapt break; 269287084Sbapt case 'q': 270287084Sbapt quiet = true; 271287084Sbapt break; 272287084Sbapt } 273287084Sbapt } 274287084Sbapt 275287084Sbapt if (quiet) 276287084Sbapt freopen(_PATH_DEVNULL, "w", stderr); 277287084Sbapt cnf = get_userconfig(cfg); 278287084Sbapt return (pw_groupnext(cnf, quiet)); 279287084Sbapt} 280287084Sbapt 281287084Sbaptint 282287084Sbaptpw_group_show(int argc, char **argv, char *arg1) 283287084Sbapt{ 284287084Sbapt struct group *grp = NULL; 285309881Sbapt char *name = NULL; 286287084Sbapt intmax_t id = -1; 287287084Sbapt int ch; 288287084Sbapt bool all, force, quiet, pretty; 289287084Sbapt 290287084Sbapt all = force = quiet = pretty = false; 291287084Sbapt 292287084Sbapt struct group fakegroup = { 293287084Sbapt "nogroup", 294287084Sbapt "*", 295287084Sbapt -1, 296287084Sbapt NULL 297287084Sbapt }; 298287084Sbapt 299287084Sbapt if (arg1 != NULL) { 300287084Sbapt if (arg1[strspn(arg1, "0123456789")] == '\0') 301287084Sbapt id = pw_checkid(arg1, GID_MAX); 302287084Sbapt else 303287084Sbapt name = arg1; 304287084Sbapt } 305287084Sbapt 306287084Sbapt while ((ch = getopt(argc, argv, "C:qn:g:FPa")) != -1) { 307287084Sbapt switch (ch) { 308287084Sbapt case 'C': 309287084Sbapt /* ignore compatibility */ 310287084Sbapt break; 311287084Sbapt case 'q': 312287084Sbapt quiet = true; 313287084Sbapt break; 314287084Sbapt case 'n': 315287084Sbapt name = optarg; 316287084Sbapt break; 317287084Sbapt case 'g': 318287084Sbapt id = pw_checkid(optarg, GID_MAX); 319287084Sbapt break; 320287084Sbapt case 'F': 321287084Sbapt force = true; 322287084Sbapt break; 323287084Sbapt case 'P': 324287084Sbapt pretty = true; 325287084Sbapt break; 326287084Sbapt case 'a': 327287084Sbapt all = true; 328287084Sbapt break; 329287084Sbapt } 330287084Sbapt } 331287084Sbapt 332287084Sbapt if (quiet) 333287084Sbapt freopen(_PATH_DEVNULL, "w", stderr); 334287084Sbapt 335287084Sbapt if (all) { 336287084Sbapt SETGRENT(); 337287084Sbapt while ((grp = GETGRENT()) != NULL) 338287084Sbapt print_group(grp, pretty); 339287084Sbapt ENDGRENT(); 340287084Sbapt return (EXIT_SUCCESS); 341287084Sbapt } 342287084Sbapt 343287084Sbapt grp = getgroup(name, id, !force); 344287084Sbapt if (grp == NULL) 345287084Sbapt grp = &fakegroup; 346287084Sbapt 347287084Sbapt return (print_group(grp, pretty)); 348287084Sbapt} 349287084Sbapt 350287084Sbaptint 351287084Sbaptpw_group_del(int argc, char **argv, char *arg1) 352287084Sbapt{ 353287084Sbapt struct userconf *cnf = NULL; 354287084Sbapt struct group *grp = NULL; 355287084Sbapt char *name; 356287084Sbapt const char *cfg = NULL; 357287084Sbapt intmax_t id = -1; 358287084Sbapt int ch, rc; 359287084Sbapt bool quiet = false; 360287084Sbapt bool nis = false; 361287084Sbapt 362287084Sbapt if (arg1 != NULL) { 363287084Sbapt if (arg1[strspn(arg1, "0123456789")] == '\0') 364287084Sbapt id = pw_checkid(arg1, GID_MAX); 365287084Sbapt else 366287084Sbapt name = arg1; 367287084Sbapt } 368287084Sbapt 369287084Sbapt while ((ch = getopt(argc, argv, "C:qn:g:Y")) != -1) { 370287084Sbapt switch (ch) { 371287084Sbapt case 'C': 372287084Sbapt cfg = optarg; 373287084Sbapt break; 374287084Sbapt case 'q': 375287084Sbapt quiet = true; 376287084Sbapt break; 377287084Sbapt case 'n': 378287084Sbapt name = optarg; 379287084Sbapt break; 380287084Sbapt case 'g': 381287084Sbapt id = pw_checkid(optarg, GID_MAX); 382287084Sbapt break; 383287084Sbapt case 'Y': 384287084Sbapt nis = true; 385287084Sbapt break; 386287084Sbapt } 387287084Sbapt } 388287084Sbapt 389287084Sbapt if (quiet) 390287084Sbapt freopen(_PATH_DEVNULL, "w", stderr); 391287084Sbapt grp = getgroup(name, id, true); 392287084Sbapt cnf = get_userconfig(cfg); 393287084Sbapt rc = delgrent(grp); 394287084Sbapt if (rc == -1) 395287084Sbapt err(EX_IOERR, "group '%s' not available (NIS?)", name); 396287084Sbapt else if (rc != 0) 397287084Sbapt err(EX_IOERR, "group update"); 398287084Sbapt pw_log(cnf, M_DELETE, W_GROUP, "%s(%ju) removed", name, 399287084Sbapt (uintmax_t)id); 400287084Sbapt 401287084Sbapt if (nis && nis_update() == 0) 402287084Sbapt pw_log(cnf, M_DELETE, W_GROUP, "NIS maps updated"); 403287084Sbapt 404287084Sbapt return (EXIT_SUCCESS); 405287084Sbapt} 406287084Sbapt 407287084Sbaptstatic bool 408287084Sbaptgrp_has_member(struct group *grp, const char *name) 409287084Sbapt{ 410287084Sbapt int j; 411287084Sbapt 412287084Sbapt for (j = 0; grp->gr_mem != NULL && grp->gr_mem[j] != NULL; j++) 413287084Sbapt if (strcmp(grp->gr_mem[j], name) == 0) 414287084Sbapt return (true); 415287084Sbapt return (false); 416287084Sbapt} 417287084Sbapt 418287084Sbaptstatic void 419287084Sbaptgrp_add_members(struct group **grp, char *members) 420287084Sbapt{ 421287084Sbapt struct passwd *pwd; 422287084Sbapt char *p; 423287084Sbapt char tok[] = ", \t"; 424287084Sbapt 425287084Sbapt if (members == NULL) 426287084Sbapt return; 427287084Sbapt for (p = strtok(members, tok); p != NULL; p = strtok(NULL, tok)) { 428287084Sbapt pwd = lookup_pwent(p); 429287084Sbapt if (grp_has_member(*grp, pwd->pw_name)) 430287084Sbapt continue; 431287084Sbapt *grp = gr_add(*grp, pwd->pw_name); 432287084Sbapt } 433287084Sbapt} 434287084Sbapt 435287084Sbaptint 436287084Sbaptgroupadd(struct userconf *cnf, char *name, gid_t id, char *members, int fd, 437287084Sbapt bool dryrun, bool pretty, bool precrypted) 438287084Sbapt{ 439287084Sbapt struct group *grp; 440287084Sbapt int rc; 441287084Sbapt 442287084Sbapt struct group fakegroup = { 443287084Sbapt "nogroup", 444287084Sbapt "*", 445287084Sbapt -1, 446287084Sbapt NULL 447287084Sbapt }; 448287084Sbapt 449287084Sbapt grp = &fakegroup; 450287084Sbapt grp->gr_name = pw_checkname(name, 0); 451287084Sbapt grp->gr_passwd = "*"; 452287084Sbapt grp->gr_gid = gr_gidpolicy(cnf, id); 453287084Sbapt grp->gr_mem = NULL; 454287084Sbapt 455287084Sbapt /* 456287084Sbapt * This allows us to set a group password Group passwords is an 457287084Sbapt * antique idea, rarely used and insecure (no secure database) Should 458287084Sbapt * be discouraged, but it is apparently still supported by some 459287084Sbapt * software. 460287084Sbapt */ 461287084Sbapt grp_set_passwd(grp, false, fd, precrypted); 462287084Sbapt grp_add_members(&grp, members); 463287084Sbapt if (dryrun) 464287084Sbapt return (print_group(grp, pretty)); 465287084Sbapt 466287084Sbapt if ((rc = addgrent(grp)) != 0) { 467287084Sbapt if (rc == -1) 468287084Sbapt errx(EX_IOERR, "group '%s' already exists", 469287084Sbapt grp->gr_name); 470287084Sbapt else 471287084Sbapt err(EX_IOERR, "group update"); 472287084Sbapt } 473287084Sbapt 474287084Sbapt pw_log(cnf, M_ADD, W_GROUP, "%s(%ju)", grp->gr_name, 475287084Sbapt (uintmax_t)grp->gr_gid); 476287084Sbapt 477287084Sbapt return (EXIT_SUCCESS); 478287084Sbapt} 479287084Sbapt 480287084Sbaptint 481287084Sbaptpw_group_add(int argc, char **argv, char *arg1) 482287084Sbapt{ 483287084Sbapt struct userconf *cnf = NULL; 484287084Sbapt char *name = NULL; 485287084Sbapt char *members = NULL; 486287084Sbapt const char *cfg = NULL; 487287084Sbapt intmax_t id = -1; 488287084Sbapt int ch, rc, fd = -1; 489287084Sbapt bool quiet, precrypted, dryrun, pretty, nis; 490287084Sbapt 491287084Sbapt quiet = precrypted = dryrun = pretty = nis = false; 492287084Sbapt 493287084Sbapt if (arg1 != NULL) { 494287084Sbapt if (arg1[strspn(arg1, "0123456789")] == '\0') 495287084Sbapt id = pw_checkid(arg1, GID_MAX); 496287084Sbapt else 497287084Sbapt name = arg1; 498287084Sbapt } 499287084Sbapt 500287084Sbapt while ((ch = getopt(argc, argv, "C:qn:g:h:H:M:oNPY")) != -1) { 501287084Sbapt switch (ch) { 502287084Sbapt case 'C': 503287084Sbapt cfg = optarg; 504287084Sbapt break; 505287084Sbapt case 'q': 506287084Sbapt quiet = true; 507287084Sbapt break; 508287084Sbapt case 'n': 509287084Sbapt name = optarg; 510287084Sbapt break; 511287084Sbapt case 'g': 512287084Sbapt id = pw_checkid(optarg, GID_MAX); 513287084Sbapt break; 514287084Sbapt case 'H': 515287084Sbapt if (fd != -1) 516287084Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 517287084Sbapt "exclusive options"); 518287084Sbapt fd = pw_checkfd(optarg); 519287084Sbapt precrypted = true; 520287084Sbapt if (fd == '-') 521287084Sbapt errx(EX_USAGE, "-H expects a file descriptor"); 522287084Sbapt break; 523287084Sbapt case 'h': 524287084Sbapt if (fd != -1) 525287084Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 526287084Sbapt "exclusive options"); 527287084Sbapt fd = pw_checkfd(optarg); 528287084Sbapt break; 529287084Sbapt case 'M': 530287084Sbapt members = optarg; 531287084Sbapt break; 532287084Sbapt case 'o': 533287084Sbapt conf.checkduplicate = false; 534287084Sbapt break; 535287084Sbapt case 'N': 536287084Sbapt dryrun = true; 537287084Sbapt break; 538287084Sbapt case 'P': 539287084Sbapt pretty = true; 540287084Sbapt break; 541287084Sbapt case 'Y': 542287084Sbapt nis = true; 543287084Sbapt break; 544287084Sbapt } 545287084Sbapt } 546287084Sbapt 547287084Sbapt if (quiet) 548287084Sbapt freopen(_PATH_DEVNULL, "w", stderr); 549287084Sbapt if (name == NULL) 550287084Sbapt errx(EX_DATAERR, "group name required"); 551287084Sbapt if (GETGRNAM(name) != NULL) 552287084Sbapt errx(EX_DATAERR, "group name `%s' already exists", name); 553287084Sbapt cnf = get_userconfig(cfg); 554287084Sbapt rc = groupadd(cnf, name, gr_gidpolicy(cnf, id), members, fd, dryrun, 555287084Sbapt pretty, precrypted); 556287084Sbapt if (nis && rc == EXIT_SUCCESS && nis_update() == 0) 557287084Sbapt pw_log(cnf, M_ADD, W_GROUP, "NIS maps updated"); 558287084Sbapt 559287084Sbapt return (rc); 560287084Sbapt} 561287084Sbapt 562287084Sbaptint 563287084Sbaptpw_group_mod(int argc, char **argv, char *arg1) 564287084Sbapt{ 565287084Sbapt struct userconf *cnf; 566287084Sbapt struct group *grp = NULL; 567287084Sbapt const char *cfg = NULL; 568287084Sbapt char *oldmembers = NULL; 569287084Sbapt char *members = NULL; 570287084Sbapt char *newmembers = NULL; 571287084Sbapt char *newname = NULL; 572287084Sbapt char *name = NULL; 573287084Sbapt intmax_t id = -1; 574287084Sbapt int ch, rc, fd = -1; 575287084Sbapt bool quiet, pretty, dryrun, nis, precrypted; 576287084Sbapt 577287084Sbapt quiet = pretty = dryrun = nis = precrypted = false; 578287084Sbapt 579287084Sbapt if (arg1 != NULL) { 580287084Sbapt if (arg1[strspn(arg1, "0123456789")] == '\0') 581287084Sbapt id = pw_checkid(arg1, GID_MAX); 582287084Sbapt else 583287084Sbapt name = arg1; 584287084Sbapt } 585287084Sbapt 586287084Sbapt while ((ch = getopt(argc, argv, "C:qn:d:g:l:h:H:M:m:NPY")) != -1) { 587287084Sbapt switch (ch) { 588287084Sbapt case 'C': 589287084Sbapt cfg = optarg; 590287084Sbapt break; 591287084Sbapt case 'q': 592287084Sbapt quiet = true; 593287084Sbapt break; 594287084Sbapt case 'n': 595287084Sbapt name = optarg; 596287084Sbapt break; 597287084Sbapt case 'g': 598287084Sbapt id = pw_checkid(optarg, GID_MAX); 599287084Sbapt break; 600287084Sbapt case 'd': 601287084Sbapt oldmembers = optarg; 602287084Sbapt break; 603287084Sbapt case 'l': 604287084Sbapt newname = optarg; 605287084Sbapt break; 606287084Sbapt case 'H': 607287084Sbapt if (fd != -1) 608287084Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 609287084Sbapt "exclusive options"); 610287084Sbapt fd = pw_checkfd(optarg); 611287084Sbapt precrypted = true; 612287084Sbapt if (fd == '-') 613287084Sbapt errx(EX_USAGE, "-H expects a file descriptor"); 614287084Sbapt break; 615287084Sbapt case 'h': 616287084Sbapt if (fd != -1) 617287084Sbapt errx(EX_USAGE, "'-h' and '-H' are mutually " 618287084Sbapt "exclusive options"); 619287084Sbapt fd = pw_checkfd(optarg); 620287084Sbapt break; 621287084Sbapt case 'M': 622287084Sbapt members = optarg; 623287084Sbapt break; 624287084Sbapt case 'm': 625287084Sbapt newmembers = optarg; 626287084Sbapt break; 627287084Sbapt case 'N': 628287084Sbapt dryrun = true; 629287084Sbapt break; 630287084Sbapt case 'P': 631287084Sbapt pretty = true; 632287084Sbapt break; 633287084Sbapt case 'Y': 634287084Sbapt nis = true; 635287084Sbapt break; 636287084Sbapt } 637287084Sbapt } 638287084Sbapt if (quiet) 639287084Sbapt freopen(_PATH_DEVNULL, "w", stderr); 640287084Sbapt cnf = get_userconfig(cfg); 641287084Sbapt grp = getgroup(name, id, true); 642287084Sbapt if (name == NULL) 643287084Sbapt name = grp->gr_name; 644287084Sbapt if (id > 0) 645287084Sbapt grp->gr_gid = id; 646287084Sbapt 647287084Sbapt if (newname != NULL) 648287084Sbapt grp->gr_name = pw_checkname(newname, 0); 649287084Sbapt 650287084Sbapt grp_set_passwd(grp, true, fd, precrypted); 651287084Sbapt /* 652287084Sbapt * Keep the same logic as old code for now: 653287084Sbapt * if -M is passed, -d and -m are ignored 654287084Sbapt * then id -d, -m is ignored 655287084Sbapt * last is -m 656287084Sbapt */ 657287084Sbapt 658287084Sbapt if (members) { 659287084Sbapt grp->gr_mem = NULL; 660287084Sbapt grp_add_members(&grp, members); 661287084Sbapt } else if (oldmembers) { 662287084Sbapt delete_members(grp, oldmembers); 663287084Sbapt } else if (newmembers) { 664287084Sbapt grp_add_members(&grp, newmembers); 665287084Sbapt } 666287084Sbapt 667292965Sbapt if (dryrun) { 668292965Sbapt print_group(grp, pretty); 669292965Sbapt return (EXIT_SUCCESS); 670292965Sbapt } 671292965Sbapt 672287084Sbapt if ((rc = chggrent(name, grp)) != 0) { 673287084Sbapt if (rc == -1) 674287084Sbapt errx(EX_IOERR, "group '%s' not available (NIS?)", 675287084Sbapt grp->gr_name); 676287084Sbapt else 677287084Sbapt err(EX_IOERR, "group update"); 678287084Sbapt } 679287084Sbapt 680287084Sbapt if (newname) 681287084Sbapt name = newname; 682287084Sbapt 683287084Sbapt /* grp may have been invalidated */ 684287084Sbapt if ((grp = GETGRNAM(name)) == NULL) 685287084Sbapt errx(EX_SOFTWARE, "group disappeared during update"); 686287084Sbapt 687287084Sbapt pw_log(cnf, M_UPDATE, W_GROUP, "%s(%ju)", grp->gr_name, 688287084Sbapt (uintmax_t)grp->gr_gid); 689287084Sbapt 690287084Sbapt if (nis && nis_update() == 0) 691287084Sbapt pw_log(cnf, M_UPDATE, W_GROUP, "NIS maps updated"); 692287084Sbapt 693287084Sbapt return (EXIT_SUCCESS); 694287084Sbapt} 695