pw_group.c revision 20253
120253Sjoerg/*- 220253Sjoerg * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>. 320253Sjoerg * 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 920253Sjoerg * notice, this list of conditions and the following disclaimer as 1020253Sjoerg * the first lines of this file unmodified. 1120253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1220253Sjoerg * notice, this list of conditions and the following disclaimer in the 1320253Sjoerg * documentation and/or other materials provided with the distribution. 1420253Sjoerg * 3. All advertising materials mentioning features or use of this software 1520253Sjoerg * must display the following acknowledgement: 1620253Sjoerg * This product includes software developed by David L. Nugent. 1720253Sjoerg * 4. The name of the author may not be used to endorse or promote products 1820253Sjoerg * derived from this software without specific prior written permission. 1920253Sjoerg * 2020253Sjoerg * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND 2120253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2220253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2320253Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE 2420253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2520253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2620253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2720253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2820253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2920253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3020253Sjoerg * SUCH DAMAGE. 3120253Sjoerg * 3220253Sjoerg * $Id$ 3320253Sjoerg */ 3420253Sjoerg 3520253Sjoerg#include <unistd.h> 3620253Sjoerg#include <ctype.h> 3720253Sjoerg#include <termios.h> 3820253Sjoerg 3920253Sjoerg#include "pw.h" 4020253Sjoerg#include "bitmap.h" 4120253Sjoerg 4220253Sjoerg 4320253Sjoergstatic int print_group(struct group * grp, int pretty); 4420253Sjoergstatic gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args); 4520253Sjoerg 4620253Sjoergint 4720253Sjoergpw_group(struct userconf * cnf, int mode, struct cargs * args) 4820253Sjoerg{ 4920253Sjoerg struct carg *a_name = getarg(args, 'n'); 5020253Sjoerg struct carg *a_gid = getarg(args, 'g'); 5120253Sjoerg struct carg *arg; 5220253Sjoerg struct group *grp = NULL; 5320253Sjoerg char *members[_UC_MAXGROUPS]; 5420253Sjoerg 5520253Sjoerg static struct group fakegroup = 5620253Sjoerg { 5720253Sjoerg "nogroup", 5820253Sjoerg "*", 5920253Sjoerg -1, 6020253Sjoerg NULL 6120253Sjoerg }; 6220253Sjoerg 6320253Sjoerg if (mode == M_PRINT && getarg(args, 'a')) { 6420253Sjoerg int pretty = getarg(args, 'p') != NULL; 6520253Sjoerg 6620253Sjoerg setgrent(); 6720253Sjoerg while ((grp = getgrent()) != NULL) 6820253Sjoerg print_group(grp, pretty); 6920253Sjoerg endgrent(); 7020253Sjoerg return X_ALLOK; 7120253Sjoerg } 7220253Sjoerg if (a_gid == NULL) { 7320253Sjoerg if (a_name == NULL) 7420253Sjoerg cmderr(X_CMDERR, "group name or id required\n"); 7520253Sjoerg 7620253Sjoerg if (mode != M_ADD && grp == NULL && isdigit(*a_name->val)) { 7720253Sjoerg (a_gid = a_name)->ch = 'g'; 7820253Sjoerg a_name = NULL; 7920253Sjoerg } 8020253Sjoerg } 8120253Sjoerg grp = (a_name != NULL) ? getgrnam(a_name->val) : getgrgid((gid_t) atoi(a_gid->val)); 8220253Sjoerg 8320253Sjoerg if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) { 8420253Sjoerg if (a_name == NULL && grp == NULL) /* Try harder */ 8520253Sjoerg grp = getgrgid(atoi(a_gid->val)); 8620253Sjoerg 8720253Sjoerg if (grp == NULL) { 8820253Sjoerg if (mode == M_PRINT && getarg(args, 'F')) { 8920253Sjoerg fakegroup.gr_name = a_name ? a_name->val : "nogroup"; 9020253Sjoerg fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1; 9120253Sjoerg return print_group(&fakegroup, getarg(args, 'p') != NULL); 9220253Sjoerg } 9320253Sjoerg cmderr(X_CMDERR, "unknown group `%s'\n", a_name ? a_name->val : a_gid->val); 9420253Sjoerg } 9520253Sjoerg if (a_name == NULL) /* Needed later */ 9620253Sjoerg a_name = addarg(args, 'n', grp->gr_name); 9720253Sjoerg 9820253Sjoerg /* 9920253Sjoerg * Handle deletions now 10020253Sjoerg */ 10120253Sjoerg if (mode == M_DELETE) { 10220253Sjoerg gid_t gid = grp->gr_gid; 10320253Sjoerg 10420253Sjoerg if (delgrent(grp) == -1) 10520253Sjoerg cmderr(X_NOUPDATE, "Error updating group file: %s\n", strerror(errno)); 10620253Sjoerg pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid); 10720253Sjoerg return X_ALLOK; 10820253Sjoerg } else if (mode == M_PRINT) 10920253Sjoerg return print_group(grp, getarg(args, 'p') != NULL); 11020253Sjoerg 11120253Sjoerg if (a_gid) 11220253Sjoerg grp->gr_gid = (gid_t) atoi(a_gid->val); 11320253Sjoerg 11420253Sjoerg if ((arg = getarg(args, 'l')) != NULL) 11520253Sjoerg grp->gr_name = arg->val; 11620253Sjoerg } else { 11720253Sjoerg if (a_name == NULL) /* Required */ 11820253Sjoerg cmderr(X_CMDERR, "group name required\n"); 11920253Sjoerg else if (grp != NULL) /* Exists */ 12020253Sjoerg cmderr(X_EXISTS, "group name `%s' already exists\n", a_name->val); 12120253Sjoerg 12220253Sjoerg memset(members, 0, sizeof members); 12320253Sjoerg grp = &fakegroup; 12420253Sjoerg grp->gr_name = a_name->val; 12520253Sjoerg grp->gr_passwd = "*"; 12620253Sjoerg grp->gr_gid = gr_gidpolicy(cnf, args); 12720253Sjoerg grp->gr_mem = members; 12820253Sjoerg } 12920253Sjoerg 13020253Sjoerg /* 13120253Sjoerg * This allows us to set a group password Group passwords is an 13220253Sjoerg * antique idea, rarely used and insecure (no secure database) Should 13320253Sjoerg * be discouraged, but it is apparently still supported by some 13420253Sjoerg * software. 13520253Sjoerg */ 13620253Sjoerg 13720253Sjoerg if ((arg = getarg(args, 'h')) != NULL) { 13820253Sjoerg if (strcmp(arg->val, "-") == 0) 13920253Sjoerg grp->gr_passwd = "*"; /* No access */ 14020253Sjoerg else { 14120253Sjoerg int fd = atoi(arg->val); 14220253Sjoerg int b; 14320253Sjoerg int istty = isatty(fd); 14420253Sjoerg struct termios t; 14520253Sjoerg char *p, line[256]; 14620253Sjoerg 14720253Sjoerg if (istty) { 14820253Sjoerg if (tcgetattr(fd, &t) == -1) 14920253Sjoerg istty = 0; 15020253Sjoerg else { 15120253Sjoerg struct termios n = t; 15220253Sjoerg 15320253Sjoerg /* Disable echo */ 15420253Sjoerg n.c_lflag &= ~(ECHO); 15520253Sjoerg tcsetattr(fd, TCSANOW, &n); 15620253Sjoerg printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name); 15720253Sjoerg fflush(stdout); 15820253Sjoerg } 15920253Sjoerg } 16020253Sjoerg b = read(fd, line, sizeof(line) - 1); 16120253Sjoerg if (istty) { /* Restore state */ 16220253Sjoerg tcsetattr(fd, TCSANOW, &t); 16320253Sjoerg fputc('\n', stdout); 16420253Sjoerg fflush(stdout); 16520253Sjoerg } 16620253Sjoerg if (b < 0) { 16720253Sjoerg perror("-h file descriptor"); 16820253Sjoerg return X_CMDERR; 16920253Sjoerg } 17020253Sjoerg line[b] = '\0'; 17120253Sjoerg if ((p = strpbrk(line, " \t\r\n")) != NULL) 17220253Sjoerg *p = '\0'; 17320253Sjoerg if (!*line) 17420253Sjoerg cmderr(X_CMDERR, "empty password read on file descriptor %d\n", fd); 17520253Sjoerg grp->gr_passwd = pw_pwcrypt(line); 17620253Sjoerg } 17720253Sjoerg } 17820253Sjoerg if ((mode == M_ADD && !addgrent(grp)) || (mode == M_UPDATE && !chggrent(a_name->val, grp))) { 17920253Sjoerg perror("group update"); 18020253Sjoerg return X_NOUPDATE; 18120253Sjoerg } 18220253Sjoerg /* grp may have been invalidated */ 18320253Sjoerg if ((grp = getgrnam(a_name->val)) == NULL) 18420253Sjoerg cmderr(X_NOTFOUND, "group disappeared during update\n"); 18520253Sjoerg 18620253Sjoerg pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid); 18720253Sjoerg 18820253Sjoerg return X_ALLOK; 18920253Sjoerg} 19020253Sjoerg 19120253Sjoerg 19220253Sjoergstatic gid_t 19320253Sjoerggr_gidpolicy(struct userconf * cnf, struct cargs * args) 19420253Sjoerg{ 19520253Sjoerg struct group *grp; 19620253Sjoerg gid_t gid = (gid_t) - 1; 19720253Sjoerg struct carg *a_gid = getarg(args, 'g'); 19820253Sjoerg 19920253Sjoerg /* 20020253Sjoerg * Check the given gid, if any 20120253Sjoerg */ 20220253Sjoerg if (a_gid != NULL) { 20320253Sjoerg gid = (gid_t) atol(a_gid->val); 20420253Sjoerg 20520253Sjoerg if ((grp = getgrgid(gid)) != NULL && getarg(args, 'o') == NULL) 20620253Sjoerg cmderr(X_EXISTS, "gid `%ld' has already been allocated\n", (long) grp->gr_gid); 20720253Sjoerg } else { 20820253Sjoerg struct bitmap bm; 20920253Sjoerg 21020253Sjoerg /* 21120253Sjoerg * We need to allocate the next available gid under one of 21220253Sjoerg * two policies a) Grab the first unused gid b) Grab the 21320253Sjoerg * highest possible unused gid 21420253Sjoerg */ 21520253Sjoerg if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 21620253Sjoerg cnf->min_gid = 1000; 21720253Sjoerg cnf->max_gid = 32000; 21820253Sjoerg } 21920253Sjoerg bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 22020253Sjoerg 22120253Sjoerg /* 22220253Sjoerg * Now, let's fill the bitmap from the password file 22320253Sjoerg */ 22420253Sjoerg setgrent(); 22520253Sjoerg while ((grp = getgrent()) != NULL) 22620253Sjoerg if (grp->gr_gid >= (int) cnf->min_gid && grp->gr_gid <= (int) cnf->max_gid) 22720253Sjoerg bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 22820253Sjoerg endgrent(); 22920253Sjoerg 23020253Sjoerg /* 23120253Sjoerg * Then apply the policy, with fallback to reuse if necessary 23220253Sjoerg */ 23320253Sjoerg if (cnf->reuse_gids) 23420253Sjoerg gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 23520253Sjoerg else { 23620253Sjoerg gid = (gid_t) (bm_lastset(&bm) + 1); 23720253Sjoerg if (!bm_isset(&bm, gid)) 23820253Sjoerg gid += cnf->min_gid; 23920253Sjoerg else 24020253Sjoerg gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 24120253Sjoerg } 24220253Sjoerg 24320253Sjoerg /* 24420253Sjoerg * Another sanity check 24520253Sjoerg */ 24620253Sjoerg if (gid < cnf->min_gid || gid > cnf->max_gid) 24720253Sjoerg cmderr(X_EXISTS, "unable to allocate a new gid - range fully used\n"); 24820253Sjoerg bm_dealloc(&bm); 24920253Sjoerg } 25020253Sjoerg return gid; 25120253Sjoerg} 25220253Sjoerg 25320253Sjoerg 25420253Sjoergstatic int 25520253Sjoergprint_group(struct group * grp, int pretty) 25620253Sjoerg{ 25720253Sjoerg if (!pretty) { 25820253Sjoerg char buf[_UC_MAXLINE]; 25920253Sjoerg 26020253Sjoerg fmtgrent(buf, grp); 26120253Sjoerg fputs(buf, stdout); 26220253Sjoerg } else { 26320253Sjoerg int i; 26420253Sjoerg 26520253Sjoerg printf("Group Name : %-10s #%lu\n" 26620253Sjoerg " Members : ", 26720253Sjoerg grp->gr_name, (long) grp->gr_gid); 26820253Sjoerg for (i = 0; i < _UC_MAXGROUPS && grp->gr_mem[i]; i++) 26920253Sjoerg printf("%s%s", i ? "," : "", grp->gr_mem[i]); 27020253Sjoerg fputs("\n\n", stdout); 27120253Sjoerg } 27220253Sjoerg return X_ALLOK; 27320253Sjoerg} 274