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