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 }