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 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 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 = (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 return (code); 391 } 392 393 /* Error printing routine */ 394 395 void 396 error(char *msg) 397 { 398 code = 1; 399 if (eflag == 0) 400 { 401 fprintf(stderr, "\n\n%s", buf); 402 eflag = 1; 403 } 404 if (longnam != 0) 405 { 406 fprintf(stderr, "\t%s\n", gettext(msg)); 407 longnam = 0; 408 return; 409 } 410 if (badchar != 0) 411 { 412 fprintf(stderr, "\t%d %s\n", badchar, gettext(msg)); 413 badchar = 0; 414 return; 415 } else if (baddigit != 0) 416 { 417 fprintf(stderr, "\t%s\n", gettext(msg)); 418 baddigit = 0; 419 return; 420 } else if (badlognam != 0) 421 { 422 fprintf(stderr, "\t%s - %s\n", nptr, gettext(msg)); 423 badlognam = 0; 424 return; 425 } else 426 { 427 fprintf(stderr, "\t%s\n", gettext(msg)); 428 return; 429 } 430 } 431