xref: /illumos-gate/usr/src/cmd/grpck/grpck.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <pwd.h>
39 #include <errno.h>
40 #include <locale.h>
41 #include <limits.h>
42 
43 #define	BADLINE "Too many/few fields"
44 #define	TOOLONG "Line too long"
45 #define	NONAME	"No group name"
46 #define	BADNAME "Bad character(s) in group name"
47 #define	BADGID  "Invalid GID"
48 #define	NULLNAME "Null login name"
49 #define	NOTFOUND "Logname not found in password file"
50 #define	DUPNAME "Duplicate logname entry"
51 #define	DUPNAME2 "Duplicate logname entry (gid first occurs in passwd entry)"
52 #define	NOMEM	"Out of memory"
53 #define	NGROUPS	"Maximum groups exceeded for logname "
54 #define	BLANKLINE "Blank line detected. Please remove line"
55 #define	LONGNAME  "Group name too long"
56 
57 int eflag, badchar, baddigit, badlognam, colons, len;
58 static int longnam = 0;
59 int code;
60 
61 #define	MYBUFSIZE (LINE_MAX)	/* max line length including newline and null */
62 #define	NUM_COLONS	3
63 
64 char *buf;
65 char *nptr;
66 char *cptr;
67 FILE *fptr;
68 gid_t gid;
69 int error();
70 
71 struct group {
72 	struct group *nxt;
73 	int cnt;
74 	gid_t grp;
75 };
76 
77 struct node {
78 	struct node *next;
79 	int ngroups;
80 	struct group *groups;
81 	char user[1];
82 };
83 
84 void *
85 emalloc(size)
86 {
87 	void *vp;
88 	vp = malloc(size);
89 	if (vp == NULL) {
90 		fprintf(stderr, "%s\n", gettext(NOMEM));
91 		exit(1);
92 	}
93 	return (vp);
94 }
95 
96 main(argc, argv)
97 int argc;
98 char *argv[];
99 {
100 	struct passwd *pwp;
101 	struct node *root = NULL;
102 	struct node *t;
103 	struct group *gp;
104 	int ngroups_max;
105 	int ngroups = 0;
106 	int listlen;
107 	int i;
108 	int lineno = 0;
109 	char *buf_off, *tmpbuf;
110 	int delim[NUM_COLONS + 1], buf_len, bufsize;
111 
112 	(void) setlocale(LC_ALL, "");
113 
114 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
115 #define	TEXT_DOMAIN "SYS_TEST"
116 #endif
117 	(void) textdomain(TEXT_DOMAIN);
118 
119 	code = 0;
120 	ngroups_max = sysconf(_SC_NGROUPS_MAX);
121 
122 	if (argc == 1)
123 		argv[1] = "/etc/group";
124 	else if (argc != 2) {
125 		fprintf(stderr, gettext("usage: %s filename\n"), *argv);
126 		exit(1);
127 	}
128 
129 	if ((fptr = fopen(argv[1], "r")) == NULL) {
130 		fprintf(stderr, gettext("cannot open file %s: %s\n"), argv[1],
131 			strerror(errno));
132 		exit(1);
133 	}
134 
135 #ifdef ORIG_SVR4
136 	while ((pwp = getpwent()) != NULL) {
137 		t = (struct node *)emalloc(sizeof (*t) + strlen(pwp->pw_name));
138 		t->next = root;
139 		root = t;
140 		strcpy(t->user, pwp->pw_name);
141 		t->ngroups = 1;
142 		if (!ngroups_max)
143 			t->groups = NULL;
144 		else {
145 			t->groups = (struct group *)
146 				emalloc(sizeof (struct group));
147 			t->groups->grp = pwp->pw_gid;
148 			t->groups->cnt = 1;
149 			t->groups->nxt = NULL;
150 		}
151 	}
152 #endif
153 
154 	bufsize = MYBUFSIZE;
155 	if ((buf = malloc(bufsize)) == NULL) {
156 		(void) fprintf(stderr, gettext(NOMEM));
157 		exit(1);
158 	}
159 	while (!feof(fptr) && !ferror(fptr)) {
160 		buf_len = 0;
161 		buf_off = buf;
162 		while (fgets(buf_off, (bufsize - buf_len), fptr) != NULL) {
163 			buf_len += strlen(buf_off);
164 			if (buf[buf_len - 1] == '\n' || feof(fptr))
165 				break;
166 			tmpbuf = realloc(buf, (bufsize + MYBUFSIZE));
167 			if (tmpbuf == NULL) {
168 				(void) fprintf(stderr, gettext(NOMEM));
169 				exit(1);
170 			}
171 			bufsize += MYBUFSIZE;
172 			buf = tmpbuf;
173 			buf_off = buf + buf_len;
174 		}
175 		if (buf_len == 0)
176 			continue;
177 
178 		/* Report error to be consistent with libc */
179 		if ((buf_len + 1) > LINE_MAX)
180 			error(TOOLONG);
181 
182 		lineno++;
183 		if (buf[0] == '\n')    /* blank lines are ignored */
184 		{
185 			code = 1;		/* exit with error code = 1 */
186 			eflag = 0;	/* force print of "blank" line */
187 			fprintf(stderr, "\n%s %d\n", gettext(BLANKLINE),
188 				lineno);
189 			continue;
190 		}
191 
192 		if (buf[buf_len - 1] == '\n') {
193 			if ((tmpbuf = strdup(buf)) == NULL) {
194 				(void) fprintf(stderr, gettext(NOMEM));
195 				exit(1);
196 			}
197 			tmpbuf[buf_len - 1] = ',';
198 		} else {
199 			if ((tmpbuf = malloc(buf_len + 2)) == NULL) {
200 				(void) fprintf(stderr, gettext(NOMEM));
201 				exit(1);
202 			}
203 			(void) strcpy(tmpbuf, buf);
204 			tmpbuf[buf_len++] = ',';
205 			tmpbuf[buf_len] = '\0';
206 		}
207 
208 		colons = 0;
209 		eflag = 0;
210 		badchar = 0;
211 		baddigit = 0;
212 		badlognam = 0;
213 		gid = (gid_t)0;
214 
215 		ngroups++;	/* Increment number of groups found */
216 		/* Check that entry is not a nameservice redirection */
217 
218 		if (buf[0] == '+' || buf[0] == '-')  {
219 			/*
220 			 * Should set flag here to allow special case checking
221 			 * in the rest of the code,
222 			 * but for now, we'll just ignore this entry.
223 			 */
224 			free(tmpbuf);
225 			continue;
226 		}
227 
228 		/*	Check number of fields	*/
229 
230 		for (i = 0; buf[i] != NULL; i++)
231 		{
232 			if (buf[i] == ':')
233 			{
234 				delim[colons] = i;
235 				if (++colons > NUM_COLONS)
236 					break;
237 			}
238 		}
239 		if (colons != NUM_COLONS)
240 		{
241 			error(BADLINE);
242 			free(tmpbuf);
243 			continue;
244 		}
245 
246 		/* check to see that group name is at least 1 character	*/
247 		/* and that all characters are lowrcase or digits.	*/
248 
249 		if (buf[0] == ':')
250 			error(NONAME);
251 		else
252 		{
253 			for (i = 0; buf[i] != ':'; i++)
254 			{
255 				if (i >= LOGNAME_MAX)
256 					longnam++;
257 				if (!(islower(buf[i]) || isdigit(buf[i])))
258 					badchar++;
259 			}
260 			if (longnam > 0)
261 				error(LONGNAME);
262 			if (badchar > 0)
263 				error(BADNAME);
264 		}
265 
266 		/*	check that GID is numeric and <= 31 bits	*/
267 
268 		len = (delim[2] - delim[1]) - 1;
269 
270 		if (len > 10 || len < 1)
271 			error(BADGID);
272 		else {
273 			for (i = (delim[1]+1); i < delim[2]; i++)
274 			{
275 				if (! (isdigit(buf[i])))
276 					baddigit++;
277 				else if (baddigit == 0)
278 					gid = gid * 10 + (gid_t)(buf[i] - '0');
279 				/* converts ascii GID to decimal */
280 			}
281 			if (baddigit > 0)
282 				error(BADGID);
283 			else if (gid < (gid_t)0)
284 				error(BADGID);
285 		}
286 
287 		/*  check that logname appears in the passwd file  */
288 
289 		nptr = &tmpbuf[delim[2]];
290 		nptr++;
291 
292 		listlen = strlen(nptr) - 1;
293 
294 		while ((cptr = strchr(nptr, ',')) != NULL)
295 		{
296 			*cptr = NULL;
297 			if (*nptr == NULL)
298 			{
299 				if (listlen)
300 					error(NULLNAME);
301 				nptr++;
302 				continue;
303 			}
304 
305 			for (t = root; t != NULL; t = t->next) {
306 				if (strcmp(t->user, nptr) == 0)
307 					break;
308 			}
309 			if (t == NULL) {
310 #ifndef ORIG_SVR4
311 				/*
312 				 * User entry not found, so check if in
313 				 *  password file
314 				 */
315 				struct passwd *pwp;
316 
317 				if ((pwp = getpwnam(nptr)) == NULL) {
318 #endif
319 					badlognam++;
320 					error(NOTFOUND);
321 					goto getnext;
322 #ifndef ORIG_SVR4
323 				}
324 
325 				/* Usrname found, so add entry to user-list */
326 				t = (struct node *)
327 					emalloc(sizeof (*t) + strlen(nptr));
328 				t->next = root;
329 				root = t;
330 				strcpy(t->user, nptr);
331 				t->ngroups = 1;
332 				if (!ngroups_max)
333 					t->groups = NULL;
334 				else {
335 					t->groups = (struct group *)
336 						emalloc(sizeof (struct group));
337 					t->groups->grp = pwp->pw_gid;
338 					t->groups->cnt = 1;
339 					t->groups->nxt = NULL;
340 				}
341 			}
342 #endif
343 			if (!ngroups_max)
344 				goto getnext;
345 
346 			t->ngroups++;
347 
348 			/*
349 			 * check for duplicate logname in group
350 			 */
351 
352 			for (gp = t->groups; gp != NULL; gp = gp->nxt) {
353 				if (gid == gp->grp) {
354 					if (gp->cnt++ == 1) {
355 						badlognam++;
356 						if (gp->nxt == NULL)
357 							error(DUPNAME2);
358 						else
359 							error(DUPNAME);
360 					}
361 					goto getnext;
362 				}
363 			}
364 
365 			gp = (struct group *)emalloc(sizeof (struct group));
366 			gp->grp = gid;
367 			gp->cnt = 1;
368 			gp->nxt = t->groups;
369 			t->groups = gp;
370 getnext:
371 			nptr = ++cptr;
372 		}
373 		free(tmpbuf);
374 	}
375 
376 	if (ngroups == 0) {
377 		fprintf(stderr, gettext("Group file '%s' is empty\n"), argv[1]);
378 		code = 1;
379 	}
380 
381 	if (ngroups_max) {
382 		for (t = root; t != NULL; t = t->next) {
383 			if (t->ngroups > ngroups_max) {
384 				fprintf(stderr, "\n\n%s%s (%d)\n",
385 				NGROUPS, t->user, t->ngroups);
386 				code = 1;
387 			}
388 		}
389 	}
390 	exit(code);
391 }
392 
393 /*	Error printing routine	*/
394 
395 error(msg)
396 
397 char *msg;
398 {
399 	code = 1;
400 	if (eflag == 0)
401 	{
402 		fprintf(stderr, "\n\n%s", buf);
403 		eflag = 1;
404 	}
405 	if (longnam != 0)
406 	{
407 		fprintf(stderr, "\t%s\n", gettext(msg));
408 		longnam = 0;
409 		return;
410 	}
411 	if (badchar != 0)
412 	{
413 		fprintf(stderr, "\t%d %s\n", badchar, gettext(msg));
414 		badchar = 0;
415 		return;
416 	} else if (baddigit != 0)
417 	{
418 		fprintf(stderr, "\t%s\n", gettext(msg));
419 		baddigit = 0;
420 		return;
421 	} else if (badlognam != 0)
422 	{
423 		fprintf(stderr, "\t%s - %s\n", nptr, gettext(msg));
424 		badlognam = 0;
425 		return;
426 	} else
427 	{
428 		fprintf(stderr, "\t%s\n", gettext(msg));
429 		return;
430 	}
431 }
432