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/types.h> 33 #include <sys/param.h> 34 #include <sys/signal.h> 35 #include <sys/sysmacros.h> 36 #include <sys/stat.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <ctype.h> 41 #include <locale.h> 42 #include <errno.h> 43 #include <unistd.h> 44 45 #define ERROR1 "Too many/few fields" 46 #define ERROR2 "Bad character(s) in logname" 47 #define ERROR2a "First char in logname not alphabetic" 48 #define ERROR2b "Logname field NULL" 49 #define ERROR2c "Logname contains no lower-case letters" 50 #define ERROR3 "Logname too long/short" 51 #define ERROR4 "Invalid UID" 52 #define ERROR5 "Invalid GID" 53 #define ERROR6 "Login directory not found" 54 #define ERROR6a "Login directory null" 55 #define ERROR7 "Optional shell file not found" 56 57 static int eflag, code = 0; 58 static int badc; 59 static int lc; 60 static char buf[512]; 61 static void error(char *); 62 63 int 64 main(int argc, char **argv) 65 { 66 int delim[512]; 67 char logbuf[512]; 68 FILE *fptr; 69 struct stat obuf; 70 uid_t uid; 71 gid_t gid; 72 int i, j, colons; 73 char *pw_file; 74 struct stat stat_buf; 75 char *str, *lastc; 76 77 (void) setlocale(LC_ALL, ""); 78 79 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 80 #define TEXT_DOMAIN "SYS_TEST" 81 #endif 82 (void) textdomain(TEXT_DOMAIN); 83 84 if (argc == 1) 85 pw_file = "/etc/passwd"; 86 else 87 pw_file = argv[1]; 88 89 if ((fptr = fopen(pw_file, "r")) == NULL) { 90 (void) fprintf(stderr, gettext("cannot open %s\n"), pw_file); 91 exit(1); 92 } 93 94 if (fstat(fileno(fptr), &stat_buf) < 0) { 95 (void) fprintf(stderr, gettext("fstat failed for %s\n"), 96 pw_file); 97 (void) fclose(fptr); 98 exit(1); 99 } 100 101 if (stat_buf.st_size == 0) { 102 (void) fprintf(stderr, gettext("file %s is empty\n"), pw_file); 103 (void) fclose(fptr); 104 exit(1); 105 } 106 107 while (fgets(buf, sizeof (buf), fptr) != NULL) { 108 109 colons = 0; 110 badc = 0; 111 lc = 0; 112 eflag = 0; 113 114 /* Check that entry is not a nameservice redirection */ 115 116 if (buf[0] == '+' || buf[0] == '-') { 117 /* 118 * Should set flag here to allow special case checking 119 * in the rest of the code, 120 * but for now, we'll just ignore this entry. 121 */ 122 continue; 123 } 124 125 /* Check number of fields */ 126 127 for (i = 0; buf[i] != NULL; i++) 128 if (buf[i] == ':') { 129 delim[colons] = i; 130 ++colons; 131 } 132 133 if (colons != 6) { 134 error(ERROR1); 135 continue; 136 } 137 delim[6] = i - 1; 138 delim[7] = NULL; 139 140 /* 141 * Check the first char is alpha; the rest alphanumeric; 142 * and that the name does not consist solely of uppercase 143 * alpha chars 144 */ 145 if (buf[0] == ':') 146 error(ERROR2b); 147 else if (!isalpha(buf[0])) 148 error(ERROR2a); 149 150 for (i = 0; buf[i] != ':'; i++) { 151 if (!isalnum(buf[i]) && 152 buf[i] != '_' && 153 buf[i] != '-' && 154 buf[i] != '.') 155 badc++; 156 else if (islower(buf[i])) 157 lc++; 158 } 159 if (lc == 0) 160 error(ERROR2c); 161 if (badc > 0) 162 error(ERROR2); 163 164 /* Check for valid number of characters in logname */ 165 166 if (i <= 0 || i > 8) 167 error(ERROR3); 168 169 /* Check that UID is numeric and <= MAXUID */ 170 171 errno = 0; 172 str = &buf[delim[1] + 1]; 173 uid = strtol(str, &lastc, 10); 174 if (lastc != str + (delim[2] - delim[1]) - 1 || 175 uid > MAXUID || errno == ERANGE) 176 error(ERROR4); 177 178 /* Check that GID is numeric and <= MAXUID */ 179 180 errno = 0; 181 str = &buf[delim[2] + 1]; 182 gid = strtol(str, &lastc, 10); 183 if (lastc != str + (delim[3] - delim[2]) - 1 || 184 gid > MAXUID || errno == ERANGE) 185 error(ERROR5); 186 187 /* Check initial working directory */ 188 189 for (j = 0, i = (delim[4] + 1); i < delim[5]; j++, i++) 190 logbuf[j] = buf[i]; 191 logbuf[j] = '\0'; 192 193 if (logbuf[0] == NULL) 194 error(ERROR6a); 195 else if ((stat(logbuf, &obuf)) == -1) 196 error(ERROR6); 197 198 /* Check program to use as shell */ 199 200 if ((buf[(delim[5] + 1)]) != '\n') { 201 202 for (j = 0, i = (delim[5] + 1); i < delim[6]; j++, i++) 203 logbuf[j] = buf[i]; 204 logbuf[j] = '\0'; 205 206 if (strcmp(logbuf, "*") == 0) /* subsystem login */ 207 continue; 208 209 if ((stat(logbuf, &obuf)) == -1) 210 error(ERROR7); 211 212 for (j = 0; j < 512; j++) 213 logbuf[j] = NULL; 214 } 215 } 216 (void) fclose(fptr); 217 return (code); 218 } 219 220 /* Error printing routine */ 221 222 static void 223 error(char *msg) 224 { 225 if (!eflag) { 226 (void) fprintf(stderr, "\n%s", buf); 227 code = 1; 228 ++eflag; 229 } 230 if (!badc) 231 (void) fprintf(stderr, "\t%s\n", gettext(msg)); 232 else { 233 (void) fprintf(stderr, "\t%d %s\n", badc, gettext(msg)); 234 badc = 0; 235 } 236 } 237