pw_group.c revision 20253
119370Spst/*- 2130803Smarcel * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>. 3130803Smarcel * All rights reserved. 4130803Smarcel * 5130803Smarcel * Redistribution and use in source and binary forms, with or without 619370Spst * modification, are permitted provided that the following conditions 719370Spst * are met: 819370Spst * 1. Redistributions of source code must retain the above copyright 998944Sobrien * notice, this list of conditions and the following disclaimer as 1019370Spst * the first lines of this file unmodified. 1198944Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1298944Sobrien * notice, this list of conditions and the following disclaimer in the 1398944Sobrien * documentation and/or other materials provided with the distribution. 1498944Sobrien * 3. All advertising materials mentioning features or use of this software 1519370Spst * must display the following acknowledgement: 1698944Sobrien * This product includes software developed by David L. Nugent. 1798944Sobrien * 4. The name of the author may not be used to endorse or promote products 1898944Sobrien * derived from this software without specific prior written permission. 1998944Sobrien * 2019370Spst * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND 2198944Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2298944Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2398944Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE 2498944Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2519370Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2619370Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2719370Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2819370Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2919370Spst * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3019370Spst * SUCH DAMAGE. 3119370Spst * 3219370Spst * $Id$ 3319370Spst */ 3419370Spst 3519370Spst#include <unistd.h> 3619370Spst#include <ctype.h> 3719370Spst#include <termios.h> 3819370Spst 3919370Spst#include "pw.h" 4019370Spst#include "bitmap.h" 4119370Spst 4219370Spst 4319370Spststatic int print_group(struct group * grp, int pretty); 4419370Spststatic gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args); 4519370Spst 4698944Sobrienint 47130803Smarcelpw_group(struct userconf * cnf, int mode, struct cargs * args) 4819370Spst{ 4998944Sobrien struct carg *a_name = getarg(args, 'n'); 5019370Spst struct carg *a_gid = getarg(args, 'g'); 5198944Sobrien struct carg *arg; 5219370Spst struct group *grp = NULL; 5398944Sobrien char *members[_UC_MAXGROUPS]; 5419370Spst 5598944Sobrien static struct group fakegroup = 5619370Spst { 5798944Sobrien "nogroup", 5819370Spst "*", 5998944Sobrien -1, 6019370Spst NULL 6198944Sobrien }; 6219370Spst 6398944Sobrien if (mode == M_PRINT && getarg(args, 'a')) { 6419370Spst int pretty = getarg(args, 'p') != NULL; 6598944Sobrien 6619370Spst setgrent(); 6798944Sobrien while ((grp = getgrent()) != NULL) 6819370Spst print_group(grp, pretty); 6998944Sobrien endgrent(); 7019370Spst return X_ALLOK; 7198944Sobrien } 7219370Spst if (a_gid == NULL) { 7398944Sobrien if (a_name == NULL) 7419370Spst cmderr(X_CMDERR, "group name or id required\n"); 7598944Sobrien 7619370Spst if (mode != M_ADD && grp == NULL && isdigit(*a_name->val)) { 7798944Sobrien (a_gid = a_name)->ch = 'g'; 7846283Sdfr a_name = NULL; 7998944Sobrien } 8046283Sdfr } 8198944Sobrien grp = (a_name != NULL) ? getgrnam(a_name->val) : getgrgid((gid_t) atoi(a_gid->val)); 8246283Sdfr 8398944Sobrien if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) { 8446283Sdfr if (a_name == NULL && grp == NULL) /* Try harder */ 8598944Sobrien grp = getgrgid(atoi(a_gid->val)); 8646283Sdfr 8798944Sobrien if (grp == NULL) { 8846283Sdfr if (mode == M_PRINT && getarg(args, 'F')) { 8998944Sobrien fakegroup.gr_name = a_name ? a_name->val : "nogroup"; 9098944Sobrien fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1; 9198944Sobrien return print_group(&fakegroup, getarg(args, 'p') != NULL); 9246283Sdfr } 9398944Sobrien cmderr(X_CMDERR, "unknown group `%s'\n", a_name ? a_name->val : a_gid->val); 9498944Sobrien } 9598944Sobrien if (a_name == NULL) /* Needed later */ 9698944Sobrien a_name = addarg(args, 'n', grp->gr_name); 9798944Sobrien 9898944Sobrien /* 9998944Sobrien * Handle deletions now 10098944Sobrien */ 10198944Sobrien if (mode == M_DELETE) { 10298944Sobrien gid_t gid = grp->gr_gid; 10398944Sobrien 104130803Smarcel if (delgrent(grp) == -1) 105130803Smarcel cmderr(X_NOUPDATE, "Error updating group file: %s\n", strerror(errno)); 10619370Spst pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid); 10719370Spst return X_ALLOK; 10898944Sobrien } else if (mode == M_PRINT) 10919370Spst return print_group(grp, getarg(args, 'p') != NULL); 11098944Sobrien 11198944Sobrien if (a_gid) 11298944Sobrien grp->gr_gid = (gid_t) atoi(a_gid->val); 11319370Spst 11419370Spst if ((arg = getarg(args, 'l')) != NULL) 11519370Spst grp->gr_name = arg->val; 11619370Spst } else { 11719370Spst if (a_name == NULL) /* Required */ 11898944Sobrien cmderr(X_CMDERR, "group name required\n"); 11998944Sobrien else if (grp != NULL) /* Exists */ 12019370Spst cmderr(X_EXISTS, "group name `%s' already exists\n", a_name->val); 12119370Spst 12219370Spst memset(members, 0, sizeof members); 12319370Spst grp = &fakegroup; 12419370Spst grp->gr_name = a_name->val; 12519370Spst grp->gr_passwd = "*"; 12619370Spst grp->gr_gid = gr_gidpolicy(cnf, args); 12719370Spst grp->gr_mem = members; 12819370Spst } 12919370Spst 13019370Spst /* 13119370Spst * This allows us to set a group password Group passwords is an 13219370Spst * antique idea, rarely used and insecure (no secure database) Should 13319370Spst * be discouraged, but it is apparently still supported by some 13419370Spst * software. 13519370Spst */ 13619370Spst 13719370Spst if ((arg = getarg(args, 'h')) != NULL) { 13819370Spst if (strcmp(arg->val, "-") == 0) 13919370Spst grp->gr_passwd = "*"; /* No access */ 14019370Spst else { 14119370Spst int fd = atoi(arg->val); 14219370Spst int b; 14319370Spst int istty = isatty(fd); 14419370Spst struct termios t; 14519370Spst char *p, line[256]; 14619370Spst 14798944Sobrien if (istty) { 14819370Spst if (tcgetattr(fd, &t) == -1) 14919370Spst istty = 0; 15019370Spst else { 15119370Spst struct termios n = t; 15298944Sobrien 15398944Sobrien /* Disable echo */ 15419370Spst n.c_lflag &= ~(ECHO); 15519370Spst tcsetattr(fd, TCSANOW, &n); 15619370Spst printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name); 15719370Spst fflush(stdout); 15819370Spst } 15919370Spst } 16098944Sobrien b = read(fd, line, sizeof(line) - 1); 16119370Spst if (istty) { /* Restore state */ 16298944Sobrien tcsetattr(fd, TCSANOW, &t); 16319370Spst fputc('\n', stdout); 16498944Sobrien fflush(stdout); 16598944Sobrien } 16698944Sobrien if (b < 0) { 16798944Sobrien perror("-h file descriptor"); 16898944Sobrien return X_CMDERR; 16919370Spst } 17019370Spst line[b] = '\0'; 17119370Spst if ((p = strpbrk(line, " \t\r\n")) != NULL) 17219370Spst *p = '\0'; 17398944Sobrien if (!*line) 17419370Spst cmderr(X_CMDERR, "empty password read on file descriptor %d\n", fd); 17519370Spst grp->gr_passwd = pw_pwcrypt(line); 17619370Spst } 17719370Spst } 17819370Spst if ((mode == M_ADD && !addgrent(grp)) || (mode == M_UPDATE && !chggrent(a_name->val, grp))) { 17919370Spst perror("group update"); 18019370Spst return X_NOUPDATE; 18198944Sobrien } 18219370Spst /* grp may have been invalidated */ 18319370Spst if ((grp = getgrnam(a_name->val)) == NULL) 18419370Spst cmderr(X_NOTFOUND, "group disappeared during update\n"); 18519370Spst 18619370Spst pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid); 18719370Spst 18819370Spst return X_ALLOK; 18919370Spst} 19019370Spst 19119370Spst 19219370Spststatic gid_t 19319370Spstgr_gidpolicy(struct userconf * cnf, struct cargs * args) 19419370Spst{ 19598944Sobrien struct group *grp; 19698944Sobrien gid_t gid = (gid_t) - 1; 19719370Spst struct carg *a_gid = getarg(args, 'g'); 19819370Spst 19919370Spst /* 20019370Spst * Check the given gid, if any 20198944Sobrien */ 20219370Spst if (a_gid != NULL) { 20319370Spst gid = (gid_t) atol(a_gid->val); 20419370Spst 20519370Spst if ((grp = getgrgid(gid)) != NULL && getarg(args, 'o') == NULL) 20698944Sobrien cmderr(X_EXISTS, "gid `%ld' has already been allocated\n", (long) grp->gr_gid); 20798944Sobrien } else { 208130803Smarcel struct bitmap bm; 20998944Sobrien 21098944Sobrien /* 21198944Sobrien * We need to allocate the next available gid under one of 21298944Sobrien * two policies a) Grab the first unused gid b) Grab the 21398944Sobrien * highest possible unused gid 21498944Sobrien */ 21598944Sobrien if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */ 21698944Sobrien cnf->min_gid = 1000; 21798944Sobrien cnf->max_gid = 32000; 21898944Sobrien } 21998944Sobrien bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1); 22098944Sobrien 22198944Sobrien /* 22298944Sobrien * Now, let's fill the bitmap from the password file 22398944Sobrien */ 22498944Sobrien setgrent(); 22598944Sobrien while ((grp = getgrent()) != NULL) 22698944Sobrien if (grp->gr_gid >= (int) cnf->min_gid && grp->gr_gid <= (int) cnf->max_gid) 22798944Sobrien bm_setbit(&bm, grp->gr_gid - cnf->min_gid); 22898944Sobrien endgrent(); 22998944Sobrien 23098944Sobrien /* 23198944Sobrien * Then apply the policy, with fallback to reuse if necessary 23219370Spst */ 23319370Spst if (cnf->reuse_gids) 23419370Spst gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 23519370Spst else { 23698944Sobrien gid = (gid_t) (bm_lastset(&bm) + 1); 23798944Sobrien if (!bm_isset(&bm, gid)) 23898944Sobrien gid += cnf->min_gid; 23998944Sobrien else 24019370Spst gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid); 24119370Spst } 24219370Spst 24319370Spst /* 24419370Spst * Another sanity check 24598944Sobrien */ 24619370Spst if (gid < cnf->min_gid || gid > cnf->max_gid) 24798944Sobrien cmderr(X_EXISTS, "unable to allocate a new gid - range fully used\n"); 24898944Sobrien bm_dealloc(&bm); 24998944Sobrien } 25019370Spst return gid; 25119370Spst} 25219370Spst 25319370Spst 25498944Sobrienstatic int 25519370Spstprint_group(struct group * grp, int pretty) 256130803Smarcel{ 25798944Sobrien if (!pretty) { 25819370Spst char buf[_UC_MAXLINE]; 25919370Spst 26098944Sobrien fmtgrent(buf, grp); 261130803Smarcel fputs(buf, stdout); 26298944Sobrien } else { 26319370Spst int i; 26419370Spst 26598944Sobrien printf("Group Name : %-10s #%lu\n" 266130803Smarcel " Members : ", 26798944Sobrien grp->gr_name, (long) grp->gr_gid); 26819370Spst for (i = 0; i < _UC_MAXGROUPS && grp->gr_mem[i]; i++) 26919370Spst printf("%s%s", i ? "," : "", grp->gr_mem[i]); 27098944Sobrien fputs("\n\n", stdout); 271130803Smarcel } 27298944Sobrien return X_ALLOK; 27319370Spst} 27498944Sobrien