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