pw_group.c revision 56000
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 56000 2000-01-15 00:20:22Z davidn $"; 3030259Scharnier#endif /* not lint */ 3130259Scharnier 3220253Sjoerg#include <ctype.h> 3330259Scharnier#include <err.h> 3420253Sjoerg#include <termios.h> 3530259Scharnier#include <unistd.h> 3620253Sjoerg 3720253Sjoerg#include "pw.h" 3820253Sjoerg#include "bitmap.h" 3920253Sjoerg 4020253Sjoerg 4120253Sjoergstatic int print_group(struct group * grp, int pretty); 4220253Sjoergstatic gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args); 4320253Sjoerg 4420253Sjoergint 4520253Sjoergpw_group(struct userconf * cnf, int mode, struct cargs * args) 4620253Sjoerg{ 4752502Sdavidn int rc; 4820253Sjoerg struct carg *a_name = getarg(args, 'n'); 4920253Sjoerg struct carg *a_gid = getarg(args, 'g'); 5020253Sjoerg struct carg *arg; 5120253Sjoerg struct group *grp = NULL; 5220747Sdavidn int grmembers = 0; 5352502Sdavidn char **members = NULL; 5420253Sjoerg 5520253Sjoerg static struct group fakegroup = 5620253Sjoerg { 5720253Sjoerg "nogroup", 5820253Sjoerg "*", 5920253Sjoerg -1, 6020253Sjoerg NULL 6120253Sjoerg }; 6220253Sjoerg 6352512Sdavidn if (mode == M_LOCK || mode == M_UNLOCK) 6452512Sdavidn errx(EX_USAGE, "'lock' command is not available for groups"); 6552512Sdavidn 6620267Sjoerg /* 6720267Sjoerg * With M_NEXT, we only need to return the 6820267Sjoerg * next gid to stdout 6920267Sjoerg */ 7052512Sdavidn if (mode == M_NEXT) { 7120267Sjoerg gid_t next = gr_gidpolicy(cnf, args); 7220267Sjoerg if (getarg(args, 'q')) 7320267Sjoerg return next; 7420267Sjoerg printf("%ld\n", (long)next); 7520267Sjoerg return EXIT_SUCCESS; 7620267Sjoerg } 7720267Sjoerg 7820253Sjoerg if (mode == M_PRINT && getarg(args, 'a')) { 7920267Sjoerg int pretty = getarg(args, 'P') != NULL; 8020253Sjoerg 8144229Sdavidn SETGRENT(); 8244229Sdavidn while ((grp = GETGRENT()) != NULL) 8320253Sjoerg print_group(grp, pretty); 8444229Sdavidn ENDGRENT(); 8520267Sjoerg return EXIT_SUCCESS; 8620253Sjoerg } 8720253Sjoerg if (a_gid == NULL) { 8820253Sjoerg if (a_name == NULL) 8930259Scharnier errx(EX_DATAERR, "group name or id required"); 9020253Sjoerg 9120253Sjoerg if (mode != M_ADD && grp == NULL && isdigit(*a_name->val)) { 9220253Sjoerg (a_gid = a_name)->ch = 'g'; 9320253Sjoerg a_name = NULL; 9420253Sjoerg } 9520253Sjoerg } 9644229Sdavidn grp = (a_name != NULL) ? GETGRNAM(a_name->val) : GETGRGID((gid_t) atoi(a_gid->val)); 9720253Sjoerg 9820253Sjoerg if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) { 9920253Sjoerg if (a_name == NULL && grp == NULL) /* Try harder */ 10044229Sdavidn grp = GETGRGID(atoi(a_gid->val)); 10120253Sjoerg 10220253Sjoerg if (grp == NULL) { 10320253Sjoerg if (mode == M_PRINT && getarg(args, 'F')) { 10420747Sdavidn char *fmems[1]; 10520747Sdavidn fmems[0] = NULL; 10620253Sjoerg fakegroup.gr_name = a_name ? a_name->val : "nogroup"; 10720253Sjoerg fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1; 10820747Sdavidn fakegroup.gr_mem = fmems; 10920267Sjoerg return print_group(&fakegroup, getarg(args, 'P') != NULL); 11020253Sjoerg } 11130259Scharnier errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val); 11220253Sjoerg } 11320253Sjoerg if (a_name == NULL) /* Needed later */ 11420253Sjoerg a_name = addarg(args, 'n', grp->gr_name); 11520253Sjoerg 11620253Sjoerg /* 11720253Sjoerg * Handle deletions now 11820253Sjoerg */ 11920253Sjoerg if (mode == M_DELETE) { 12020253Sjoerg gid_t gid = grp->gr_gid; 12120253Sjoerg 12252502Sdavidn rc = delgrent(grp); 12352502Sdavidn if (rc == -1) 12452502Sdavidn err(EX_IOERR, "group '%s' not available (NIS?)", grp->gr_name); 12552502Sdavidn else if (rc != 0) { 12656000Sdavidn warn("group update"); 12752502Sdavidn return EX_IOERR; 12852502Sdavidn } 12920253Sjoerg pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid); 13020267Sjoerg return EXIT_SUCCESS; 13120253Sjoerg } else if (mode == M_PRINT) 13220267Sjoerg return print_group(grp, getarg(args, 'P') != NULL); 13320253Sjoerg 13420253Sjoerg if (a_gid) 13520253Sjoerg grp->gr_gid = (gid_t) atoi(a_gid->val); 13620253Sjoerg 13720253Sjoerg if ((arg = getarg(args, 'l')) != NULL) 13820679Sdavidn grp->gr_name = pw_checkname((u_char *)arg->val, 0); 13920253Sjoerg } else { 14020253Sjoerg if (a_name == NULL) /* Required */ 14130259Scharnier errx(EX_DATAERR, "group name required"); 14220253Sjoerg else if (grp != NULL) /* Exists */ 14330259Scharnier errx(EX_DATAERR, "group name `%s' already exists", a_name->val); 14420253Sjoerg 14520747Sdavidn extendarray(&members, &grmembers, 200); 14620747Sdavidn members[0] = NULL; 14720253Sjoerg grp = &fakegroup; 14820679Sdavidn grp->gr_name = pw_checkname((u_char *)a_name->val, 0); 14920253Sjoerg grp->gr_passwd = "*"; 15020253Sjoerg grp->gr_gid = gr_gidpolicy(cnf, args); 15120253Sjoerg grp->gr_mem = members; 15220253Sjoerg } 15320253Sjoerg 15420253Sjoerg /* 15520253Sjoerg * This allows us to set a group password Group passwords is an 15620253Sjoerg * antique idea, rarely used and insecure (no secure database) Should 15720253Sjoerg * be discouraged, but it is apparently still supported by some 15820253Sjoerg * software. 15920253Sjoerg */ 16020253Sjoerg 16120253Sjoerg if ((arg = getarg(args, 'h')) != NULL) { 16220253Sjoerg if (strcmp(arg->val, "-") == 0) 16320253Sjoerg grp->gr_passwd = "*"; /* No access */ 16420253Sjoerg else { 16520253Sjoerg int fd = atoi(arg->val); 16620253Sjoerg int b; 16720253Sjoerg int istty = isatty(fd); 16820253Sjoerg struct termios t; 16920253Sjoerg char *p, line[256]; 17020253Sjoerg 17120253Sjoerg if (istty) { 17220253Sjoerg if (tcgetattr(fd, &t) == -1) 17320253Sjoerg istty = 0; 17420253Sjoerg else { 17520253Sjoerg struct termios n = t; 17620253Sjoerg 17720253Sjoerg /* Disable echo */ 17820253Sjoerg n.c_lflag &= ~(ECHO); 17920253Sjoerg tcsetattr(fd, TCSANOW, &n); 18020253Sjoerg printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name); 18120253Sjoerg fflush(stdout); 18220253Sjoerg } 18320253Sjoerg } 18420253Sjoerg b = read(fd, line, sizeof(line) - 1); 18520253Sjoerg if (istty) { /* Restore state */ 18620253Sjoerg tcsetattr(fd, TCSANOW, &t); 18720253Sjoerg fputc('\n', stdout); 18820253Sjoerg fflush(stdout); 18920253Sjoerg } 19020253Sjoerg if (b < 0) { 19130259Scharnier warn("-h file descriptor"); 19220267Sjoerg return EX_OSERR; 19320253Sjoerg } 19420253Sjoerg line[b] = '\0'; 19520253Sjoerg if ((p = strpbrk(line, " \t\r\n")) != NULL) 19620253Sjoerg *p = '\0'; 19720253Sjoerg if (!*line) 19830259Scharnier errx(EX_DATAERR, "empty password read on file descriptor %d", fd); 19920253Sjoerg grp->gr_passwd = pw_pwcrypt(line); 20020253Sjoerg } 20120253Sjoerg } 20220267Sjoerg 20320267Sjoerg if (((arg = getarg(args, 'M')) != NULL || (arg = getarg(args, 'm')) != NULL) && arg->val) { 20420267Sjoerg int i = 0; 20520267Sjoerg char *p; 20620267Sjoerg struct passwd *pwd; 20720267Sjoerg 20820747Sdavidn /* Make sure this is not stay NULL with -M "" */ 20920747Sdavidn extendarray(&members, &grmembers, 200); 21020267Sjoerg if (arg->ch == 'm') { 21120747Sdavidn int k = 0; 21220747Sdavidn 21320747Sdavidn while (grp->gr_mem[k] != NULL) { 21420747Sdavidn if (extendarray(&members, &grmembers, i + 2) != -1) { 21520747Sdavidn members[i++] = grp->gr_mem[k]; 21620747Sdavidn } 21720747Sdavidn k++; 21820267Sjoerg } 21920267Sjoerg } 22020747Sdavidn for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { 22120267Sjoerg int j; 22244229Sdavidn if ((pwd = GETPWNAM(p)) == NULL) { 22320267Sjoerg if (!isdigit(*p) || (pwd = getpwuid((uid_t) atoi(p))) == NULL) 22430259Scharnier errx(EX_NOUSER, "user `%s' does not exist", p); 22520267Sjoerg } 22620267Sjoerg /* 22720267Sjoerg * Check for duplicates 22820267Sjoerg */ 22920267Sjoerg for (j = 0; j < i && strcmp(members[j], pwd->pw_name)!=0; j++) 23020267Sjoerg ; 23120747Sdavidn if (j == i && extendarray(&members, &grmembers, i + 2) != -1) 23220267Sjoerg members[i++] = newstr(pwd->pw_name); 23320267Sjoerg } 23420747Sdavidn while (i < grmembers) 23520267Sjoerg members[i++] = NULL; 23620267Sjoerg grp->gr_mem = members; 23720267Sjoerg } 23820267Sjoerg 23920267Sjoerg if (getarg(args, 'N') != NULL) 24020267Sjoerg return print_group(grp, getarg(args, 'P') != NULL); 24120267Sjoerg 24252502Sdavidn if (mode == M_ADD && (rc = addgrent(grp)) != 0) { 24352502Sdavidn if (rc == -1) 24452502Sdavidn warnx("group '%s' already exists", grp->gr_name); 24552502Sdavidn else 24652502Sdavidn warn("group update"); 24720267Sjoerg return EX_IOERR; 24852502Sdavidn } else if (mode == M_UPDATE && (rc = chggrent(a_name->val, grp)) != 0) { 24952502Sdavidn if (rc == -1) 25052502Sdavidn warnx("group '%s' not available (NIS?)", grp->gr_name); 25152502Sdavidn else 25256000Sdavidn warn("group update"); 25352502Sdavidn return EX_IOERR; 25420253Sjoerg } 25520253Sjoerg /* grp may have been invalidated */ 25644229Sdavidn if ((grp = GETGRNAM(a_name->val)) == NULL) 25730259Scharnier errx(EX_SOFTWARE, "group disappeared during update"); 25820253Sjoerg 25920253Sjoerg pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid); 26020253Sjoerg 26120747Sdavidn if (members) 26220747Sdavidn free(members); 26320747Sdavidn 26420267Sjoerg return EXIT_SUCCESS; 26520253Sjoerg} 26620253Sjoerg 26720253Sjoerg 26820253Sjoergstatic gid_t 26920253Sjoerggr_gidpolicy(struct userconf * cnf, struct cargs * args) 27020253Sjoerg{ 27120253Sjoerg struct group *grp; 27220253Sjoerg gid_t gid = (gid_t) - 1; 27320253Sjoerg struct carg *a_gid = getarg(args, 'g'); 27420253Sjoerg 27520253Sjoerg /* 27620253Sjoerg * Check the given gid, if any 27720253Sjoerg */ 27820253Sjoerg if (a_gid != NULL) { 27920253Sjoerg gid = (gid_t) atol(a_gid->val); 28020253Sjoerg 28144229Sdavidn if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL) 28230259Scharnier errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid); 28320253Sjoerg } else { 28420253Sjoerg struct bitmap bm; 28520253Sjoerg 28620253Sjoerg /* 28720253Sjoerg * We need to allocate the next available gid under one of 28820253Sjoerg * two policies a) Grab the first unused gid b) Grab the 28920253Sjoerg * highest possible unused gid 29020253Sjoerg */ 29120253Sjoerg if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 29220253Sjoerg cnf->min_gid = 1000; 29320253Sjoerg cnf->max_gid = 32000; 29420253Sjoerg } 29520253Sjoerg bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 29620253Sjoerg 29720253Sjoerg /* 29820253Sjoerg * Now, let's fill the bitmap from the password file 29920253Sjoerg */ 30044229Sdavidn SETGRENT(); 30144229Sdavidn while ((grp = GETGRENT()) != NULL) 30256000Sdavidn if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid && 30356000Sdavidn (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid) 30420253Sjoerg bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 30544229Sdavidn ENDGRENT(); 30620253Sjoerg 30720253Sjoerg /* 30820253Sjoerg * Then apply the policy, with fallback to reuse if necessary 30920253Sjoerg */ 31020253Sjoerg if (cnf->reuse_gids) 31120253Sjoerg gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 31220253Sjoerg else { 31320253Sjoerg gid = (gid_t) (bm_lastset(&bm) + 1); 31420253Sjoerg if (!bm_isset(&bm, gid)) 31520253Sjoerg gid += cnf->min_gid; 31620253Sjoerg else 31720253Sjoerg gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 31820253Sjoerg } 31920253Sjoerg 32020253Sjoerg /* 32120253Sjoerg * Another sanity check 32220253Sjoerg */ 32320253Sjoerg if (gid < cnf->min_gid || gid > cnf->max_gid) 32430259Scharnier errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used"); 32520253Sjoerg bm_dealloc(&bm); 32620253Sjoerg } 32720253Sjoerg return gid; 32820253Sjoerg} 32920253Sjoerg 33020253Sjoerg 33120253Sjoergstatic int 33220253Sjoergprint_group(struct group * grp, int pretty) 33320253Sjoerg{ 33420253Sjoerg if (!pretty) { 33520747Sdavidn int buflen = 0; 33620747Sdavidn char *buf = NULL; 33720253Sjoerg 33820747Sdavidn fmtgrent(&buf, &buflen, grp); 33920253Sjoerg fputs(buf, stdout); 34020747Sdavidn free(buf); 34120253Sjoerg } else { 34220253Sjoerg int i; 34320253Sjoerg 34422398Sdavidn printf("Group Name: %-15s #%lu\n" 34520747Sdavidn " Members: ", 34620253Sjoerg grp->gr_name, (long) grp->gr_gid); 34720747Sdavidn for (i = 0; grp->gr_mem[i]; i++) 34820253Sjoerg printf("%s%s", i ? "," : "", grp->gr_mem[i]); 34920253Sjoerg fputs("\n\n", stdout); 35020253Sjoerg } 35120267Sjoerg return EXIT_SUCCESS; 35220253Sjoerg} 353