xref: /original-bsd/usr.bin/passwd/passwd.c (revision e9b82df0)
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