OpenSolaris_b135/ucbcmd/vipw/vipw.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

/*
 * Portions of this source code were derived from Berkeley 4.3 BSD
 * under license from the Regents of the University of California.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/fcntl.h>

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <strings.h>

/*
 * Password file editor with locking.
 */

#define	DEFAULT_EDITOR	"/usr/bin/vi"

static int copyfile(char *, char *);
static int editfile(char *, char *, char *, time_t *);
static int sanity_check(char *, time_t *, char *);
static int validsh(char *);

char	*ptemp = "/etc/ptmp";
char	*stemp = "/etc/stmp";
char	*passwd = "/etc/passwd";
char	*shadow = "/etc/shadow";
char	buf[BUFSIZ];

int
main(void)
{
	int fd;
	FILE *ft, *fp;
	char *editor;
	int ok = 0;
	time_t o_mtime, n_mtime;
	struct stat osbuf, sbuf, oshdbuf, shdbuf;
	char c;

	(void)signal(SIGINT, SIG_IGN);
	(void)signal(SIGQUIT, SIG_IGN);
	(void)signal(SIGHUP, SIG_IGN);
	setbuf(stderr, (char *)NULL);

	editor = getenv("VISUAL");
	if (editor == 0)
		editor = getenv("EDITOR");
	if (editor == 0)
		editor = DEFAULT_EDITOR;

	(void)umask(0077);
	if (stat(passwd, &osbuf) < 0) {
                (void)fprintf(stderr,"vipw: can't stat passwd file.\n");
                goto bad;
        } 

	if (copyfile(passwd, ptemp))
		goto bad;

	if (stat(ptemp, &sbuf) < 0) {
                (void)fprintf(stderr,
			"vipw: can't stat ptemp file, %s unchanged\n",
			passwd);
		goto bad;
	}

	o_mtime = sbuf.st_mtime;

	if (editfile(editor, ptemp, passwd, &n_mtime)) {
		if (sanity_check(ptemp, &n_mtime, passwd))
			goto bad;
		if (o_mtime >= n_mtime)
			goto bad;
	}

	ok++;
	if (o_mtime < n_mtime) {
		fprintf(stdout, "\nYou have modified the password file.\n");
		fprintf(stdout,
	"Press 'e' to edit the shadow file for consistency,\n 'q' to quit: ");
		if ((c = getchar()) == 'q') {
			if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
				(void) fprintf(stderr, "vipw: %s: ", ptemp);
				perror("chmod");
				goto bad;
			}
			if (rename(ptemp, passwd) < 0) {
				(void) fprintf(stderr, "vipw: %s: ", ptemp);
				perror("rename");
				goto bad;
			}
			if (((osbuf.st_gid != sbuf.st_gid) ||
					(osbuf.st_uid != sbuf.st_uid)) &&
			(chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
				(void) fprintf(stderr, "vipw: %s ", ptemp);
				perror("chown");
			}
			goto bad;
		} else if (c == 'e') {
			if (stat(shadow, &oshdbuf) < 0) {
				(void) fprintf(stderr,
					"vipw: can't stat shadow file.\n");
				goto bad;
			}

			if (copyfile(shadow, stemp))
				goto bad;
			if (stat(stemp, &shdbuf) < 0) {
				(void) fprintf(stderr,
					"vipw: can't stat stmp file.\n");
				goto bad;
			}

			if (editfile(editor, stemp, shadow, &o_mtime))
				goto bad;
			ok++;
			if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
				(void) fprintf(stderr, "vipw: %s: ", ptemp);
				perror("chmod");
				goto bad;
			}
			if (chmod(stemp, (oshdbuf.st_mode & 0400)) < 0) {
				(void) fprintf(stderr, "vipw: %s: ", stemp);
				perror("chmod");
				goto bad;
			}
			if (rename(ptemp, passwd) < 0) {
				(void) fprintf(stderr, "vipw: %s: ", ptemp);
				perror("rename");
				goto bad;
			}
			if (((osbuf.st_gid != sbuf.st_gid) ||
					(osbuf.st_uid != sbuf.st_uid)) &&
			(chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
				(void) fprintf(stderr, "vipw: %s ", ptemp);
				perror("chown");
			}
			if (rename(stemp, shadow) < 0) {
				(void) fprintf(stderr, "vipw: %s: ", stemp);
				perror("rename");
				goto bad;
			} else if (((oshdbuf.st_gid != shdbuf.st_gid) ||
					(oshdbuf.st_uid != shdbuf.st_uid)) &&
			(chown(shadow, oshdbuf.st_uid, oshdbuf.st_gid) < 0)) {
				(void) fprintf(stderr, "vipw: %s ", stemp);
				perror("chown");
				}
		}
	}
bad:
	(void) unlink(ptemp);
	(void) unlink(stemp);
	return (ok ? 0 : 1);
	/* NOTREACHED */
}


