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 (c) 2013 Gary Mills 23 * 24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 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 #ifdef LOGNAME_MAX_ILLUMOS 59 #define _LOGNAME_MAX LOGNAME_MAX_ILLUMOS 60 #else /* LOGNAME_MAX_ILLUMOS */ 61 #define _LOGNAME_MAX LOGNAME_MAX 62 #endif /* LOGNAME_MAX_ILLUMOS */ 63 64 int eflag, badchar, baddigit, badlognam, colons, len; 65 static int longnam = 0; 66 int code; 67 68 #define MYBUFSIZE (LINE_MAX) /* max line length including newline and null */ 69 #define NUM_COLONS 3 70 71 char *buf; 72 char *nptr; 73 char *cptr; 74 FILE *fptr; 75 gid_t gid; 76 void error(char *msg); 77 78 struct group { 79 struct group *nxt; 80 int cnt; 81 gid_t grp; 82 }; 83 84 struct node { 85 struct node *next; 86 int ngroups; 87 struct group *groups; 88 char user[1]; 89 }; 90 91 void * 92 emalloc(size_t size) 93 { 94 void *vp; 95 vp = malloc(size); 96 if (vp == NULL) { 97 fprintf(stderr, "%s\n", gettext(NOMEM)); 98 exit(1); 99 } 100 return (vp); 101 } 102 103 int 104 main(int argc, char *argv[]) 105 { 106 struct passwd *pwp; 107 struct node *root = NULL; 108 struct node *t; 109 struct group *gp; 110 int ngroups_max; 111 int ngroups = 0; 112 int listlen; 113 int i; 114 int lineno = 0; 115 char *buf_off, *tmpbuf; 116 int delim[NUM_COLONS + 1], buf_len, bufsize; 117 118 (void) setlocale(LC_ALL, ""); 119 120 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 121 #define TEXT_DOMAIN "SYS_TEST" 122 #endif 123 (void) textdomain(TEXT_DOMAIN); 124 125 code = 0; 126 ngroups_max = sysconf(_SC_NGROUPS_MAX); 127 128 if (argc == 1) 129 argv[1] = "/etc/group"; 130 else if (argc != 2) { 131 fprintf(stderr, gettext("usage: %s filename\n"), *argv); 132 exit(1); 133 } 134 135 if ((fptr = fopen(argv[1], "r")) == NULL) { 136 fprintf(stderr, gettext("cannot open file %s: %s\n"), argv[1], 137 strerror(errno)); 138 exit(1); 139 } 140 141 #ifdef ORIG_SVR4 142 while ((pwp = getpwent()) != NULL) { 143 t = (struct node *)emalloc(sizeof (*t) + strlen(pwp->pw_name)); 144 t->next = root; 145 root = t; 146 strcpy(t->user, pwp->pw_name); 147 t->ngroups = 1; 148 if (!ngroups_max) 149 t->groups = NULL; 150 else { 151 t->groups = (struct group *) 152 emalloc(sizeof (struct group)); 153 t->groups->grp = pwp->pw_gid; 154 t->groups->cnt = 1; 155 t->groups->nxt = NULL; 156 } 157 } 158 #endif 159 160 bufsize = MYBUFSIZE; 161 if ((buf = malloc(bufsize)) == NULL) { 162 (void) fprintf(stderr, gettext(NOMEM)); 163 exit(1); 164 } 165 while (!feof(fptr) && !ferror(fptr)) { 166 buf_len = 0; 167 buf_off = buf; 168 while (fgets(buf_off, (bufsize - buf_len), fptr) != NULL) { 169 buf_len += strlen(buf_off); 170 if (buf[buf_len - 1] == '\n' || feof(fptr)) 171 break; 172 tmpbuf = realloc(buf, (bufsize + MYBUFSIZE)); 173 if (tmpbuf == NULL) { 174 (void) fprintf(stderr, gettext(NOMEM)); 175 exit(1); 176 } 177 bufsize += MYBUFSIZE; 178 buf = tmpbuf; 179 buf_off = buf + buf_len; 180 } 181 if (buf_len == 0) 182 continue; 183 184 /* Report error to be consistent with libc */ 185 if ((buf_len + 1) > LINE_MAX) 186 error(TOOLONG); 187 188 lineno++; 189 if (buf[0] == '\n') /* blank lines are ignored */ 190 { 191 code = 1; /* exit with error code = 1 */ 192 eflag = 0; /* force print of "blank" line */ 193 fprintf(stderr, "\n%s %d\n", gettext(BLANKLINE), 194 lineno); 195 continue; 196 } 197 198 if (buf[buf_len - 1] == '\n') { 199 if ((tmpbuf = strdup(buf)) == NULL) { 200 (void) fprintf(stderr, gettext(NOMEM)); 201 exit(1); 202 } 203 tmpbuf[buf_len - 1] = ','; 204 } else { 205 if ((tmpbuf = malloc(buf_len + 2)) == NULL) { 206 (void) fprintf(stderr, gettext(NOMEM)); 207 exit(1); 208 } 209 (void) strcpy(tmpbuf, buf); 210 tmpbuf[buf_len++] = ','; 211 tmpbuf[buf_len] = '\0'; 212 } 213 214 colons = 0; 215 eflag = 0; 216 badchar = 0; 217 baddigit = 0; 218 badlognam = 0; 219 gid = 0; 220 221 ngroups++; /* Increment number of groups found */ 222 /* Check that entry is not a nameservice redirection */ 223 224 if (buf[0] == '+' || buf[0] == '-') { 225 /* 226 * Should set flag here to allow special case checking 227 * in the rest of the code, 228 * but for now, we'll just ignore this entry. 229 */ 230 free(tmpbuf); 231 continue; 232 } 233 234 /* Check number of fields */ 235 236 for (i = 0; buf[i] != NULL; i++) { 237 if (buf[i] == ':') { 238 delim[colons] = i; 239 if (++colons > NUM_COLONS) 240 break; 241 } 242 } 243 if (colons != NUM_COLONS) { 244 error(BADLINE); 245 free(tmpbuf); 246 continue; 247 } 248 249 /* check to see that group name is at least 1 character */ 250 /* and that all characters are lowrcase or digits. */ 251 252 if (buf[0] == ':') 253 error(NONAME); 254 else { 255 for (i = 0; buf[i] != ':'; i++) { 256 if (i >= _LOGNAME_MAX) 257 longnam++; 258 if (!(islower(buf[i]) || isdigit(buf[i]))) 259 badchar++; 260 } 261 if (longnam > 0) 262 error(LONGNAME); 263 if (badchar > 0) 264 error(BADNAME); 265 } 266 267 /* check that GID is numeric and <= 31 bits */ 268 269 len = (delim[2] - delim[1]) - 1; 270 271 if (len > 10 || len < 1) 272 error(BADGID); 273 else { 274 for (i = (delim[1]+1); i < delim[2]; i++) { 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)MAXUID) 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 *cptr = NULL; 296 if (*nptr == NULL) { 297 if (listlen) 298 error(NULLNAME); 299 nptr++; 300 continue; 301 } 302 303 for (t = root; t != NULL; t = t->next) { 304 if (strcmp(t->user, nptr) == 0) 305 break; 306 } 307 if (t == NULL) { 308 #ifndef ORIG_SVR4 309 /* 310 * User entry not found, so check if in 311 * password file 312 */ 313 struct passwd *pwp; 314 315 if ((pwp = getpwnam(nptr)) == NULL) { 316 #endif 317 badlognam++; 318 error(NOTFOUND); 319 goto getnext; 320 #ifndef ORIG_SVR4 321 } 322 323 /* Usrname found, so add entry to user-list */ 324 t = (struct node *) 325 emalloc(sizeof (*t) + strlen(nptr)); 326 t->next = root; 327 root = t; 328 strcpy(t->user, nptr); 329 t->ngroups = 1; 330 if (!ngroups_max) 331 t->groups = NULL; 332 else { 333 t->groups = (struct group *) 334 emalloc(sizeof (struct group)); 335 t->groups->grp = pwp->pw_gid; 336 t->groups->cnt = 1; 337 t->groups->nxt = NULL; 338 } 339 } 340 #endif 341 if (!ngroups_max) 342 goto getnext; 343 344 t->ngroups++; 345 346 /* 347 * check for duplicate logname in group 348 */ 349 350 for (gp = t->groups; gp != NULL; gp = gp->nxt) { 351 if (gid == gp->grp) { 352 if (gp->cnt++ == 1) { 353 badlognam++; 354 if (gp->nxt == NULL) 355 error(DUPNAME2); 356 else 357 error(DUPNAME); 358 } 359 goto getnext; 360 } 361 } 362 363 gp = (struct group *)emalloc(sizeof (struct group)); 364 gp->grp = gid; 365 gp->cnt = 1; 366 gp->nxt = t->groups; 367 t->groups = gp; 368 getnext: 369 nptr = ++cptr; 370 } 371 free(tmpbuf); 372 } 373 374 if (ngroups == 0) { 375 fprintf(stderr, gettext("Group file '%s' is empty\n"), argv[1]); 376 code = 1; 377 } 378 379 if (ngroups_max) { 380 for (t = root; t != NULL; t = t->next) { 381 if (t->ngroups > ngroups_max) { 382 fprintf(stderr, "\n\n%s%s (%d)\n", 383 NGROUPS, t->user, t->ngroups); 384 code = 1; 385 } 386 } 387 } 388 return (code); 389 } 390 391 /* Error printing routine */ 392 393 void 394 error(char *msg) 395 { 396 code = 1; 397 if (eflag == 0) { 398 fprintf(stderr, "\n\n%s", buf); 399 eflag = 1; 400 } 401 if (longnam != 0) { 402 fprintf(stderr, "\t%s\n", gettext(msg)); 403 longnam = 0; 404 return; 405 } 406 if (badchar != 0) { 407 fprintf(stderr, "\t%d %s\n", badchar, gettext(msg)); 408 badchar = 0; 409 return; 410 } else if (baddigit != 0) { 411 fprintf(stderr, "\t%s\n", gettext(msg)); 412 baddigit = 0; 413 return; 414 } else if (badlognam != 0) { 415 fprintf(stderr, "\t%s - %s\n", nptr, gettext(msg)); 416 badlognam = 0; 417 return; 418 } else { 419 fprintf(stderr, "\t%s\n", gettext(msg)); 420 return; 421 } 422 }