1: /* 2: * Copyright (c) 1987 Regents of the University of California. 3: * All rights reserved. 4: * 5: * Redistribution and use in source and binary forms are permitted 6: * provided that the above copyright notice and this paragraph are 7: * duplicated in all such forms and that any documentation, 8: * advertising materials, and other materials related to such 9: * distribution and use acknowledge that the software was developed 10: * by the University of California, Berkeley. The name of the 11: * University may not be used to endorse or promote products derived 12: * from this software without specific prior written permission. 13: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15: * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16: */ 17: 18: #if !defined(lint) && defined(DOSCCS) 19: char copyright[] = 20: "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 21: All rights reserved.\n"; 22: 23: static char sccsid[] = "@(#)vipw.c 5.9.1 (2.11BSD) 1996/1/12"; 24: #endif /* not lint */ 25: 26: #include <sys/param.h> 27: #include <sys/stat.h> 28: #include <sys/signal.h> 29: #include <sys/file.h> 30: #include <sys/time.h> 31: #include <sys/resource.h> 32: #include <errno.h> 33: #include <pwd.h> 34: #include <stdio.h> 35: #include <strings.h> 36: #include <stdlib.h> 37: 38: char *passwd, *temp; 39: 40: main() 41: { 42: register int n, fd_passwd, fd; 43: struct rlimit rlim; 44: struct stat s1, s2; 45: FILE *tfp; 46: char *fend, *tend; 47: char buf[1024], from[MAXPATHLEN], to[MAXPATHLEN]; 48: 49: (void)signal(SIGHUP, SIG_IGN); 50: (void)signal(SIGINT, SIG_IGN); 51: (void)signal(SIGQUIT, SIG_IGN); 52: (void)signal(SIGTSTP, SIG_IGN); 53: 54: rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 55: (void)setrlimit(RLIMIT_CPU, &rlim); 56: (void)setrlimit(RLIMIT_FSIZE, &rlim); 57: 58: (void)umask(0); 59: 60: temp = _PATH_PTMP; 61: if ((fd = open(temp, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) { 62: if (errno == EEXIST) 63: (void)fprintf(stderr, "vipw: password file busy.\n"); 64: else 65: (void)fprintf(stderr, 66: "vipw: %s: %s\n", temp, strerror(errno)); 67: exit(1); 68: } 69: passwd = _PATH_MASTERPASSWD; 70: if ((fd_passwd = open(passwd, O_RDONLY, 0)) < 0) { 71: (void)fprintf(stderr, "vipw: %s: %s\n", passwd, 72: strerror(errno)); 73: exit(1); 74: } 75: while ((n = read(fd_passwd, buf, sizeof(buf))) > 0) 76: if (write(fd, buf, n) != n) 77: goto syserr; 78: 79: if (n == -1 || close(fd_passwd)) { 80: syserr: (void)fprintf(stderr, "vipw: %s: %s; ", 81: passwd, strerror(errno)); 82: stop(1); 83: } 84: if (!(tfp = fdopen(fd, "r"))) { 85: (void)fprintf(stderr, "vipw: %s: %s; ", 86: temp, strerror(errno)); 87: stop(1); 88: } 89: 90: for (;;) { 91: (void)fstat(fd, &s1); 92: if (edit()) { 93: (void)fprintf(stderr, "vipw: edit failed; "); 94: stop(1); 95: } 96: (void)fstat(fd, &s2); 97: if (s1.st_mtime == s2.st_mtime) { 98: (void)fprintf(stderr, "vipw: no changes made; "); 99: stop(0); 100: } 101: rewind(tfp); 102: if (!check(tfp)) 103: break; 104: if (prompt()) 105: stop(0); 106: } 107: 108: switch(fork()) { 109: case 0: 110: break; 111: case -1: 112: (void)fprintf(stderr, "vipw: can't fork; "); 113: stop(1); 114: /* NOTREACHED */ 115: default: 116: exit(0); 117: /* NOTREACHED */ 118: } 119: 120: if (makedb(temp)) { 121: (void)fprintf(stderr, "vipw: mkpasswd failed; "); 122: stop(1); 123: } 124: 125: /* 126: * possible race; have to rename four files, and someone could slip 127: * in between them. LOCK_EX and rename the ``passwd.dir'' file first 128: * so that getpwent(3) can't slip in; the lock should never fail and 129: * it's unclear what to do if it does. Rename ``ptmp'' last so that 130: * passwd/vipw/chpass can't slip in. 131: */ 132: (void)setpriority(PRIO_PROCESS, 0, -20); 133: fend = strcpy(from, temp) + strlen(temp); 134: tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD); 135: bcopy(".dir", fend, 5); 136: bcopy(".dir", tend, 5); 137: if ((fd = open(from, O_RDONLY, 0)) >= 0) 138: (void)flock(fd, LOCK_EX); 139: /* here we go... */ 140: (void)rename(from, to); 141: bcopy(".pag", fend, 5); 142: bcopy(".pag", tend, 5); 143: (void)rename(from, to); 144: bcopy(".orig", fend, 6); 145: (void)rename(from, _PATH_PASSWD); 146: (void)rename(temp, passwd); 147: /* done! */ 148: exit(0); 149: } 150: 151: check(tfp) 152: FILE *tfp; 153: { 154: long id; 155: int root; 156: register int lcnt; 157: register char *p, *sh; 158: char buf[256], *getusershell(); 159: char *bp; 160: 161: for (lcnt = 1; fgets(buf, sizeof(buf), tfp); ++lcnt) { 162: bp = buf; 163: /* skip lines that are too big */ 164: if (!(p = index(buf, '\n'))) { 165: (void)fprintf(stderr, "vipw: line too long"); 166: goto bad; 167: } 168: *p = '\0'; 169: if (!(p = strsep(&bp, ":"))) /* login */ 170: goto general; 171: root = !strcmp(p, "root"); 172: (void)strsep(&bp, ":"); /* passwd */ 173: if (!(p = strsep(&bp, ":"))) /* uid */ 174: goto general; 175: id = atol(p); 176: if (root && id) { 177: (void)fprintf(stderr, "vipw: root uid should be 0"); 178: goto bad; 179: } 180: if (id > USHRT_MAX) { 181: (void)fprintf(stderr, "vipw: %s > max uid value (%u)", 182: p, USHRT_MAX); 183: goto bad; 184: } 185: if (!(p = strsep(&bp, ":"))) /* gid */ 186: goto general; 187: id = atol(p); 188: if (id > USHRT_MAX) { 189: (void)fprintf(stderr, "vipw: %s > max gid value (%u)", 190: p, USHRT_MAX); 191: goto bad; 192: } 193: (void)strsep(&bp, ":"); /* class */ 194: (void)strsep(&bp, ":"); /* change */ 195: (void)strsep(&bp, ":"); /* expire */ 196: (void)strsep(&bp, ":"); /* gecos */ 197: (void)strsep(&bp, ":"); /* directory */ 198: if (!(p = strsep(&bp, ":"))) /* shell */ 199: goto general; 200: if (root && *p) /* empty == /bin/sh */ 201: for (setusershell();;) 202: if (!(sh = getusershell())) { 203: (void)fprintf(stderr, 204: "vipw: warning, unknown root shell.\n"); 205: break; 206: } 207: else if (!strcmp(p, sh)) 208: break; 209: if (strsep(&bp, ":")) { /* too many */ 210: general: (void)fprintf(stderr, "vipw: corrupted entry"); 211: bad: (void)fprintf(stderr, "; line #%d.\n", lcnt); 212: (void)fflush(stderr); 213: return(1); 214: } 215: } 216: return(0); 217: } 218: 219: makedb(file) 220: char *file; 221: { 222: int status, pid, w; 223: 224: if (!(pid = vfork())) { 225: execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL); 226: _exit(127); 227: } 228: while ((w = wait(&status)) != pid && w != -1); 229: return(w == -1 || status); 230: } 231: 232: edit() 233: { 234: int status, pid, w; 235: char *p, *editor; 236: 237: if (editor = getenv("EDITOR")) { 238: if (p = rindex(editor, '/')) 239: ++p; 240: else 241: p = editor; 242: } 243: else 244: p = editor = "vi"; 245: if (!(pid = vfork())) { 246: execlp(editor, p, temp, NULL); 247: (void)fprintf(stderr, "vipw: %s: %s\n", editor, 248: strerror(errno)); 249: _exit(127); 250: } 251: while ((w = wait(&status)) != pid && w != -1); 252: return(w == -1 || status); 253: } 254: 255: prompt() 256: { 257: register int c; 258: 259: for (;;) { 260: (void)printf("re-edit the password file? [y]: "); 261: (void)fflush(stdout); 262: c = getchar(); 263: if (c != EOF && c != (int)'\n') 264: while (getchar() != (int)'\n'); 265: return(c == (int)'n'); 266: } 267: /* NOTREACHED */ 268: } 269: 270: stop(val) 271: int val; 272: { 273: (void)fprintf(stderr, "%s unchanged.\n", passwd); 274: (void)unlink(temp); 275: exit(val); 276: }