pw_group.c revision 285401
1139825Simp/*- 21541Srgrimes * Copyright (C) 1996 31541Srgrimes * David L. Nugent. All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 71541Srgrimes * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 91541Srgrimes * notice, this list of conditions and the following disclaimer. 101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111541Srgrimes * notice, this list of conditions and the following disclaimer in the 121541Srgrimes * documentation and/or other materials provided with the distribution. 131541Srgrimes * 141541Srgrimes * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 151541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241541Srgrimes * SUCH DAMAGE. 251541Srgrimes */ 261541Srgrimes 271541Srgrimes#ifndef lint 281541Srgrimesstatic const char rcsid[] = 291541Srgrimes "$FreeBSD: head/usr.sbin/pw/pw_group.c 285401 2015-07-11 19:07:47Z bapt $"; 301541Srgrimes#endif /* not lint */ 311541Srgrimes 321541Srgrimes#include <ctype.h> 3350477Speter#include <err.h> 341541Srgrimes#include <termios.h> 351541Srgrimes#include <stdbool.h> 361541Srgrimes#include <unistd.h> 371541Srgrimes#include <grp.h> 381541Srgrimes#include <libutil.h> 3950520Sphk 4034925Sdufault#include "pw.h" 4183366Sjulian#include "bitmap.h" 421541Srgrimes 431541Srgrimes 441541Srgrimesstatic struct passwd *lookup_pwent(const char *user); 451541Srgrimesstatic void delete_members(char ***members, int *grmembers, int *i, 461541Srgrimes struct carg *arg, struct group *grp); 471541Srgrimesstatic int print_group(struct group * grp); 481541Srgrimesstatic gid_t gr_gidpolicy(struct userconf * cnf, long id); 491541Srgrimes 501541Srgrimesstatic void 51126293Sdesset_passwd(struct group *grp, bool update) 521541Srgrimes{ 531541Srgrimes int b; 541541Srgrimes int istty; 558876Srgrimes struct termios t, n; 561541Srgrimes char *p, line[256]; 571541Srgrimes 58122707Strhodes if (conf.fd == '-') { 591541Srgrimes grp->gr_passwd = "*"; /* No access */ 601541Srgrimes return; 611541Srgrimes } 621541Srgrimes 631541Srgrimes if ((istty = isatty(conf.fd))) { 6411863Sphk n = t; 6511865Sphk /* Disable echo */ 661541Srgrimes n.c_lflag &= ~(ECHO); 671541Srgrimes tcsetattr(conf.fd, TCSANOW, &n); 681541Srgrimes printf("%sassword for group %s:", update ? "New p" : "P", 69217616Smdf grp->gr_name); 7011863Sphk fflush(stdout); 7111863Sphk } 7278435Spirzyk b = read(conf.fd, line, sizeof(line) - 1); 7378435Spirzyk if (istty) { /* Restore state */ 7478435Spirzyk tcsetattr(conf.fd, TCSANOW, &t); 75217616Smdf fputc('\n', stdout); 761541Srgrimes fflush(stdout); 7711863Sphk } 7811863Sphk if (b < 0) 7911863Sphk err(EX_OSERR, "-h file descriptor"); 8012910Sphk line[b] = '\0'; 8141728Struckman if ((p = strpbrk(line, " \t\r\n")) != NULL) 8246155Sphk *p = '\0'; 8363212Sabial if (!*line) 84101650Smux errx(EX_DATAERR, "empty password read on file descriptor %d", 85109246Sdillon conf.fd); 86121305Ssilby if (conf.precrypted) { 87244123Spjd if (strchr(line, ':') != 0) 88244123Spjd errx(EX_DATAERR, "wrong encrypted passwrd"); 89187656Sjhb grp->gr_passwd = line; 90196176Sbz } else 91216060Smdf grp->gr_passwd = pw_pwcrypt(line); 92224159Srwatson} 93224159Srwatson 94244101Salfredint 95224159Srwatsonpw_groupnext(struct userconf *cnf, bool quiet) 9611863Sphk{ 9719268Sjulian gid_t next = gr_gidpolicy(cnf, -1); 98109246Sdillon 99109246Sdillon if (quiet) 100109246Sdillon return (next); 101109246Sdillon printf("%u\n", next); 102109246Sdillon 103109246Sdillon return (EXIT_SUCCESS); 104109246Sdillon} 105109246Sdillon 106109246Sdillonstatic int 107109246Sdillonpw_groupshow(const char *name, long id, struct group *fakegroup) 10819268Sjulian{ 10919268Sjulian struct group *grp = NULL; 11019268Sjulian 11134925Sdufault if (id < 0 && name == NULL && !conf.all) 11219268Sjulian errx(EX_DATAERR, "groupname or id or '-a' required"); 11319268Sjulian 11412582Sphk if (conf.all) { 11512582Sphk SETGRENT(); 11680339Sroam while ((grp = GETGRENT()) != NULL) 11780339Sroam print_group(grp); 11880339Sroam ENDGRENT(); 11980339Sroam 12080339Sroam return (EXIT_SUCCESS); 12180339Sroam } 12255205Speter 123217239Sjhb grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 124217239Sjhb if (grp == NULL) { 125219819Sjeff if (conf.force) { 126219819Sjeff grp = fakegroup; 12711863Sphk } else { 128120781Sbms if (name == NULL) 129217915Smdf errx(EX_DATAERR, "unknown gid `%ld'", id); 130217915Smdf errx(EX_DATAERR, "unknown group `%s'", name); 131120781Sbms } 132136404Speter } 133232449Sjmallett 134232449Sjmallett return (print_group(grp)); 135136404Speter} 136136404Speter 137136404Speterstatic int 13812243Sphkpw_groupdel(const char *name, long id) 13912243Sphk{ 14012243Sphk struct group *grp = NULL; 14112243Sphk int rc; 14212243Sphk 14386183Srwatson grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 144217915Smdf if (grp == NULL) { 14512243Sphk if (name == NULL) 14638517Sdfr errx(EX_DATAERR, "unknown gid `%ld'", id); 14738517Sdfr errx(EX_DATAERR, "unknown group `%s'", name); 14838517Sdfr } 14912243Sphk 15038517Sdfr rc = delgrent(grp); 15138517Sdfr if (rc == -1) 15238517Sdfr err(EX_IOERR, "group '%s' not available (NIS?)", name); 153127052Struckman else if (rc != 0) 154136404Speter err(EX_IOERR, "group update"); 15512243Sphk pw_log(conf.userconf, M_DELETE, W_GROUP, "%s(%ld) removed", name, id); 15612243Sphk 15760938Sjake return (EXIT_SUCCESS); 15844078Sdfr} 15912243Sphk 16012243Sphkint 16112243Sphkpw_group(int mode, char *name, long id, struct cargs * args) 16212243Sphk{ 16311863Sphk int rc; 16444078Sdfr struct carg *arg; 16560938Sjake struct group *grp = NULL; 16611863Sphk int grmembers = 0; 167100113Smarkm char **members = NULL; 16811863Sphk struct userconf *cnf = conf.userconf; 169219819Sjeff 17012623Sphk static struct group fakegroup = 17162573Sphk { 17212623Sphk "nogroup", 17363212Sabial "*", 174216060Smdf -1, 175141433Sphk NULL 17611863Sphk }; 17711863Sphk 17812243Sphk if (mode == M_NEXT) 17912243Sphk return (pw_groupnext(cnf, conf.quiet)); 18012243Sphk 18162573Sphk if (mode == M_PRINT) 182155758Sandre return (pw_groupshow(name, id, &fakegroup)); 18362573Sphk 184217616Smdf if (mode == M_DELETE) 18562573Sphk return (pw_groupdel(name, id)); 18662573Sphk 18711865Sphk if (mode == M_LOCK || mode == M_UNLOCK) 188194784Sjeff errx(EX_USAGE, "'lock' command is not available for groups"); 189194935Sjeff 190194784Sjeff if (id < 0 && name == NULL) 191194784Sjeff errx(EX_DATAERR, "group name or id required"); 19244078Sdfr 19344078Sdfr grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 19444078Sdfr 19544078Sdfr if (mode == M_UPDATE) { 19644078Sdfr if (name == NULL && grp == NULL) /* Try harder */ 19744078Sdfr grp = GETGRGID(id); 19863212Sabial 19944078Sdfr if (grp == NULL) { 20044078Sdfr if (name == NULL) 20144078Sdfr errx(EX_DATAERR, "unknown group `%s'", name); 20263212Sabial else 20363212Sabial errx(EX_DATAERR, "unknown group `%ld'", id); 20463212Sabial } 205132784Skan if (name == NULL) /* Needed later */ 206132784Skan name = grp->gr_name; 20763212Sabial 20863212Sabial if (id > 0) 20963212Sabial grp->gr_gid = (gid_t) id; 21063212Sabial 21163212Sabial if (conf.newname != NULL) 21263212Sabial grp->gr_name = pw_checkname(conf.newname, 0); 21363212Sabial } else { 21463212Sabial if (name == NULL) /* Required */ 21563212Sabial errx(EX_DATAERR, "group name required"); 21663212Sabial else if (grp != NULL) /* Exists */ 21763212Sabial errx(EX_DATAERR, "group name `%s' already exists", name); 21863212Sabial 21963212Sabial extendarray(&members, &grmembers, 200); 220108649Sjake members[0] = NULL; 221108649Sjake grp = &fakegroup; 222108649Sjake grp->gr_name = pw_checkname(name, 0); 223217313Smdf grp->gr_passwd = "*"; 224217313Smdf grp->gr_gid = gr_gidpolicy(cnf, id); 225217313Smdf grp->gr_mem = members; 226217313Smdf } 227217313Smdf 228217313Smdf /* 229217313Smdf * This allows us to set a group password Group passwords is an 230217313Smdf * antique idea, rarely used and insecure (no secure database) Should 231217313Smdf * be discouraged, but it is apparently still supported by some 232217313Smdf * software. 233217313Smdf */ 234217313Smdf 235217313Smdf if (conf.which == W_GROUP && conf.fd != -1) 236217313Smdf set_passwd(grp, mode == M_UPDATE); 237217313Smdf 238217313Smdf if (((arg = getarg(args, 'M')) != NULL || 239217313Smdf (arg = getarg(args, 'd')) != NULL || 240217313Smdf (arg = getarg(args, 'm')) != NULL) && arg->val) { 241217313Smdf int i = 0; 242217313Smdf char *p; 243217313Smdf struct passwd *pwd; 244217313Smdf 245217313Smdf /* Make sure this is not stay NULL with -M "" */ 246217313Smdf extendarray(&members, &grmembers, 200); 247217313Smdf if (arg->ch == 'd') 248217313Smdf delete_members(&members, &grmembers, &i, arg, grp); 249217313Smdf else if (arg->ch == 'm') { 250217313Smdf int k = 0; 251217313Smdf 252217313Smdf if (grp->gr_mem != NULL) { 253217313Smdf while (grp->gr_mem[k] != NULL) { 254217313Smdf if (extendarray(&members, &grmembers, i + 2) != -1) 255217313Smdf members[i++] = grp->gr_mem[k]; 256217313Smdf k++; 257217313Smdf } 258217313Smdf } 259217313Smdf } 260217313Smdf 261217313Smdf if (arg->ch != 'd') 262217313Smdf for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { 263217313Smdf int j; 264217313Smdf 265217313Smdf /* 266217313Smdf * Check for duplicates 267217313Smdf */ 268217313Smdf pwd = lookup_pwent(p); 269217313Smdf for (j = 0; j < i && strcmp(members[j], pwd->pw_name) != 0; j++) 270160469Simp ; 271160469Simp if (j == i && extendarray(&members, &grmembers, i + 2) != -1) 272160469Simp members[i++] = newstr(pwd->pw_name); 273160469Simp } 274160469Simp while (i < grmembers) 275160469Simp members[i++] = NULL; 27638859Sbde grp->gr_mem = members; 27712623Sphk } 27844078Sdfr 279188039Simp if (conf.dryrun) 280216060Smdf return print_group(grp); 281100113Smarkm 28211863Sphk if (mode == M_ADD && (rc = addgrent(grp)) != 0) { 28363212Sabial if (rc == -1) 284160469Simp errx(EX_IOERR, "group '%s' already exists", 28563212Sabial grp->gr_name); 28638859Sbde else 28744078Sdfr err(EX_IOERR, "group update"); 288108649Sjake } else if (mode == M_UPDATE && (rc = chggrent(name, grp)) != 0) { 289105582Sphk if (rc == -1) 290181888Sjulian errx(EX_IOERR, "group '%s' not available (NIS?)", 29111863Sphk grp->gr_name); 29263212Sabial else 293105582Sphk err(EX_IOERR, "group update"); 294188039Simp } 29563212Sabial 29638859Sbde if (conf.newname != NULL) 29712623Sphk name = conf.newname; 298105582Sphk /* grp may have been invalidated */ 29929210Sbde if ((grp = GETGRNAM(name)) == NULL) 30011863Sphk errx(EX_SOFTWARE, "group disappeared during update"); 30163212Sabial 302105582Sphk pw_log(cnf, mode, W_GROUP, "%s(%u)", grp->gr_name, grp->gr_gid); 303160469Simp 30463212Sabial free(members); 30538859Sbde 306217313Smdf return EXIT_SUCCESS; 307217313Smdf} 308217313Smdf 309217313Smdf 310217313Smdf/* 31111863Sphk * Lookup a passwd entry using a name or UID. 312217313Smdf */ 313217313Smdfstatic struct passwd * 314217313Smdflookup_pwent(const char *user) 315217313Smdf{ 316217313Smdf struct passwd *pwd; 31763212Sabial 31862622Sjhb if ((pwd = GETPWNAM(user)) == NULL && 319217313Smdf (!isdigit((unsigned char)*user) || 320217313Smdf (pwd = getpwuid((uid_t) atoi(user))) == NULL)) 321217313Smdf errx(EX_NOUSER, "user `%s' does not exist", user); 322217313Smdf 323217313Smdf return (pwd); 32462622Sjhb} 325217313Smdf 326217313Smdf 327217313Smdf/* 328217313Smdf * Delete requested members from a group. 329217313Smdf */ 33063212Sabialstatic void 33142095Sdfrdelete_members(char ***members, int *grmembers, int *i, struct carg *arg, 332217313Smdf struct group *grp) 333217313Smdf{ 334217313Smdf bool matchFound; 335217313Smdf char *user; 336217313Smdf char *valueCopy; 33738517Sdfr char *valuePtr; 338217313Smdf int k; 339217313Smdf struct passwd *pwd; 340217313Smdf 341217313Smdf if (grp->gr_mem == NULL) 342217313Smdf return; 34363212Sabial 344112744Srobert k = 0; 345217313Smdf while (grp->gr_mem[k] != NULL) { 346217313Smdf matchFound = false; 347217313Smdf if ((valueCopy = strdup(arg->val)) == NULL) 348217313Smdf errx(EX_UNAVAILABLE, "out of memory"); 349217313Smdf valuePtr = valueCopy; 35062622Sjhb while ((user = strsep(&valuePtr, ", \t")) != NULL) { 351217313Smdf pwd = lookup_pwent(user); 352217313Smdf if (strcmp(grp->gr_mem[k], pwd->pw_name) == 0) { 353217313Smdf matchFound = true; 354217313Smdf break; 355217313Smdf } 35663212Sabial } 357180661Spjd free(valueCopy); 358217313Smdf 359217313Smdf if (!matchFound && 360217313Smdf extendarray(members, grmembers, *i + 2) != -1) 361217616Smdf (*members)[(*i)++] = grp->gr_mem[k]; 362217616Smdf 363180661Spjd k++; 364217313Smdf } 365217313Smdf 366217616Smdf return; 367217313Smdf} 368217616Smdf 369180661Spjd 370217313Smdfstatic gid_t 371217313Smdfgr_gidpolicy(struct userconf * cnf, long id) 372217313Smdf{ 373217616Smdf struct group *grp; 374217616Smdf gid_t gid = (gid_t) - 1; 375217313Smdf 376217313Smdf /* 377217313Smdf * Check the given gid, if any 378217616Smdf */ 379217313Smdf if (id > 0) { 380217616Smdf gid = (gid_t) id; 381217313Smdf 38238859Sbde if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) 38312705Sphk errx(EX_DATAERR, "gid `%u' has already been allocated", grp->gr_gid); 384105582Sphk } else { 38529210Sbde struct bitmap bm; 38611863Sphk 38763212Sabial /* 388105582Sphk * We need to allocate the next available gid under one of 389160469Simp * two policies a) Grab the first unused gid b) Grab the 39063212Sabial * highest possible unused gid 39138859Sbde */ 39212623Sphk if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 393105582Sphk cnf->min_gid = 1000; 39412623Sphk cnf->max_gid = 32000; 39529210Sbde } 39611863Sphk bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 39763212Sabial 398105582Sphk /* 399160469Simp * Now, let's fill the bitmap from the password file 40063212Sabial */ 40138859Sbde SETGRENT(); 40212623Sphk while ((grp = GETGRENT()) != NULL) 403217586Smdf if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && 404105582Sphk (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) 40529210Sbde bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 40663212Sabial ENDGRENT(); 40763212Sabial 408105582Sphk /* 409160469Simp * Then apply the policy, with fallback to reuse if necessary 41063212Sabial */ 411175019Sjhb if (cnf->reuse_gids) 412175019Sjhb gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 413175019Sjhb else { 414175019Sjhb gid = (gid_t) (bm_lastset(&bm) + 1); 415175019Sjhb if (!bm_isset(&bm, gid)) 416224159Srwatson gid += cnf->min_gid; 417224159Srwatson else 418191688Szec gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 41955205Speter } 42011863Sphk 4211541Srgrimes /* 4221541Srgrimes * Another sanity check 4231541Srgrimes */ 4241541Srgrimes if (gid < cnf->min_gid || gid > cnf->max_gid) 4251541Srgrimes errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used"); 4261541Srgrimes bm_dealloc(&bm); 42796755Strhodes } 4281541Srgrimes return gid; 4291541Srgrimes} 4301541Srgrimes 4311541Srgrimes 4321541Srgrimesstatic int 43334925Sdufaultprint_group(struct group * grp) 43434030Sdufault{ 43534030Sdufault if (!conf.pretty) { 4361541Srgrimes char *buf = NULL; 4371541Srgrimes 4381541Srgrimes buf = gr_make(grp); 4391541Srgrimes printf("%s\n", buf); 44014498Shsu free(buf); 4411541Srgrimes } else { 4421541Srgrimes int i; 4431541Srgrimes 4441541Srgrimes printf("Group Name: %-15s #%lu\n" 4451541Srgrimes " Members: ", 44634925Sdufault grp->gr_name, (long) grp->gr_gid); 4471541Srgrimes if (grp->gr_mem != NULL) { 4481541Srgrimes for (i = 0; grp->gr_mem[i]; i++) 4491541Srgrimes printf("%s%s", i ? "," : "", grp->gr_mem[i]); 4501541Srgrimes } 4511541Srgrimes fputs("\n\n", stdout); 4521541Srgrimes } 4531541Srgrimes return EXIT_SUCCESS; 4541541Srgrimes} 4551541Srgrimes