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 this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #ifndef lint 14 char copyright[] = 15 "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 16 All rights reserved.\n"; 17 #endif /* not lint */ 18 19 #ifndef lint 20 static char sccsid[] = "@(#)vipw.c 5.4 (Berkeley) 02/19/88"; 21 #endif /* not lint */ 22 23 #include <machine/machparam.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <sys/signal.h> 27 #include <sys/file.h> 28 #include <stdio.h> 29 #include <errno.h> 30 31 /* 32 * Password file editor with locking. 33 */ 34 static char *passwd = "/etc/passwd", buf[BUFSIZ]; 35 36 main() 37 { 38 register int n, fd_passwd, fd_temp; 39 static char *temp = "/etc/ptmp"; 40 struct stat s1, s2; 41 char *editor, *getenv(); 42 43 (void)signal(SIGHUP, SIG_IGN); 44 (void)signal(SIGINT, SIG_IGN); 45 (void)signal(SIGQUIT, SIG_IGN); 46 47 setbuf(stderr, (char *)NULL); 48 (void)umask(0); 49 50 if ((fd_passwd = open(passwd, O_RDONLY, 0)) < 0) { 51 fputs("vipw: ", stderr); 52 perror(passwd); 53 exit(1); 54 } 55 if ((fd_temp = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) { 56 extern int errno; 57 58 if (errno == EEXIST) { 59 fputs("vipw: password file busy.\n", stderr); 60 exit(1); 61 } 62 fputs("vipw: ", stderr); 63 perror(temp); 64 exit(1); 65 } 66 while ((n = read(fd_passwd, buf, sizeof(buf))) > 0) 67 if (write(fd_temp, buf, n) != n) { 68 perror("vipw: write"); 69 goto bad; 70 } 71 if (n == -1) { 72 perror("vipw: read"); 73 goto bad; 74 } 75 (void)close(fd_passwd); 76 if (fsync(fd_temp)) { 77 perror("vipw: fsync"); 78 goto bad; 79 } 80 if (fstat(fd_temp, &s1)) { 81 perror("vipw: fstat"); 82 goto bad; 83 } 84 (void)close(fd_temp); 85 86 if (!(editor = getenv("EDITOR"))) 87 editor = "vi"; 88 (void)sprintf(buf, "%s %s", editor, temp); 89 if (system(buf)) { 90 perror("vipw: system"); 91 goto bad; 92 } 93 94 if (!freopen(temp, "r", stdin)) { 95 fprintf(stderr, "vipw: can't reopen temp file; %s unchanged.\n", passwd); 96 goto bad; 97 } 98 if (fstat(fileno(stdin), &s2)) { 99 fprintf(stderr, "vipw: can't stat temp file; %s unchanged.\n", passwd); 100 goto bad; 101 } 102 if (s1.st_mtime == s2.st_mtime) { 103 fprintf(stderr, "vipw: %s unchanged.\n", passwd); 104 goto bad; 105 } 106 if (!s2.st_size) { 107 fprintf(stderr, "vipw: bad temp file; %s unchanged.\n", passwd); 108 goto bad; 109 } 110 if (check()) { 111 static char *temp_pag = "/etc/ptmp.pag", 112 *temp_dir = "/etc/ptmp.dir", 113 *passwd_pag = "/etc/passwd.pag", 114 *passwd_dir = "/etc/passwd.dir"; 115 116 if (makedb(temp) < 0) 117 fputs("vipw: mkpasswd failed.\n", stderr); 118 else if (rename(temp_pag, passwd_pag) < 0) { 119 fprintf(stderr, "vipw: "); 120 perror(temp_pag); 121 } 122 else if (rename(temp_dir, passwd_dir) < 0) { 123 fprintf(stderr, "vipw: "); 124 perror(temp_dir); 125 } 126 else if (rename(temp, passwd) < 0) { 127 fprintf(stderr, "vipw: "); 128 perror("rename"); 129 } 130 else 131 exit(0); 132 (void)unlink(temp_pag); 133 (void)unlink(temp_dir); 134 } 135 bad: (void)unlink(temp); 136 exit(1); 137 } 138 139 #define CHN ((char *)NULL) 140 static 141 check() 142 { 143 register char *cp, *sh; 144 register long id; 145 register int root; 146 long atol(); 147 char *token(), *getusershell(); 148 149 for (root = 0; gets(buf); root = 0) { 150 if (!*buf) { 151 fputs("vipw: empty line.\n", stderr); 152 continue; 153 } 154 if (!(cp = token(buf)) || !*cp) /* login */ 155 goto bad; 156 if (!strcmp(cp, "root")) 157 root = 1; 158 (void)token(CHN); /* passwd */ 159 if (!(cp = token(CHN)) || !*cp) /* uid */ 160 goto bad; 161 id = atol(cp); 162 if (root && id) { 163 fprintf(stderr, "vipw: root uid should be 0; %s unchanged.\n", passwd); 164 return(0); 165 } 166 if (id > USHRT_MAX) { 167 fprintf(stderr, "vipw: %s > max uid value (%u); %s unchanged.\n", cp, USHRT_MAX, passwd); 168 return(0); 169 } 170 if (!(cp = token(CHN)) || !*cp) /* gid */ 171 goto bad; 172 id = atol(cp); 173 if (id > USHRT_MAX) { 174 fprintf(stderr, "vipw: %s > max gid value (%u); %s unchanged.\n", cp, USHRT_MAX, passwd); 175 return(0); 176 } 177 (void)token(CHN); /* gcos */ 178 if (!token(CHN)) /* home directory */ 179 goto bad; 180 if (!(cp = token(CHN))) /* shell */ 181 goto bad; 182 if (root && *cp) /* empty == /bin/sh */ 183 for (;;) 184 if (!(sh = getusershell())) { 185 fprintf(stderr, "vipw: illegal shell (%s) for root; %s unchanged.\n", cp, passwd); 186 return(0); 187 } 188 else if (!strcmp(cp, sh)) 189 break; 190 if (token(CHN)) { /* too many fields */ 191 bad: fprintf(stderr, "vipw: corrupted entry; %s unchanged.\n", passwd); 192 return(0); 193 } 194 } 195 return(1); 196 } 197 198 static 199 makedb(file) 200 char *file; 201 { 202 int status, pid, w; 203 204 if (!(pid = vfork())) { 205 execl("/etc/mkpasswd", "mkpasswd", file, 0); 206 _exit(127); 207 } 208 while ((w = wait(&status)) != pid && w != -1); 209 if (w == -1 || status) 210 return(-1); 211 return(0); 212 } 213 214 static char * 215 token(bfr) 216 char *bfr; 217 { 218 static char *cp; 219 char *start; 220 221 if (bfr) /* re-init string */ 222 cp = bfr; 223 else if (!cp) /* check if hit EOS last time */ 224 return(CHN); 225 else if (!bfr) /* start at next char after ':' */ 226 ++cp; 227 for (start = cp;; ++cp) 228 if (!*cp) { /* found EOS; mark it for next time */ 229 cp = CHN; 230 break; 231 } 232 else if (*cp == ':') { /* found ':'; end token */ 233 *cp = '\0'; 234 break; 235 } 236 return(start); /* return token */ 237 } 238