pw_group.c revision 285395
1163953Srrs/*-
2169382Srrs * Copyright (C) 1996
3163953Srrs *	David L. Nugent.  All rights reserved.
4163953Srrs *
5163953Srrs * Redistribution and use in source and binary forms, with or without
6163953Srrs * modification, are permitted provided that the following conditions
7163953Srrs * are met:
8163953Srrs * 1. Redistributions of source code must retain the above copyright
9163953Srrs *    notice, this list of conditions and the following disclaimer.
10163953Srrs * 2. Redistributions in binary form must reproduce the above copyright
11163953Srrs *    notice, this list of conditions and the following disclaimer in the
12163953Srrs *    documentation and/or other materials provided with the distribution.
13163953Srrs *
14163953Srrs * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15163953Srrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16163953Srrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17163953Srrs * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18163953Srrs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19163953Srrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20163953Srrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21163953Srrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22163953Srrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23163953Srrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24163953Srrs * SUCH DAMAGE.
25163953Srrs */
26163953Srrs
27163953Srrs#ifndef lint
28163953Srrsstatic const char rcsid[] =
29163953Srrs  "$FreeBSD: head/usr.sbin/pw/pw_group.c 285395 2015-07-11 16:58:47Z bapt $";
30163953Srrs#endif /* not lint */
31163953Srrs
32163953Srrs#include <ctype.h>
33163953Srrs#include <err.h>
34163953Srrs#include <termios.h>
35163953Srrs#include <stdbool.h>
36163953Srrs#include <unistd.h>
37163953Srrs#include <grp.h>
38163953Srrs#include <libutil.h>
39167598Srrs
40167598Srrs#include "pw.h"
41163953Srrs#include "bitmap.h"
42167598Srrs
43168299Srrs
44167598Srrsstatic struct passwd *lookup_pwent(const char *user);
45167598Srrsstatic void	delete_members(char ***members, int *grmembers, int *i,
46167598Srrs    struct carg *arg, struct group *grp);
47167598Srrsstatic int	print_group(struct group * grp);
48168299Srrsstatic gid_t    gr_gidpolicy(struct userconf * cnf, long id);
49168299Srrs
50167598Srrsstatic void
51167598Srrsset_passwd(struct group *grp, bool update)
52167598Srrs{
53168299Srrs	int		 b;
54168299Srrs	int		 istty;
55168299Srrs	struct termios	 t, n;
56168299Srrs	char		*p, line[256];
57167598Srrs
58168299Srrs	if (conf.fd == '-') {
59167598Srrs		grp->gr_passwd = "*";	/* No access */
60167598Srrs		return;
61167598Srrs	}
62167598Srrs
63170056Srrs	if ((istty = isatty(conf.fd))) {
64170181Srrs		n = t;
65170056Srrs		/* Disable echo */
66167598Srrs		n.c_lflag &= ~(ECHO);
67167598Srrs		tcsetattr(conf.fd, TCSANOW, &n);
68167598Srrs		printf("%sassword for group %s:", update ? "New p" : "P",
69167598Srrs		    grp->gr_name);
70167598Srrs		fflush(stdout);
71163953Srrs	}
72163953Srrs	b = read(conf.fd, line, sizeof(line) - 1);
73163953Srrs	if (istty) {	/* Restore state */
74163953Srrs		tcsetattr(conf.fd, TCSANOW, &t);
75167598Srrs		fputc('\n', stdout);
76167598Srrs		fflush(stdout);
77167598Srrs	}
78167598Srrs	if (b < 0)
79167598Srrs		err(EX_OSERR, "-h file descriptor");
80167598Srrs	line[b] = '\0';
81167598Srrs	if ((p = strpbrk(line, " \t\r\n")) != NULL)
82167598Srrs		*p = '\0';
83167598Srrs	if (!*line)
84167598Srrs		errx(EX_DATAERR, "empty password read on file descriptor %d",
85167598Srrs		    conf.fd);
86171477Srrs	if (conf.precrypted) {
87171477Srrs		if (strchr(line, ':') != 0)
88171477Srrs			errx(EX_DATAERR, "wrong encrypted passwrd");
89171477Srrs		grp->gr_passwd = line;
90171440Srrs	} else
91171440Srrs		grp->gr_passwd = pw_pwcrypt(line);
92171440Srrs}
93171440Srrs
94171440Srrsint
95163953Srrspw_groupnext(struct userconf *cnf, bool quiet)
96163953Srrs{
97163953Srrs	gid_t next = gr_gidpolicy(cnf, -1);
98163953Srrs
99163953Srrs	if (quiet)
100163953Srrs		return (next);
101163953Srrs	printf("%u\n", next);
102163953Srrs
103163953Srrs	return (EXIT_SUCCESS);
104163953Srrs}
105163953Srrs
106163953Srrsint
107163953Srrspw_group(int mode, char *name, long id, struct cargs * args)
108163953Srrs{
109163953Srrs	int		rc;
110163953Srrs	struct carg    *arg;
111163953Srrs	struct group   *grp = NULL;
112163953Srrs	int	        grmembers = 0;
113163953Srrs	char           **members = NULL;
114163953Srrs	struct userconf	*cnf = conf.userconf;
115163953Srrs
116163953Srrs	static struct group fakegroup =
117163953Srrs	{
118163953Srrs		"nogroup",
119163953Srrs		"*",
120163953Srrs		-1,
121163953Srrs		NULL
122163953Srrs	};
123163953Srrs
124163953Srrs	if (mode == M_NEXT)
125163953Srrs		return (pw_groupnext(cnf, getarg(args, 'q') != NULL));
126163953Srrs
127163953Srrs	if (mode == M_LOCK || mode == M_UNLOCK)
128163953Srrs		errx(EX_USAGE, "'lock' command is not available for groups");
129163953Srrs
130163953Srrs	if (mode == M_PRINT && getarg(args, 'a')) {
131163953Srrs		SETGRENT();
132163953Srrs		while ((grp = GETGRENT()) != NULL)
133163953Srrs			print_group(grp);
134163953Srrs		ENDGRENT();
135163953Srrs		return EXIT_SUCCESS;
136163953Srrs	}
137163953Srrs	if (id < 0 && name == NULL)
138163953Srrs		errx(EX_DATAERR, "group name or id required");
139163953Srrs
140163953Srrs	grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id);
141163953Srrs
142163953Srrs	if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
143163953Srrs		if (name == NULL && grp == NULL)	/* Try harder */
144163953Srrs			grp = GETGRGID(id);
145163953Srrs
146163953Srrs		if (grp == NULL) {
147163953Srrs			if (mode == M_PRINT && getarg(args, 'F')) {
148163953Srrs				char	*fmems[1];
149163953Srrs				fmems[0] = NULL;
150163953Srrs				fakegroup.gr_name = name ? name : "nogroup";
151163953Srrs				fakegroup.gr_gid = (gid_t) id;
152163953Srrs				fakegroup.gr_mem = fmems;
153163953Srrs				return print_group(&fakegroup);
154163953Srrs			}
155163953Srrs			if (name == NULL)
156163953Srrs				errx(EX_DATAERR, "unknown group `%s'", name);
157163953Srrs			else
158163953Srrs				errx(EX_DATAERR, "unknown group `%ld'", id);
159163953Srrs		}
160163953Srrs		if (name == NULL)	/* Needed later */
161163953Srrs			name = grp->gr_name;
162163953Srrs
163163953Srrs		/*
164163953Srrs		 * Handle deletions now
165163953Srrs		 */
166163953Srrs		if (mode == M_DELETE) {
167163953Srrs			rc = delgrent(grp);
168163953Srrs			if (rc == -1)
169163953Srrs				err(EX_IOERR, "group '%s' not available (NIS?)",
170163953Srrs				    name);
171163953Srrs			else if (rc != 0) {
172163953Srrs				err(EX_IOERR, "group update");
173163953Srrs			}
174163953Srrs			pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", name, id);
175163953Srrs			return EXIT_SUCCESS;
176163953Srrs		} else if (mode == M_PRINT)
177163953Srrs			return print_group(grp);
178163953Srrs
179163953Srrs		if (id > 0)
180163953Srrs			grp->gr_gid = (gid_t) id;
181163953Srrs
182163953Srrs		if (conf.newname != NULL)
183163953Srrs			grp->gr_name = pw_checkname(conf.newname, 0);
184163953Srrs	} else {
185163953Srrs		if (name == NULL)	/* Required */
186163953Srrs			errx(EX_DATAERR, "group name required");
187163953Srrs		else if (grp != NULL)	/* Exists */
188163953Srrs			errx(EX_DATAERR, "group name `%s' already exists", name);
189163953Srrs
190163953Srrs		extendarray(&members, &grmembers, 200);
191163953Srrs		members[0] = NULL;
192163953Srrs		grp = &fakegroup;
193163953Srrs		grp->gr_name = pw_checkname(name, 0);
194163953Srrs		grp->gr_passwd = "*";
195163953Srrs		grp->gr_gid = gr_gidpolicy(cnf, id);
196163953Srrs		grp->gr_mem = members;
197163953Srrs	}
198164181Srrs
199164181Srrs	/*
200168709Srrs	 * This allows us to set a group password Group passwords is an
201165220Srrs	 * antique idea, rarely used and insecure (no secure database) Should
202168709Srrs	 * be discouraged, but it is apparently still supported by some
203168709Srrs	 * software.
204168709Srrs	 */
205168709Srrs
206168709Srrs	if (conf.which == W_GROUP && conf.fd != -1)
207168709Srrs		set_passwd(grp, mode == M_UPDATE);
208168709Srrs
209168709Srrs	if (((arg = getarg(args, 'M')) != NULL ||
210169208Srrs	    (arg = getarg(args, 'd')) != NULL ||
211169208Srrs	    (arg = getarg(args, 'm')) != NULL) && arg->val) {
212169208Srrs		int	i = 0;
213169208Srrs		char   *p;
214163953Srrs		struct passwd	*pwd;
215170744Srrs
216170744Srrs		/* Make sure this is not stay NULL with -M "" */
217170744Srrs		extendarray(&members, &grmembers, 200);
218170744Srrs		if (arg->ch == 'd')
219163953Srrs			delete_members(&members, &grmembers, &i, arg, grp);
220170744Srrs		else if (arg->ch == 'm') {
221170744Srrs			int	k = 0;
222163953Srrs
223163953Srrs			if (grp->gr_mem != NULL) {
224163953Srrs				while (grp->gr_mem[k] != NULL) {
225163953Srrs					if (extendarray(&members, &grmembers, i + 2) != -1)
226163953Srrs						members[i++] = grp->gr_mem[k];
227163953Srrs					k++;
228163953Srrs				}
229163953Srrs			}
230163953Srrs		}
231163953Srrs
232163953Srrs		if (arg->ch != 'd')
233163953Srrs			for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
234163953Srrs				int	j;
235163953Srrs
236163953Srrs				/*
237163953Srrs				 * Check for duplicates
238163953Srrs				 */
239163953Srrs				pwd = lookup_pwent(p);
240163953Srrs				for (j = 0; j < i && strcmp(members[j], pwd->pw_name) != 0; j++)
241170744Srrs					;
242170744Srrs				if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
243163953Srrs					members[i++] = newstr(pwd->pw_name);
244170744Srrs			}
245163953Srrs		while (i < grmembers)
246163953Srrs			members[i++] = NULL;
247163953Srrs		grp->gr_mem = members;
248163953Srrs	}
249163953Srrs
250163953Srrs	if (conf.dryrun)
251163953Srrs		return print_group(grp);
252163953Srrs
253163953Srrs	if (mode == M_ADD && (rc = addgrent(grp)) != 0) {
254163953Srrs		if (rc == -1)
255167598Srrs			errx(EX_IOERR, "group '%s' already exists",
256167598Srrs			    grp->gr_name);
257167598Srrs		else
258167598Srrs			err(EX_IOERR, "group update");
259167598Srrs	} else if (mode == M_UPDATE && (rc = chggrent(name, grp)) != 0) {
260163953Srrs		if (rc == -1)
261163953Srrs			errx(EX_IOERR, "group '%s' not available (NIS?)",
262163953Srrs			    grp->gr_name);
263163953Srrs		else
264163953Srrs			err(EX_IOERR, "group update");
265163953Srrs	}
266163953Srrs
267163953Srrs	if (conf.newname != NULL)
268163953Srrs		name = conf.newname;
269163953Srrs	/* grp may have been invalidated */
270163953Srrs	if ((grp = GETGRNAM(name)) == NULL)
271163953Srrs		errx(EX_SOFTWARE, "group disappeared during update");
272163953Srrs
273163953Srrs	pw_log(cnf, mode, W_GROUP, "%s(%u)", grp->gr_name, grp->gr_gid);
274163953Srrs
275163953Srrs	free(members);
276163953Srrs
277163953Srrs	return EXIT_SUCCESS;
278163953Srrs}
279163953Srrs
280163953Srrs
281163953Srrs/*
282163953Srrs * Lookup a passwd entry using a name or UID.
283163953Srrs */
284163953Srrsstatic struct passwd *
285163953Srrslookup_pwent(const char *user)
286163953Srrs{
287163953Srrs	struct passwd *pwd;
288163953Srrs
289163953Srrs	if ((pwd = GETPWNAM(user)) == NULL &&
290163953Srrs	    (!isdigit((unsigned char)*user) ||
291163953Srrs	    (pwd = getpwuid((uid_t) atoi(user))) == NULL))
292163953Srrs		errx(EX_NOUSER, "user `%s' does not exist", user);
293163953Srrs
294163953Srrs	return (pwd);
295163953Srrs}
296163953Srrs
297163953Srrs
298163953Srrs/*
299163953Srrs * Delete requested members from a group.
300163953Srrs */
301163953Srrsstatic void
302163953Srrsdelete_members(char ***members, int *grmembers, int *i, struct carg *arg,
303163953Srrs    struct group *grp)
304163953Srrs{
305163953Srrs	bool matchFound;
306163953Srrs	char *user;
307163953Srrs	char *valueCopy;
308163953Srrs	char *valuePtr;
309163953Srrs	int k;
310163953Srrs	struct passwd *pwd;
311163953Srrs
312163953Srrs	if (grp->gr_mem == NULL)
313163953Srrs		return;
314163953Srrs
315163953Srrs	k = 0;
316163953Srrs	while (grp->gr_mem[k] != NULL) {
317163953Srrs		matchFound = false;
318163953Srrs		if ((valueCopy = strdup(arg->val)) == NULL)
319163953Srrs			errx(EX_UNAVAILABLE, "out of memory");
320163953Srrs		valuePtr = valueCopy;
321163953Srrs		while ((user = strsep(&valuePtr, ", \t")) != NULL) {
322163953Srrs			pwd = lookup_pwent(user);
323163953Srrs			if (strcmp(grp->gr_mem[k], pwd->pw_name) == 0) {
324163953Srrs				matchFound = true;
325163953Srrs				break;
326163953Srrs			}
327163953Srrs		}
328163953Srrs		free(valueCopy);
329163953Srrs
330163953Srrs		if (!matchFound &&
331163953Srrs		    extendarray(members, grmembers, *i + 2) != -1)
332163953Srrs			(*members)[(*i)++] = grp->gr_mem[k];
333163953Srrs
334163953Srrs		k++;
335163953Srrs	}
336163953Srrs
337163953Srrs	return;
338163953Srrs}
339163953Srrs
340163953Srrs
341163953Srrsstatic          gid_t
342163953Srrsgr_gidpolicy(struct userconf * cnf, long id)
343163953Srrs{
344163953Srrs	struct group   *grp;
345163953Srrs	gid_t           gid = (gid_t) - 1;
346163953Srrs
347163953Srrs	/*
348163953Srrs	 * Check the given gid, if any
349163953Srrs	 */
350163953Srrs	if (id > 0) {
351163953Srrs		gid = (gid_t) id;
352163953Srrs
353163953Srrs		if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate)
354163953Srrs			errx(EX_DATAERR, "gid `%u' has already been allocated", grp->gr_gid);
355163953Srrs	} else {
356163953Srrs		struct bitmap   bm;
357163953Srrs
358163953Srrs		/*
359163953Srrs		 * We need to allocate the next available gid under one of
360163953Srrs		 * two policies a) Grab the first unused gid b) Grab the
361163953Srrs		 * highest possible unused gid
362163953Srrs		 */
363163953Srrs		if (cnf->min_gid >= cnf->max_gid) {	/* Sanity claus^H^H^H^Hheck */
364163953Srrs			cnf->min_gid = 1000;
365163953Srrs			cnf->max_gid = 32000;
366163953Srrs		}
367163953Srrs		bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
368163953Srrs
369163953Srrs		/*
370163953Srrs		 * Now, let's fill the bitmap from the password file
371163953Srrs		 */
372163953Srrs		SETGRENT();
373163953Srrs		while ((grp = GETGRENT()) != NULL)
374163953Srrs			if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid &&
375164205Srrs                            (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid)
376167598Srrs				bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
377163953Srrs		ENDGRENT();
378163953Srrs
379163953Srrs		/*
380163953Srrs		 * Then apply the policy, with fallback to reuse if necessary
381163953Srrs		 */
382163953Srrs		if (cnf->reuse_gids)
383163953Srrs			gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
384163953Srrs		else {
385163953Srrs			gid = (gid_t) (bm_lastset(&bm) + 1);
386163953Srrs			if (!bm_isset(&bm, gid))
387163953Srrs				gid += cnf->min_gid;
388163953Srrs			else
389163953Srrs				gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
390163953Srrs		}
391163953Srrs
392163953Srrs		/*
393163953Srrs		 * Another sanity check
394163953Srrs		 */
395163953Srrs		if (gid < cnf->min_gid || gid > cnf->max_gid)
396163953Srrs			errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
397163953Srrs		bm_dealloc(&bm);
398163953Srrs	}
399163953Srrs	return gid;
400163953Srrs}
401163953Srrs
402163953Srrs
403163953Srrsstatic int
404163953Srrsprint_group(struct group * grp)
405163953Srrs{
406163953Srrs	if (!conf.pretty) {
407163953Srrs		char           *buf = NULL;
408163953Srrs
409163953Srrs		buf = gr_make(grp);
410163953Srrs		printf("%s\n", buf);
411163953Srrs		free(buf);
412163953Srrs	} else {
413163953Srrs		int             i;
414163953Srrs
415163953Srrs		printf("Group Name: %-15s   #%lu\n"
416163953Srrs		       "   Members: ",
417163953Srrs		       grp->gr_name, (long) grp->gr_gid);
418163953Srrs		if (grp->gr_mem != NULL) {
419163953Srrs			for (i = 0; grp->gr_mem[i]; i++)
420163953Srrs				printf("%s%s", i ? "," : "", grp->gr_mem[i]);
421163953Srrs		}
422163953Srrs		fputs("\n\n", stdout);
423163953Srrs	}
424163953Srrs	return EXIT_SUCCESS;
425163953Srrs}
426163953Srrs