yppasswdd_server.c revision 79452
1/*
2 * Copyright (c) 1995, 1996
3 *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34static const char rcsid[] =
35  "$FreeBSD: head/usr.sbin/rpc.yppasswdd/yppasswdd_server.c 79452 2001-07-09 09:24:06Z brian $";
36#endif /* not lint */
37
38#include <stdio.h>
39#include <string.h>
40#include <ctype.h>
41#include <stdlib.h>
42#include <unistd.h>
43#include <dirent.h>
44#include <sys/stat.h>
45#include <sys/socket.h>
46#include <netinet/in.h>
47#include <arpa/inet.h>
48#include <limits.h>
49#include <db.h>
50#include <pwd.h>
51#include <errno.h>
52#include <signal.h>
53#include <rpc/rpc.h>
54#include <rpcsvc/yp.h>
55#include <sys/types.h>
56#include <sys/wait.h>
57#include <sys/param.h>
58#include <sys/fcntl.h>
59struct dom_binding {};
60#include <rpcsvc/ypclnt.h>
61#include "yppasswdd_extern.h"
62#include "yppasswd.h"
63#include "yppasswd_private.h"
64
65struct cmessage {
66        struct cmsghdr cmsg;
67        struct cmsgcred cmcred;
68};
69
70char *tempname;
71
72void reaper(sig)
73	int sig;
74{
75	extern pid_t pid;
76	extern int pstat;
77	int st;
78	int saved_errno;
79
80	saved_errno = errno;
81
82	if (sig > 0) {
83		if (sig == SIGCHLD)
84			while(wait3(&st, WNOHANG, NULL) > 0) ;
85	} else {
86		pid = waitpid(pid, &pstat, 0);
87	}
88
89	errno = saved_errno;
90	return;
91}
92
93void install_reaper(on)
94	int on;
95{
96	if (on) {
97		signal(SIGCHLD, reaper);
98	} else {
99		signal(SIGCHLD, SIG_DFL);
100	}
101	return;
102}
103
104static struct passwd yp_password;
105
106static void copy_yp_pass(p, x, m)
107char *p;
108int x, m;
109{
110	register char *t, *s = p;
111	static char *buf;
112
113	yp_password.pw_fields = 0;
114
115	buf = (char *)realloc(buf, m + 10);
116	bzero(buf, m + 10);
117
118	/* Turn all colons into NULLs */
119	while (strchr(s, ':')) {
120		s = (strchr(s, ':') + 1);
121		*(s - 1)= '\0';
122	}
123
124	t = buf;
125#define EXPAND(e)       e = t; while ((*t++ = *p++));
126        EXPAND(yp_password.pw_name);
127	yp_password.pw_fields |= _PWF_NAME;
128        EXPAND(yp_password.pw_passwd);
129	yp_password.pw_fields |= _PWF_PASSWD;
130	yp_password.pw_uid = atoi(p);
131        p += (strlen(p) + 1);
132	yp_password.pw_fields |= _PWF_UID;
133	yp_password.pw_gid = atoi(p);
134        p += (strlen(p) + 1);
135	yp_password.pw_fields |= _PWF_GID;
136	if (x) {
137		EXPAND(yp_password.pw_class);
138		yp_password.pw_fields |= _PWF_CLASS;
139		yp_password.pw_change = atol(p);
140		p += (strlen(p) + 1);
141		yp_password.pw_fields |= _PWF_CHANGE;
142		yp_password.pw_expire = atol(p);
143		p += (strlen(p) + 1);
144		yp_password.pw_fields |= _PWF_EXPIRE;
145	}
146        EXPAND(yp_password.pw_gecos);
147	yp_password.pw_fields |= _PWF_GECOS;
148        EXPAND(yp_password.pw_dir);
149	yp_password.pw_fields |= _PWF_DIR;
150        EXPAND(yp_password.pw_shell);
151	yp_password.pw_fields |= _PWF_SHELL;
152
153	return;
154}
155
156static int validchars(arg)
157	char *arg;
158{
159	int i;
160
161	for (i = 0; i < strlen(arg); i++) {
162		if (iscntrl(arg[i])) {
163			yp_error("string contains a control character");
164			return(1);
165		}
166		if (arg[i] == ':') {
167			yp_error("string contains a colon");
168			return(1);
169		}
170		/* Be evil: truncate strings with \n in them silently. */
171		if (arg[i] == '\n') {
172			arg[i] = '\0';
173			return(0);
174		}
175	}
176	return(0);
177}
178
179static int validate_master(opw, npw)
180	struct passwd *opw;
181	struct x_master_passwd *npw;
182{
183
184	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
185		yp_error("client tried to modify an NIS entry");
186		return(1);
187	}
188
189	if (validchars(npw->pw_shell)) {
190		yp_error("specified shell contains invalid characters");
191		return(1);
192	}
193
194	if (validchars(npw->pw_gecos)) {
195		yp_error("specified gecos field contains invalid characters");
196		return(1);
197	}
198
199	if (validchars(npw->pw_passwd)) {
200		yp_error("specified password contains invalid characters");
201		return(1);
202	}
203	return(0);
204}
205
206static int validate(opw, npw)
207	struct passwd *opw;
208	struct x_passwd *npw;
209{
210
211	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
212		yp_error("client tried to modify an NIS entry");
213		return(1);
214	}
215
216	if (npw->pw_uid != opw->pw_uid) {
217		yp_error("UID mismatch: client says user %s has UID %d",
218			 npw->pw_name, npw->pw_uid);
219		yp_error("database says user %s has UID %d", opw->pw_name,
220			 opw->pw_uid);
221		return(1);
222	}
223
224	if (npw->pw_gid != opw->pw_gid) {
225		yp_error("GID mismatch: client says user %s has GID %d",
226			 npw->pw_name, npw->pw_gid);
227		yp_error("database says user %s has GID %d", opw->pw_name,
228			 opw->pw_gid);
229		return(1);
230	}
231
232	/*
233	 * Don't allow the user to shoot himself in the foot,
234	 * even on purpose.
235	 */
236	if (!ok_shell(npw->pw_shell)) {
237		yp_error("%s is not a valid shell", npw->pw_shell);
238		return(1);
239	}
240
241	if (validchars(npw->pw_shell)) {
242		yp_error("specified shell contains invalid characters");
243		return(1);
244	}
245
246	if (validchars(npw->pw_gecos)) {
247		yp_error("specified gecos field contains invalid characters");
248		return(1);
249	}
250
251	if (validchars(npw->pw_passwd)) {
252		yp_error("specified password contains invalid characters");
253		return(1);
254	}
255	return(0);
256}
257
258/*
259 * Kludge alert:
260 * In order to have one rpc.yppasswdd support multiple domains,
261 * we have to cheat: we search each directory under /var/yp
262 * and try to match the user in each master.passwd.byname
263 * map that we find. If the user matches (username, uid and gid
264 * all agree), then we use that domain. If we match the user in
265 * more than one database, we must abort.
266 */
267static char *find_domain(pw)
268	struct x_passwd *pw;
269{
270	struct stat statbuf;
271	struct dirent *dirp;
272	DIR *dird;
273	char yp_mapdir[MAXPATHLEN + 2];
274	static char domain[YPMAXDOMAIN];
275	char *tmp = NULL;
276	DBT key, data;
277	int hit = 0;
278
279	yp_error("performing multidomain lookup");
280
281	if ((dird = opendir(yp_dir)) == NULL) {
282		yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
283		return(NULL);
284	}
285
286	while ((dirp = readdir(dird)) != NULL) {
287		snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
288							yp_dir, dirp->d_name);
289		if (stat(yp_mapdir, &statbuf) < 0) {
290			yp_error("stat(%s) failed: %s", yp_mapdir,
291							strerror(errno));
292			closedir(dird);
293			return(NULL);
294		}
295		if (S_ISDIR(statbuf.st_mode)) {
296			tmp = (char *)dirp->d_name;
297			key.data = pw->pw_name;
298			key.size = strlen(pw->pw_name);
299
300			if (yp_get_record(tmp,"master.passwd.byname",
301			  		&key, &data, 0) != YP_TRUE) {
302				continue;
303			}
304			*(char *)(data.data + data.size) = '\0';
305			copy_yp_pass(data.data, 1, data.size);
306			if (yp_password.pw_uid == pw->pw_uid &&
307			    yp_password.pw_gid == pw->pw_gid) {
308				hit++;
309				snprintf(domain, YPMAXDOMAIN, "%s", tmp);
310			}
311		}
312	}
313
314	closedir(dird);
315	if (hit > 1) {
316		yp_error("found same user in two different domains");
317		return(NULL);
318	} else
319		return((char *)&domain);
320}
321
322static int update_inplace(pw, domain)
323	struct passwd *pw;
324	char *domain;
325{
326	DB *dbp = NULL;
327	DBT key = { NULL, 0 };
328	DBT data = { NULL, 0 };
329	char pwbuf[YPMAXRECORD];
330	char keybuf[20];
331	int i;
332	char *maps[] = { "master.passwd.byname", "master.passwd.byuid",
333			 "passwd.byname", "passwd.byuid" };
334
335	char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
336			    "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
337			    "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" };
338	char *ptr = NULL;
339	char *yp_last = "YP_LAST_MODIFIED";
340	char yplastbuf[YPMAXRECORD];
341
342	snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
343
344	for (i = 0; i < 4; i++) {
345
346		if (i % 2) {
347			snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
348			key.data = (char *)&keybuf;
349			key.size = strlen(keybuf);
350		} else {
351			key.data = pw->pw_name;
352			key.size = strlen(pw->pw_name);
353		}
354
355		/*
356		 * XXX The passwd.byname and passwd.byuid maps come in
357		 * two flavors: secure and insecure. The secure version
358		 * has a '*' in the password field whereas the insecure one
359		 * has a real crypted password. The maps will be insecure
360		 * if they were built with 'unsecure = TRUE' enabled in
361		 * /var/yp/Makefile, but we'd have no way of knowing if
362		 * this has been done unless we were to try parsing the
363		 * Makefile, which is a disgusting thought. Instead, we
364		 * read the records from the maps, skip to the first ':'
365		 * in them, and then look at the character immediately
366		 * following it. If it's an '*' then the map is 'secure'
367		 * and we must not insert a real password into the pw_passwd
368		 * field. If it's not an '*', then we put the real crypted
369		 * password in.
370		 */
371		if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
372			yp_error("couldn't read %s/%s: %s", domain,
373						maps[i], strerror(errno));
374			return(1);
375		}
376
377		if ((ptr = strchr(data.data, ':')) == NULL) {
378			yp_error("no colon in passwd record?!");
379			return(1);
380		}
381
382		/*
383		 * XXX Supposing we have more than one user with the same
384		 * UID? (Or more than one user with the same name?) We could
385		 * end up modifying the wrong record if were not careful.
386		 */
387		if (i % 2) {
388			if (strncmp(data.data, pw->pw_name,
389							strlen(pw->pw_name))) {
390				yp_error("warning: found entry for UID %d \
391in map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
392					ptr - (char *)data.data, data.data);
393				yp_error("there may be more than one user \
394with the same UID - continuing");
395				continue;
396			}
397		} else {
398			/*
399			 * We're really being ultra-paranoid here.
400			 * This is generally a 'can't happen' condition.
401			 */
402			snprintf(pwbuf, sizeof(pwbuf), ":%d:%d:", pw->pw_uid,
403								  pw->pw_gid);
404			if (!strstr(data.data, pwbuf)) {
405				yp_error("warning: found entry for user %s \
406in map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
407				yp_error("there may be more than one user
408with the same name - continuing");
409				continue;
410			}
411		}
412
413		if (i < 2) {
414			snprintf(pwbuf, sizeof(pwbuf), formats[i],
415			   pw->pw_name, pw->pw_passwd, pw->pw_uid,
416			   pw->pw_gid, pw->pw_class, pw->pw_change,
417			   pw->pw_expire, pw->pw_gecos, pw->pw_dir,
418			   pw->pw_shell);
419		} else {
420			snprintf(pwbuf, sizeof(pwbuf), formats[i],
421			   pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
422			   pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
423			   pw->pw_shell);
424		}
425
426#define FLAGS O_RDWR|O_CREAT
427
428		if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
429			yp_error("couldn't open %s/%s r/w: %s",domain,
430						maps[i],strerror(errno));
431			return(1);
432		}
433
434		data.data = pwbuf;
435		data.size = strlen(pwbuf);
436
437		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
438			yp_error("failed to update record in %s/%s", domain,
439								maps[i]);
440			(void)(dbp->close)(dbp);
441			return(1);
442		}
443
444		key.data = yp_last;
445		key.size = strlen(yp_last);
446		data.data = (char *)&yplastbuf;
447		data.size = strlen(yplastbuf);
448
449		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
450			yp_error("failed to update timestamp in %s/%s", domain,
451								maps[i]);
452			(void)(dbp->close)(dbp);
453			return(1);
454		}
455
456		(void)(dbp->close)(dbp);
457	}
458
459	return(0);
460}
461
462static char *yp_mktmpnam()
463{
464	static char path[MAXPATHLEN];
465	char *p;
466
467	sprintf(path,"%s",passfile);
468	if ((p = strrchr(path, '/')))
469		++p;
470	else
471		p = path;
472	strcpy(p, "yppwtmp.XXXXXX");
473	return(mktemp(path));
474}
475
476int *
477yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
478{
479	static int  result;
480	struct sockaddr_in *rqhost;
481	DBT key, data;
482	int rval = 0;
483	int pfd, tfd;
484	int pid;
485	int passwd_changed = 0;
486	int shell_changed = 0;
487	int gecos_changed = 0;
488	char *oldshell = NULL;
489	char *oldgecos = NULL;
490	char *passfile_hold;
491	char passfile_buf[MAXPATHLEN + 2];
492	char *domain = yppasswd_domain;
493	static struct sockaddr_in clntaddr;
494	static struct timeval t_saved, t_test;
495
496	/*
497	 * Normal user updates always use the 'default' master.passwd file.
498	 */
499
500	passfile = passfile_default;
501	result = 1;
502
503	rqhost = svc_getcaller(rqstp->rq_xprt);
504
505	gettimeofday(&t_test, NULL);
506	if (!bcmp((char *)rqhost, (char *)&clntaddr,
507						sizeof(struct sockaddr_in)) &&
508		t_test.tv_sec > t_saved.tv_sec &&
509		t_test.tv_sec - t_saved.tv_sec < 300) {
510
511		bzero((char *)&clntaddr, sizeof(struct sockaddr_in));
512		bzero((char *)&t_saved, sizeof(struct timeval));
513		return(NULL);
514	}
515
516	bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in));
517	gettimeofday(&t_saved, NULL);
518
519	if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
520		yp_error("rejected update request from unauthorized host");
521		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
522		return(&result);
523	}
524
525	/*
526	 * Step one: find the user. (It's kinda pointless to
527	 * proceed if the user doesn't exist.) We look for the
528	 * user in the master.passwd.byname database, _NOT_ by
529	 * using getpwent() and friends! We can't use getpwent()
530	 * since the NIS master server is not guaranteed to be
531	 * configured as an NIS client.
532	 */
533
534	if (multidomain) {
535		if ((domain = find_domain(&argp->newpw)) == NULL) {
536			yp_error("multidomain lookup failed - aborting update");
537			return(&result);
538		} else
539			yp_error("updating user %s in domain %s",
540					argp->newpw.pw_name, domain);
541	}
542
543	key.data = argp->newpw.pw_name;
544	key.size = strlen(argp->newpw.pw_name);
545
546	if ((rval=yp_get_record(domain,"master.passwd.byname",
547		  	&key, &data, 0)) != YP_TRUE) {
548		if (rval == YP_NOKEY) {
549			yp_error("user %s not found in passwd database",
550			 	argp->newpw.pw_name);
551		} else {
552			yp_error("database access error: %s",
553			 	yperr_string(rval));
554		}
555		return(&result);
556	}
557
558	/* Nul terminate, please. */
559	*(char *)(data.data + data.size) = '\0';
560
561	copy_yp_pass(data.data, 1, data.size);
562
563	/* Step 2: check that the supplied oldpass is valid. */
564
565	if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
566					yp_password.pw_passwd)) {
567		yp_error("rejected change attempt -- bad password");
568		yp_error("client address: %s username: %s",
569			  inet_ntoa(rqhost->sin_addr),
570			  argp->newpw.pw_name);
571		return(&result);
572	}
573
574	/* Step 3: validate the arguments passed to us by the client. */
575
576	if (validate(&yp_password, &argp->newpw)) {
577		yp_error("rejecting change attempt: bad arguments");
578		yp_error("client address: %s username: %s",
579			 inet_ntoa(rqhost->sin_addr),
580			 argp->newpw.pw_name);
581		svcerr_decode(rqstp->rq_xprt);
582		return(&result);
583	}
584
585	/* Step 4: update the user's passwd structure. */
586
587	if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
588		oldshell = yp_password.pw_shell;
589		yp_password.pw_shell = argp->newpw.pw_shell;
590		shell_changed++;
591	}
592
593
594	if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
595		oldgecos = yp_password.pw_gecos;
596		yp_password.pw_gecos = argp->newpw.pw_gecos;
597		gecos_changed++;
598	}
599
600	if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
601		yp_password.pw_passwd = argp->newpw.pw_passwd;
602		yp_password.pw_change = 0;
603		passwd_changed++;
604	}
605
606	/*
607	 * If the caller specified a domain other than our 'default'
608	 * domain, change the path to master.passwd accordingly.
609	 */
610
611	if (strcmp(domain, yppasswd_domain)) {
612		snprintf(passfile_buf, sizeof(passfile_buf),
613			"%s/%s/master.passwd", yp_dir, domain);
614		passfile = (char *)&passfile_buf;
615	}
616
617	/* Step 5: make a new password file with the updated info. */
618
619	if ((pfd = pw_lock()) < 0) {
620		return (&result);
621	}
622	if ((tfd = pw_tmp()) < 0) {
623		return (&result);
624	}
625
626	if (pw_copy(pfd, tfd, &yp_password)) {
627		yp_error("failed to created updated password file -- \
628cleaning up and bailing out");
629		unlink(tempname);
630		return(&result);
631	}
632
633	passfile_hold = yp_mktmpnam();
634	rename(passfile, passfile_hold);
635	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
636		rename(tempname, passfile);
637	} else {
638		if (pw_mkdb(argp->newpw.pw_name) < 0) {
639			yp_error("pwd_mkdb failed");
640			return(&result);
641		}
642	}
643
644	if (inplace) {
645		if ((rval = update_inplace(&yp_password, domain))) {
646			yp_error("inplace update failed -- rebuilding maps");
647		}
648	}
649
650	switch((pid = fork())) {
651	case 0:
652		if (inplace && !rval) {
653    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
654				yppasswd_domain, "pushpw", (char *)NULL);
655		} else {
656    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
657				yppasswd_domain, (char *)NULL);
658		}
659    		yp_error("couldn't exec map update process: %s",
660					strerror(errno));
661		unlink(passfile);
662		rename(passfile_hold, passfile);
663    		exit(1);
664		break;
665	case -1:
666		yp_error("fork() failed: %s", strerror(errno));
667		unlink(passfile);
668		rename(passfile_hold, passfile);
669		return(&result);
670		break;
671	default:
672		unlink(passfile_hold);
673		break;
674	}
675
676	if (verbose) {
677		yp_error("update completed for user %s (uid %d):",
678						argp->newpw.pw_name,
679						argp->newpw.pw_uid);
680
681		if (passwd_changed)
682			yp_error("password changed");
683
684		if (gecos_changed)
685			yp_error("gecos changed ('%s' -> '%s')",
686					oldgecos, argp->newpw.pw_gecos);
687
688		if (shell_changed)
689			yp_error("shell changed ('%s' -> '%s')",
690					oldshell, argp->newpw.pw_shell);
691	}
692
693	result = 0;
694	return (&result);
695}
696
697/*
698 * Note that this function performs a little less sanity checking
699 * than the last one. Since only the superuser is allowed to use it,
700 * it is assumed that the caller knows what he's doing.
701 */
702int *yppasswdproc_update_master_1_svc(master_yppasswd *argp,
703					struct svc_req *rqstp)
704{
705	static int result;
706	int pfd, tfd;
707	int pid;
708	int rval = 0;
709	DBT key, data;
710	char *passfile_hold;
711	char passfile_buf[MAXPATHLEN + 2];
712	struct sockaddr_in *rqhost;
713	struct cmessage			*cm;
714	SVCXPRT				*transp;
715
716	result = 1;
717
718	/*
719	 * NO AF_INET CONNETCIONS ALLOWED!
720	 */
721	rqhost = svc_getcaller(rqstp->rq_xprt);
722	if (rqhost->sin_family != AF_UNIX) {
723		yp_error("Alert! %s/%d attempted to use superuser-only \
724procedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
725		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
726		return(&result);
727	}
728
729	transp = rqstp->rq_xprt;
730
731	if (transp->xp_verf.oa_length < sizeof(struct cmessage) ||
732		transp->xp_verf.oa_base == NULL ||
733		transp->xp_verf.oa_flavor != AUTH_UNIX) {
734		yp_error("caller didn't send proper credentials");
735		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
736		return(&result);
737	}
738
739	cm = (struct cmessage *)transp->xp_verf.oa_base;
740	if (cm->cmsg.cmsg_type != SCM_CREDS) {
741		yp_error("caller didn't send proper credentials");
742		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
743		return(&result);
744	}
745
746 	if (cm->cmcred.cmcred_euid) {
747		yp_error("caller euid is %d, expecting 0 -- rejecting request",
748				cm->cmcred.cmcred_euid);
749		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
750		return(&result);
751	}
752
753	passfile = passfile_default;
754
755	key.data = argp->newpw.pw_name;
756	key.size = strlen(argp->newpw.pw_name);
757
758	/*
759	 * The superuser may add entries to the passwd maps if
760	 * rpc.yppasswdd is started with the -a flag. Paranoia
761	 * prevents me from allowing additions by default.
762	 */
763	if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
764			  &key, &data, 0)) != YP_TRUE) {
765		if (rval == YP_NOKEY) {
766			yp_error("user %s not found in passwd database",
767				 argp->newpw.pw_name);
768			if (allow_additions)
769				yp_error("notice: adding user %s to \
770master.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
771			else
772				yp_error("restart rpc.yppasswdd with the -a flag to \
773allow additions to be made to the password database");
774		} else {
775			yp_error("database access error: %s",
776				 yperr_string(rval));
777		}
778		if (!allow_additions)
779			return(&result);
780	} else {
781
782		/* Nul terminate, please. */
783		*(char *)(data.data + data.size) = '\0';
784
785		copy_yp_pass(data.data, 1, data.size);
786	}
787
788	/*
789	 * Perform a small bit of sanity checking.
790	 */
791	if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
792		yp_error("rejecting update attempt for %s: bad arguments",
793			 argp->newpw.pw_name);
794		return(&result);
795	}
796
797	/*
798	 * If the caller specified a domain other than our 'default'
799	 * domain, change the path to master.passwd accordingly.
800	 */
801
802	if (strcmp(argp->domain, yppasswd_domain)) {
803		snprintf(passfile_buf, sizeof(passfile_buf),
804			"%s/%s/master.passwd", yp_dir, argp->domain);
805		passfile = (char *)&passfile_buf;
806	}
807
808	if ((pfd = pw_lock()) < 0) {
809		return (&result);
810	}
811	if ((tfd = pw_tmp()) < 0) {
812		return (&result);
813	}
814
815	if (pw_copy(pfd, tfd, (struct passwd  *)&argp->newpw)) {
816		yp_error("failed to created updated password file -- \
817cleaning up and bailing out");
818		unlink(tempname);
819		return(&result);
820	}
821
822	passfile_hold = yp_mktmpnam();
823	rename(passfile, passfile_hold);
824	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
825		rename(tempname, passfile);
826	} else {
827		if (pw_mkdb(argp->newpw.pw_name) < 0) {
828			yp_error("pwd_mkdb failed");
829			return(&result);
830		}
831	}
832
833	if (inplace) {
834		if ((rval = update_inplace((struct passwd *)&argp->newpw,
835							argp->domain))) {
836			yp_error("inplace update failed -- rebuilding maps");
837		}
838	}
839
840	switch((pid = fork())) {
841	case 0:
842		if (inplace && !rval) {
843    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
844				argp->domain, "pushpw", (char *)NULL);
845    		} else {
846			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
847				argp->domain, (char *)NULL);
848		}
849    		yp_error("couldn't exec map update process: %s",
850					strerror(errno));
851		unlink(passfile);
852		rename(passfile_hold, passfile);
853    		exit(1);
854		break;
855	case -1:
856		yp_error("fork() failed: %s", strerror(errno));
857		unlink(passfile);
858		rename(passfile_hold, passfile);
859		return(&result);
860		break;
861	default:
862		unlink(passfile_hold);
863		break;
864	}
865
866	yp_error("performed update of user %s (uid %d) domain %s",
867						argp->newpw.pw_name,
868						argp->newpw.pw_uid,
869						argp->domain);
870
871	result = 0;
872	return(&result);
873}
874