10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*1553Scf46844 * Common Development and Distribution License (the "License").
6*1553Scf46844 * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*1553Scf46844 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
270Sstevel@tonic-gate /* All Rights Reserved */
280Sstevel@tonic-gate
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate * Portions of this source code were derived from Berkeley 4.3 BSD
310Sstevel@tonic-gate * under license from the Regents of the University of California.
320Sstevel@tonic-gate */
330Sstevel@tonic-gate
34273Scf46844 #pragma ident "%Z%%M% %I% %E% SMI"
350Sstevel@tonic-gate
360Sstevel@tonic-gate #include <sys/types.h>
370Sstevel@tonic-gate #include <sys/stat.h>
380Sstevel@tonic-gate #include <sys/file.h>
390Sstevel@tonic-gate #include <sys/fcntl.h>
400Sstevel@tonic-gate
410Sstevel@tonic-gate #include <stdio.h>
420Sstevel@tonic-gate #include <errno.h>
430Sstevel@tonic-gate #include <signal.h>
44273Scf46844 #include <stdlib.h>
45273Scf46844 #include <strings.h>
460Sstevel@tonic-gate
470Sstevel@tonic-gate /*
480Sstevel@tonic-gate * Password file editor with locking.
490Sstevel@tonic-gate */
500Sstevel@tonic-gate
510Sstevel@tonic-gate #define DEFAULT_EDITOR "/usr/bin/vi"
520Sstevel@tonic-gate
53273Scf46844 static int copyfile(char *, char *);
54273Scf46844 static int editfile(char *, char *, char *, time_t *);
55273Scf46844 static int sanity_check(char *, time_t *, char *);
56273Scf46844 static int validsh(char *);
57273Scf46844
580Sstevel@tonic-gate char *ptemp = "/etc/ptmp";
590Sstevel@tonic-gate char *stemp = "/etc/stmp";
600Sstevel@tonic-gate char *passwd = "/etc/passwd";
610Sstevel@tonic-gate char *shadow = "/etc/shadow";
620Sstevel@tonic-gate char buf[BUFSIZ];
630Sstevel@tonic-gate
64273Scf46844 int
main(void)65273Scf46844 main(void)
660Sstevel@tonic-gate {
670Sstevel@tonic-gate int fd;
680Sstevel@tonic-gate FILE *ft, *fp;
690Sstevel@tonic-gate char *editor;
700Sstevel@tonic-gate int ok = 0;
710Sstevel@tonic-gate time_t o_mtime, n_mtime;
720Sstevel@tonic-gate struct stat osbuf, sbuf, oshdbuf, shdbuf;
730Sstevel@tonic-gate char c;
740Sstevel@tonic-gate
750Sstevel@tonic-gate (void)signal(SIGINT, SIG_IGN);
760Sstevel@tonic-gate (void)signal(SIGQUIT, SIG_IGN);
770Sstevel@tonic-gate (void)signal(SIGHUP, SIG_IGN);
780Sstevel@tonic-gate setbuf(stderr, (char *)NULL);
790Sstevel@tonic-gate
800Sstevel@tonic-gate editor = getenv("VISUAL");
810Sstevel@tonic-gate if (editor == 0)
820Sstevel@tonic-gate editor = getenv("EDITOR");
830Sstevel@tonic-gate if (editor == 0)
840Sstevel@tonic-gate editor = DEFAULT_EDITOR;
850Sstevel@tonic-gate
860Sstevel@tonic-gate (void)umask(0077);
870Sstevel@tonic-gate if (stat(passwd, &osbuf) < 0) {
880Sstevel@tonic-gate (void)fprintf(stderr,"vipw: can't stat passwd file.\n");
890Sstevel@tonic-gate goto bad;
900Sstevel@tonic-gate }
910Sstevel@tonic-gate
920Sstevel@tonic-gate if (copyfile(passwd, ptemp))
930Sstevel@tonic-gate goto bad;
940Sstevel@tonic-gate
950Sstevel@tonic-gate if (stat(ptemp, &sbuf) < 0) {
960Sstevel@tonic-gate (void)fprintf(stderr,
970Sstevel@tonic-gate "vipw: can't stat ptemp file, %s unchanged\n",
980Sstevel@tonic-gate passwd);
990Sstevel@tonic-gate goto bad;
1000Sstevel@tonic-gate }
1010Sstevel@tonic-gate
1020Sstevel@tonic-gate o_mtime = sbuf.st_mtime;
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate if (editfile(editor, ptemp, passwd, &n_mtime)) {
1050Sstevel@tonic-gate if (sanity_check(ptemp, &n_mtime, passwd))
1060Sstevel@tonic-gate goto bad;
1070Sstevel@tonic-gate if (o_mtime >= n_mtime)
1080Sstevel@tonic-gate goto bad;
1090Sstevel@tonic-gate }
1100Sstevel@tonic-gate
1110Sstevel@tonic-gate ok++;
1120Sstevel@tonic-gate if (o_mtime < n_mtime) {
1130Sstevel@tonic-gate fprintf(stdout, "\nYou have modified the password file.\n");
1140Sstevel@tonic-gate fprintf(stdout,
1150Sstevel@tonic-gate "Press 'e' to edit the shadow file for consistency,\n 'q' to quit: ");
1160Sstevel@tonic-gate if ((c = getchar()) == 'q') {
1170Sstevel@tonic-gate if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
1180Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s: ", ptemp);
1190Sstevel@tonic-gate perror("chmod");
1200Sstevel@tonic-gate goto bad;
1210Sstevel@tonic-gate }
1220Sstevel@tonic-gate if (rename(ptemp, passwd) < 0) {
1230Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s: ", ptemp);
1240Sstevel@tonic-gate perror("rename");
1250Sstevel@tonic-gate goto bad;
1260Sstevel@tonic-gate }
1270Sstevel@tonic-gate if (((osbuf.st_gid != sbuf.st_gid) ||
1280Sstevel@tonic-gate (osbuf.st_uid != sbuf.st_uid)) &&
1290Sstevel@tonic-gate (chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
1300Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s ", ptemp);
1310Sstevel@tonic-gate perror("chown");
1320Sstevel@tonic-gate }
1330Sstevel@tonic-gate goto bad;
1340Sstevel@tonic-gate } else if (c == 'e') {
1350Sstevel@tonic-gate if (stat(shadow, &oshdbuf) < 0) {
1360Sstevel@tonic-gate (void) fprintf(stderr,
1370Sstevel@tonic-gate "vipw: can't stat shadow file.\n");
1380Sstevel@tonic-gate goto bad;
1390Sstevel@tonic-gate }
1400Sstevel@tonic-gate
1410Sstevel@tonic-gate if (copyfile(shadow, stemp))
1420Sstevel@tonic-gate goto bad;
1430Sstevel@tonic-gate if (stat(stemp, &shdbuf) < 0) {
1440Sstevel@tonic-gate (void) fprintf(stderr,
1450Sstevel@tonic-gate "vipw: can't stat stmp file.\n");
1460Sstevel@tonic-gate goto bad;
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate if (editfile(editor, stemp, shadow, &o_mtime))
1500Sstevel@tonic-gate goto bad;
1510Sstevel@tonic-gate ok++;
1520Sstevel@tonic-gate if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
1530Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s: ", ptemp);
1540Sstevel@tonic-gate perror("chmod");
1550Sstevel@tonic-gate goto bad;
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate if (chmod(stemp, (oshdbuf.st_mode & 0400)) < 0) {
1580Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s: ", stemp);
1590Sstevel@tonic-gate perror("chmod");
1600Sstevel@tonic-gate goto bad;
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate if (rename(ptemp, passwd) < 0) {
1630Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s: ", ptemp);
1640Sstevel@tonic-gate perror("rename");
1650Sstevel@tonic-gate goto bad;
1660Sstevel@tonic-gate }
1670Sstevel@tonic-gate if (((osbuf.st_gid != sbuf.st_gid) ||
1680Sstevel@tonic-gate (osbuf.st_uid != sbuf.st_uid)) &&
1690Sstevel@tonic-gate (chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
1700Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s ", ptemp);
1710Sstevel@tonic-gate perror("chown");
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate if (rename(stemp, shadow) < 0) {
1740Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s: ", stemp);
1750Sstevel@tonic-gate perror("rename");
1760Sstevel@tonic-gate goto bad;
1770Sstevel@tonic-gate } else if (((oshdbuf.st_gid != shdbuf.st_gid) ||
1780Sstevel@tonic-gate (oshdbuf.st_uid != shdbuf.st_uid)) &&
1790Sstevel@tonic-gate (chown(shadow, oshdbuf.st_uid, oshdbuf.st_gid) < 0)) {
1800Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s ", stemp);
1810Sstevel@tonic-gate perror("chown");
1820Sstevel@tonic-gate }
1830Sstevel@tonic-gate }
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate bad:
1860Sstevel@tonic-gate (void) unlink(ptemp);
1870Sstevel@tonic-gate (void) unlink(stemp);
188273Scf46844 return (ok ? 0 : 1);
1890Sstevel@tonic-gate /* NOTREACHED */
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate
193273Scf46844 int
copyfile(char * from,char * to)194273Scf46844 copyfile(char *from, char *to)
1950Sstevel@tonic-gate {
1960Sstevel@tonic-gate int fd;
1970Sstevel@tonic-gate FILE *fp, *ft;
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate fd = open(to, O_WRONLY|O_CREAT|O_EXCL, 0600);
2000Sstevel@tonic-gate if (fd < 0) {
2010Sstevel@tonic-gate if (errno == EEXIST) {
2020Sstevel@tonic-gate (void) fprintf(stderr, "vipw: %s file busy\n", from);
2030Sstevel@tonic-gate exit(1);
2040Sstevel@tonic-gate }
2050Sstevel@tonic-gate (void) fprintf(stderr, "vipw: "); perror(to);
2060Sstevel@tonic-gate exit(1);
2070Sstevel@tonic-gate }
2080Sstevel@tonic-gate ft = fdopen(fd, "w");
2090Sstevel@tonic-gate if (ft == NULL) {
2100Sstevel@tonic-gate (void) fprintf(stderr, "vipw: "); perror(to);
2110Sstevel@tonic-gate return( 1 );
2120Sstevel@tonic-gate }
2130Sstevel@tonic-gate fp = fopen(from, "r");
2140Sstevel@tonic-gate if (fp == NULL) {
2150Sstevel@tonic-gate (void) fprintf(stderr, "vipw: "); perror(from);
2160Sstevel@tonic-gate return( 1 );
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate while (fgets(buf, sizeof (buf) - 1, fp) != NULL)
2190Sstevel@tonic-gate fputs(buf, ft);
2200Sstevel@tonic-gate (void) fclose(ft);
2210Sstevel@tonic-gate (void) fclose(fp);
2220Sstevel@tonic-gate return( 0 );
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate
225273Scf46844 int
editfile(char * editor,char * temp,char * orig,time_t * mtime)226273Scf46844 editfile(char *editor, char *temp, char *orig, time_t *mtime)
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate (void)sprintf(buf, "%s %s", editor, temp);
2290Sstevel@tonic-gate if (system(buf) == 0) {
2300Sstevel@tonic-gate return (sanity_check(temp, mtime, orig));
2310Sstevel@tonic-gate }
2320Sstevel@tonic-gate return(1);
2330Sstevel@tonic-gate }
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate
236273Scf46844 int
validsh(char * rootsh)237273Scf46844 validsh(char *rootsh)
2380Sstevel@tonic-gate {
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate char *sh, *getusershell();
2410Sstevel@tonic-gate int ret = 0;
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate setusershell();
2440Sstevel@tonic-gate while((sh = getusershell()) != NULL ) {
2450Sstevel@tonic-gate if( strcmp( rootsh, sh) == 0 ) {
2460Sstevel@tonic-gate ret = 1;
2470Sstevel@tonic-gate break;
2480Sstevel@tonic-gate }
2490Sstevel@tonic-gate }
2500Sstevel@tonic-gate endusershell();
2510Sstevel@tonic-gate return(ret);
2520Sstevel@tonic-gate }
2530Sstevel@tonic-gate
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate * sanity checks
2560Sstevel@tonic-gate * return 0 if ok, 1 otherwise
2570Sstevel@tonic-gate */
258273Scf46844 int
sanity_check(char * temp,time_t * mtime,char * orig)259273Scf46844 sanity_check(char *temp, time_t *mtime, char *orig)
2600Sstevel@tonic-gate {
2610Sstevel@tonic-gate int i, ok = 0;
2620Sstevel@tonic-gate FILE *ft;
263*1553Scf46844 struct stat sbuf, statbuf;
264*1553Scf46844 char *ldir;
2650Sstevel@tonic-gate int isshadow = 0;
2660Sstevel@tonic-gate
2670Sstevel@tonic-gate if (!strcmp(orig, shadow))
2680Sstevel@tonic-gate isshadow = 1;
2690Sstevel@tonic-gate
2700Sstevel@tonic-gate /* sanity checks */
2710Sstevel@tonic-gate if (stat(temp, &sbuf) < 0) {
2720Sstevel@tonic-gate (void)fprintf(stderr,
2730Sstevel@tonic-gate "vipw: can't stat %s file, %s unchanged\n",
2740Sstevel@tonic-gate temp, orig);
2750Sstevel@tonic-gate return(1);
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate *mtime = sbuf.st_mtime;
2780Sstevel@tonic-gate if (sbuf.st_size == 0) {
2790Sstevel@tonic-gate (void)fprintf(stderr, "vipw: bad %s file, %s unchanged\n",
2800Sstevel@tonic-gate temp, orig);
2810Sstevel@tonic-gate return(1);
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate ft = fopen(temp, "r");
2840Sstevel@tonic-gate if (ft == NULL) {
2850Sstevel@tonic-gate (void)fprintf(stderr,
2860Sstevel@tonic-gate "vipw: can't reopen %s file, %s unchanged\n",
2870Sstevel@tonic-gate temp, orig);
2880Sstevel@tonic-gate return(1);
2890Sstevel@tonic-gate }
2900Sstevel@tonic-gate
2910Sstevel@tonic-gate while (fgets(buf, sizeof (buf) - 1, ft) != NULL) {
292273Scf46844 char *cp;
2930Sstevel@tonic-gate
2940Sstevel@tonic-gate cp = index(buf, '\n');
2950Sstevel@tonic-gate if (cp == 0)
2960Sstevel@tonic-gate continue; /* ??? allow very long lines
2970Sstevel@tonic-gate * and passwd files that do
2980Sstevel@tonic-gate * not end in '\n' ???
2990Sstevel@tonic-gate */
3000Sstevel@tonic-gate *cp = '\0';
3010Sstevel@tonic-gate
3020Sstevel@tonic-gate cp = index(buf, ':');
3030Sstevel@tonic-gate if (cp == 0) /* lines without colon
3040Sstevel@tonic-gate * separated fields
3050Sstevel@tonic-gate */
3060Sstevel@tonic-gate continue;
3070Sstevel@tonic-gate *cp = '\0';
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate if (strcmp(buf, "root"))
3100Sstevel@tonic-gate continue;
3110Sstevel@tonic-gate
3120Sstevel@tonic-gate /* root password */
3130Sstevel@tonic-gate *cp = ':';
3140Sstevel@tonic-gate cp = index(cp + 1, ':');
3150Sstevel@tonic-gate if (cp == 0)
3160Sstevel@tonic-gate goto bad_root;
3170Sstevel@tonic-gate
3180Sstevel@tonic-gate /* root uid for password */
3190Sstevel@tonic-gate if (!isshadow)
3200Sstevel@tonic-gate if (atoi(cp + 1) != 0) {
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate (void)fprintf(stderr, "root UID != 0:\n%s\n",
3230Sstevel@tonic-gate buf);
3240Sstevel@tonic-gate break;
3250Sstevel@tonic-gate }
3260Sstevel@tonic-gate /* root uid for passwd and sp_lstchg for shadow */
3270Sstevel@tonic-gate cp = index(cp + 1, ':');
3280Sstevel@tonic-gate if (cp == 0)
3290Sstevel@tonic-gate goto bad_root;
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate /* root's gid for passwd and sp_min for shadow*/
3320Sstevel@tonic-gate cp = index(cp + 1, ':');
3330Sstevel@tonic-gate if (cp == 0)
3340Sstevel@tonic-gate goto bad_root;
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate /* root's gecos for passwd and sp_max for shadow*/
3370Sstevel@tonic-gate cp = index(cp + 1, ':');
3380Sstevel@tonic-gate if (isshadow) {
3390Sstevel@tonic-gate for (i=0; i<3; i++)
3400Sstevel@tonic-gate if ((cp = index(cp + 1, ':')) == 0)
3410Sstevel@tonic-gate goto bad_root;
3420Sstevel@tonic-gate } else {
3430Sstevel@tonic-gate if (cp == 0) {
3440Sstevel@tonic-gate bad_root: (void)fprintf(stderr,
3450Sstevel@tonic-gate "Missing fields in root entry:\n%s\n", buf);
3460Sstevel@tonic-gate break;
3470Sstevel@tonic-gate }
3480Sstevel@tonic-gate }
3490Sstevel@tonic-gate if (!isshadow) {
3500Sstevel@tonic-gate /* root's login directory */
351*1553Scf46844 ldir = ++cp;
352*1553Scf46844 cp = index(cp, ':');
353*1553Scf46844 if (cp == 0)
354*1553Scf46844 goto bad_root;
355*1553Scf46844 *cp = '\0';
356*1553Scf46844 if (stat(ldir, &statbuf) < 0) {
357*1553Scf46844 *cp = ':';
358*1553Scf46844 (void) fprintf(stderr,
359*1553Scf46844 "root login dir doesn't exist:\n%s\n",
360*1553Scf46844 buf);
361*1553Scf46844 break;
362*1553Scf46844 } else if (!S_ISDIR(statbuf.st_mode)) {
363*1553Scf46844 *cp = ':';
364*1553Scf46844 (void) fprintf(stderr,
365*1553Scf46844 "root login dir is not a directory:\n%s\n",
366*1553Scf46844 buf);
3670Sstevel@tonic-gate break;
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate
370*1553Scf46844 *cp = ':';
3710Sstevel@tonic-gate /* root's login shell */
372*1553Scf46844 ++cp;
3730Sstevel@tonic-gate if (*cp && ! validsh(cp)) {
3740Sstevel@tonic-gate (void)fprintf(stderr,
3750Sstevel@tonic-gate "Invalid root shell:\n%s\n", buf);
3760Sstevel@tonic-gate break;
3770Sstevel@tonic-gate }
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate
3800Sstevel@tonic-gate ok++;
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate (void)fclose(ft);
3830Sstevel@tonic-gate if (ok)
3840Sstevel@tonic-gate return(0);
3850Sstevel@tonic-gate else {
3860Sstevel@tonic-gate (void)fprintf(stderr,
3870Sstevel@tonic-gate "vipw: you mangled the %s file, %s unchanged\n",
3880Sstevel@tonic-gate temp, orig);
3890Sstevel@tonic-gate return(1);
3900Sstevel@tonic-gate }
3910Sstevel@tonic-gate }
392