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 * 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 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 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