Home | History | Annotate | Download | only in vipw
      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 2006 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
     27 /*	  All Rights Reserved  	*/
     28 
     29 /*
     30  * Portions of this source code were derived from Berkeley 4.3 BSD
     31  * under license from the Regents of the University of California.
     32  */
     33 
     34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     35 
     36 #include <sys/types.h>
     37 #include <sys/stat.h>
     38 #include <sys/file.h>
     39 #include <sys/fcntl.h>
     40 
     41 #include <stdio.h>
     42 #include <errno.h>
     43 #include <signal.h>
     44 #include <stdlib.h>
     45 #include <strings.h>
     46 
     47 /*
     48  * Password file editor with locking.
     49  */
     50 
     51 #define	DEFAULT_EDITOR	"/usr/bin/vi"
     52 
     53 static int copyfile(char *, char *);
     54 static int editfile(char *, char *, char *, time_t *);
     55 static int sanity_check(char *, time_t *, char *);
     56 static int validsh(char *);
     57 
     58 char	*ptemp = "/etc/ptmp";
     59 char	*stemp = "/etc/stmp";
     60 char	*passwd = "/etc/passwd";
     61 char	*shadow = "/etc/shadow";
     62 char	buf[BUFSIZ];
     63 
     64 int
     65 main(void)
     66 {
     67 	int fd;
     68 	FILE *ft, *fp;
     69 	char *editor;
     70 	int ok = 0;
     71 	time_t o_mtime, n_mtime;
     72 	struct stat osbuf, sbuf, oshdbuf, shdbuf;
     73 	char c;
     74 
     75 	(void)signal(SIGINT, SIG_IGN);
     76 	(void)signal(SIGQUIT, SIG_IGN);
     77 	(void)signal(SIGHUP, SIG_IGN);
     78 	setbuf(stderr, (char *)NULL);
     79 
     80 	editor = getenv("VISUAL");
     81 	if (editor == 0)
     82 		editor = getenv("EDITOR");
     83 	if (editor == 0)
     84 		editor = DEFAULT_EDITOR;
     85 
     86 	(void)umask(0077);
     87 	if (stat(passwd, &osbuf) < 0) {
     88                 (void)fprintf(stderr,"vipw: can't stat passwd file.\n");
     89                 goto bad;
     90         }
     91 
     92 	if (copyfile(passwd, ptemp))
     93 		goto bad;
     94 
     95 	if (stat(ptemp, &sbuf) < 0) {
     96                 (void)fprintf(stderr,
     97 			"vipw: can't stat ptemp file, %s unchanged\n",
     98 			passwd);
     99 		goto bad;
    100 	}
    101 
    102 	o_mtime = sbuf.st_mtime;
    103 
    104 	if (editfile(editor, ptemp, passwd, &n_mtime)) {
    105 		if (sanity_check(ptemp, &n_mtime, passwd))
    106 			goto bad;
    107 		if (o_mtime >= n_mtime)
    108 			goto bad;
    109 	}
    110 
    111 	ok++;
    112 	if (o_mtime < n_mtime) {
    113 		fprintf(stdout, "\nYou have modified the password file.\n");
    114 		fprintf(stdout,
    115 	"Press 'e' to edit the shadow file for consistency,\n 'q' to quit: ");
    116 		if ((c = getchar()) == 'q') {
    117 			if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
    118 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
    119 				perror("chmod");
    120 				goto bad;
    121 			}
    122 			if (rename(ptemp, passwd) < 0) {
    123 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
    124 				perror("rename");
    125 				goto bad;
    126 			}
    127 			if (((osbuf.st_gid != sbuf.st_gid) ||
    128 					(osbuf.st_uid != sbuf.st_uid)) &&
    129 			(chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
    130 				(void) fprintf(stderr, "vipw: %s ", ptemp);
    131 				perror("chown");
    132 			}
    133 			goto bad;
    134 		} else if (c == 'e') {
    135 			if (stat(shadow, &oshdbuf) < 0) {
    136 				(void) fprintf(stderr,
    137 					"vipw: can't stat shadow file.\n");
    138 				goto bad;
    139 			}
    140 
    141 			if (copyfile(shadow, stemp))
    142 				goto bad;
    143 			if (stat(stemp, &shdbuf) < 0) {
    144 				(void) fprintf(stderr,
    145 					"vipw: can't stat stmp file.\n");
    146 				goto bad;
    147 			}
    148 
    149 			if (editfile(editor, stemp, shadow, &o_mtime))
    150 				goto bad;
    151 			ok++;
    152 			if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
    153 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
    154 				perror("chmod");
    155 				goto bad;
    156 			}
    157 			if (chmod(stemp, (oshdbuf.st_mode & 0400)) < 0) {
    158 				(void) fprintf(stderr, "vipw: %s: ", stemp);
    159 				perror("chmod");
    160 				goto bad;
    161 			}
    162 			if (rename(ptemp, passwd) < 0) {
    163 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
    164 				perror("rename");
    165 				goto bad;
    166 			}
    167 			if (((osbuf.st_gid != sbuf.st_gid) ||
    168 					(osbuf.st_uid != sbuf.st_uid)) &&
    169 			(chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
    170 				(void) fprintf(stderr, "vipw: %s ", ptemp);
    171 				perror("chown");
    172 			}
    173 			if (rename(stemp, shadow) < 0) {
    174 				(void) fprintf(stderr, "vipw: %s: ", stemp);
    175 				perror("rename");
    176 				goto bad;
    177 			} else if (((oshdbuf.st_gid != shdbuf.st_gid) ||
    178 					(oshdbuf.st_uid != shdbuf.st_uid)) &&
    179 			(chown(shadow, oshdbuf.st_uid, oshdbuf.st_gid) < 0)) {
    180 				(void) fprintf(stderr, "vipw: %s ", stemp);
    181 				perror("chown");
    182 				}
    183 		}
    184 	}
    185 bad:
    186 	(void) unlink(ptemp);
    187 	(void) unlink(stemp);
    188 	return (ok ? 0 : 1);
    189 	/* NOTREACHED */
    190 }
    191 
    192 
    193 int
    194 copyfile(char *from, char *to)
    195 {
    196 	int fd;
    197 	FILE *fp, *ft;
    198 
    199 	fd = open(to, O_WRONLY|O_CREAT|O_EXCL, 0600);
    200 	if (fd < 0) {
    201 		if (errno == EEXIST) {
    202 			(void) fprintf(stderr, "vipw: %s file busy\n", from);
    203 			exit(1);
    204 		}
    205 		(void) fprintf(stderr, "vipw: "); perror(to);
    206 		exit(1);
    207 	}
    208 	ft = fdopen(fd, "w");
    209 	if (ft == NULL) {
    210 		(void) fprintf(stderr, "vipw: "); perror(to);
    211 		return( 1 );
    212 	}
    213 	fp = fopen(from, "r");
    214 	if (fp == NULL) {
    215 		(void) fprintf(stderr, "vipw: "); perror(from);
    216 		return( 1 );
    217 	}
    218 	while (fgets(buf, sizeof (buf) - 1, fp) != NULL)
    219 		fputs(buf, ft);
    220 	(void) fclose(ft);
    221 	(void) fclose(fp);
    222 	return( 0 );
    223 }
    224 
    225 int
    226 editfile(char *editor, char *temp, char *orig, time_t *mtime)
    227 {
    228 	(void)sprintf(buf, "%s %s", editor, temp);
    229 	if (system(buf) == 0) {
    230 		return (sanity_check(temp, mtime, orig));
    231 	}
    232 	return(1);
    233 }
    234 
    235 
    236 int
    237 validsh(char *rootsh)
    238 {
    239 
    240 	char	*sh, *getusershell();
    241 	int	ret = 0;
    242 
    243 	setusershell();
    244 	while((sh = getusershell()) != NULL ) {
    245 		if( strcmp( rootsh, sh) == 0 ) {
    246 			ret = 1;
    247 			break;
    248 		}
    249 	}
    250 	endusershell();
    251 	return(ret);
    252 }
    253 
    254 /*
    255  * sanity checks
    256  * return 0 if ok, 1 otherwise
    257  */
    258 int
    259 sanity_check(char *temp, time_t *mtime, char *orig)
    260 {
    261 	int i, ok = 0;
    262 	FILE *ft;
    263 	struct stat sbuf, statbuf;
    264 	char *ldir;
    265 	int isshadow = 0;
    266 
    267 	if (!strcmp(orig, shadow))
    268 		isshadow = 1;
    269 
    270 	/* sanity checks */
    271 	if (stat(temp, &sbuf) < 0) {
    272 		(void)fprintf(stderr,
    273 		    "vipw: can't stat %s file, %s unchanged\n",
    274 		    temp, orig);
    275 		return(1);
    276 	}
    277 	*mtime = sbuf.st_mtime;
    278 	if (sbuf.st_size == 0) {
    279 		(void)fprintf(stderr, "vipw: bad %s file, %s unchanged\n",
    280 		    temp, orig);
    281 		return(1);
    282 	}
    283 	ft = fopen(temp, "r");
    284 	if (ft == NULL) {
    285 		(void)fprintf(stderr,
    286 		    "vipw: can't reopen %s file, %s unchanged\n",
    287 		    temp, orig);
    288 		return(1);
    289 	}
    290 
    291 	while (fgets(buf, sizeof (buf) - 1, ft) != NULL) {
    292 		char *cp;
    293 
    294 		cp = index(buf, '\n');
    295 		if (cp == 0)
    296 			continue;	/* ??? allow very long lines
    297 					 * and passwd files that do
    298 					 * not end in '\n' ???
    299 					 */
    300 		*cp = '\0';
    301 
    302 		cp = index(buf, ':');
    303 		if (cp == 0)		/* lines without colon
    304 					 * separated fields
    305 					 */
    306 			continue;
    307 		*cp = '\0';
    308 
    309 		if (strcmp(buf, "root"))
    310 			continue;
    311 
    312 		/* root password */
    313 		*cp = ':';
    314 		cp = index(cp + 1, ':');
    315 		if (cp == 0)
    316 			goto bad_root;
    317 
    318 		/* root uid for password */
    319 		if (!isshadow)
    320 			if (atoi(cp + 1) != 0) {
    321 
    322 				(void)fprintf(stderr, "root UID != 0:\n%s\n",
    323 				    buf);
    324 				break;
    325 			}
    326 		/* root uid for passwd and sp_lstchg for shadow */
    327 		cp = index(cp + 1, ':');
    328 		if (cp == 0)
    329 			goto bad_root;
    330 
    331 		/* root's gid for passwd and sp_min for shadow*/
    332 		cp = index(cp + 1, ':');
    333 		if (cp == 0)
    334 			goto bad_root;
    335 
    336 		/* root's gecos for passwd and sp_max for shadow*/
    337 		cp = index(cp + 1, ':');
    338 		if (isshadow) {
    339 			for (i=0; i<3; i++)
    340 				if ((cp = index(cp + 1, ':')) == 0)
    341 					goto bad_root;
    342 		} else {
    343 			if (cp == 0) {
    344 bad_root:		(void)fprintf(stderr,
    345 				    "Missing fields in root entry:\n%s\n", buf);
    346 				break;
    347 			}
    348 		}
    349 		if (!isshadow) {
    350 			/* root's login directory */
    351 			ldir = ++cp;
    352 			cp = index(cp, ':');
    353 			if (cp == 0)
    354 				goto bad_root;
    355 			*cp = '\0';
    356 			if (stat(ldir, &statbuf) < 0) {
    357 				*cp = ':';
    358 				(void) fprintf(stderr,
    359 				    "root login dir doesn't exist:\n%s\n",
    360 				    buf);
    361 				break;
    362 			} else if (!S_ISDIR(statbuf.st_mode)) {
    363 				*cp = ':';
    364 				(void) fprintf(stderr,
    365 				    "root login dir is not a directory:\n%s\n",
    366 				    buf);
    367 				break;
    368 			}
    369 
    370 			*cp = ':';
    371 			/* root's login shell */
    372 			++cp;
    373 			if (*cp && ! validsh(cp)) {
    374 				(void)fprintf(stderr,
    375 				    "Invalid root shell:\n%s\n", buf);
    376 				break;
    377 			}
    378 		}
    379 
    380 		ok++;
    381 	}
    382 	(void)fclose(ft);
    383 	if (ok)
    384 		return(0);
    385 	else {
    386 		(void)fprintf(stderr,
    387 		    "vipw: you mangled the %s file, %s unchanged\n",
    388 		    temp, orig);
    389 		return(1);
    390 	}
    391 }
    392