yppasswdd_server.c revision 14063
1247835Skib/*
2247835Skib * Copyright (c) 1995, 1996
3247835Skib *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4247835Skib *
5247835Skib * Redistribution and use in source and binary forms, with or without
6247835Skib * modification, are permitted provided that the following conditions
7247835Skib * are met:
8247835Skib * 1. Redistributions of source code must retain the above copyright
9247835Skib *    notice, this list of conditions and the following disclaimer.
10247835Skib * 2. Redistributions in binary form must reproduce the above copyright
11247835Skib *    notice, this list of conditions and the following disclaimer in the
12247835Skib *    documentation and/or other materials provided with the distribution.
13247835Skib * 3. All advertising materials mentioning features or use of this software
14247835Skib *    must display the following acknowledgement:
15247835Skib *	This product includes software developed by Bill Paul.
16247835Skib * 4. Neither the name of the author nor the names of any co-contributors
17247835Skib *    may be used to endorse or promote products derived from this software
18247835Skib *    without specific prior written permission.
19247835Skib *
20247835Skib * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21247835Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22247835Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23247835Skib * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24247835Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25247835Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26247835Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27247835Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28247835Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29247835Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30247835Skib * SUCH DAMAGE.
31247835Skib *
32247835Skib *	$Id: yppasswdd_server.c,v 1.8 1996/02/09 04:38:19 wpaul Exp $
33247835Skib */
34247835Skib
35247835Skib#include <stdio.h>
36247835Skib#include <string.h>
37247835Skib#include <ctype.h>
38247835Skib#include <stdlib.h>
39247835Skib#include <unistd.h>
40247835Skib#include <dirent.h>
41247835Skib#include <sys/stat.h>
42247835Skib#include <sys/socket.h>
43247835Skib#include <netinet/in.h>
44247835Skib#include <arpa/inet.h>
45247835Skib#include <limits.h>
46247835Skib#include <db.h>
47247835Skib#include <pwd.h>
48247835Skib#include <errno.h>
49247835Skib#include <signal.h>
50247835Skib#include <rpc/rpc.h>
51247835Skib#include <rpcsvc/yp.h>
52247835Skib#include <sys/types.h>
53247835Skib#include <sys/wait.h>
54247835Skib#include <sys/param.h>
55247835Skibstruct dom_binding {};
56247835Skib#include <rpcsvc/ypclnt.h>
57247835Skib#include "yppasswdd_extern.h"
58247835Skib#include "yppasswd.h"
59247835Skib#include "yppasswd_private.h"
60247835Skib#include "yppasswd_comm.h"
61247835Skib
62247835Skib#ifndef lint
63247835Skibstatic const char rcsid[] = "$Id: yppasswdd_server.c,v 1.8 1996/02/09 04:38:19 wpaul Exp $";
64247835Skib#endif /* not lint */
65247835Skib
66247835Skibchar *tempname;
67247835Skib
68247835Skibvoid reaper(sig)
69247835Skib	int sig;
70247835Skib{
71247835Skib	extern pid_t pid;
72247835Skib	extern int pstat;
73247835Skib	int st;
74247835Skib
75247835Skib	if (sig > 0) {
76247835Skib		if (sig == SIGCHLD)
77247835Skib			while(wait3(&st, WNOHANG, NULL) > 0) ;
78247835Skib	} else {
79247835Skib		pid = waitpid(pid, &pstat, 0);
80247835Skib	}
81247835Skib	return;
82247835Skib}
83247835Skib
84247835Skibvoid install_reaper(on)
85247835Skib	int on;
86247835Skib{
87247835Skib	if (on) {
88247835Skib		signal(SIGCHLD, reaper);
89247835Skib	} else {
90247835Skib		signal(SIGCHLD, SIG_DFL);
91247835Skib	}
92247835Skib	return;
93247835Skib}
94247835Skib
95247835Skibstatic struct passwd yp_password;
96247835Skib
97247835Skibstatic void copy_yp_pass(p, x, m)
98247835Skibchar *p;
99247835Skibint x, m;
100247835Skib{
101247835Skib	register char *t, *s = p;
102247835Skib	static char *buf;
103247835Skib
104247835Skib	yp_password.pw_fields = 0;
105247835Skib
106247835Skib	buf = (char *)realloc(buf, m + 10);
107247835Skib	bzero(buf, m + 10);
108247835Skib
109280183Sdumbbell	/* Turn all colons into NULLs */
110280183Sdumbbell	while (strchr(s, ':')) {
111280183Sdumbbell		s = (strchr(s, ':') + 1);
112247835Skib		*(s - 1)= '\0';
113247835Skib	}
114247835Skib
115280183Sdumbbell	t = buf;
116247835Skib#define EXPAND(e)       e = t; while ((*t++ = *p++));
117247835Skib        EXPAND(yp_password.pw_name);
118247835Skib	yp_password.pw_fields |= _PWF_NAME;
119247835Skib        EXPAND(yp_password.pw_passwd);
120247835Skib	yp_password.pw_fields |= _PWF_PASSWD;
121247835Skib	yp_password.pw_uid = atoi(p);
122247835Skib        p += (strlen(p) + 1);
123247835Skib	yp_password.pw_fields |= _PWF_UID;
124247835Skib	yp_password.pw_gid = atoi(p);
125247835Skib        p += (strlen(p) + 1);
126247835Skib	yp_password.pw_fields |= _PWF_GID;
127247835Skib	if (x) {
128247835Skib		EXPAND(yp_password.pw_class);
129247835Skib		yp_password.pw_fields |= _PWF_CLASS;
130247835Skib		yp_password.pw_change = atol(p);
131247835Skib		p += (strlen(p) + 1);
132247835Skib		yp_password.pw_fields |= _PWF_CHANGE;
133247835Skib		yp_password.pw_expire = atol(p);
134247835Skib		p += (strlen(p) + 1);
135247835Skib		yp_password.pw_fields |= _PWF_EXPIRE;
136247835Skib	}
137247835Skib        EXPAND(yp_password.pw_gecos);
138247835Skib	yp_password.pw_fields |= _PWF_GECOS;
139247835Skib        EXPAND(yp_password.pw_dir);
140247835Skib	yp_password.pw_fields |= _PWF_DIR;
141247835Skib        EXPAND(yp_password.pw_shell);
142247835Skib	yp_password.pw_fields |= _PWF_SHELL;
143247835Skib
144247835Skib	return;
145247835Skib}
146247835Skib
147247835Skibstatic int validchars(arg)
148247835Skib	char *arg;
149247835Skib{
150247835Skib	int i;
151247835Skib
152247835Skib	for (i = 0; i < strlen(arg); i++) {
153247835Skib		if (iscntrl(arg[i])) {
154247835Skib			yp_error("string contains a control character");
155280183Sdumbbell			return(1);
156280183Sdumbbell		}
157280183Sdumbbell		if (arg[i] == ':') {
158247835Skib			yp_error("string contains a colon");
159247835Skib			return(1);
160247835Skib		}
161247835Skib		/* Be evil: truncate strings with \n in them silently. */
162247835Skib		if (arg[i] == '\n') {
163247835Skib			arg[i] = '\0';
164247835Skib			return(0);
165247835Skib		}
166247835Skib	}
167247835Skib	return(0);
168247835Skib}
169247835Skib
170247835Skibstatic int validate_master(opw, npw)
171247835Skib	struct passwd *opw;
172247835Skib	struct x_master_passwd *npw;
173247835Skib{
174247835Skib
175247835Skib	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
176247835Skib		yp_error("client tried to modify an NIS entry");
177247835Skib		return(1);
178247835Skib	}
179247835Skib
180247835Skib	if (validchars(npw->pw_shell)) {
181247835Skib		yp_error("specified shell contains invalid characters");
182247835Skib		return(1);
183247835Skib	}
184247835Skib
185247835Skib	if (validchars(npw->pw_gecos)) {
186247835Skib		yp_error("specified gecos field contains invalid characters");
187247835Skib		return(1);
188247835Skib	}
189247835Skib
190247835Skib	if (validchars(npw->pw_passwd)) {
191247835Skib		yp_error("specified password contains invalid characters");
192247835Skib		return(1);
193247835Skib	}
194247835Skib	return(0);
195247835Skib}
196247835Skib
197247835Skibstatic int validate(opw, npw)
198247835Skib	struct passwd *opw;
199247835Skib	struct x_passwd *npw;
200247835Skib{
201247835Skib
202247835Skib	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
203247835Skib		yp_error("client tried to modify an NIS entry");
204247835Skib		return(1);
205247835Skib	}
206247835Skib
207247835Skib	if (npw->pw_uid != opw->pw_uid) {
208247835Skib		yp_error("UID mismatch: client says user %s has UID %d",
209247835Skib			 npw->pw_name, npw->pw_uid);
210280183Sdumbbell		yp_error("database says user %s has UID %d", opw->pw_name,
211280183Sdumbbell			 opw->pw_uid);
212280183Sdumbbell		return(1);
213247835Skib	}
214247835Skib
215247835Skib	if (npw->pw_gid != opw->pw_gid) {
216247835Skib		yp_error("GID mismatch: client says user %s has GID %d",
217247835Skib			 npw->pw_name, npw->pw_gid);
218247835Skib		yp_error("database says user %s has GID %d", opw->pw_name,
219247835Skib			 opw->pw_gid);
220247835Skib		return(1);
221280183Sdumbbell	}
222247835Skib
223247835Skib	/*
224247835Skib	 * Don't allow the user to shoot himself in the foot,
225247835Skib	 * even on purpose.
226247835Skib	 */
227247835Skib	if (!ok_shell(npw->pw_shell)) {
228247835Skib		yp_error("%s is not a valid shell", npw->pw_shell);
229247835Skib		return(1);
230247835Skib	}
231247835Skib
232247835Skib	if (validchars(npw->pw_shell)) {
233247835Skib		yp_error("specified shell contains invalid characters");
234247835Skib		return(1);
235247835Skib	}
236247835Skib
237247835Skib	if (validchars(npw->pw_gecos)) {
238247835Skib		yp_error("specified gecos field contains invalid characters");
239247835Skib		return(1);
240247835Skib	}
241247835Skib
242247835Skib	if (validchars(npw->pw_passwd)) {
243247835Skib		yp_error("specified password contains invalid characters");
244247835Skib		return(1);
245247835Skib	}
246247835Skib	return(0);
247247835Skib}
248247835Skib
249247835Skib/*
250247835Skib * Kludge alert:
251247835Skib * In order to have one rpc.yppasswdd support multiple domains,
252247835Skib * we have to cheat: we search each directory under /var/yp
253247835Skib * and try to match the user in each master.passwd.byname
254247835Skib * map that we find. If the user matches (username, uid and gid
255247835Skib * all agree), then we use that domain. If we match the user in
256247835Skib * more than one database, we must abort.
257247835Skib */
258247835Skibstatic char *find_domain(pw)
259247835Skib	struct x_passwd *pw;
260247835Skib{
261247835Skib	struct stat statbuf;
262247835Skib	struct dirent *dirp;
263247835Skib	DIR *dird;
264247835Skib	char yp_mapdir[MAXPATHLEN + 2];
265247835Skib	char *domain = NULL;
266247835Skib	char *tmp = NULL;
267247835Skib	DBT key, data;
268247835Skib	int hit = 0;
269247835Skib
270247835Skib	yp_error("performing multidomain lookup");
271247835Skib
272247835Skib	if ((dird = opendir(yp_dir)) == NULL) {
273247835Skib		yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
274247835Skib		return(NULL);
275247835Skib	}
276247835Skib
277247835Skib	while ((dirp = readdir(dird)) != NULL) {
278247835Skib		snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
279247835Skib							yp_dir, dirp->d_name);
280247835Skib		if (stat(yp_mapdir, &statbuf) < 0) {
281247835Skib			yp_error("stat(%s) failed: %s", yp_mapdir,
282247835Skib							strerror(errno));
283247835Skib			closedir(dird);
284247835Skib			return(NULL);
285247835Skib		}
286247835Skib		if (S_ISDIR(statbuf.st_mode)) {
287247835Skib			tmp = (char *)dirp->d_name;
288280183Sdumbbell			key.data = pw->pw_name;
289280183Sdumbbell			key.size = strlen(pw->pw_name);
290280183Sdumbbell
291247835Skib			if (yp_get_record(tmp,"master.passwd.byname",
292247835Skib			  		&key, &data, 0) != YP_TRUE) {
293247835Skib				continue;
294247835Skib			}
295247835Skib			*(char *)(data.data + data.size) = '\0';
296247835Skib			copy_yp_pass(data.data, 1, data.size);
297247835Skib			if (yp_password.pw_uid == pw->pw_uid &&
298247835Skib			    yp_password.pw_gid == pw->pw_gid) {
299247835Skib				hit++;
300247835Skib				domain = tmp;
301247835Skib			}
302247835Skib		}
303247835Skib	}
304247835Skib
305247835Skib	closedir(dird);
306247835Skib	if (hit > 1) {
307247835Skib		yp_error("found same user in two different domains");
308247835Skib		return(NULL);
309247835Skib	} else
310247835Skib		return(domain);
311280183Sdumbbell}
312247835Skib
313247835Skibint *
314247835Skibyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
315247835Skib{
316247835Skib	static int  result;
317247835Skib	struct sockaddr_in *rqhost;
318247835Skib	DBT key, data;
319247835Skib	int rval = 0;
320247835Skib	int pfd, tfd;
321247835Skib	int pid;
322247835Skib	int passwd_changed = 0;
323247835Skib	int shell_changed = 0;
324247835Skib	int gecos_changed = 0;
325247835Skib	char *oldshell = NULL;
326247835Skib	char *oldgecos = NULL;
327247835Skib	char *passfile_hold;
328247835Skib	char passfile_buf[MAXPATHLEN + 2];
329247835Skib	char template[] = "/etc/yppwtmp.XXXXX";
330247835Skib	char *domain = yppasswd_domain;
331247835Skib
332247835Skib	/*
333247835Skib	 * Normal user updates always use the 'default' master.passwd file.
334247835Skib	 */
335247835Skib
336247835Skib	passfile = passfile_default;
337247835Skib	result = 1;
338247835Skib
339247835Skib	rqhost = svc_getcaller(rqstp->rq_xprt);
340247835Skib
341247835Skib	/*
342247835Skib	 * Step one: find the user. (It's kinda pointless to
343247835Skib	 * proceed if the user doesn't exist.) We look for the
344247835Skib	 * user in the master.passwd.byname database, _NOT_ by
345247835Skib	 * using getpwent() and friends! We can't use getpwent()
346247835Skib	 * since the NIS master server is not guaranteed to be
347247835Skib	 * configured as an NIS client.
348247835Skib	 */
349
350	if (multidomain) {
351		if ((domain = find_domain(&argp->newpw)) == NULL) {
352			yp_error("multidomain lookup failed - aborting update");
353			return(&result);
354		} else
355			yp_error("updating user %s in domain %s",
356					argp->newpw.pw_name, domain);
357	}
358
359	key.data = argp->newpw.pw_name;
360	key.size = strlen(argp->newpw.pw_name);
361
362	if ((rval=yp_get_record(domain,"master.passwd.byname",
363		  	&key, &data, 0)) != YP_TRUE) {
364		if (rval == YP_NOKEY) {
365			yp_error("user %s not found in passwd database",
366			 	argp->newpw.pw_name);
367		} else {
368			yp_error("database access error: %s",
369			 	yperr_string(rval));
370		}
371		return(&result);
372	}
373
374	/* Nul terminate, please. */
375	*(char *)(data.data + data.size) = '\0';
376
377	copy_yp_pass(data.data, 1, data.size);
378
379	/* Step 2: check that the supplied oldpass is valid. */
380
381	if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
382					yp_password.pw_passwd)) {
383		yp_error("rejected change attempt -- bad password");
384		yp_error("client address: %s username: %s",
385			  inet_ntoa(rqhost->sin_addr),
386			  argp->newpw.pw_name);
387		return(&result);
388	}
389
390	/* Step 3: validate the arguments passed to us by the client. */
391
392	if (validate(&yp_password, &argp->newpw)) {
393		yp_error("rejecting change attempt: bad arguments");
394		yp_error("client address: %s username: %s",
395			 inet_ntoa(rqhost->sin_addr),
396			 argp->newpw.pw_name);
397		svcerr_decode(rqstp->rq_xprt);
398		return(&result);
399	}
400
401	/* Step 4: update the user's passwd structure. */
402
403	if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
404		oldshell = yp_password.pw_shell;
405		yp_password.pw_shell = argp->newpw.pw_shell;
406		shell_changed++;
407	}
408
409
410	if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
411		oldgecos = yp_password.pw_gecos;
412		yp_password.pw_gecos = argp->newpw.pw_gecos;
413		gecos_changed++;
414	}
415
416	if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
417		yp_password.pw_passwd = argp->newpw.pw_passwd;
418		passwd_changed++;
419	}
420
421	/*
422	 * If the caller specified a domain other than our 'default'
423	 * domain, change the path to master.passwd accordingly.
424	 */
425
426	if (strcmp(domain, yppasswd_domain)) {
427		snprintf(passfile_buf, sizeof(passfile_buf),
428			"/var/yp/%s/master.passwd", domain);
429		passfile = (char *)&passfile_buf;
430	}
431
432	/* Step 5: make a new password file with the updated info. */
433
434	if ((pfd = pw_lock()) < 0) {
435		return (&result);
436	}
437	if ((tfd = pw_tmp()) < 0) {
438		return (&result);
439	}
440
441	if (pw_copy(pfd, tfd, &yp_password)) {
442		yp_error("failed to created updated password file -- \
443cleaning up and bailing out");
444		unlink(tempname);
445		return(&result);
446	}
447
448	passfile_hold = mktemp((char *)&template);
449	rename(passfile, passfile_hold);
450	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
451		rename(tempname, passfile);
452	} else {
453		if (pw_mkdb() < 0) {
454			yp_error("pwd_mkdb failed");
455			return(&result);
456		}
457	}
458
459	switch((pid = fork())) {
460	case 0:
461		/* unlink(passfile_hold); */
462    		execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
463			yppasswd_domain, NULL);
464    		yp_error("couldn't exec map update process: %s",
465					strerror(errno));
466		unlink(passfile);
467		rename(passfile_hold, passfile);
468    		exit(1);
469		break;
470	case -1:
471		yp_error("fork() failed: %s", strerror(errno));
472		return(&result);
473		unlink(passfile);
474		rename(passfile_hold, passfile);
475		break;
476	default:
477		break;
478	}
479
480	if (verbose) {
481		yp_error("update completed for user %s (uid %d):",
482						argp->newpw.pw_name,
483						argp->newpw.pw_uid);
484
485		if (passwd_changed)
486			yp_error("password changed");
487
488		if (gecos_changed)
489			yp_error("gecos changed ('%s' -> '%s')",
490					oldgecos, argp->newpw.pw_gecos);
491
492		if (shell_changed)
493			yp_error("shell changed ('%s' -> '%s')",
494					oldshell, argp->newpw.pw_shell);
495	}
496
497	result = 0;
498	return (&result);
499
500}
501
502/*
503 * Note that this function performs a little less sanity checking
504 * than the last one. Since only the superuser is allowed to use it,
505 * it is assumed that the caller knows what he's doing.
506 */
507static int update_master(master_yppasswd *argp)
508{
509	int result;
510	int pfd, tfd;
511	int pid;
512	int rval = 0;
513	DBT key, data;
514	char *passfile_hold;
515	char passfile_buf[MAXPATHLEN + 2];
516	char template[] = "/etc/yppwtmp.XXXXX";
517
518	result = 1;
519	passfile = passfile_default;
520
521	key.data = argp->newpw.pw_name;
522	key.size = strlen(argp->newpw.pw_name);
523
524	/*
525	 * The superuser may add entries to the passwd maps if
526	 * rpc.yppasswdd is started with the -a flag. Paranoia
527	 * prevents me from allowing additions by default.
528	 */
529	if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
530			  &key, &data, 0)) != YP_TRUE) {
531		if (rval == YP_NOKEY) {
532			yp_error("user %s not found in passwd database",
533				 argp->newpw.pw_name);
534			if (allow_additions)
535				yp_error("notice: adding user %s to \
536master.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
537			else
538				yp_error("restart %s with the -a flag to \
539allow additions to be made to the password database", progname);
540		} else {
541			yp_error("database access error: %s",
542				 yperr_string(rval));
543		}
544		if (!allow_additions)
545			return(result);
546	} else {
547
548		/* Nul terminate, please. */
549		*(char *)(data.data + data.size) = '\0';
550
551		copy_yp_pass(data.data, 1, data.size);
552	}
553
554	/*
555	 * Perform a small bit of sanity checking.
556	 */
557	if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
558		yp_error("rejecting update attempt for %s: bad arguments",
559			 argp->newpw.pw_name);
560		return(result);
561	}
562
563	/*
564	 * If the caller specified a domain other than our 'default'
565	 * domain, change the path to master.passwd accordingly.
566	 */
567
568	if (strcmp(argp->domain, yppasswd_domain)) {
569		snprintf(passfile_buf, sizeof(passfile_buf),
570			"/var/yp/%s/master.passwd", argp->domain);
571		passfile = (char *)&passfile_buf;
572	}
573
574	if ((pfd = pw_lock()) < 0) {
575		return (result);
576	}
577	if ((tfd = pw_tmp()) < 0) {
578		return (result);
579	}
580
581	if (pw_copy(pfd, tfd, (struct passwd  *)&argp->newpw)) {
582		yp_error("failed to created updated password file -- \
583cleaning up and bailing out");
584		unlink(tempname);
585		return(result);
586	}
587
588	passfile_hold = mktemp((char *)&template);
589	rename(passfile, passfile_hold);
590	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
591		rename(tempname, passfile);
592	} else {
593		if (pw_mkdb() < 0) {
594			yp_error("pwd_mkdb failed");
595			return(result);
596		}
597	}
598
599	switch((pid = fork())) {
600	case 0:
601		close(yp_sock);
602    		execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
603			argp->domain, NULL);
604    		yp_error("couldn't exec map update process: %s",
605					strerror(errno));
606		unlink(passfile);
607		rename(passfile_hold, passfile);
608    		exit(1);
609		break;
610	case -1:
611		yp_error("fork() failed: %s", strerror(errno));
612		unlink(passfile);
613		rename(passfile_hold, passfile);
614		return(result);
615		break;
616	default:
617		break;
618	}
619
620	yp_error("performed update of user %s (uid %d) domain %s",
621						argp->newpw.pw_name,
622						argp->newpw.pw_uid,
623						argp->domain);
624
625	result = 0;
626	return(result);
627}
628
629/*
630 * Pseudo-dispatcher for private 'superuser-only' update handler.
631 */
632void do_master()
633{
634	struct master_yppasswd *pw;
635	int resp;
636
637	if ((pw = getdat(yp_sock)) == NULL) {
638		return;
639	}
640
641	yp_error("received update request from superuser on localhost");
642	resp = update_master(pw);
643	sendresp(resp);
644
645	/* Remember to free args. */
646	xdr_free(xdr_master_yppasswd, (char *)pw);
647
648	return;
649}
650