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.9 (Berkeley) 04/22/89"; 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 if (!(tfp = fdopen(fd, "r"))) { 87 (void)fprintf(stderr, "vipw: %s: %s; ", 88 temp, strerror(errno)); 89 stop(1); 90 } 91 92 for (;;) { 93 (void)fstat(fd, &s1); 94 if (edit()) { 95 (void)fprintf(stderr, "vipw: edit failed; "); 96 stop(1); 97 } 98 (void)fstat(fd, &s2); 99 if (s1.st_mtime == s2.st_mtime) { 100 (void)fprintf(stderr, "vipw: no changes made; "); 101 stop(0); 102 } 103 rewind(tfp); 104 if (!check(tfp)) 105 break; 106 if (prompt()) 107 stop(0); 108 } 109 110 switch(fork()) { 111 case 0: 112 break; 113 case -1: 114 (void)fprintf(stderr, "vipw: can't fork; "); 115 stop(1); 116 /* NOTREACHED */ 117 default: 118 exit(0); 119 /* NOTREACHED */ 120 } 121 122 if (makedb(temp)) { 123 (void)fprintf(stderr, "vipw: mkpasswd failed; "); 124 stop(1); 125 } 126 127 /* 128 * possible race; have to rename four files, and someone could slip 129 * in between them. LOCK_EX and rename the ``passwd.dir'' file first 130 * so that getpwent(3) can't slip in; the lock should never fail and 131 * it's unclear what to do if it does. Rename ``ptmp'' last so that 132 * passwd/vipw/chpass can't slip in. 133 */ 134 (void)setpriority(PRIO_PROCESS, 0, -20); 135 fend = strcpy(from, temp) + strlen(temp); 136 tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD); 137 bcopy(".dir", fend, 5); 138 bcopy(".dir", tend, 5); 139 if ((fd = open(from, O_RDONLY, 0)) >= 0) 140 (void)flock(fd, LOCK_EX); 141 /* here we go... */ 142 (void)rename(from, to); 143 bcopy(".pag", fend, 5); 144 bcopy(".pag", tend, 5); 145 (void)rename(from, to); 146 bcopy(".orig", fend, 6); 147 (void)rename(from, _PATH_PASSWD); 148 (void)rename(temp, passwd); 149 /* done! */ 150 exit(0); 151 } 152 153 check(tfp) 154 FILE *tfp; 155 { 156 register long id; 157 register int lcnt, root; 158 register char *p, *sh; 159 long atol(); 160 char buf[1024], *getusershell(); 161 162 for (lcnt = 1; fgets(buf, sizeof(buf), tfp); ++lcnt) { 163 /* skip lines that are too big */ 164 if (!index(buf, '\n')) { 165 (void)fprintf(stderr, "vipw: line too long"); 166 goto bad; 167 } 168 if (!(p = strsep(buf, ":\n"))) /* login */ 169 goto general; 170 root = !strcmp(p, "root"); 171 (void)strsep((char *)NULL, ":\n"); /* passwd */ 172 if (!(p = strsep((char *)NULL, ":\n"))) /* uid */ 173 goto general; 174 id = atol(p); 175 if (root && id) { 176 (void)fprintf(stderr, "vipw: root uid should be 0"); 177 goto bad; 178 } 179 if (id > USHRT_MAX) { 180 (void)fprintf(stderr, "vipw: %s > max uid value (%d)", 181 p, USHRT_MAX); 182 goto bad; 183 } 184 if (!(p = strsep((char *)NULL, ":\n"))) /* gid */ 185 goto general; 186 id = atol(p); 187 if (id > USHRT_MAX) { 188 (void)fprintf(stderr, "vipw: %s > max gid value (%d)", 189 p, USHRT_MAX); 190 goto bad; 191 } 192 (void)strsep((char *)NULL, ":\n"); /* class */ 193 (void)strsep((char *)NULL, ":\n"); /* change */ 194 (void)strsep((char *)NULL, ":\n"); /* expire */ 195 (void)strsep((char *)NULL, ":\n"); /* gecos */ 196 (void)strsep((char *)NULL, ":\n"); /* directory */ 197 if (!(p = strsep((char *)NULL, ":\n"))) /* shell */ 198 goto general; 199 if (root && *p) /* empty == /bin/sh */ 200 for (setusershell();;) 201 if (!(sh = getusershell())) { 202 (void)fprintf(stderr, 203 "vipw: warning, unknown root shell.\n"); 204 break; 205 } 206 else if (!strcmp(p, sh)) 207 break; 208 if (strsep((char *)NULL, ":\n")) { /* too many */ 209 general: (void)fprintf(stderr, "vipw: corrupted entry"); 210 bad: (void)fprintf(stderr, "; line #%d.\n", lcnt); 211 (void)fflush(stderr); 212 return(1); 213 } 214 } 215 return(0); 216 } 217 218 makedb(file) 219 char *file; 220 { 221 int status, pid, w; 222 223 if (!(pid = vfork())) { 224 execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL); 225 _exit(127); 226 } 227 while ((w = wait(&status)) != pid && w != -1); 228 return(w == -1 || status); 229 } 230 231 edit() 232 { 233 extern int errno; 234 int status, pid, w; 235 char *p, *editor, *getenv(), *strerror(); 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 } 277