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