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