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