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