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