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