root/usr/src/cmd/pwck/pwck.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2013 Gary Mills
 *
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/sysmacros.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <locale.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>

#define ERROR1  "Too many/few fields"
#define ERROR2  "Bad character(s) in logname"
#define ERROR2a "First char in logname not alphabetic"
#define ERROR2b "Logname field NULL"
#define ERROR2c "Logname contains no lower-case letters"
#define ERROR3  "Logname too long/short"
#define ERROR4  "Invalid UID"
#define ERROR5  "Invalid GID"
#define ERROR6  "Login directory not found"
#define ERROR6a "Login directory null"
#define ERROR7  "Optional shell file not found"

static int eflag, code = 0;
static int badc;
static int lc;
static char buf[512];
static void error(char *);

int
main(int argc, char **argv)
{
        int delim[512];
        char logbuf[512];
        FILE *fptr;
        struct stat obuf;
        uid_t uid;
        gid_t gid;
        int i, j, colons;
        char *pw_file;
        struct stat stat_buf;
        char *str, *lastc;

        (void) setlocale(LC_ALL, "");

#if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
        (void) textdomain(TEXT_DOMAIN);

        if (argc == 1)
                pw_file = "/etc/passwd";
        else
                pw_file = argv[1];

        if ((fptr = fopen(pw_file, "r")) == NULL) {
                (void) fprintf(stderr, gettext("cannot open %s\n"), pw_file);
                exit(1);
        }

        if (fstat(fileno(fptr), &stat_buf) < 0) {
                (void) fprintf(stderr, gettext("fstat failed for %s\n"),
                    pw_file);
                (void) fclose(fptr);
                exit(1);
        }

        if (stat_buf.st_size == 0) {
                (void) fprintf(stderr, gettext("file %s is empty\n"), pw_file);
                (void) fclose(fptr);
                exit(1);
        }

        while (fgets(buf, sizeof (buf), fptr) != NULL) {

                colons = 0;
                badc = 0;
                lc = 0;
                eflag = 0;

                /* Check that entry is not a nameservice redirection */

                if (buf[0] == '+' || buf[0] == '-')  {
                        /*
                         * Should set flag here to allow special case checking
                         * in the rest of the code,
                         * but for now, we'll just ignore this entry.
                         */
                        continue;
                }

                /* Check number of fields */

                for (i = 0; buf[i] != '\0'; i++)
                        if (buf[i] == ':') {
                                delim[colons] = i;
                                ++colons;
                        }

                if (colons != 6) {
                        error(ERROR1);
                        continue;
                }
                delim[6] = i - 1;
                delim[7] = '\0';

                /*
                 * Check the first char is alpha; the rest alphanumeric;
                 * and that the name does not consist solely of uppercase
                 * alpha chars
                 */
                if (buf[0] == ':')
                        error(ERROR2b);
                else if (!isalpha(buf[0]))
                        error(ERROR2a);

                for (i = 0; buf[i] != ':'; i++) {
                        if (!isalnum(buf[i]) &&
                            buf[i] != '_' &&
                            buf[i] != '-' &&
                            buf[i] != '.')
                                badc++;
                        else if (islower(buf[i]))
                                lc++;
                }
                if (lc == 0)
                        error(ERROR2c);
                if (badc > 0)
                        error(ERROR2);

                /* Check for valid number of characters in logname */

                if (i <= 0 || i > LOGNAME_MAX)
                        error(ERROR3);

                /* Check that UID is numeric and <= MAXUID */

                errno = 0;
                str = &buf[delim[1] + 1];
                uid = strtol(str, &lastc, 10);
                if (lastc != str + (delim[2] - delim[1]) - 1 ||
                    uid > MAXUID || errno == ERANGE)
                        error(ERROR4);

                /* Check that GID is numeric and <= MAXUID */

                errno = 0;
                str = &buf[delim[2] + 1];
                gid = strtol(str, &lastc, 10);
                if (lastc != str + (delim[3] - delim[2]) - 1 ||
                    gid > MAXUID || errno == ERANGE)
                        error(ERROR5);

                /* Check initial working directory */

                for (j = 0, i = (delim[4] + 1); i < delim[5]; j++, i++)
                        logbuf[j] = buf[i];
                logbuf[j] = '\0';

                if (logbuf[0] == '\0')
                        error(ERROR6a);
                else if ((stat(logbuf, &obuf)) == -1)
                        error(ERROR6);

                /* Check program to use as shell  */

                if ((buf[(delim[5] + 1)]) != '\n') {

                        for (j = 0, i = (delim[5] + 1); i < delim[6]; j++, i++)
                                logbuf[j] = buf[i];
                        logbuf[j] = '\0';

                        if (strcmp(logbuf, "*") == 0)   /* subsystem login */
                                continue;

                        if ((stat(logbuf, &obuf)) == -1)
                                error(ERROR7);

                        for (j = 0; j < 512; j++)
                                logbuf[j] = '\0';
                }
        }
        (void) fclose(fptr);
        return (code);
}

/* Error printing routine */

static void
error(char *msg)
{
        if (!eflag) {
                (void) fprintf(stderr, "\n%s", buf);
                code = 1;
                ++eflag;
        }
        if (!badc)
                (void) fprintf(stderr, "\t%s\n", gettext(msg));
        else {
                (void) fprintf(stderr, "\t%d %s\n", badc, gettext(msg));
                badc = 0;
        }
}