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 <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> 59 struct dom_binding {}; 60 #include <rpcsvc/ypclnt.h> 61 #include "yppasswdd_extern.h" 62 #include "yppasswd.h" 63 #include "yppasswd_private.h" 64 65 char *tempname; 66 67 void 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 88 void 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 99 static struct passwd yp_password; 100 101 static void copy_yp_pass(p, x, m) 102 char *p; 103 int 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 151 static 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 174 static 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 201 static 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 */ 262 static 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 317 static 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 \ 386 in 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 \ 389 with 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 \ 401 in map %s@%s with wrong UID", pw->pw_name, maps[i], domain); 402 yp_error("there may ne more than one user 403 with 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 457 static 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 471 int * 472 yppasswdproc_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 -- \ 623 cleaning 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 struct cmessage { 693 struct cmsghdr cmsg; 694 struct cmsgcred cmcred; 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 */ 702 int *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 \ 724 procedure!\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 \ 770 master.passwd database for domain %s", argp->newpw.pw_name, argp->domain); 771 else 772 yp_error("restart rpc.yppasswdd with the -a flag to \ 773 allow 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 -- \ 817 cleaning 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", NULL); 845 } else { 846 execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 847 argp->domain, 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