yppasswdd_server.c revision 16134
1253789Srpaulo/*
2253789Srpaulo * Copyright (c) 1995, 1996
3253789Srpaulo *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4253789Srpaulo *
5253789Srpaulo * Redistribution and use in source and binary forms, with or without
6253789Srpaulo * modification, are permitted provided that the following conditions
7253789Srpaulo * are met:
8253789Srpaulo * 1. Redistributions of source code must retain the above copyright
9253789Srpaulo *    notice, this list of conditions and the following disclaimer.
10253789Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11253789Srpaulo *    notice, this list of conditions and the following disclaimer in the
12253789Srpaulo *    documentation and/or other materials provided with the distribution.
13253789Srpaulo * 3. All advertising materials mentioning features or use of this software
14253789Srpaulo *    must display the following acknowledgement:
15253789Srpaulo *	This product includes software developed by Bill Paul.
16253789Srpaulo * 4. Neither the name of the author nor the names of any co-contributors
17253789Srpaulo *    may be used to endorse or promote products derived from this software
18253789Srpaulo *    without specific prior written permission.
19253789Srpaulo *
20253789Srpaulo * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21253789Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22253789Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23253789Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24253789Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25253789Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26253789Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27253789Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28253789Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29253789Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30253789Srpaulo * SUCH DAMAGE.
31253789Srpaulo *
32253789Srpaulo *	$Id: yppasswdd_server.c,v 1.16 1996/06/04 00:00:19 wpaul Exp $
33253789Srpaulo */
34253789Srpaulo
35253789Srpaulo#include <stdio.h>
36253789Srpaulo#include <string.h>
37253789Srpaulo#include <ctype.h>
38253789Srpaulo#include <stdlib.h>
39253789Srpaulo#include <unistd.h>
40253789Srpaulo#include <dirent.h>
41253789Srpaulo#include <sys/stat.h>
42253789Srpaulo#include <sys/socket.h>
43253789Srpaulo#include <netinet/in.h>
44253789Srpaulo#include <arpa/inet.h>
45253789Srpaulo#include <limits.h>
46253789Srpaulo#include <db.h>
47253789Srpaulo#include <pwd.h>
48253789Srpaulo#include <errno.h>
49253789Srpaulo#include <signal.h>
50253789Srpaulo#include <rpc/rpc.h>
51253789Srpaulo#include <rpcsvc/yp.h>
52253789Srpaulo#include <sys/types.h>
53253789Srpaulo#include <sys/wait.h>
54253789Srpaulo#include <sys/param.h>
55253789Srpaulo#include <sys/fcntl.h>
56253789Srpaulostruct dom_binding {};
57253789Srpaulo#include <rpcsvc/ypclnt.h>
58253789Srpaulo#include "yppasswdd_extern.h"
59253789Srpaulo#include "yppasswd.h"
60253789Srpaulo#include "yppasswd_private.h"
61253789Srpaulo#include "yppasswd_comm.h"
62253789Srpaulo
63253789Srpaulo#ifndef lint
64253789Srpaulostatic const char rcsid[] = "$Id: yppasswdd_server.c,v 1.16 1996/06/04 00:00:19 wpaul Exp $";
65253789Srpaulo#endif /* not lint */
66253789Srpaulo
67253789Srpaulochar *tempname;
68253789Srpaulo
69253789Srpaulovoid reaper(sig)
70253789Srpaulo	int sig;
71253789Srpaulo{
72253789Srpaulo	extern pid_t pid;
73253789Srpaulo	extern int pstat;
74253789Srpaulo	int st;
75253789Srpaulo
76253789Srpaulo	if (sig > 0) {
77253789Srpaulo		if (sig == SIGCHLD)
78253789Srpaulo			while(wait3(&st, WNOHANG, NULL) > 0) ;
79253789Srpaulo	} else {
80253789Srpaulo		pid = waitpid(pid, &pstat, 0);
81253789Srpaulo	}
82253789Srpaulo	return;
83253789Srpaulo}
84253789Srpaulo
85253789Srpaulovoid install_reaper(on)
86253789Srpaulo	int on;
87253789Srpaulo{
88253789Srpaulo	if (on) {
89253789Srpaulo		signal(SIGCHLD, reaper);
90253789Srpaulo	} else {
91253789Srpaulo		signal(SIGCHLD, SIG_DFL);
92253789Srpaulo	}
93253789Srpaulo	return;
94253789Srpaulo}
95253789Srpaulo
96253789Srpaulostatic struct passwd yp_password;
97253789Srpaulo
98253789Srpaulostatic void copy_yp_pass(p, x, m)
99253789Srpaulochar *p;
100253789Srpauloint x, m;
101253789Srpaulo{
102253789Srpaulo	register char *t, *s = p;
103253789Srpaulo	static char *buf;
104253789Srpaulo
105253789Srpaulo	yp_password.pw_fields = 0;
106253789Srpaulo
107253789Srpaulo	buf = (char *)realloc(buf, m + 10);
108253789Srpaulo	bzero(buf, m + 10);
109253789Srpaulo
110253789Srpaulo	/* Turn all colons into NULLs */
111253789Srpaulo	while (strchr(s, ':')) {
112253789Srpaulo		s = (strchr(s, ':') + 1);
113253789Srpaulo		*(s - 1)= '\0';
114253789Srpaulo	}
115253789Srpaulo
116253789Srpaulo	t = buf;
117253789Srpaulo#define EXPAND(e)       e = t; while ((*t++ = *p++));
118253789Srpaulo        EXPAND(yp_password.pw_name);
119253789Srpaulo	yp_password.pw_fields |= _PWF_NAME;
120253789Srpaulo        EXPAND(yp_password.pw_passwd);
121253789Srpaulo	yp_password.pw_fields |= _PWF_PASSWD;
122253789Srpaulo	yp_password.pw_uid = atoi(p);
123253789Srpaulo        p += (strlen(p) + 1);
124253789Srpaulo	yp_password.pw_fields |= _PWF_UID;
125253789Srpaulo	yp_password.pw_gid = atoi(p);
126253789Srpaulo        p += (strlen(p) + 1);
127253789Srpaulo	yp_password.pw_fields |= _PWF_GID;
128253789Srpaulo	if (x) {
129253789Srpaulo		EXPAND(yp_password.pw_class);
130253789Srpaulo		yp_password.pw_fields |= _PWF_CLASS;
131253789Srpaulo		yp_password.pw_change = atol(p);
132253789Srpaulo		p += (strlen(p) + 1);
133253789Srpaulo		yp_password.pw_fields |= _PWF_CHANGE;
134253789Srpaulo		yp_password.pw_expire = atol(p);
135253789Srpaulo		p += (strlen(p) + 1);
136253789Srpaulo		yp_password.pw_fields |= _PWF_EXPIRE;
137253789Srpaulo	}
138253789Srpaulo        EXPAND(yp_password.pw_gecos);
139253789Srpaulo	yp_password.pw_fields |= _PWF_GECOS;
140253789Srpaulo        EXPAND(yp_password.pw_dir);
141253789Srpaulo	yp_password.pw_fields |= _PWF_DIR;
142253789Srpaulo        EXPAND(yp_password.pw_shell);
143253789Srpaulo	yp_password.pw_fields |= _PWF_SHELL;
144253789Srpaulo
145253789Srpaulo	return;
146253789Srpaulo}
147253789Srpaulo
148253789Srpaulostatic int validchars(arg)
149253789Srpaulo	char *arg;
150253789Srpaulo{
151253789Srpaulo	int i;
152253789Srpaulo
153253789Srpaulo	for (i = 0; i < strlen(arg); i++) {
154253789Srpaulo		if (iscntrl(arg[i])) {
155253789Srpaulo			yp_error("string contains a control character");
156253789Srpaulo			return(1);
157253789Srpaulo		}
158253789Srpaulo		if (arg[i] == ':') {
159253789Srpaulo			yp_error("string contains a colon");
160253789Srpaulo			return(1);
161253789Srpaulo		}
162253789Srpaulo		/* Be evil: truncate strings with \n in them silently. */
163253789Srpaulo		if (arg[i] == '\n') {
164253789Srpaulo			arg[i] = '\0';
165253789Srpaulo			return(0);
166253789Srpaulo		}
167253789Srpaulo	}
168253789Srpaulo	return(0);
169253789Srpaulo}
170253789Srpaulo
171253789Srpaulostatic int validate_master(opw, npw)
172253789Srpaulo	struct passwd *opw;
173253789Srpaulo	struct x_master_passwd *npw;
174253789Srpaulo{
175253789Srpaulo
176253789Srpaulo	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
177253789Srpaulo		yp_error("client tried to modify an NIS entry");
178253789Srpaulo		return(1);
179253789Srpaulo	}
180253789Srpaulo
181253789Srpaulo	if (validchars(npw->pw_shell)) {
182253789Srpaulo		yp_error("specified shell contains invalid characters");
183253789Srpaulo		return(1);
184253789Srpaulo	}
185253789Srpaulo
186253789Srpaulo	if (validchars(npw->pw_gecos)) {
187253789Srpaulo		yp_error("specified gecos field contains invalid characters");
188253789Srpaulo		return(1);
189253789Srpaulo	}
190253789Srpaulo
191253789Srpaulo	if (validchars(npw->pw_passwd)) {
192253789Srpaulo		yp_error("specified password contains invalid characters");
193253789Srpaulo		return(1);
194253789Srpaulo	}
195253789Srpaulo	return(0);
196253789Srpaulo}
197253789Srpaulo
198253789Srpaulostatic int validate(opw, npw)
199253789Srpaulo	struct passwd *opw;
200253789Srpaulo	struct x_passwd *npw;
201253789Srpaulo{
202253789Srpaulo
203253789Srpaulo	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
204253789Srpaulo		yp_error("client tried to modify an NIS entry");
205253789Srpaulo		return(1);
206253789Srpaulo	}
207253789Srpaulo
208253789Srpaulo	if (npw->pw_uid != opw->pw_uid) {
209253789Srpaulo		yp_error("UID mismatch: client says user %s has UID %d",
210253789Srpaulo			 npw->pw_name, npw->pw_uid);
211253789Srpaulo		yp_error("database says user %s has UID %d", opw->pw_name,
212253789Srpaulo			 opw->pw_uid);
213253789Srpaulo		return(1);
214253789Srpaulo	}
215253789Srpaulo
216253789Srpaulo	if (npw->pw_gid != opw->pw_gid) {
217253789Srpaulo		yp_error("GID mismatch: client says user %s has GID %d",
218253789Srpaulo			 npw->pw_name, npw->pw_gid);
219253789Srpaulo		yp_error("database says user %s has GID %d", opw->pw_name,
220253789Srpaulo			 opw->pw_gid);
221253789Srpaulo		return(1);
222253789Srpaulo	}
223253789Srpaulo
224253789Srpaulo	/*
225253789Srpaulo	 * Don't allow the user to shoot himself in the foot,
226253789Srpaulo	 * even on purpose.
227253789Srpaulo	 */
228253789Srpaulo	if (!ok_shell(npw->pw_shell)) {
229253789Srpaulo		yp_error("%s is not a valid shell", npw->pw_shell);
230253789Srpaulo		return(1);
231253789Srpaulo	}
232253789Srpaulo
233253789Srpaulo	if (validchars(npw->pw_shell)) {
234253789Srpaulo		yp_error("specified shell contains invalid characters");
235253789Srpaulo		return(1);
236253789Srpaulo	}
237253789Srpaulo
238253789Srpaulo	if (validchars(npw->pw_gecos)) {
239253789Srpaulo		yp_error("specified gecos field contains invalid characters");
240253789Srpaulo		return(1);
241253789Srpaulo	}
242253789Srpaulo
243253789Srpaulo	if (validchars(npw->pw_passwd)) {
244253789Srpaulo		yp_error("specified password contains invalid characters");
245253789Srpaulo		return(1);
246253789Srpaulo	}
247253789Srpaulo	return(0);
248253789Srpaulo}
249253789Srpaulo
250253789Srpaulo/*
251253789Srpaulo * Kludge alert:
252253789Srpaulo * In order to have one rpc.yppasswdd support multiple domains,
253253789Srpaulo * we have to cheat: we search each directory under /var/yp
254253789Srpaulo * and try to match the user in each master.passwd.byname
255253789Srpaulo * map that we find. If the user matches (username, uid and gid
256253789Srpaulo * all agree), then we use that domain. If we match the user in
257253789Srpaulo * more than one database, we must abort.
258253789Srpaulo */
259253789Srpaulostatic char *find_domain(pw)
260253789Srpaulo	struct x_passwd *pw;
261253789Srpaulo{
262253789Srpaulo	struct stat statbuf;
263253789Srpaulo	struct dirent *dirp;
264253789Srpaulo	DIR *dird;
265253789Srpaulo	char yp_mapdir[MAXPATHLEN + 2];
266253789Srpaulo	static char domain[YPMAXDOMAIN];
267253789Srpaulo	char *tmp = NULL;
268253789Srpaulo	DBT key, data;
269253789Srpaulo	int hit = 0;
270253789Srpaulo
271253789Srpaulo	yp_error("performing multidomain lookup");
272253789Srpaulo
273253789Srpaulo	if ((dird = opendir(yp_dir)) == NULL) {
274253789Srpaulo		yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
275253789Srpaulo		return(NULL);
276253789Srpaulo	}
277253789Srpaulo
278253789Srpaulo	while ((dirp = readdir(dird)) != NULL) {
279253789Srpaulo		snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
280253789Srpaulo							yp_dir, dirp->d_name);
281253789Srpaulo		if (stat(yp_mapdir, &statbuf) < 0) {
282253789Srpaulo			yp_error("stat(%s) failed: %s", yp_mapdir,
283253789Srpaulo							strerror(errno));
284253789Srpaulo			closedir(dird);
285253789Srpaulo			return(NULL);
286253789Srpaulo		}
287253789Srpaulo		if (S_ISDIR(statbuf.st_mode)) {
288253789Srpaulo			tmp = (char *)dirp->d_name;
289253789Srpaulo			key.data = pw->pw_name;
290253789Srpaulo			key.size = strlen(pw->pw_name);
291253789Srpaulo
292253789Srpaulo			if (yp_get_record(tmp,"master.passwd.byname",
293253789Srpaulo			  		&key, &data, 0) != YP_TRUE) {
294253789Srpaulo				continue;
295253789Srpaulo			}
296253789Srpaulo			*(char *)(data.data + data.size) = '\0';
297253789Srpaulo			copy_yp_pass(data.data, 1, data.size);
298253789Srpaulo			if (yp_password.pw_uid == pw->pw_uid &&
299253789Srpaulo			    yp_password.pw_gid == pw->pw_gid) {
300253789Srpaulo				hit++;
301253789Srpaulo				snprintf(domain, YPMAXDOMAIN, "%s", tmp);
302253789Srpaulo			}
303253789Srpaulo		}
304253789Srpaulo	}
305253789Srpaulo
306253789Srpaulo	closedir(dird);
307253789Srpaulo	if (hit > 1) {
308253789Srpaulo		yp_error("found same user in two different domains");
309253789Srpaulo		return(NULL);
310253789Srpaulo	} else
311253789Srpaulo		return((char *)&domain);
312253789Srpaulo}
313253789Srpaulo
314253789Srpaulostatic int update_inplace(pw, domain)
315253789Srpaulo	struct passwd *pw;
316253789Srpaulo	char *domain;
317253789Srpaulo{
318253789Srpaulo	DB *dbp = NULL;
319253789Srpaulo	DBT key = { NULL, 0 };
320253789Srpaulo	DBT data = { NULL, 0 };
321253789Srpaulo	char pwbuf[YPMAXRECORD];
322253789Srpaulo	char keybuf[20];
323253789Srpaulo	int rval, i;
324253789Srpaulo	char *maps[] = { "master.passwd.byname", "master.passwd.byuid",
325253789Srpaulo			 "passwd.byname", "passwd.byuid" };
326253789Srpaulo
327253789Srpaulo	char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
328253789Srpaulo			    "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
329253789Srpaulo			    "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" };
330253789Srpaulo	char *ptr = NULL;
331253789Srpaulo	char *yp_last = "YP_LAST_MODIFIED";
332253789Srpaulo	char yplastbuf[YPMAXRECORD];
333253789Srpaulo
334253789Srpaulo	snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
335253789Srpaulo
336253789Srpaulo	for (i = 0; i < 4; i++) {
337253789Srpaulo
338253789Srpaulo		if (i % 2) {
339253789Srpaulo			snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
340253789Srpaulo			key.data = (char *)&keybuf;
341253789Srpaulo			key.size = strlen(keybuf);
342253789Srpaulo		} else {
343253789Srpaulo			key.data = pw->pw_name;
344253789Srpaulo			key.size = strlen(pw->pw_name);
345253789Srpaulo		}
346253789Srpaulo
347253789Srpaulo		/*
348253789Srpaulo		 * XXX The passwd.byname and passwd.byuid maps come in
349253789Srpaulo		 * two flavors: secure and insecure. The secure version
350253789Srpaulo		 * has a '*' in the password field whereas the insecure one
351253789Srpaulo		 * has a real crypted password. The maps will be insecure
352253789Srpaulo		 * if they were built with 'unsecure = TRUE' enabled in
353253789Srpaulo		 * /var/yp/Makefile, but we'd have no way of knowing if
354253789Srpaulo		 * this has been done unless we were to try parsing the
355253789Srpaulo		 * Makefile, which is a disgusting thought. Instead, we
356253789Srpaulo		 * read the records from the maps, skip to the first ':'
357253789Srpaulo		 * in them, and then look at the character immediately
358253789Srpaulo		 * following it. If it's an '*' then the map is 'secure'
359253789Srpaulo		 * and we must not insert a real password into the pw_passwd
360253789Srpaulo		 * field. If it's not an '*', then we put the real crypted
361253789Srpaulo		 * password in.
362253789Srpaulo		 */
363253789Srpaulo		if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
364253789Srpaulo			yp_error("couldn't read %s/%s: %s", domain,
365253789Srpaulo						maps[i], strerror(errno));
366253789Srpaulo			return(1);
367253789Srpaulo		}
368253789Srpaulo
369253789Srpaulo		if ((ptr = strchr(data.data, ':')) == NULL) {
370253789Srpaulo			yp_error("no colon in passwd record?!");
371253789Srpaulo			return(1);
372253789Srpaulo		}
373253789Srpaulo
374253789Srpaulo		if (i < 2) {
375253789Srpaulo			snprintf(pwbuf, sizeof(pwbuf), formats[i],
376253789Srpaulo			   pw->pw_name, pw->pw_passwd, pw->pw_uid,
377253789Srpaulo			   pw->pw_gid, pw->pw_class, pw->pw_change,
378253789Srpaulo			   pw->pw_expire, pw->pw_gecos, pw->pw_dir,
379253789Srpaulo			   pw->pw_shell);
380253789Srpaulo		} else {
381253789Srpaulo			snprintf(pwbuf, sizeof(pwbuf), formats[i],
382253789Srpaulo			   pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
383253789Srpaulo			   pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
384253789Srpaulo			   pw->pw_shell);
385253789Srpaulo		}
386253789Srpaulo
387253789Srpaulo#define FLAGS O_RDWR|O_CREAT
388253789Srpaulo
389253789Srpaulo		if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
390253789Srpaulo			yp_error("couldn't open %s/%s r/w: %s",domain,
391253789Srpaulo						maps[i],strerror(errno));
392253789Srpaulo			return(1);
393253789Srpaulo		}
394253789Srpaulo
395253789Srpaulo		data.data = pwbuf;
396253789Srpaulo		data.size = strlen(pwbuf);
397253789Srpaulo
398253789Srpaulo		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
399253789Srpaulo			yp_error("failed to update record in %s/%s", domain,
400253789Srpaulo								maps[i]);
401253789Srpaulo			(void)(dbp->close)(dbp);
402253789Srpaulo			return(1);
403253789Srpaulo		}
404253789Srpaulo
405253789Srpaulo		key.data = yp_last;
406253789Srpaulo		key.size = strlen(yp_last);
407253789Srpaulo		data.data = (char *)&yplastbuf;
408253789Srpaulo		data.size = strlen(yplastbuf);
409253789Srpaulo
410253789Srpaulo		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
411253789Srpaulo			yp_error("failed to update timestamp in %s/%s", domain,
412253789Srpaulo								maps[i]);
413253789Srpaulo			(void)(dbp->close)(dbp);
414253789Srpaulo			return(1);
415253789Srpaulo		}
416253789Srpaulo
417253789Srpaulo		(void)(dbp->close)(dbp);
418253789Srpaulo	}
419253789Srpaulo
420253789Srpaulo	return(0);
421253789Srpaulo}
422253789Srpaulo
423253789Srpaulo
424253789Srpauloint *
425253789Srpauloyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
426253789Srpaulo{
427253789Srpaulo	static int  result;
428253789Srpaulo	struct sockaddr_in *rqhost;
429253789Srpaulo	DBT key, data;
430253789Srpaulo	int rval = 0;
431253789Srpaulo	int pfd, tfd;
432253789Srpaulo	int pid;
433253789Srpaulo	int passwd_changed = 0;
434253789Srpaulo	int shell_changed = 0;
435253789Srpaulo	int gecos_changed = 0;
436253789Srpaulo	char *oldshell = NULL;
437253789Srpaulo	char *oldgecos = NULL;
438253789Srpaulo	char *passfile_hold;
439253789Srpaulo	char passfile_buf[MAXPATHLEN + 2];
440253789Srpaulo	char template[] = "/etc/yppwtmp.XXXXX";
441253789Srpaulo	char *domain = yppasswd_domain;
442253789Srpaulo
443253789Srpaulo	/*
444253789Srpaulo	 * Normal user updates always use the 'default' master.passwd file.
445253789Srpaulo	 */
446253789Srpaulo
447253789Srpaulo	passfile = passfile_default;
448253789Srpaulo	result = 1;
449253789Srpaulo
450253789Srpaulo	rqhost = svc_getcaller(rqstp->rq_xprt);
451253789Srpaulo
452253789Srpaulo	if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
453253789Srpaulo		yp_error("rejected update request from unauthorized host");
454253789Srpaulo		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
455253789Srpaulo		return(&result);
456253789Srpaulo	}
457253789Srpaulo
458253789Srpaulo	/*
459253789Srpaulo	 * Step one: find the user. (It's kinda pointless to
460253789Srpaulo	 * proceed if the user doesn't exist.) We look for the
461253789Srpaulo	 * user in the master.passwd.byname database, _NOT_ by
462253789Srpaulo	 * using getpwent() and friends! We can't use getpwent()
463253789Srpaulo	 * since the NIS master server is not guaranteed to be
464253789Srpaulo	 * configured as an NIS client.
465253789Srpaulo	 */
466253789Srpaulo
467253789Srpaulo	if (multidomain) {
468253789Srpaulo		if ((domain = find_domain(&argp->newpw)) == NULL) {
469253789Srpaulo			yp_error("multidomain lookup failed - aborting update");
470253789Srpaulo			return(&result);
471253789Srpaulo		} else
472253789Srpaulo			yp_error("updating user %s in domain %s",
473253789Srpaulo					argp->newpw.pw_name, domain);
474253789Srpaulo	}
475253789Srpaulo
476253789Srpaulo	key.data = argp->newpw.pw_name;
477253789Srpaulo	key.size = strlen(argp->newpw.pw_name);
478253789Srpaulo
479253789Srpaulo	if ((rval=yp_get_record(domain,"master.passwd.byname",
480253789Srpaulo		  	&key, &data, 0)) != YP_TRUE) {
481253789Srpaulo		if (rval == YP_NOKEY) {
482253789Srpaulo			yp_error("user %s not found in passwd database",
483253789Srpaulo			 	argp->newpw.pw_name);
484253789Srpaulo		} else {
485253789Srpaulo			yp_error("database access error: %s",
486253789Srpaulo			 	yperr_string(rval));
487253789Srpaulo		}
488253789Srpaulo		return(&result);
489253789Srpaulo	}
490253789Srpaulo
491253789Srpaulo	/* Nul terminate, please. */
492253789Srpaulo	*(char *)(data.data + data.size) = '\0';
493253789Srpaulo
494253789Srpaulo	copy_yp_pass(data.data, 1, data.size);
495253789Srpaulo
496253789Srpaulo	/* Step 2: check that the supplied oldpass is valid. */
497253789Srpaulo
498253789Srpaulo	if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
499253789Srpaulo					yp_password.pw_passwd)) {
500253789Srpaulo		yp_error("rejected change attempt -- bad password");
501253789Srpaulo		yp_error("client address: %s username: %s",
502253789Srpaulo			  inet_ntoa(rqhost->sin_addr),
503253789Srpaulo			  argp->newpw.pw_name);
504253789Srpaulo		return(&result);
505253789Srpaulo	}
506253789Srpaulo
507253789Srpaulo	/* Step 3: validate the arguments passed to us by the client. */
508253789Srpaulo
509253789Srpaulo	if (validate(&yp_password, &argp->newpw)) {
510253789Srpaulo		yp_error("rejecting change attempt: bad arguments");
511253789Srpaulo		yp_error("client address: %s username: %s",
512253789Srpaulo			 inet_ntoa(rqhost->sin_addr),
513253789Srpaulo			 argp->newpw.pw_name);
514253789Srpaulo		svcerr_decode(rqstp->rq_xprt);
515253789Srpaulo		return(&result);
516253789Srpaulo	}
517253789Srpaulo
518266577Shselasky	/* Step 4: update the user's passwd structure. */
519253789Srpaulo
520253789Srpaulo	if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
521253789Srpaulo		oldshell = yp_password.pw_shell;
522253789Srpaulo		yp_password.pw_shell = argp->newpw.pw_shell;
523253789Srpaulo		shell_changed++;
524253789Srpaulo	}
525253789Srpaulo
526253789Srpaulo
527253789Srpaulo	if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
528253789Srpaulo		oldgecos = yp_password.pw_gecos;
529253789Srpaulo		yp_password.pw_gecos = argp->newpw.pw_gecos;
530266577Shselasky		gecos_changed++;
531253789Srpaulo	}
532253789Srpaulo
533253789Srpaulo	if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
534253789Srpaulo		yp_password.pw_passwd = argp->newpw.pw_passwd;
535253789Srpaulo		passwd_changed++;
536253789Srpaulo	}
537253789Srpaulo
538253789Srpaulo	/*
539253789Srpaulo	 * If the caller specified a domain other than our 'default'
540253789Srpaulo	 * domain, change the path to master.passwd accordingly.
541253789Srpaulo	 */
542253789Srpaulo
543253789Srpaulo	if (strcmp(domain, yppasswd_domain)) {
544253789Srpaulo		snprintf(passfile_buf, sizeof(passfile_buf),
545253789Srpaulo			"/var/yp/%s/master.passwd", domain);
546253789Srpaulo		passfile = (char *)&passfile_buf;
547253789Srpaulo	}
548253789Srpaulo
549253789Srpaulo	/* Step 5: make a new password file with the updated info. */
550253789Srpaulo
551253789Srpaulo	if ((pfd = pw_lock()) < 0) {
552253789Srpaulo		return (&result);
553253789Srpaulo	}
554253789Srpaulo	if ((tfd = pw_tmp()) < 0) {
555253789Srpaulo		return (&result);
556253789Srpaulo	}
557253789Srpaulo
558253789Srpaulo	if (pw_copy(pfd, tfd, &yp_password)) {
559253789Srpaulo		yp_error("failed to created updated password file -- \
560253789Srpaulocleaning up and bailing out");
561253789Srpaulo		unlink(tempname);
562253789Srpaulo		return(&result);
563253789Srpaulo	}
564253789Srpaulo
565253789Srpaulo	passfile_hold = mktemp((char *)&template);
566253789Srpaulo	rename(passfile, passfile_hold);
567253789Srpaulo	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
568253789Srpaulo		rename(tempname, passfile);
569253789Srpaulo	} else {
570253789Srpaulo		if (pw_mkdb() < 0) {
571253789Srpaulo			yp_error("pwd_mkdb failed");
572253789Srpaulo			return(&result);
573253789Srpaulo		}
574253789Srpaulo	}
575253789Srpaulo
576253789Srpaulo	if (inplace) {
577253789Srpaulo		if ((rval = update_inplace(&yp_password, domain))) {
578253789Srpaulo			yp_error("inplace update failed -- rebuilding maps");
579253789Srpaulo		}
580253789Srpaulo	}
581253789Srpaulo
582253789Srpaulo	switch((pid = fork())) {
583253789Srpaulo	case 0:
584253789Srpaulo		/* unlink(passfile_hold); */
585253789Srpaulo		if (inplace && !rval) {
586253789Srpaulo    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
587253789Srpaulo				yppasswd_domain, "pushpw", NULL);
588253789Srpaulo		} else {
589253789Srpaulo    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
590253789Srpaulo				yppasswd_domain, NULL);
591253789Srpaulo		}
592253789Srpaulo    		yp_error("couldn't exec map update process: %s",
593253789Srpaulo					strerror(errno));
594266577Shselasky		unlink(passfile);
595253789Srpaulo		rename(passfile_hold, passfile);
596253789Srpaulo    		exit(1);
597253789Srpaulo		break;
598253789Srpaulo	case -1:
599253789Srpaulo		yp_error("fork() failed: %s", strerror(errno));
600253789Srpaulo		return(&result);
601253789Srpaulo		unlink(passfile);
602253789Srpaulo		rename(passfile_hold, passfile);
603253789Srpaulo		break;
604253789Srpaulo	default:
605253789Srpaulo		break;
606253789Srpaulo	}
607253789Srpaulo
608253789Srpaulo	if (verbose) {
609253789Srpaulo		yp_error("update completed for user %s (uid %d):",
610253789Srpaulo						argp->newpw.pw_name,
611253789Srpaulo						argp->newpw.pw_uid);
612253789Srpaulo
613253789Srpaulo		if (passwd_changed)
614253789Srpaulo			yp_error("password changed");
615253789Srpaulo
616253789Srpaulo		if (gecos_changed)
617253789Srpaulo			yp_error("gecos changed ('%s' -> '%s')",
618253789Srpaulo					oldgecos, argp->newpw.pw_gecos);
619253789Srpaulo
620253789Srpaulo		if (shell_changed)
621253789Srpaulo			yp_error("shell changed ('%s' -> '%s')",
622253789Srpaulo					oldshell, argp->newpw.pw_shell);
623253789Srpaulo	}
624253789Srpaulo
625253789Srpaulo	result = 0;
626253789Srpaulo	return (&result);
627253789Srpaulo
628253789Srpaulo}
629253789Srpaulo
630253789Srpaulo/*
631253789Srpaulo * Note that this function performs a little less sanity checking
632253789Srpaulo * than the last one. Since only the superuser is allowed to use it,
633253789Srpaulo * it is assumed that the caller knows what he's doing.
634253789Srpaulo */
635253789Srpaulostatic int update_master(master_yppasswd *argp)
636253789Srpaulo{
637253789Srpaulo	int result;
638253789Srpaulo	int pfd, tfd;
639253789Srpaulo	int pid;
640253789Srpaulo	int rval = 0;
641253789Srpaulo	DBT key, data;
642253789Srpaulo	char *passfile_hold;
643253789Srpaulo	char passfile_buf[MAXPATHLEN + 2];
644253789Srpaulo	char template[] = "/etc/yppwtmp.XXXXX";
645253789Srpaulo
646253789Srpaulo	result = 1;
647253789Srpaulo	passfile = passfile_default;
648253789Srpaulo
649253789Srpaulo	key.data = argp->newpw.pw_name;
650253789Srpaulo	key.size = strlen(argp->newpw.pw_name);
651253789Srpaulo
652253789Srpaulo	/*
653253789Srpaulo	 * The superuser may add entries to the passwd maps if
654253789Srpaulo	 * rpc.yppasswdd is started with the -a flag. Paranoia
655253789Srpaulo	 * prevents me from allowing additions by default.
656253789Srpaulo	 */
657253789Srpaulo	if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
658253789Srpaulo			  &key, &data, 0)) != YP_TRUE) {
659253789Srpaulo		if (rval == YP_NOKEY) {
660253789Srpaulo			yp_error("user %s not found in passwd database",
661253789Srpaulo				 argp->newpw.pw_name);
662253789Srpaulo			if (allow_additions)
663253789Srpaulo				yp_error("notice: adding user %s to \
664253789Srpaulomaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
665253789Srpaulo			else
666253789Srpaulo				yp_error("restart %s with the -a flag to \
667253789Srpauloallow additions to be made to the password database", progname);
668253789Srpaulo		} else {
669253789Srpaulo			yp_error("database access error: %s",
670253789Srpaulo				 yperr_string(rval));
671253789Srpaulo		}
672253789Srpaulo		if (!allow_additions)
673253789Srpaulo			return(result);
674253789Srpaulo	} else {
675253789Srpaulo
676253789Srpaulo		/* Nul terminate, please. */
677253789Srpaulo		*(char *)(data.data + data.size) = '\0';
678253789Srpaulo
679253789Srpaulo		copy_yp_pass(data.data, 1, data.size);
680253789Srpaulo	}
681253789Srpaulo
682253789Srpaulo	/*
683253789Srpaulo	 * Perform a small bit of sanity checking.
684253789Srpaulo	 */
685253789Srpaulo	if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
686253789Srpaulo		yp_error("rejecting update attempt for %s: bad arguments",
687253789Srpaulo			 argp->newpw.pw_name);
688253789Srpaulo		return(result);
689253789Srpaulo	}
690253789Srpaulo
691253789Srpaulo	/*
692253789Srpaulo	 * If the caller specified a domain other than our 'default'
693253789Srpaulo	 * domain, change the path to master.passwd accordingly.
694253789Srpaulo	 */
695253789Srpaulo
696253789Srpaulo	if (strcmp(argp->domain, yppasswd_domain)) {
697253789Srpaulo		snprintf(passfile_buf, sizeof(passfile_buf),
698253789Srpaulo			"/var/yp/%s/master.passwd", argp->domain);
699253789Srpaulo		passfile = (char *)&passfile_buf;
700253789Srpaulo	}
701267349Shselasky
702267349Shselasky	if ((pfd = pw_lock()) < 0) {
703267349Shselasky		return (result);
704253789Srpaulo	}
705253789Srpaulo	if ((tfd = pw_tmp()) < 0) {
706253789Srpaulo		return (result);
707253789Srpaulo	}
708253789Srpaulo
709253789Srpaulo	if (pw_copy(pfd, tfd, (struct passwd  *)&argp->newpw)) {
710253789Srpaulo		yp_error("failed to created updated password file -- \
711253789Srpaulocleaning up and bailing out");
712253789Srpaulo		unlink(tempname);
713253789Srpaulo		return(result);
714253789Srpaulo	}
715253789Srpaulo
716253789Srpaulo	passfile_hold = mktemp((char *)&template);
717253789Srpaulo	rename(passfile, passfile_hold);
718253789Srpaulo	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
719253789Srpaulo		rename(tempname, passfile);
720253789Srpaulo	} else {
721253789Srpaulo		if (pw_mkdb() < 0) {
722253789Srpaulo			yp_error("pwd_mkdb failed");
723253789Srpaulo			return(result);
724253789Srpaulo		}
725253789Srpaulo	}
726253789Srpaulo
727253789Srpaulo	if (inplace) {
728253789Srpaulo		if ((rval = update_inplace((struct passwd *)&argp->newpw,
729253789Srpaulo							argp->domain))) {
730253789Srpaulo			yp_error("inplace update failed -- rebuilding maps");
731253789Srpaulo		}
732253789Srpaulo	}
733253789Srpaulo
734253789Srpaulo	switch((pid = fork())) {
735253789Srpaulo	case 0:
736253789Srpaulo		close(yp_sock);
737253789Srpaulo		if (inplace && !rval) {
738253789Srpaulo    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
739253789Srpaulo				argp->domain, "pushpw", NULL);
740253789Srpaulo    		} else {
741253789Srpaulo			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
742253789Srpaulo				argp->domain, NULL);
743253789Srpaulo		}
744253789Srpaulo    		yp_error("couldn't exec map update process: %s",
745253789Srpaulo					strerror(errno));
746253789Srpaulo		unlink(passfile);
747253789Srpaulo		rename(passfile_hold, passfile);
748253789Srpaulo    		exit(1);
749253789Srpaulo		break;
750253789Srpaulo	case -1:
751253789Srpaulo		yp_error("fork() failed: %s", strerror(errno));
752253789Srpaulo		unlink(passfile);
753267349Shselasky		rename(passfile_hold, passfile);
754253789Srpaulo		return(result);
755267349Shselasky		break;
756253789Srpaulo	default:
757253789Srpaulo		break;
758253789Srpaulo	}
759253789Srpaulo
760253789Srpaulo	yp_error("performed update of user %s (uid %d) domain %s",
761253789Srpaulo						argp->newpw.pw_name,
762253789Srpaulo						argp->newpw.pw_uid,
763253789Srpaulo						argp->domain);
764253789Srpaulo
765253789Srpaulo	result = 0;
766253789Srpaulo	return(result);
767253789Srpaulo}
768253789Srpaulo
769253789Srpaulo/*
770253789Srpaulo * Pseudo-dispatcher for private 'superuser-only' update handler.
771 */
772void do_master()
773{
774	struct master_yppasswd *pw;
775
776	if ((pw = getdat(yp_sock)) == NULL) {
777		return;
778	}
779
780	yp_error("received update request from superuser on localhost");
781	sendresp(update_master(pw));
782
783	/* Remember to free args. */
784	xdr_free(xdr_master_yppasswd, (char *)pw);
785
786	return;
787}
788