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