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