int
copyfile(char *from, char *to)
{
	int fd;
	FILE *fp, *ft;

	fd = open(to, O_WRONLY|O_CREAT|O_EXCL, 0600);
	if (fd < 0) {
		if (errno == EEXIST) {
			(void) fprintf(stderr, "vipw: %s file busy\n", from);
			exit(1);
		}
		(void) fprintf(stderr, "vipw: "); perror(to);
		exit(1);
	}
	ft = fdopen(fd, "w");
	if (ft == NULL) {
		(void) fprintf(stderr, "vipw: "); perror(to);
		return( 1 );
	}
	fp = fopen(from, "r");
	if (fp == NULL) {
		(void) fprintf(stderr, "vipw: "); perror(from);
		return( 1 );
	}
	while (fgets(buf, sizeof (buf) - 1, fp) != NULL)
		fputs(buf, ft);
	(void) fclose(ft);
	(void) fclose(fp);
	return( 0 );
}

int
editfile(char *editor, char *temp, char *orig, time_t *mtime)
{
	(void)sprintf(buf, "%s %s", editor, temp);
	if (system(buf) == 0) {
		return (sanity_check(temp, mtime, orig));
	}
	return(1);
}


int
validsh(char *rootsh)
{

	char	*sh, *getusershell();
	int	ret = 0;

	setusershell();
	while((sh = getusershell()) != NULL ) {
		if( strcmp( rootsh, sh) == 0 ) {
			ret = 1;
			break;
		}
	}
	endusershell();
	return(ret);
}

/*
 * sanity checks
 * return 0 if ok, 1 otherwise
 */
int
sanity_check(char *temp, time_t *mtime, char *orig)
{
	int i, ok = 0;
	FILE *ft;
	struct stat sbuf, statbuf;
	char *ldir;
	int isshadow = 0;

	if (!strcmp(orig, shadow))
		isshadow = 1;

	/* sanity checks */
	if (stat(temp, &sbuf) < 0) {
		(void)fprintf(stderr,
		    "vipw: can't stat %s file, %s unchanged\n",
		    temp, orig);
		return(1);
	}
	*mtime = sbuf.st_mtime;
	if (sbuf.st_size == 0) {
		(void)fprintf(stderr, "vipw: bad %s file, %s unchanged\n",
		    temp, orig);
		return(1);
	}
	ft = fopen(temp, "r");
	if (ft == NULL) {
		(void)fprintf(stderr,
		    "vipw: can't reopen %s file, %s unchanged\n",
		    temp, orig);
		return(1);
	}

	while (fgets(buf, sizeof (buf) - 1, ft) != NULL) {
		char *cp;

		cp = index(buf, '\n');
		if (cp == 0)
			continue;	/* ??? allow very long lines
					 * and passwd files that do
					 * not end in '\n' ???
					 */
		*cp = '\0';

		cp = index(buf, ':');
		if (cp == 0)		/* lines without colon
					 * separated fields
					 */
			continue;
		*cp = '\0';

		if (strcmp(buf, "root"))
			continue;

		/* root password */
		*cp = ':';
		cp = index(cp + 1, ':');
		if (cp == 0)
			goto bad_root;

		/* root uid for password */
		if (!isshadow)
			if (atoi(cp + 1) != 0) {

				(void)fprintf(stderr, "root UID != 0:\n%s\n",
				    buf);
				break;
			}
		/* root uid for passwd and sp_lstchg for shadow */
		cp = index(cp + 1, ':');
		if (cp == 0)
			goto bad_root;

		/* root's gid for passwd and sp_min for shadow*/
		cp = index(cp + 1, ':');
		if (cp == 0)
			goto bad_root;

		/* root's gecos for passwd and sp_max for shadow*/
		cp = index(cp + 1, ':');
		if (isshadow) {
			for (i=0; i<3; i++)
				if ((cp = index(cp + 1, ':')) == 0)
					goto bad_root;
		} else {
			if (cp == 0) {
bad_root:		(void)fprintf(stderr,
				    "Missing fields in root entry:\n%s\n", buf);
				break;
			}
		}
		if (!isshadow) {
			/* root's login directory */
			ldir = ++cp;
			cp = index(cp, ':');
			if (cp == 0)
				goto bad_root;
			*cp = '\0';
			if (stat(ldir, &statbuf) < 0) {
				*cp = ':';
				(void) fprintf(stderr,
				    "root login dir doesn't exist:\n%s\n",
				    buf);
				break;
			} else if (!S_ISDIR(statbuf.st_mode)) {
				*cp = ':';
				(void) fprintf(stderr,
				    "root login dir is not a directory:\n%s\n",
				    buf);
				break;
			}

			*cp = ':';
			/* root's login shell */
			++cp;
			if (*cp && ! validsh(cp)) {
				(void)fprintf(stderr,
				    "Invalid root shell:\n%s\n", buf);
				break;
			}
		}

		ok++;
	}
	(void)fclose(ft);
	if (ok)
		return(0);
	else {
		(void)fprintf(stderr,
		    "vipw: you mangled the %s file, %s unchanged\n",
		    temp, orig);
		return(1);
	}			
}