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