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