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