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 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <sys/param.h> 33 #include <sys/types.h> 34 #include <unistd.h> 35 #include <stdlib.h> 36 #include <stdio.h> 37 #include <string.h> 38 #include <ctype.h> 39 #include <pwd.h> 40 #include <errno.h> 41 #include <locale.h> 42 #include <limits.h> 43 44 #define BADLINE "Too many/few fields" 45 #define TOOLONG "Line too long" 46 #define NONAME "No group name" 47 #define BADNAME "Bad character(s) in group name" 48 #define BADGID "Invalid GID" 49 #define NULLNAME "Null login name" 50 #define NOTFOUND "Logname not found in password file" 51 #define DUPNAME "Duplicate logname entry" 52 #define DUPNAME2 "Duplicate logname entry (gid first occurs in passwd entry)" 53 #define NOMEM "Out of memory" 54 #define NGROUPS "Maximum groups exceeded for logname " 55 #define BLANKLINE "Blank line detected. Please remove line" 56 #define LONGNAME "Group name too long" 57 58 int eflag, badchar, baddigit, badlognam, colons, len; 59 static int longnam = 0; 60 int code; 61 62 #define MYBUFSIZE (LINE_MAX) /* max line length including newline and null */ 63 #define NUM_COLONS 3 64 65 char *buf; 66 char *nptr; 67 char *cptr; 68 FILE *fptr; 69 gid_t gid; 70 void error(char *msg); 71 72 struct group { 73 struct group *nxt; 74 int cnt; 75 gid_t grp; 76 }; 77 78 struct node { 79 struct node *next; 80 int ngroups; 81 struct group *groups; 82 char user[1]; 83 }; 84 85 void * 86 emalloc(size_t size) 87 { 88 void *vp; 89 vp = malloc(size); 90 if (vp == NULL) { 91 fprintf(stderr, "%s\n", gettext(NOMEM)); 92 exit(1); 93 } 94 return (vp); 95 } 96 97 int 98 main(int argc, 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 = 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 if (buf[i] == ':') { 232 delim[colons] = i; 233 if (++colons > NUM_COLONS) 234 break; 235 } 236 } 237 if (colons != NUM_COLONS) { 238 error(BADLINE); 239 free(tmpbuf); 240 continue; 241 } 242 243 /* check to see that group name is at least 1 character */ 244 /* and that all characters are lowrcase or digits. */ 245 246 if (buf[0] == ':') 247 error(NONAME); 248 else { 249 for (i = 0; buf[i] != ':'; i++) { 250 if (i >= LOGNAME_MAX) 251 longnam++; 252 if (!(islower(buf[i]) || isdigit(buf[i]))) 253 badchar++; 254 } 255 if (longnam > 0) 256 error(LONGNAME); 257 if (badchar > 0) 258 error(BADNAME); 259 } 260 261 /* check that GID is numeric and <= 31 bits */ 262 263 len = (delim[2] - delim[1]) - 1; 264 265 if (len > 10 || len < 1) 266 error(BADGID); 267 else { 268 for (i = (delim[1]+1); i < delim[2]; i++) { 269 if (! (isdigit(buf[i]))) 270 baddigit++; 271 else if (baddigit == 0) 272 gid = gid * 10 + (gid_t)(buf[i] - '0'); 273 /* converts ascii GID to decimal */ 274 } 275 if (baddigit > 0) 276 error(BADGID); 277 else if (gid > (gid_t)MAXUID) 278 error(BADGID); 279 } 280 281 /* check that logname appears in the passwd file */ 282 283 nptr = &tmpbuf[delim[2]]; 284 nptr++; 285 286 listlen = strlen(nptr) - 1; 287 288 while ((cptr = strchr(nptr, ',')) != NULL) { 289 *cptr = NULL; 290 if (*nptr == NULL) { 291 if (listlen) 292 error(NULLNAME); 293 nptr++; 294 continue; 295 } 296 297 for (t = root; t != NULL; t = t->next) { 298 if (strcmp(t->user, nptr) == 0) 299 break; 300 } 301 if (t == NULL) { 302 #ifndef ORIG_SVR4 303 /* 304 * User entry not found, so check if in 305 * password file 306 */ 307 struct passwd *pwp; 308 309 if ((pwp = getpwnam(nptr)) == NULL) { 310 #endif 311 badlognam++; 312 error(NOTFOUND); 313 goto getnext; 314 #ifndef ORIG_SVR4 315 } 316 317 /* Usrname found, so add entry to user-list */ 318 t = (struct node *) 319 emalloc(sizeof (*t) + strlen(nptr)); 320 t->next = root; 321 root = t; 322 strcpy(t->user, nptr); 323 t->ngroups = 1; 324 if (!ngroups_max) 325 t->groups = NULL; 326 else { 327 t->groups = (struct group *) 328 emalloc(sizeof (struct group)); 329 t->groups->grp = pwp->pw_gid; 330 t->groups->cnt = 1; 331 t->groups->nxt = NULL; 332 } 333 } 334 #endif 335 if (!ngroups_max) 336 goto getnext; 337 338 t->ngroups++; 339 340 /* 341 * check for duplicate logname in group 342 */ 343 344 for (gp = t->groups; gp != NULL; gp = gp->nxt) { 345 if (gid == gp->grp) { 346 if (gp->cnt++ == 1) { 347 badlognam++; 348 if (gp->nxt == NULL) 349 error(DUPNAME2); 350 else 351 error(DUPNAME); 352 } 353 goto getnext; 354 } 355 } 356 357 gp = (struct group *)emalloc(sizeof (struct group)); 358 gp->grp = gid; 359 gp->cnt = 1; 360 gp->nxt = t->groups; 361 t->groups = gp; 362 getnext: 363 nptr = ++cptr; 364 } 365 free(tmpbuf); 366 } 367 368 if (ngroups == 0) { 369 fprintf(stderr, gettext("Group file '%s' is empty\n"), argv[1]); 370 code = 1; 371 } 372 373 if (ngroups_max) { 374 for (t = root; t != NULL; t = t->next) { 375 if (t->ngroups > ngroups_max) { 376 fprintf(stderr, "\n\n%s%s (%d)\n", 377 NGROUPS, t->user, t->ngroups); 378 code = 1; 379 } 380 } 381 } 382 return (code); 383 } 384 385 /* Error printing routine */ 386 387 void 388 error(char *msg) 389 { 390 code = 1; 391 if (eflag == 0) { 392 fprintf(stderr, "\n\n%s", buf); 393 eflag = 1; 394 } 395 if (longnam != 0) { 396 fprintf(stderr, "\t%s\n", gettext(msg)); 397 longnam = 0; 398 return; 399 } 400 if (badchar != 0) { 401 fprintf(stderr, "\t%d %s\n", badchar, gettext(msg)); 402 badchar = 0; 403 return; 404 } else if (baddigit != 0) { 405 fprintf(stderr, "\t%s\n", gettext(msg)); 406 baddigit = 0; 407 return; 408 } else if (badlognam != 0) { 409 fprintf(stderr, "\t%s - %s\n", nptr, gettext(msg)); 410 badlognam = 0; 411 return; 412 } else { 413 fprintf(stderr, "\t%s\n", gettext(msg)); 414 return; 415 } 416 }