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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2001 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #ident "%Z%%M% %I% %E% SMI" 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/file.h> 40 #include <sys/fcntl.h> 41 42 #include <stdio.h> 43 #include <errno.h> 44 #include <signal.h> 45 46 /* 47 * Password file editor with locking. 48 */ 49 50 #define DEFAULT_EDITOR "/usr/bin/vi" 51 52 char *ptemp = "/etc/ptmp"; 53 char *stemp = "/etc/stmp"; 54 char *passwd = "/etc/passwd"; 55 char *shadow = "/etc/shadow"; 56 char buf[BUFSIZ]; 57 char *getenv(); 58 char *index(); 59 extern int errno; 60 61 main() 62 { 63 int fd; 64 FILE *ft, *fp; 65 char *editor; 66 int ok = 0; 67 time_t o_mtime, n_mtime; 68 struct stat osbuf, sbuf, oshdbuf, shdbuf; 69 char c; 70 71 (void)signal(SIGINT, SIG_IGN); 72 (void)signal(SIGQUIT, SIG_IGN); 73 (void)signal(SIGHUP, SIG_IGN); 74 setbuf(stderr, (char *)NULL); 75 76 editor = getenv("VISUAL"); 77 if (editor == 0) 78 editor = getenv("EDITOR"); 79 if (editor == 0) 80 editor = DEFAULT_EDITOR; 81 82 (void)umask(0077); 83 if (stat(passwd, &osbuf) < 0) { 84 (void)fprintf(stderr,"vipw: can't stat passwd file.\n"); 85 goto bad; 86 } 87 88 if (copyfile(passwd, ptemp)) 89 goto bad; 90 91 if (stat(ptemp, &sbuf) < 0) { 92 (void)fprintf(stderr, 93 "vipw: can't stat ptemp file, %s unchanged\n", 94 passwd); 95 goto bad; 96 } 97 98 o_mtime = sbuf.st_mtime; 99 100 if (editfile(editor, ptemp, passwd, &n_mtime)) { 101 if (sanity_check(ptemp, &n_mtime, passwd)) 102 goto bad; 103 if (o_mtime >= n_mtime) 104 goto bad; 105 } 106 107 ok++; 108 if (o_mtime < n_mtime) { 109 fprintf(stdout, "\nYou have modified the password file.\n"); 110 fprintf(stdout, 111 "Press 'e' to edit the shadow file for consistency,\n 'q' to quit: "); 112 if ((c = getchar()) == 'q') { 113 if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) { 114 (void) fprintf(stderr, "vipw: %s: ", ptemp); 115 perror("chmod"); 116 goto bad; 117 } 118 if (rename(ptemp, passwd) < 0) { 119 (void) fprintf(stderr, "vipw: %s: ", ptemp); 120 perror("rename"); 121 goto bad; 122 } 123 if (((osbuf.st_gid != sbuf.st_gid) || 124 (osbuf.st_uid != sbuf.st_uid)) && 125 (chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) { 126 (void) fprintf(stderr, "vipw: %s ", ptemp); 127 perror("chown"); 128 } 129 goto bad; 130 } else if (c == 'e') { 131 if (stat(shadow, &oshdbuf) < 0) { 132 (void) fprintf(stderr, 133 "vipw: can't stat shadow file.\n"); 134 goto bad; 135 } 136 137 if (copyfile(shadow, stemp)) 138 goto bad; 139 if (stat(stemp, &shdbuf) < 0) { 140 (void) fprintf(stderr, 141 "vipw: can't stat stmp file.\n"); 142 goto bad; 143 } 144 145 if (editfile(editor, stemp, shadow, &o_mtime)) 146 goto bad; 147 ok++; 148 if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) { 149 (void) fprintf(stderr, "vipw: %s: ", ptemp); 150 perror("chmod"); 151 goto bad; 152 } 153 if (chmod(stemp, (oshdbuf.st_mode & 0400)) < 0) { 154 (void) fprintf(stderr, "vipw: %s: ", stemp); 155 perror("chmod"); 156 goto bad; 157 } 158 if (rename(ptemp, passwd) < 0) { 159 (void) fprintf(stderr, "vipw: %s: ", ptemp); 160 perror("rename"); 161 goto bad; 162 } 163 if (((osbuf.st_gid != sbuf.st_gid) || 164 (osbuf.st_uid != sbuf.st_uid)) && 165 (chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) { 166 (void) fprintf(stderr, "vipw: %s ", ptemp); 167 perror("chown"); 168 } 169 if (rename(stemp, shadow) < 0) { 170 (void) fprintf(stderr, "vipw: %s: ", stemp); 171 perror("rename"); 172 goto bad; 173 } else if (((oshdbuf.st_gid != shdbuf.st_gid) || 174 (oshdbuf.st_uid != shdbuf.st_uid)) && 175 (chown(shadow, oshdbuf.st_uid, oshdbuf.st_gid) < 0)) { 176 (void) fprintf(stderr, "vipw: %s ", stemp); 177 perror("chown"); 178 } 179 } 180 } 181 bad: 182 (void) unlink(ptemp); 183 (void) unlink(stemp); 184 exit(ok ? 0 : 1); 185 /* NOTREACHED */ 186 } 187 188 189 copyfile(from, to) 190 char *from, *to; 191 { 192 int fd; 193 FILE *fp, *ft; 194 195 fd = open(to, O_WRONLY|O_CREAT|O_EXCL, 0600); 196 if (fd < 0) { 197 if (errno == EEXIST) { 198 (void) fprintf(stderr, "vipw: %s file busy\n", from); 199 exit(1); 200 } 201 (void) fprintf(stderr, "vipw: "); perror(to); 202 exit(1); 203 } 204 ft = fdopen(fd, "w"); 205 if (ft == NULL) { 206 (void) fprintf(stderr, "vipw: "); perror(to); 207 return( 1 ); 208 } 209 fp = fopen(from, "r"); 210 if (fp == NULL) { 211 (void) fprintf(stderr, "vipw: "); perror(from); 212 return( 1 ); 213 } 214 while (fgets(buf, sizeof (buf) - 1, fp) != NULL) 215 fputs(buf, ft); 216 (void) fclose(ft); 217 (void) fclose(fp); 218 return( 0 ); 219 } 220 221 editfile(editor, temp, orig, mtime) 222 char *editor, *temp, *orig; 223 time_t *mtime; 224 { 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 validsh(rootsh) 235 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 sanity_check(temp, mtime, orig) 257 char *temp, *orig; 258 time_t *mtime; 259 { 260 int i, ok = 0; 261 FILE *ft; 262 struct stat sbuf; 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 register 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 if (strncmp(++cp, "/:", 2)) { 350 (void)fprintf(stderr, 351 "Root login directory != \"/\" or%s\n%s\n", 352 " default shell missing:", buf); 353 break; 354 } 355 356 /* root's login shell */ 357 cp += 2; 358 if (*cp && ! validsh(cp)) { 359 (void)fprintf(stderr, 360 "Invalid root shell:\n%s\n", buf); 361 break; 362 } 363 } 364 365 ok++; 366 } 367 (void)fclose(ft); 368 if (ok) 369 return(0); 370 else { 371 (void)fprintf(stderr, 372 "vipw: you mangled the %s file, %s unchanged\n", 373 temp, orig); 374 return(1); 375 } 376 } 377