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$";
3030259Scharnier#endif /* not lint */
3130259Scharnier
3220253Sjoerg#include <stdio.h>
3320253Sjoerg#include <stdlib.h>
3420253Sjoerg#include <string.h>
3520253Sjoerg#include <unistd.h>
3620253Sjoerg#include <stdarg.h>
3720253Sjoerg#include <sys/types.h>
3820253Sjoerg#include <sys/stat.h>
3920253Sjoerg#include <pwd.h>
4020253Sjoerg#include <grp.h>
4120253Sjoerg#include <fcntl.h>
4220253Sjoerg#include <sys/param.h>
4320253Sjoerg#include <ctype.h>
4420253Sjoerg
4520253Sjoerg#include "pwupd.h"
4620253Sjoerg
4720253Sjoergstatic int
4820253Sjoergisingroup(char const * name, char **mem)
4920253Sjoerg{
5020253Sjoerg	int             i;
5120253Sjoerg
5220747Sdavidn	for (i = 0; mem[i] != NULL; i++)
5320253Sjoerg		if (strcmp(name, mem[i]) == 0)
5420253Sjoerg			return i;
5520253Sjoerg	return -1;
5620253Sjoerg}
5720253Sjoerg
5820253Sjoergint
5920253Sjoergeditgroups(char *name, char **groups)
6020253Sjoerg{
6120253Sjoerg	int             rc = 0;
6220253Sjoerg	int             infd;
6344229Sdavidn	char		groupfile[MAXPATHLEN];
6444229Sdavidn	char		grouptmp[MAXPATHLEN];
6520253Sjoerg
6644229Sdavidn	strncpy(groupfile, getgrpath(_GROUP), MAXPATHLEN - 5);
6744229Sdavidn	groupfile[MAXPATHLEN - 5] = '\0';
6844229Sdavidn	strcpy(grouptmp, groupfile);
6944229Sdavidn	strcat(grouptmp, ".new");
7044229Sdavidn
71126753Skensmith	if ((infd = open(groupfile, O_RDWR | O_CREAT | O_EXLOCK, 0644)) != -1) {
7220253Sjoerg		FILE           *infp;
7320253Sjoerg
7420253Sjoerg		if ((infp = fdopen(infd, "r+")) == NULL)
7520253Sjoerg			close(infd);
7620253Sjoerg		else {
7720253Sjoerg			int             outfd;
7820253Sjoerg
79126753Skensmith			if ((outfd = open(grouptmp, O_RDWR | O_CREAT | O_TRUNC, 0644)) != -1) {
8020253Sjoerg				FILE           *outfp;
8120253Sjoerg
8220253Sjoerg				if ((outfp = fdopen(outfd, "w+")) == NULL)
8320253Sjoerg					close(outfd);
8420253Sjoerg				else {
8520747Sdavidn					int		linelen = PWBUFSZ;
8620747Sdavidn					int		outlen =  PWBUFSZ;
8720747Sdavidn					int		memlen = 200; /* Arbitrary */
8820747Sdavidn					char           *line = malloc(linelen);
8920747Sdavidn					char           *outl = malloc(outlen);
9020747Sdavidn					char	      **mems = malloc(memlen * sizeof(char *));
9120747Sdavidn					int		namlen = strlen(name);
9220253Sjoerg
9320747Sdavidn					if (line == NULL || outl == NULL || mems == NULL) {
9420747Sdavidn					    mem_abort:
9520747Sdavidn						rc = 0;
9620747Sdavidn					} else {
9720747Sdavidn						while (fgets(line, linelen, infp) != NULL) {
9820747Sdavidn							char           *p;
9920747Sdavidn							int		l;
10020253Sjoerg
10120747Sdavidn							while ((p = strchr(line, '\n')) == NULL)
10220747Sdavidn							{
10320747Sdavidn								if (extendline(&line, &linelen, linelen + PWBUFSZ) == -1) {
10420747Sdavidn									goto mem_abort;
10520747Sdavidn								}
10620747Sdavidn								l = strlen(line);
10720747Sdavidn								if (fgets(line + l, linelen - l, infp) == NULL)
10820747Sdavidn									break;	/* No newline terminator on last line */
10920253Sjoerg							}
11020747Sdavidn							l = strlen(line) + namlen + 1;
11120747Sdavidn							if (extendline(&outl, &outlen, l) == -1) {
11220747Sdavidn								goto mem_abort;
11320747Sdavidn							}
11420747Sdavidn							if (*line == '#')
11520747Sdavidn								strcpy(outl, line);
11620747Sdavidn							else if (*line == '\n')
11720747Sdavidn								*outl = '\0';
11820747Sdavidn							else {
11920747Sdavidn								int             i,
12020747Sdavidn									        mno = 0;
12120747Sdavidn								char           *cp = line;
12220747Sdavidn								char const     *sep = ":\n";
12320747Sdavidn								struct group    grp;
12420253Sjoerg
12520747Sdavidn								memset(&grp, 0, sizeof grp);
12620747Sdavidn								for (i = 0; (p = strsep(&cp, sep)) != NULL; i++) {
12720747Sdavidn									switch (i) {
12820747Sdavidn									case 0:	/* Group name */
12920747Sdavidn										grp.gr_name = p;
13020747Sdavidn										break;
13120747Sdavidn									case 1:	/* Group password */
13220747Sdavidn										grp.gr_passwd = p;
13320747Sdavidn										break;
13420747Sdavidn									case 2:	/* Group id */
13520747Sdavidn										grp.gr_gid = atoi(p);
13620747Sdavidn										break;
13720747Sdavidn									case 3:	/* Member list */
13820747Sdavidn										cp = p;
13920747Sdavidn										sep = ",\n";
14020747Sdavidn										break;
14120747Sdavidn									default:	/* Individual members */
14220747Sdavidn										if (*p) {
14320747Sdavidn											if (extendarray(&mems, &memlen, mno + 2) == -1) {
14420747Sdavidn												goto mem_abort;
14520747Sdavidn											}
14620747Sdavidn											mems[mno++] = p;
14720747Sdavidn										}
14820747Sdavidn										break;
14920747Sdavidn									}
15020253Sjoerg								}
15120747Sdavidn								if (i < 2)	/* Bail out - insufficient fields */
15220747Sdavidn									continue;
15320253Sjoerg
15420747Sdavidn								grp.gr_mem = mems;
15520747Sdavidn								for (i = mno; i < memlen; i++)
15620253Sjoerg									mems[i] = NULL;
15720253Sjoerg
15820253Sjoerg								/*
15920747Sdavidn								 * Delete from group, or add to group?
16020253Sjoerg								 */
16120747Sdavidn								if (groups == NULL || isingroup(grp.gr_name, groups) == -1) {	/* Delete */
16220747Sdavidn									int             idx;
16320253Sjoerg
16420747Sdavidn									while ((idx = isingroup(name, mems)) != -1) {
16520747Sdavidn										for (i = idx; i < (memlen - 1); i++)
16620747Sdavidn											mems[i] = mems[i + 1];
16720747Sdavidn										mems[i] = NULL;
16820747Sdavidn										--mno;
16920747Sdavidn									}
17020747Sdavidn									/*
17120747Sdavidn									 * Special case - deleting user and group may be user's own
17220747Sdavidn									 */
17320747Sdavidn									if (groups == NULL && mems[0] == NULL && strcmp(name, grp.gr_name) == 0) {
17420747Sdavidn										/*
17520747Sdavidn										 * First, make _sure_ we don't have other members
17620747Sdavidn										 */
17720747Sdavidn										struct passwd  *pwd;
17820747Sdavidn
17944229Sdavidn										SETPWENT();
18044229Sdavidn										while ((pwd = GETPWENT()) != NULL && (gid_t)pwd->pw_gid != (gid_t)grp.gr_gid);
18144229Sdavidn										ENDPWENT();
18220747Sdavidn										if (pwd == NULL)	/* No members at all */
18320747Sdavidn											continue;	/* Drop the group */
18420747Sdavidn									}
18520747Sdavidn								} else if (isingroup(name, mems) == -1) {
18620747Sdavidn									if (extendarray(&mems, &memlen, mno + 2) == -1) {
18720747Sdavidn										goto mem_abort;
18820747Sdavidn									}
18920747Sdavidn									grp.gr_mem = mems;    /* May have realloced() */
19020747Sdavidn									mems[mno++] = name;
19120747Sdavidn									mems[mno  ] = NULL;
19220253Sjoerg								}
19320747Sdavidn								fmtgrentry(&outl, &outlen, &grp, PWF_GROUP);
19420747Sdavidn							}
19520747Sdavidn							fputs(outl, outfp);
19620253Sjoerg						}
19720747Sdavidn						if (fflush(outfp) != EOF) {
19820747Sdavidn							rc = 1;
19920253Sjoerg
20020747Sdavidn							/*
20120747Sdavidn							 * Copy data back into the original file and truncate
20220747Sdavidn							 */
20320747Sdavidn							rewind(infp);
20420747Sdavidn							rewind(outfp);
20520747Sdavidn							while (fgets(outl, outlen, outfp) != NULL)
20620747Sdavidn								fputs(outl, infp);
20720253Sjoerg
20820747Sdavidn							/*
20920747Sdavidn							 * This is a gross hack, but we may have corrupted the
210126753Skensmith							 * original file.
21120747Sdavidn							 */
21220747Sdavidn							if (fflush(infp) == EOF || ferror(infp))
21320747Sdavidn								rc = rename(grouptmp, groupfile) == 0;
21420747Sdavidn							else
21520747Sdavidn								ftruncate(infd, ftell(infp));
21620747Sdavidn						}
21720253Sjoerg					}
21820747Sdavidn					free(mems);
21920747Sdavidn					free(outl);
22020747Sdavidn			    		free(line);
22120253Sjoerg					fclose(outfp);
22220253Sjoerg				}
22320253Sjoerg				remove(grouptmp);
22420253Sjoerg			}
22520253Sjoerg			fclose(infp);
22620253Sjoerg		}
22720253Sjoerg	}
22820253Sjoerg	return rc;
22920253Sjoerg}
230