pw_group.c revision 50479
1252190Srpaulo/*-
2252190Srpaulo * Copyright (C) 1996
3252190Srpaulo *	David L. Nugent.  All rights reserved.
4252190Srpaulo *
5252190Srpaulo * Redistribution and use in source and binary forms, with or without
6252190Srpaulo * modification, are permitted provided that the following conditions
7252190Srpaulo * are met:
8252190Srpaulo * 1. Redistributions of source code must retain the above copyright
9252190Srpaulo *    notice, this list of conditions and the following disclaimer.
10252190Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11252190Srpaulo *    notice, this list of conditions and the following disclaimer in the
12252190Srpaulo *    documentation and/or other materials provided with the distribution.
13252190Srpaulo *
14252190Srpaulo * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15252190Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16252190Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17252190Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18252190Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19252190Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20252190Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21252190Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22252190Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23252190Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24252190Srpaulo * SUCH DAMAGE.
25252190Srpaulo */
26252190Srpaulo
27252190Srpaulo#ifndef lint
28252190Srpaulostatic const char rcsid[] =
29281806Srpaulo  "$FreeBSD: head/usr.sbin/pw/pw_group.c 50479 1999-08-28 01:35:59Z peter $";
30252190Srpaulo#endif /* not lint */
31252190Srpaulo
32252190Srpaulo#include <ctype.h>
33252190Srpaulo#include <err.h>
34252190Srpaulo#include <termios.h>
35252190Srpaulo#include <unistd.h>
36252190Srpaulo
37252190Srpaulo#include "pw.h"
38281806Srpaulo#include "bitmap.h"
39252190Srpaulo
40281806Srpaulo
41252190Srpaulostatic int      print_group(struct group * grp, int pretty);
42252190Srpaulostatic gid_t    gr_gidpolicy(struct userconf * cnf, struct cargs * args);
43252190Srpaulo
44252190Srpauloint
45252190Srpaulopw_group(struct userconf * cnf, int mode, struct cargs * args)
46252190Srpaulo{
47252190Srpaulo	struct carg    *a_name = getarg(args, 'n');
48252190Srpaulo	struct carg    *a_gid = getarg(args, 'g');
49252190Srpaulo	struct carg    *arg;
50252190Srpaulo	struct group   *grp = NULL;
51252190Srpaulo	int	        grmembers = 0;
52252190Srpaulo	char          **members = NULL;
53252190Srpaulo
54252190Srpaulo	static struct group fakegroup =
55252190Srpaulo	{
56252190Srpaulo		"nogroup",
57252190Srpaulo		"*",
58252190Srpaulo		-1,
59252190Srpaulo		NULL
60281806Srpaulo	};
61281806Srpaulo
62252190Srpaulo	/*
63281806Srpaulo	 * With M_NEXT, we only need to return the
64281806Srpaulo	 * next gid to stdout
65281806Srpaulo	 */
66252190Srpaulo	if (mode == M_NEXT)
67252190Srpaulo	{
68252190Srpaulo		gid_t next = gr_gidpolicy(cnf, args);
69252190Srpaulo		if (getarg(args, 'q'))
70252190Srpaulo			return next;
71252190Srpaulo		printf("%ld\n", (long)next);
72252190Srpaulo		return EXIT_SUCCESS;
73252190Srpaulo	}
74252190Srpaulo
75252190Srpaulo	if (mode == M_PRINT && getarg(args, 'a')) {
76252190Srpaulo		int             pretty = getarg(args, 'P') != NULL;
77281806Srpaulo
78252190Srpaulo		SETGRENT();
79252190Srpaulo		while ((grp = GETGRENT()) != NULL)
80252190Srpaulo			print_group(grp, pretty);
81252190Srpaulo		ENDGRENT();
82252190Srpaulo		return EXIT_SUCCESS;
83252190Srpaulo	}
84252190Srpaulo	if (a_gid == NULL) {
85252190Srpaulo		if (a_name == NULL)
86252190Srpaulo			errx(EX_DATAERR, "group name or id required");
87252190Srpaulo
88252190Srpaulo		if (mode != M_ADD && grp == NULL && isdigit(*a_name->val)) {
89252190Srpaulo			(a_gid = a_name)->ch = 'g';
90252190Srpaulo			a_name = NULL;
91252190Srpaulo		}
92281806Srpaulo	}
93281806Srpaulo	grp = (a_name != NULL) ? GETGRNAM(a_name->val) : GETGRGID((gid_t) atoi(a_gid->val));
94252190Srpaulo
95252190Srpaulo	if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
96281806Srpaulo		if (a_name == NULL && grp == NULL)	/* Try harder */
97281806Srpaulo			grp = GETGRGID(atoi(a_gid->val));
98252190Srpaulo
99252190Srpaulo		if (grp == NULL) {
100252190Srpaulo			if (mode == M_PRINT && getarg(args, 'F')) {
101252190Srpaulo				char	*fmems[1];
102252190Srpaulo				fmems[0] = NULL;
103252190Srpaulo				fakegroup.gr_name = a_name ? a_name->val : "nogroup";
104252190Srpaulo				fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
105252190Srpaulo				fakegroup.gr_mem = fmems;
106252190Srpaulo				return print_group(&fakegroup, getarg(args, 'P') != NULL);
107252190Srpaulo			}
108281806Srpaulo			errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val);
109252190Srpaulo		}
110252190Srpaulo		if (a_name == NULL)	/* Needed later */
111252190Srpaulo			a_name = addarg(args, 'n', grp->gr_name);
112252190Srpaulo
113252190Srpaulo		/*
114252190Srpaulo		 * Handle deletions now
115281806Srpaulo		 */
116281806Srpaulo		if (mode == M_DELETE) {
117281806Srpaulo			gid_t           gid = grp->gr_gid;
118252190Srpaulo
119281806Srpaulo			if (delgrent(grp) == -1)
120252190Srpaulo				err(EX_IOERR, "error updating group file");
121281806Srpaulo			pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
122252190Srpaulo			return EXIT_SUCCESS;
123252190Srpaulo		} else if (mode == M_PRINT)
124252190Srpaulo			return print_group(grp, getarg(args, 'P') != NULL);
125252190Srpaulo
126252190Srpaulo		if (a_gid)
127252190Srpaulo			grp->gr_gid = (gid_t) atoi(a_gid->val);
128252190Srpaulo
129252190Srpaulo		if ((arg = getarg(args, 'l')) != NULL)
130281806Srpaulo			grp->gr_name = pw_checkname((u_char *)arg->val, 0);
131281806Srpaulo	} else {
132281806Srpaulo		if (a_name == NULL)	/* Required */
133252190Srpaulo			errx(EX_DATAERR, "group name required");
134281806Srpaulo		else if (grp != NULL)	/* Exists */
135252190Srpaulo			errx(EX_DATAERR, "group name `%s' already exists", a_name->val);
136252190Srpaulo
137252190Srpaulo		extendarray(&members, &grmembers, 200);
138252190Srpaulo		members[0] = NULL;
139252190Srpaulo		grp = &fakegroup;
140252190Srpaulo		grp->gr_name = pw_checkname((u_char *)a_name->val, 0);
141252190Srpaulo		grp->gr_passwd = "*";
142252190Srpaulo		grp->gr_gid = gr_gidpolicy(cnf, args);
143252190Srpaulo		grp->gr_mem = members;
144252190Srpaulo	}
145252190Srpaulo
146252190Srpaulo	/*
147252190Srpaulo	 * This allows us to set a group password Group passwords is an
148252190Srpaulo	 * antique idea, rarely used and insecure (no secure database) Should
149252190Srpaulo	 * be discouraged, but it is apparently still supported by some
150281806Srpaulo	 * software.
151281806Srpaulo	 */
152281806Srpaulo
153252190Srpaulo	if ((arg = getarg(args, 'h')) != NULL) {
154252190Srpaulo		if (strcmp(arg->val, "-") == 0)
155252190Srpaulo			grp->gr_passwd = "*";	/* No access */
156252190Srpaulo		else {
157252190Srpaulo			int             fd = atoi(arg->val);
158252190Srpaulo			int             b;
159252190Srpaulo			int             istty = isatty(fd);
160252190Srpaulo			struct termios  t;
161252190Srpaulo			char           *p, line[256];
162252190Srpaulo
163252190Srpaulo			if (istty) {
164252190Srpaulo				if (tcgetattr(fd, &t) == -1)
165252190Srpaulo					istty = 0;
166252190Srpaulo				else {
167252190Srpaulo					struct termios  n = t;
168252190Srpaulo
169252190Srpaulo					/* Disable echo */
170252190Srpaulo					n.c_lflag &= ~(ECHO);
171281806Srpaulo					tcsetattr(fd, TCSANOW, &n);
172281806Srpaulo					printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
173281806Srpaulo					fflush(stdout);
174252190Srpaulo				}
175252190Srpaulo			}
176252190Srpaulo			b = read(fd, line, sizeof(line) - 1);
177252190Srpaulo			if (istty) {	/* Restore state */
178252190Srpaulo				tcsetattr(fd, TCSANOW, &t);
179252190Srpaulo				fputc('\n', stdout);
180252190Srpaulo				fflush(stdout);
181252190Srpaulo			}
182252190Srpaulo			if (b < 0) {
183252190Srpaulo				warn("-h file descriptor");
184252190Srpaulo				return EX_OSERR;
185252190Srpaulo			}
186252190Srpaulo			line[b] = '\0';
187252190Srpaulo			if ((p = strpbrk(line, " \t\r\n")) != NULL)
188252190Srpaulo				*p = '\0';
189281806Srpaulo			if (!*line)
190252190Srpaulo				errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
191281806Srpaulo			grp->gr_passwd = pw_pwcrypt(line);
192281806Srpaulo		}
193252190Srpaulo	}
194281806Srpaulo
195281806Srpaulo	if (((arg = getarg(args, 'M')) != NULL || (arg = getarg(args, 'm')) != NULL) && arg->val) {
196281806Srpaulo		int	i = 0;
197281806Srpaulo		char   *p;
198281806Srpaulo		struct passwd	*pwd;
199281806Srpaulo
200252190Srpaulo		/* Make sure this is not stay NULL with -M "" */
201252190Srpaulo		extendarray(&members, &grmembers, 200);
202252190Srpaulo		if (arg->ch == 'm') {
203252190Srpaulo			int	k = 0;
204252190Srpaulo
205252190Srpaulo			while (grp->gr_mem[k] != NULL) {
206252190Srpaulo				if (extendarray(&members, &grmembers, i + 2) != -1) {
207252190Srpaulo					members[i++] = grp->gr_mem[k];
208252190Srpaulo				}
209252190Srpaulo				k++;
210252190Srpaulo			}
211252190Srpaulo		}
212252190Srpaulo		for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
213252190Srpaulo			int     j;
214252190Srpaulo			if ((pwd = GETPWNAM(p)) == NULL) {
215252190Srpaulo				if (!isdigit(*p) || (pwd = getpwuid((uid_t) atoi(p))) == NULL)
216252190Srpaulo					errx(EX_NOUSER, "user `%s' does not exist", p);
217252190Srpaulo			}
218252190Srpaulo			/*
219252190Srpaulo			 * Check for duplicates
220252190Srpaulo			 */
221252190Srpaulo			for (j = 0; j < i && strcmp(members[j], pwd->pw_name)!=0; j++)
222281806Srpaulo				;
223281806Srpaulo			if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
224252190Srpaulo				members[i++] = newstr(pwd->pw_name);
225281806Srpaulo		}
226281806Srpaulo		while (i < grmembers)
227252190Srpaulo			members[i++] = NULL;
228252190Srpaulo		grp->gr_mem = members;
229252190Srpaulo	}
230252190Srpaulo
231252190Srpaulo	if (getarg(args, 'N') != NULL)
232252190Srpaulo		return print_group(grp, getarg(args, 'P') != NULL);
233281806Srpaulo
234281806Srpaulo	if ((mode == M_ADD && !addgrent(grp)) || (mode == M_UPDATE && !chggrent(a_name->val, grp))) {
235281806Srpaulo		warn("group update");
236252190Srpaulo		return EX_IOERR;
237252190Srpaulo	}
238252190Srpaulo	/* grp may have been invalidated */
239252190Srpaulo	if ((grp = GETGRNAM(a_name->val)) == NULL)
240252190Srpaulo		errx(EX_SOFTWARE, "group disappeared during update");
241252190Srpaulo
242252190Srpaulo	pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
243252190Srpaulo
244252190Srpaulo	if (members)
245252190Srpaulo		free(members);
246252190Srpaulo
247252190Srpaulo	return EXIT_SUCCESS;
248252190Srpaulo}
249252190Srpaulo
250252190Srpaulo
251252190Srpaulostatic          gid_t
252252190Srpaulogr_gidpolicy(struct userconf * cnf, struct cargs * args)
253252190Srpaulo{
254252190Srpaulo	struct group   *grp;
255252190Srpaulo	gid_t           gid = (gid_t) - 1;
256252190Srpaulo	struct carg    *a_gid = getarg(args, 'g');
257252190Srpaulo
258252190Srpaulo	/*
259252190Srpaulo	 * Check the given gid, if any
260252190Srpaulo	 */
261252190Srpaulo	if (a_gid != NULL) {
262252190Srpaulo		gid = (gid_t) atol(a_gid->val);
263252190Srpaulo
264252190Srpaulo		if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL)
265252190Srpaulo			errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid);
266252190Srpaulo	} else {
267281806Srpaulo		struct bitmap   bm;
268281806Srpaulo
269252190Srpaulo		/*
270281806Srpaulo		 * We need to allocate the next available gid under one of
271252190Srpaulo		 * two policies a) Grab the first unused gid b) Grab the
272252190Srpaulo		 * highest possible unused gid
273281806Srpaulo		 */
274252190Srpaulo		if (cnf->min_gid >= cnf->max_gid) {	/* Sanity claus^H^H^H^Hheck */
275252190Srpaulo			cnf->min_gid = 1000;
276281806Srpaulo			cnf->max_gid = 32000;
277252190Srpaulo		}
278252190Srpaulo		bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
279252190Srpaulo
280252190Srpaulo		/*
281252190Srpaulo		 * Now, let's fill the bitmap from the password file
282252190Srpaulo		 */
283252190Srpaulo		SETGRENT();
284281806Srpaulo		while ((grp = GETGRENT()) != NULL)
285281806Srpaulo			if (grp->gr_gid >= (int) cnf->min_gid && grp->gr_gid <= (int) cnf->max_gid)
286281806Srpaulo				bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
287281806Srpaulo		ENDGRENT();
288252190Srpaulo
289252190Srpaulo		/*
290252190Srpaulo		 * Then apply the policy, with fallback to reuse if necessary
291252190Srpaulo		 */
292252190Srpaulo		if (cnf->reuse_gids)
293252190Srpaulo			gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
294252190Srpaulo		else {
295252190Srpaulo			gid = (gid_t) (bm_lastset(&bm) + 1);
296252190Srpaulo			if (!bm_isset(&bm, gid))
297252190Srpaulo				gid += cnf->min_gid;
298252190Srpaulo			else
299252190Srpaulo				gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
300252190Srpaulo		}
301252190Srpaulo
302252190Srpaulo		/*
303252190Srpaulo		 * Another sanity check
304252190Srpaulo		 */
305252190Srpaulo		if (gid < cnf->min_gid || gid > cnf->max_gid)
306252190Srpaulo			errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
307252190Srpaulo		bm_dealloc(&bm);
308252190Srpaulo	}
309252190Srpaulo	return gid;
310252190Srpaulo}
311252190Srpaulo
312252190Srpaulo
313252190Srpaulostatic int
314252190Srpauloprint_group(struct group * grp, int pretty)
315252190Srpaulo{
316252190Srpaulo	if (!pretty) {
317252190Srpaulo		int		buflen = 0;
318252190Srpaulo		char           *buf = NULL;
319252190Srpaulo
320252190Srpaulo		fmtgrent(&buf, &buflen, grp);
321252190Srpaulo		fputs(buf, stdout);
322252190Srpaulo		free(buf);
323252190Srpaulo	} else {
324281806Srpaulo		int             i;
325281806Srpaulo
326281806Srpaulo		printf("Group Name: %-15s   #%lu\n"
327281806Srpaulo		       "   Members: ",
328281806Srpaulo		       grp->gr_name, (long) grp->gr_gid);
329252190Srpaulo		for (i = 0; grp->gr_mem[i]; i++)
330252190Srpaulo			printf("%s%s", i ? "," : "", grp->gr_mem[i]);
331252190Srpaulo		fputs("\n\n", stdout);
332281806Srpaulo	}
333281806Srpaulo	return EXIT_SUCCESS;
334252190Srpaulo}
335252190Srpaulo