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.38 (Berkeley) 09/18/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 <sys/wait.h> 34 #include <errno.h> 35 #include <pwd.h> 36 #include <stdio.h> 37 #include <ctype.h> 38 #include <strings.h> 39 40 uid_t uid; 41 42 main(argc, argv) 43 int argc; 44 char **argv; 45 { 46 extern int errno; 47 struct passwd *pw; 48 struct rlimit rlim; 49 FILE *temp_fp; 50 int fd; 51 char *fend, *np, *passwd, *temp, *tend, *uname; 52 char from[MAXPATHLEN], to[MAXPATHLEN]; 53 char *getnewpasswd(), *getlogin(); 54 55 uid = getuid(); 56 uname = getlogin(); 57 switch(--argc) { 58 case 0: 59 break; 60 case 1: 61 uname = argv[1]; 62 break; 63 default: 64 fprintf(stderr, "usage: passwd [user]\n"); 65 exit(1); 66 } 67 if (!(pw = getpwnam(uname))) { 68 fprintf(stderr, "passwd: unknown user %s.\n", uname); 69 exit(1); 70 } 71 if (uid && uid != pw->pw_uid) { 72 fprintf(stderr, "passwd: %s\n", strerror(EACCES)); 73 exit(1); 74 } 75 76 (void)signal(SIGHUP, SIG_IGN); 77 (void)signal(SIGINT, SIG_IGN); 78 (void)signal(SIGQUIT, SIG_IGN); 79 (void)signal(SIGTSTP, SIG_IGN); 80 81 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; 82 (void)setrlimit(RLIMIT_CPU, &rlim); 83 (void)setrlimit(RLIMIT_FSIZE, &rlim); 84 85 (void)umask(0); 86 87 temp = _PATH_PTMP; 88 if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) { 89 if (errno == EEXIST) { 90 fprintf(stderr, 91 "passwd: password file busy -- try again later.\n"); 92 exit(0); 93 } 94 fprintf(stderr, "passwd: %s: %s", temp, strerror(errno)); 95 goto bad; 96 } 97 if (!(temp_fp = fdopen(fd, "w"))) { 98 fprintf(stderr, "passwd: can't write %s", temp); 99 goto bad; 100 } 101 passwd = _PATH_MASTERPASSWD; 102 if (!freopen(passwd, "r", stdin)) { 103 fprintf(stderr, "passwd: can't read %s", passwd); 104 goto bad; 105 } 106 107 printf("Changing password for %s.\n", pw->pw_name); 108 np = getnewpasswd(pw, temp); 109 110 if (!copy(pw->pw_name, np, temp_fp, pw)) 111 goto bad; 112 113 (void)fclose(temp_fp); 114 (void)fclose(stdin); 115 116 switch(fork()) { 117 case 0: 118 break; 119 case -1: 120 fprintf(stderr, "passwd: can't fork"); 121 goto bad; 122 /* NOTREACHED */ 123 default: 124 exit(0); 125 /* NOTREACHED */ 126 } 127 128 if (makedb(temp)) { 129 fprintf(stderr, "passwd: mkpasswd failed"); 130 bad: fprintf(stderr, "; password unchanged.\n"); 131 (void)unlink(temp); 132 exit(1); 133 } 134 135 /* 136 * possible race; have to rename four files, and someone could slip 137 * in between them. LOCK_EX and rename the ``passwd.dir'' file first 138 * so that getpwent(3) can't slip in; the lock should never fail and 139 * it's unclear what to do if it does. Rename ``ptmp'' last so that 140 * passwd/vipw/chpass can't slip in. 141 */ 142 (void)setpriority(PRIO_PROCESS, 0, -20); 143 fend = strcpy(from, temp) + strlen(temp); 144 tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD); 145 bcopy(".dir", fend, 5); 146 bcopy(".dir", tend, 5); 147 if ((fd = open(from, O_RDONLY, 0)) >= 0) 148 (void)flock(fd, LOCK_EX); 149 /* here we go... */ 150 (void)rename(from, to); 151 bcopy(".pag", fend, 5); 152 bcopy(".pag", tend, 5); 153 (void)rename(from, to); 154 bcopy(".orig", fend, 6); 155 (void)rename(from, _PATH_PASSWD); 156 (void)rename(temp, passwd); 157 /* done! */ 158 exit(0); 159 } 160 161 copy(name, np, fp, pw) 162 char *name, *np; 163 FILE *fp; 164 struct passwd *pw; 165 { 166 register int done; 167 register char *p; 168 char buf[1024]; 169 170 for (done = 0; fgets(buf, sizeof(buf), stdin);) { 171 /* skip lines that are too big */ 172 if (!index(buf, '\n')) { 173 fprintf(stderr, "passwd: line too long.\n"); 174 return(0); 175 } 176 if (done) { 177 fprintf(fp, "%s", buf); 178 continue; 179 } 180 if (!(p = index(buf, ':'))) { 181 fprintf(stderr, "passwd: corrupted entry.\n"); 182 return(0); 183 } 184 *p = '\0'; 185 if (strcmp(buf, name)) { 186 *p = ':'; 187 fprintf(fp, "%s", buf); 188 continue; 189 } 190 if (!(p = index(++p, ':'))) { 191 fprintf(stderr, "passwd: corrupted entry.\n"); 192 return(0); 193 } 194 /* 195 * reset change time to zero; when classes are implemented, 196 * go and get the "offset" value for this class and reset 197 * the timer. 198 */ 199 fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 200 pw->pw_name, np, pw->pw_uid, pw->pw_gid, 201 pw->pw_class, 0L, pw->pw_expire, pw->pw_gecos, 202 pw->pw_dir, pw->pw_shell); 203 done = 1; 204 } 205 return(1); 206 } 207 208 char * 209 getnewpasswd(pw, temp) 210 register struct passwd *pw; 211 char *temp; 212 { 213 register char *p, *t; 214 char buf[_PASSWORD_LEN+1], salt[2], *crypt(), *getpass(); 215 int tries = 0; 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 && (uid != 0 || tries++ < 2)) { 234 printf("Please enter a longer password.\n"); 235 continue; 236 } 237 for (t = p; *t && islower(*t); ++t); 238 if (!*t && (uid != 0 || tries++ < 2)) { 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 #ifdef NEWSALT 250 salt[0] = '_'; 251 to64(&salt[1], (long)(29*25), 4); 252 to64(&salt[5], (long)random(), 4); 253 #else 254 to64(&salt[0], (long)random(), 2); 255 #endif 256 return(crypt(buf, salt)); 257 } 258 259 static unsigned char itoa64[] = /* 0..63 => ascii-64 */ 260 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 261 262 to64(s, v, n) 263 register char *s; 264 register long v; 265 register int n; 266 { 267 while (--n >= 0) { 268 *s++ = itoa64[v&0x3f]; 269 v >>= 6; 270 } 271 } 272 273 makedb(file) 274 char *file; 275 { 276 union wait pstat; 277 pid_t pid, waitpid(); 278 279 if (!(pid = vfork())) { 280 execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL); 281 _exit(127); 282 } 283 return(waitpid(pid, &pstat, 0) == -1 ? -1 : pstat.w_status); 284 } 285