pw_group.c revision 285395
1163953Srrs/*- 2169382Srrs * Copyright (C) 1996 3163953Srrs * David L. Nugent. All rights reserved. 4163953Srrs * 5163953Srrs * Redistribution and use in source and binary forms, with or without 6163953Srrs * modification, are permitted provided that the following conditions 7163953Srrs * are met: 8163953Srrs * 1. Redistributions of source code must retain the above copyright 9163953Srrs * notice, this list of conditions and the following disclaimer. 10163953Srrs * 2. Redistributions in binary form must reproduce the above copyright 11163953Srrs * notice, this list of conditions and the following disclaimer in the 12163953Srrs * documentation and/or other materials provided with the distribution. 13163953Srrs * 14163953Srrs * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 15163953Srrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16163953Srrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17163953Srrs * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 18163953Srrs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19163953Srrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20163953Srrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21163953Srrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22163953Srrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23163953Srrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24163953Srrs * SUCH DAMAGE. 25163953Srrs */ 26163953Srrs 27163953Srrs#ifndef lint 28163953Srrsstatic const char rcsid[] = 29163953Srrs "$FreeBSD: head/usr.sbin/pw/pw_group.c 285395 2015-07-11 16:58:47Z bapt $"; 30163953Srrs#endif /* not lint */ 31163953Srrs 32163953Srrs#include <ctype.h> 33163953Srrs#include <err.h> 34163953Srrs#include <termios.h> 35163953Srrs#include <stdbool.h> 36163953Srrs#include <unistd.h> 37163953Srrs#include <grp.h> 38163953Srrs#include <libutil.h> 39167598Srrs 40167598Srrs#include "pw.h" 41163953Srrs#include "bitmap.h" 42167598Srrs 43168299Srrs 44167598Srrsstatic struct passwd *lookup_pwent(const char *user); 45167598Srrsstatic void delete_members(char ***members, int *grmembers, int *i, 46167598Srrs struct carg *arg, struct group *grp); 47167598Srrsstatic int print_group(struct group * grp); 48168299Srrsstatic gid_t gr_gidpolicy(struct userconf * cnf, long id); 49168299Srrs 50167598Srrsstatic void 51167598Srrsset_passwd(struct group *grp, bool update) 52167598Srrs{ 53168299Srrs int b; 54168299Srrs int istty; 55168299Srrs struct termios t, n; 56168299Srrs char *p, line[256]; 57167598Srrs 58168299Srrs if (conf.fd == '-') { 59167598Srrs grp->gr_passwd = "*"; /* No access */ 60167598Srrs return; 61167598Srrs } 62167598Srrs 63170056Srrs if ((istty = isatty(conf.fd))) { 64170181Srrs n = t; 65170056Srrs /* Disable echo */ 66167598Srrs n.c_lflag &= ~(ECHO); 67167598Srrs tcsetattr(conf.fd, TCSANOW, &n); 68167598Srrs printf("%sassword for group %s:", update ? "New p" : "P", 69167598Srrs grp->gr_name); 70167598Srrs fflush(stdout); 71163953Srrs } 72163953Srrs b = read(conf.fd, line, sizeof(line) - 1); 73163953Srrs if (istty) { /* Restore state */ 74163953Srrs tcsetattr(conf.fd, TCSANOW, &t); 75167598Srrs fputc('\n', stdout); 76167598Srrs fflush(stdout); 77167598Srrs } 78167598Srrs if (b < 0) 79167598Srrs err(EX_OSERR, "-h file descriptor"); 80167598Srrs line[b] = '\0'; 81167598Srrs if ((p = strpbrk(line, " \t\r\n")) != NULL) 82167598Srrs *p = '\0'; 83167598Srrs if (!*line) 84167598Srrs errx(EX_DATAERR, "empty password read on file descriptor %d", 85167598Srrs conf.fd); 86171477Srrs if (conf.precrypted) { 87171477Srrs if (strchr(line, ':') != 0) 88171477Srrs errx(EX_DATAERR, "wrong encrypted passwrd"); 89171477Srrs grp->gr_passwd = line; 90171440Srrs } else 91171440Srrs grp->gr_passwd = pw_pwcrypt(line); 92171440Srrs} 93171440Srrs 94171440Srrsint 95163953Srrspw_groupnext(struct userconf *cnf, bool quiet) 96163953Srrs{ 97163953Srrs gid_t next = gr_gidpolicy(cnf, -1); 98163953Srrs 99163953Srrs if (quiet) 100163953Srrs return (next); 101163953Srrs printf("%u\n", next); 102163953Srrs 103163953Srrs return (EXIT_SUCCESS); 104163953Srrs} 105163953Srrs 106163953Srrsint 107163953Srrspw_group(int mode, char *name, long id, struct cargs * args) 108163953Srrs{ 109163953Srrs int rc; 110163953Srrs struct carg *arg; 111163953Srrs struct group *grp = NULL; 112163953Srrs int grmembers = 0; 113163953Srrs char **members = NULL; 114163953Srrs struct userconf *cnf = conf.userconf; 115163953Srrs 116163953Srrs static struct group fakegroup = 117163953Srrs { 118163953Srrs "nogroup", 119163953Srrs "*", 120163953Srrs -1, 121163953Srrs NULL 122163953Srrs }; 123163953Srrs 124163953Srrs if (mode == M_NEXT) 125163953Srrs return (pw_groupnext(cnf, getarg(args, 'q') != NULL)); 126163953Srrs 127163953Srrs if (mode == M_LOCK || mode == M_UNLOCK) 128163953Srrs errx(EX_USAGE, "'lock' command is not available for groups"); 129163953Srrs 130163953Srrs if (mode == M_PRINT && getarg(args, 'a')) { 131163953Srrs SETGRENT(); 132163953Srrs while ((grp = GETGRENT()) != NULL) 133163953Srrs print_group(grp); 134163953Srrs ENDGRENT(); 135163953Srrs return EXIT_SUCCESS; 136163953Srrs } 137163953Srrs if (id < 0 && name == NULL) 138163953Srrs errx(EX_DATAERR, "group name or id required"); 139163953Srrs 140163953Srrs grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 141163953Srrs 142163953Srrs if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) { 143163953Srrs if (name == NULL && grp == NULL) /* Try harder */ 144163953Srrs grp = GETGRGID(id); 145163953Srrs 146163953Srrs if (grp == NULL) { 147163953Srrs if (mode == M_PRINT && getarg(args, 'F')) { 148163953Srrs char *fmems[1]; 149163953Srrs fmems[0] = NULL; 150163953Srrs fakegroup.gr_name = name ? name : "nogroup"; 151163953Srrs fakegroup.gr_gid = (gid_t) id; 152163953Srrs fakegroup.gr_mem = fmems; 153163953Srrs return print_group(&fakegroup); 154163953Srrs } 155163953Srrs if (name == NULL) 156163953Srrs errx(EX_DATAERR, "unknown group `%s'", name); 157163953Srrs else 158163953Srrs errx(EX_DATAERR, "unknown group `%ld'", id); 159163953Srrs } 160163953Srrs if (name == NULL) /* Needed later */ 161163953Srrs name = grp->gr_name; 162163953Srrs 163163953Srrs /* 164163953Srrs * Handle deletions now 165163953Srrs */ 166163953Srrs if (mode == M_DELETE) { 167163953Srrs rc = delgrent(grp); 168163953Srrs if (rc == -1) 169163953Srrs err(EX_IOERR, "group '%s' not available (NIS?)", 170163953Srrs name); 171163953Srrs else if (rc != 0) { 172163953Srrs err(EX_IOERR, "group update"); 173163953Srrs } 174163953Srrs pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", name, id); 175163953Srrs return EXIT_SUCCESS; 176163953Srrs } else if (mode == M_PRINT) 177163953Srrs return print_group(grp); 178163953Srrs 179163953Srrs if (id > 0) 180163953Srrs grp->gr_gid = (gid_t) id; 181163953Srrs 182163953Srrs if (conf.newname != NULL) 183163953Srrs grp->gr_name = pw_checkname(conf.newname, 0); 184163953Srrs } else { 185163953Srrs if (name == NULL) /* Required */ 186163953Srrs errx(EX_DATAERR, "group name required"); 187163953Srrs else if (grp != NULL) /* Exists */ 188163953Srrs errx(EX_DATAERR, "group name `%s' already exists", name); 189163953Srrs 190163953Srrs extendarray(&members, &grmembers, 200); 191163953Srrs members[0] = NULL; 192163953Srrs grp = &fakegroup; 193163953Srrs grp->gr_name = pw_checkname(name, 0); 194163953Srrs grp->gr_passwd = "*"; 195163953Srrs grp->gr_gid = gr_gidpolicy(cnf, id); 196163953Srrs grp->gr_mem = members; 197163953Srrs } 198164181Srrs 199164181Srrs /* 200168709Srrs * This allows us to set a group password Group passwords is an 201165220Srrs * antique idea, rarely used and insecure (no secure database) Should 202168709Srrs * be discouraged, but it is apparently still supported by some 203168709Srrs * software. 204168709Srrs */ 205168709Srrs 206168709Srrs if (conf.which == W_GROUP && conf.fd != -1) 207168709Srrs set_passwd(grp, mode == M_UPDATE); 208168709Srrs 209168709Srrs if (((arg = getarg(args, 'M')) != NULL || 210169208Srrs (arg = getarg(args, 'd')) != NULL || 211169208Srrs (arg = getarg(args, 'm')) != NULL) && arg->val) { 212169208Srrs int i = 0; 213169208Srrs char *p; 214163953Srrs struct passwd *pwd; 215170744Srrs 216170744Srrs /* Make sure this is not stay NULL with -M "" */ 217170744Srrs extendarray(&members, &grmembers, 200); 218170744Srrs if (arg->ch == 'd') 219163953Srrs delete_members(&members, &grmembers, &i, arg, grp); 220170744Srrs else if (arg->ch == 'm') { 221170744Srrs int k = 0; 222163953Srrs 223163953Srrs if (grp->gr_mem != NULL) { 224163953Srrs while (grp->gr_mem[k] != NULL) { 225163953Srrs if (extendarray(&members, &grmembers, i + 2) != -1) 226163953Srrs members[i++] = grp->gr_mem[k]; 227163953Srrs k++; 228163953Srrs } 229163953Srrs } 230163953Srrs } 231163953Srrs 232163953Srrs if (arg->ch != 'd') 233163953Srrs for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { 234163953Srrs int j; 235163953Srrs 236163953Srrs /* 237163953Srrs * Check for duplicates 238163953Srrs */ 239163953Srrs pwd = lookup_pwent(p); 240163953Srrs for (j = 0; j < i && strcmp(members[j], pwd->pw_name) != 0; j++) 241170744Srrs ; 242170744Srrs if (j == i && extendarray(&members, &grmembers, i + 2) != -1) 243163953Srrs members[i++] = newstr(pwd->pw_name); 244170744Srrs } 245163953Srrs while (i < grmembers) 246163953Srrs members[i++] = NULL; 247163953Srrs grp->gr_mem = members; 248163953Srrs } 249163953Srrs 250163953Srrs if (conf.dryrun) 251163953Srrs return print_group(grp); 252163953Srrs 253163953Srrs if (mode == M_ADD && (rc = addgrent(grp)) != 0) { 254163953Srrs if (rc == -1) 255167598Srrs errx(EX_IOERR, "group '%s' already exists", 256167598Srrs grp->gr_name); 257167598Srrs else 258167598Srrs err(EX_IOERR, "group update"); 259167598Srrs } else if (mode == M_UPDATE && (rc = chggrent(name, grp)) != 0) { 260163953Srrs if (rc == -1) 261163953Srrs errx(EX_IOERR, "group '%s' not available (NIS?)", 262163953Srrs grp->gr_name); 263163953Srrs else 264163953Srrs err(EX_IOERR, "group update"); 265163953Srrs } 266163953Srrs 267163953Srrs if (conf.newname != NULL) 268163953Srrs name = conf.newname; 269163953Srrs /* grp may have been invalidated */ 270163953Srrs if ((grp = GETGRNAM(name)) == NULL) 271163953Srrs errx(EX_SOFTWARE, "group disappeared during update"); 272163953Srrs 273163953Srrs pw_log(cnf, mode, W_GROUP, "%s(%u)", grp->gr_name, grp->gr_gid); 274163953Srrs 275163953Srrs free(members); 276163953Srrs 277163953Srrs return EXIT_SUCCESS; 278163953Srrs} 279163953Srrs 280163953Srrs 281163953Srrs/* 282163953Srrs * Lookup a passwd entry using a name or UID. 283163953Srrs */ 284163953Srrsstatic struct passwd * 285163953Srrslookup_pwent(const char *user) 286163953Srrs{ 287163953Srrs struct passwd *pwd; 288163953Srrs 289163953Srrs if ((pwd = GETPWNAM(user)) == NULL && 290163953Srrs (!isdigit((unsigned char)*user) || 291163953Srrs (pwd = getpwuid((uid_t) atoi(user))) == NULL)) 292163953Srrs errx(EX_NOUSER, "user `%s' does not exist", user); 293163953Srrs 294163953Srrs return (pwd); 295163953Srrs} 296163953Srrs 297163953Srrs 298163953Srrs/* 299163953Srrs * Delete requested members from a group. 300163953Srrs */ 301163953Srrsstatic void 302163953Srrsdelete_members(char ***members, int *grmembers, int *i, struct carg *arg, 303163953Srrs struct group *grp) 304163953Srrs{ 305163953Srrs bool matchFound; 306163953Srrs char *user; 307163953Srrs char *valueCopy; 308163953Srrs char *valuePtr; 309163953Srrs int k; 310163953Srrs struct passwd *pwd; 311163953Srrs 312163953Srrs if (grp->gr_mem == NULL) 313163953Srrs return; 314163953Srrs 315163953Srrs k = 0; 316163953Srrs while (grp->gr_mem[k] != NULL) { 317163953Srrs matchFound = false; 318163953Srrs if ((valueCopy = strdup(arg->val)) == NULL) 319163953Srrs errx(EX_UNAVAILABLE, "out of memory"); 320163953Srrs valuePtr = valueCopy; 321163953Srrs while ((user = strsep(&valuePtr, ", \t")) != NULL) { 322163953Srrs pwd = lookup_pwent(user); 323163953Srrs if (strcmp(grp->gr_mem[k], pwd->pw_name) == 0) { 324163953Srrs matchFound = true; 325163953Srrs break; 326163953Srrs } 327163953Srrs } 328163953Srrs free(valueCopy); 329163953Srrs 330163953Srrs if (!matchFound && 331163953Srrs extendarray(members, grmembers, *i + 2) != -1) 332163953Srrs (*members)[(*i)++] = grp->gr_mem[k]; 333163953Srrs 334163953Srrs k++; 335163953Srrs } 336163953Srrs 337163953Srrs return; 338163953Srrs} 339163953Srrs 340163953Srrs 341163953Srrsstatic gid_t 342163953Srrsgr_gidpolicy(struct userconf * cnf, long id) 343163953Srrs{ 344163953Srrs struct group *grp; 345163953Srrs gid_t gid = (gid_t) - 1; 346163953Srrs 347163953Srrs /* 348163953Srrs * Check the given gid, if any 349163953Srrs */ 350163953Srrs if (id > 0) { 351163953Srrs gid = (gid_t) id; 352163953Srrs 353163953Srrs if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) 354163953Srrs errx(EX_DATAERR, "gid `%u' has already been allocated", grp->gr_gid); 355163953Srrs } else { 356163953Srrs struct bitmap bm; 357163953Srrs 358163953Srrs /* 359163953Srrs * We need to allocate the next available gid under one of 360163953Srrs * two policies a) Grab the first unused gid b) Grab the 361163953Srrs * highest possible unused gid 362163953Srrs */ 363163953Srrs if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 364163953Srrs cnf->min_gid = 1000; 365163953Srrs cnf->max_gid = 32000; 366163953Srrs } 367163953Srrs bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 368163953Srrs 369163953Srrs /* 370163953Srrs * Now, let's fill the bitmap from the password file 371163953Srrs */ 372163953Srrs SETGRENT(); 373163953Srrs while ((grp = GETGRENT()) != NULL) 374163953Srrs if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && 375164205Srrs (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) 376167598Srrs bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 377163953Srrs ENDGRENT(); 378163953Srrs 379163953Srrs /* 380163953Srrs * Then apply the policy, with fallback to reuse if necessary 381163953Srrs */ 382163953Srrs if (cnf->reuse_gids) 383163953Srrs gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 384163953Srrs else { 385163953Srrs gid = (gid_t) (bm_lastset(&bm) + 1); 386163953Srrs if (!bm_isset(&bm, gid)) 387163953Srrs gid += cnf->min_gid; 388163953Srrs else 389163953Srrs gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 390163953Srrs } 391163953Srrs 392163953Srrs /* 393163953Srrs * Another sanity check 394163953Srrs */ 395163953Srrs if (gid < cnf->min_gid || gid > cnf->max_gid) 396163953Srrs errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used"); 397163953Srrs bm_dealloc(&bm); 398163953Srrs } 399163953Srrs return gid; 400163953Srrs} 401163953Srrs 402163953Srrs 403163953Srrsstatic int 404163953Srrsprint_group(struct group * grp) 405163953Srrs{ 406163953Srrs if (!conf.pretty) { 407163953Srrs char *buf = NULL; 408163953Srrs 409163953Srrs buf = gr_make(grp); 410163953Srrs printf("%s\n", buf); 411163953Srrs free(buf); 412163953Srrs } else { 413163953Srrs int i; 414163953Srrs 415163953Srrs printf("Group Name: %-15s #%lu\n" 416163953Srrs " Members: ", 417163953Srrs grp->gr_name, (long) grp->gr_gid); 418163953Srrs if (grp->gr_mem != NULL) { 419163953Srrs for (i = 0; grp->gr_mem[i]; i++) 420163953Srrs printf("%s%s", i ? "," : "", grp->gr_mem[i]); 421163953Srrs } 422163953Srrs fputs("\n\n", stdout); 423163953Srrs } 424163953Srrs return EXIT_SUCCESS; 425163953Srrs} 426163953Srrs