1 /* 2 * Copyright (c) 1988 The 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)passwd.c 4.34 (Berkeley) 02/23/89"; 26 #endif /* not lint */ 27 28 #include <sys/param.h> 29 #include <sys/file.h> 30 #include <sys/signal.h> 31 #include <sys/time.h> 32 #include <sys/resource.h> 33 #include <errno.h> 34 #include <pwd.h> 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <strings.h> 38 39 uid_t uid; 40 41 main(argc, argv) 42 int argc; 43 char **argv; 44 { 45 extern int errno; 46 struct passwd *pw; 47 struct rlimit rlim; 48 FILE *temp_fp; 49 int fd; 50 char *fend, *np, *passwd, *temp, *tend; 51 char from[MAXPATHLEN], to[MAXPATHLEN]; 52 char *getnewpasswd(); 53 54 uid = getuid(); 55 switch(--argc) { 56 case 0: 57 if (!(pw = getpwuid(uid))) { 58 fprintf(stderr, "passwd: unknown user: uid %u\n", uid); 59 exit(1); 60 } 61 break; 62 case 1: 63 if (!(pw = getpwnam(argv[1]))) { 64 fprintf(stderr, "passwd: unknown user %s.\n", argv[1]); 65 exit(1); 66 } 67 if (uid && uid != pw->pw_uid) { 68 fprintf(stderr, "passwd: %s\n", strerror(EACCES)); 69 exit(1); 70 } 71 break; 72 default: 73 fprintf(stderr, "usage: passwd [user]\n"); 74 exit(1); 75 } 76 77 (void)signal(SIGHUP, SIG_IGN); 78 (void)signal(SIGINT, SIG_IGN); 79 (void)signal(SIGQUIT, SIG_IGN); 80 (void)signal(SIGTSTP, SIG_IGN); 81 82 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 83 (void)setrlimit(RLIMIT_CPU, &rlim); 84 (void)setrlimit(RLIMIT_FSIZE, &rlim); 85 86 (void)umask(0); 87 88 temp = _PATH_PTMP; 89 if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) { 90 if (errno == EEXIST) { 91 fprintf(stderr, 92 "passwd: password file busy -- try again later.\n"); 93 exit(0); 94 } 95 fprintf(stderr, "passwd: %s: %s", temp, strerror(errno)); 96 goto bad; 97 } 98 if (!(temp_fp = fdopen(fd, "w"))) { 99 fprintf(stderr, "passwd: can't write %s\n", temp); 100 goto bad; 101 } 102 passwd = _PATH_MASTERPASSWD; 103 if (!freopen(passwd, "r", stdin)) { 104 fprintf(stderr, "passwd: can't read %s\n", passwd); 105 goto bad; 106 } 107 108 printf("Changing password for %s.\n", pw->pw_name); 109 np = getnewpasswd(pw, temp); 110 111 if (!copy(pw->pw_name, np, temp_fp, pw)) 112 goto bad; 113 114 (void)fclose(temp_fp); 115 (void)fclose(stdin); 116 117 switch(fork()) { 118 case 0: 119 break; 120 case -1: 121 fprintf(stderr, "passwd: can't fork"); 122 goto bad; 123 /* NOTREACHED */ 124 default: 125 exit(0); 126 /* NOTREACHED */ 127 } 128 129 if (makedb(temp)) { 130 fprintf(stderr, "passwd: mkpasswd failed"); 131 bad: fprintf(stderr, "; password unchanged.\n"); 132 (void)unlink(temp); 133 exit(1); 134 } 135 136 /* 137 * possible race; have to rename four files, and someone could slip 138 * in between them. LOCK_EX and rename the ``passwd.dir'' file first 139 * so that getpwent(3) can't slip in; the lock should never fail and 140 * it's unclear what to do if it does. Rename ``ptmp'' last so that 141 * passwd/vipw/chpass can't slip in. 142 */ 143 (void)setpriority(PRIO_PROCESS, 0, -20); 144 fend = strcpy(from, temp) + strlen(temp); 145 tend = strcpy(to, passwd) + strlen(passwd); 146 bcopy(".dir", fend, 5); 147 bcopy(".dir", tend, 5); 148 if ((fd = open(from, O_RDONLY, 0)) >= 0) 149 (void)flock(fd, LOCK_EX); 150 /* here we go... */ 151 (void)rename(from, to); 152 bcopy(".pag", fend, 5); 153 bcopy(".pag", tend, 5); 154 (void)rename(from, to); 155 bcopy(".orig", fend, 6); 156 (void)rename(from, _PATH_PASSWD); 157 (void)rename(temp, passwd); 158 /* done! */ 159 exit(0); 160 } 161 162 copy(name, np, fp, pw) 163 char *name, *np; 164 FILE *fp; 165 struct passwd *pw; 166 { 167 register int done; 168 register char *p; 169 char buf[1024]; 170 171 for (done = 0; fgets(buf, sizeof(buf), stdin);) { 172 /* skip lines that are too big */ 173 if (!index(buf, '\n')) { 174 fprintf(stderr, "passwd: line too long.\n"); 175 return(0); 176 } 177 if (done) { 178 fprintf(fp, "%s", buf); 179 continue; 180 } 181 if (!(p = index(buf, ':'))) { 182 fprintf(stderr, "passwd: corrupted entry.\n"); 183 return(0); 184 } 185 *p = '\0'; 186 if (strcmp(buf, name)) { 187 *p = ':'; 188 fprintf(fp, "%s", buf); 189 continue; 190 } 191 if (!(p = index(++p, ':'))) { 192 fprintf(stderr, "passwd: corrupted entry.\n"); 193 return(0); 194 } 195 /* 196 * reset change time to zero; when classes are implemented, 197 * go and get the "offset" value for this class and reset 198 * the timer. 199 */ 200 fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 201 pw->pw_name, np, pw->pw_uid, pw->pw_gid, 202 pw->pw_class, 0L, pw->pw_expire, pw->pw_gecos, 203 pw->pw_dir, pw->pw_shell); 204 done = 1; 205 } 206 return(1); 207 } 208 209 char * 210 getnewpasswd(pw, temp) 211 register struct passwd *pw; 212 char *temp; 213 { 214 register char *p, *t; 215 char buf[10], salt[2], *crypt(), *getpass(); 216 time_t time(); 217 218 if (uid && pw->pw_passwd && 219 strcmp(crypt(getpass("Old password:"), pw->pw_passwd), 220 pw->pw_passwd)) { 221 (void)printf("passwd: %s.\n", strerror(EACCES)); 222 (void)unlink(temp); 223 exit(1); 224 } 225 226 for (buf[0] = '\0';;) { 227 p = getpass("New password:"); 228 if (!*p) { 229 (void)printf("Password unchanged.\n"); 230 (void)unlink(temp); 231 exit(0); 232 } 233 if (strlen(p) <= 5) { 234 printf("Please enter a longer password.\n"); 235 continue; 236 } 237 for (t = p; *t && islower(*t); ++t); 238 if (!*t) { 239 printf("Please don't use an all-lower case password.\nUnusual capitalization, control characters or digits are suggested.\n"); 240 continue; 241 } 242 (void)strcpy(buf, p); 243 if (!strcmp(buf, getpass("Retype new password:"))) 244 break; 245 printf("Mismatch; try again, EOF to quit.\n"); 246 } 247 /* grab a random printable character that isn't a colon */ 248 (void)srandom((int)time((time_t *)NULL)); 249 while ((salt[0] = random() % 93 + 33) == ':'); 250 while ((salt[1] = random() % 93 + 33) == ':'); 251 return(crypt(buf, salt)); 252 } 253 254 makedb(file) 255 char *file; 256 { 257 int status, pid, w; 258 259 if (!(pid = vfork())) { 260 execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL); 261 _exit(127); 262 } 263 while ((w = wait(&status)) != pid && w != -1); 264 return(w == -1 || status); 265 } 266