yppasswdd_server.c revision 96222
155714Skris/* 255714Skris * Copyright (c) 1995, 1996 3160814Ssimon * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 855714Skris * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 3. All advertising materials mentioning features or use of this software 1455714Skris * must display the following acknowledgement: 1555714Skris * This product includes software developed by Bill Paul. 1655714Skris * 4. Neither the name of the author nor the names of any co-contributors 1755714Skris * may be used to endorse or promote products derived from this software 1855714Skris * without specific prior written permission. 1955714Skris * 2055714Skris * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2155714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2255714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2355714Skris * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE 2455714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2555714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2655714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2755714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2855714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2955714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3055714Skris * SUCH DAMAGE. 3155714Skris */ 3255714Skris 3355714Skris#ifndef lint 3455714Skrisstatic const char rcsid[] = 3555714Skris "$FreeBSD: head/usr.sbin/rpc.yppasswdd/yppasswdd_server.c 96222 2002-05-08 15:42:37Z des $"; 3655714Skris#endif /* not lint */ 3755714Skris 3855714Skris#include <sys/param.h> 3955714Skris#include <sys/fcntl.h> 4055714Skris#include <sys/socket.h> 4155714Skris#include <sys/stat.h> 4255714Skris#include <sys/wait.h> 4355714Skris 4455714Skris#include <arpa/inet.h> 4555714Skris#include <netinet/in.h> 4655714Skris 4755714Skris#include <ctype.h> 4855714Skris#include <db.h> 4955714Skris#include <dirent.h> 5055714Skris#include <errno.h> 5155714Skris#include <limits.h> 5255714Skris#include <pwd.h> 5355714Skris#include <signal.h> 5455714Skris#include <stdio.h> 5555714Skris#include <stdlib.h> 5655714Skris#include <string.h> 5759191Skris#include <unistd.h> 5859191Skris 5955714Skris#include <libgen.h> 6055714Skris#include <libutil.h> 6155714Skris 6255714Skris#include <rpc/rpc.h> 6355714Skris#include <rpcsvc/yp.h> 6455714Skrisstruct dom_binding; 6555714Skris#include <rpcsvc/ypclnt.h> 66109998Smarkm#include "yppasswdd_extern.h" 67160814Ssimon#include "yppasswd.h" 68160814Ssimon#include "yppasswd_private.h" 69160814Ssimon#include "ypxfr_extern.h" 70160814Ssimon#include "yp_extern.h" 7155714Skris 7255714Skrisstatic struct passwd yp_password; 73160814Ssimon 74160814Ssimonstatic void 75160814Ssimoncopy_yp_pass(char *p, int x, int m) 76160814Ssimon{ 77160814Ssimon char *t, *s = p; 78160814Ssimon static char *buf; 79160814Ssimon 80160814Ssimon yp_password.pw_fields = 0; 81160814Ssimon 82160814Ssimon buf = realloc(buf, m + 10); 83160814Ssimon bzero(buf, m + 10); 84160814Ssimon 85160814Ssimon /* Turn all colons into NULLs */ 86160814Ssimon while (strchr(s, ':')) { 87160814Ssimon s = (strchr(s, ':') + 1); 88160814Ssimon *(s - 1)= '\0'; 89160814Ssimon } 90160814Ssimon 91160814Ssimon t = buf; 92160814Ssimon#define EXPAND(e) e = t; while ((*t++ = *p++)); 93160814Ssimon EXPAND(yp_password.pw_name); 94160814Ssimon yp_password.pw_fields |= _PWF_NAME; 95160814Ssimon EXPAND(yp_password.pw_passwd); 96160814Ssimon yp_password.pw_fields |= _PWF_PASSWD; 97160814Ssimon yp_password.pw_uid = atoi(p); 98160814Ssimon p += (strlen(p) + 1); 99160814Ssimon yp_password.pw_fields |= _PWF_UID; 100160814Ssimon yp_password.pw_gid = atoi(p); 101160814Ssimon p += (strlen(p) + 1); 102160814Ssimon yp_password.pw_fields |= _PWF_GID; 103160814Ssimon if (x) { 104160814Ssimon EXPAND(yp_password.pw_class); 10555714Skris yp_password.pw_fields |= _PWF_CLASS; 10655714Skris yp_password.pw_change = atol(p); 10755714Skris p += (strlen(p) + 1); 10855714Skris yp_password.pw_fields |= _PWF_CHANGE; 10955714Skris yp_password.pw_expire = atol(p); 110160814Ssimon p += (strlen(p) + 1); 111160814Ssimon yp_password.pw_fields |= _PWF_EXPIRE; 112160814Ssimon } 113160814Ssimon EXPAND(yp_password.pw_gecos); 114160814Ssimon yp_password.pw_fields |= _PWF_GECOS; 115160814Ssimon EXPAND(yp_password.pw_dir); 116160814Ssimon yp_password.pw_fields |= _PWF_DIR; 117160814Ssimon EXPAND(yp_password.pw_shell); 118160814Ssimon yp_password.pw_fields |= _PWF_SHELL; 119160814Ssimon 120160814Ssimon return; 121160814Ssimon} 122160814Ssimon 123160814Ssimonstatic int 124160814Ssimonvalidchars(char *arg) 125160814Ssimon{ 126160814Ssimon size_t i; 127160814Ssimon 128160814Ssimon for (i = 0; i < strlen(arg); i++) { 129160814Ssimon if (iscntrl(arg[i])) { 130160814Ssimon yp_error("string contains a control character"); 131160814Ssimon return(1); 132160814Ssimon } 133160814Ssimon if (arg[i] == ':') { 134160814Ssimon yp_error("string contains a colon"); 135160814Ssimon return(1); 136160814Ssimon } 137160814Ssimon /* Be evil: truncate strings with \n in them silently. */ 138160814Ssimon if (arg[i] == '\n') { 13955714Skris arg[i] = '\0'; 14055714Skris return(0); 14155714Skris } 14255714Skris } 14355714Skris return(0); 14455714Skris} 14555714Skris 14655714Skrisstatic int 14755714Skrisvalidate_master(struct passwd *opw __unused, struct x_master_passwd *npw) 14855714Skris{ 14955714Skris 15055714Skris if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 151109998Smarkm yp_error("client tried to modify an NIS entry"); 152160814Ssimon return(1); 153160814Ssimon } 15455714Skris 15555714Skris if (validchars(npw->pw_shell)) { 15655714Skris yp_error("specified shell contains invalid characters"); 15755714Skris return(1); 158 } 159 160 if (validchars(npw->pw_gecos)) { 161 yp_error("specified gecos field contains invalid characters"); 162 return(1); 163 } 164 165 if (validchars(npw->pw_passwd)) { 166 yp_error("specified password contains invalid characters"); 167 return(1); 168 } 169 return(0); 170} 171 172static int 173validate(struct passwd *opw, struct x_passwd *npw) 174{ 175 176 if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 177 yp_error("client tried to modify an NIS entry"); 178 return(1); 179 } 180 181 if ((uid_t)npw->pw_uid != opw->pw_uid) { 182 yp_error("UID mismatch: client says user %s has UID %d", 183 npw->pw_name, npw->pw_uid); 184 yp_error("database says user %s has UID %d", opw->pw_name, 185 opw->pw_uid); 186 return(1); 187 } 188 189 if ((gid_t)npw->pw_gid != opw->pw_gid) { 190 yp_error("GID mismatch: client says user %s has GID %d", 191 npw->pw_name, npw->pw_gid); 192 yp_error("database says user %s has GID %d", opw->pw_name, 193 opw->pw_gid); 194 return(1); 195 } 196 197 /* 198 * Don't allow the user to shoot himself in the foot, 199 * even on purpose. 200 */ 201 if (!ok_shell(npw->pw_shell)) { 202 yp_error("%s is not a valid shell", npw->pw_shell); 203 return(1); 204 } 205 206 if (validchars(npw->pw_shell)) { 207 yp_error("specified shell contains invalid characters"); 208 return(1); 209 } 210 211 if (validchars(npw->pw_gecos)) { 212 yp_error("specified gecos field contains invalid characters"); 213 return(1); 214 } 215 216 if (validchars(npw->pw_passwd)) { 217 yp_error("specified password contains invalid characters"); 218 return(1); 219 } 220 return(0); 221} 222 223/* 224 * Kludge alert: 225 * In order to have one rpc.yppasswdd support multiple domains, 226 * we have to cheat: we search each directory under /var/yp 227 * and try to match the user in each master.passwd.byname 228 * map that we find. If the user matches (username, uid and gid 229 * all agree), then we use that domain. If we match the user in 230 * more than one database, we must abort. 231 */ 232static char * 233find_domain(struct x_passwd *pw) 234{ 235 struct stat statbuf; 236 struct dirent *dirp; 237 DIR *dird; 238 char yp_mapdir[MAXPATHLEN + 2]; 239 static char domain[YPMAXDOMAIN]; 240 char *tmp = NULL; 241 DBT key, data; 242 int hit = 0; 243 244 yp_error("performing multidomain lookup"); 245 246 if ((dird = opendir(yp_dir)) == NULL) { 247 yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno)); 248 return(NULL); 249 } 250 251 while ((dirp = readdir(dird)) != NULL) { 252 snprintf(yp_mapdir, sizeof yp_mapdir, "%s/%s", 253 yp_dir, dirp->d_name); 254 if (stat(yp_mapdir, &statbuf) < 0) { 255 yp_error("stat(%s) failed: %s", yp_mapdir, 256 strerror(errno)); 257 closedir(dird); 258 return(NULL); 259 } 260 if (S_ISDIR(statbuf.st_mode)) { 261 tmp = (char *)dirp->d_name; 262 key.data = pw->pw_name; 263 key.size = strlen(pw->pw_name); 264 265 if (yp_get_record(tmp,"master.passwd.byname", 266 &key, &data, 0) != YP_TRUE) { 267 continue; 268 } 269 *((char *)data.data + data.size) = '\0'; 270 copy_yp_pass(data.data, 1, data.size); 271 if (yp_password.pw_uid == (uid_t)pw->pw_uid && 272 yp_password.pw_gid == (gid_t)pw->pw_gid) { 273 hit++; 274 snprintf(domain, YPMAXDOMAIN, "%s", tmp); 275 } 276 } 277 } 278 279 closedir(dird); 280 if (hit > 1) { 281 yp_error("found same user in two different domains"); 282 return(NULL); 283 } else 284 return((char *)&domain); 285} 286 287static const char *maps[] = { 288 "master.passwd.byname", 289 "master.passwd.byuid", 290 "passwd.byname", 291 "passwd.byuid" 292}; 293 294static const char *formats[] = { 295 "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 296 "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 297 "%s:%s:%d:%d:%s:%s:%s", 298 "%s:%s:%d:%d:%s:%s:%s" 299}; 300 301static int 302update_inplace(struct passwd *pw, char *domain) 303{ 304 DB *dbp = NULL; 305 DBT key = { NULL, 0 }; 306 DBT data = { NULL, 0 }; 307 char pwbuf[YPMAXRECORD]; 308 char keybuf[20]; 309 int i; 310 char *ptr = NULL; 311 static char yp_last[] = "YP_LAST_MODIFIED"; 312 char yplastbuf[YPMAXRECORD]; 313 314 snprintf(yplastbuf, sizeof yplastbuf, "%llu", 315 (unsigned long long)time(NULL)); 316 317 for (i = 0; i < 4; i++) { 318 319 if (i % 2) { 320 snprintf(keybuf, sizeof keybuf, 321 "%llu", (unsigned long long)pw->pw_uid); 322 key.data = &keybuf; 323 key.size = strlen(keybuf); 324 } else { 325 key.data = pw->pw_name; 326 key.size = strlen(pw->pw_name); 327 } 328 329 /* 330 * XXX The passwd.byname and passwd.byuid maps come in 331 * two flavors: secure and insecure. The secure version 332 * has a '*' in the password field whereas the insecure one 333 * has a real crypted password. The maps will be insecure 334 * if they were built with 'unsecure = TRUE' enabled in 335 * /var/yp/Makefile, but we'd have no way of knowing if 336 * this has been done unless we were to try parsing the 337 * Makefile, which is a disgusting thought. Instead, we 338 * read the records from the maps, skip to the first ':' 339 * in them, and then look at the character immediately 340 * following it. If it's an '*' then the map is 'secure' 341 * and we must not insert a real password into the pw_passwd 342 * field. If it's not an '*', then we put the real crypted 343 * password in. 344 */ 345 if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) { 346 yp_error("couldn't read %s/%s: %s", domain, 347 maps[i], strerror(errno)); 348 return(1); 349 } 350 351 if ((ptr = strchr(data.data, ':')) == NULL) { 352 yp_error("no colon in passwd record?!"); 353 return(1); 354 } 355 356 /* 357 * XXX Supposing we have more than one user with the same 358 * UID? (Or more than one user with the same name?) We could 359 * end up modifying the wrong record if were not careful. 360 */ 361 if (i % 2) { 362 if (strncmp(data.data, pw->pw_name, 363 strlen(pw->pw_name))) { 364 yp_error("warning: found entry for UID %d \ 365in map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain, 366 ptr - (char *)data.data, (char *)data.data); 367 yp_error("there may be more than one user \ 368with the same UID - continuing"); 369 continue; 370 } 371 } else { 372 /* 373 * We're really being ultra-paranoid here. 374 * This is generally a 'can't happen' condition. 375 */ 376 snprintf(pwbuf, sizeof pwbuf, ":%d:%d:", pw->pw_uid, 377 pw->pw_gid); 378 if (!strstr(data.data, pwbuf)) { 379 yp_error("warning: found entry for user %s \ 380in map %s@%s with wrong UID", pw->pw_name, maps[i], domain); 381 yp_error("there may be more than one user 382with the same name - continuing"); 383 continue; 384 } 385 } 386 387 if (i < 2) { 388 snprintf(pwbuf, sizeof pwbuf, formats[i], 389 pw->pw_name, pw->pw_passwd, pw->pw_uid, 390 pw->pw_gid, pw->pw_class, pw->pw_change, 391 pw->pw_expire, pw->pw_gecos, pw->pw_dir, 392 pw->pw_shell); 393 } else { 394 snprintf(pwbuf, sizeof pwbuf, formats[i], 395 pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd, 396 pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, 397 pw->pw_shell); 398 } 399 400#define FLAGS O_RDWR|O_CREAT 401 402 if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) { 403 yp_error("couldn't open %s/%s r/w: %s",domain, 404 maps[i],strerror(errno)); 405 return(1); 406 } 407 408 data.data = pwbuf; 409 data.size = strlen(pwbuf); 410 411 if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 412 yp_error("failed to update record in %s/%s", domain, 413 maps[i]); 414 (void)(dbp->close)(dbp); 415 return(1); 416 } 417 418 key.data = yp_last; 419 key.size = strlen(yp_last); 420 data.data = (char *)&yplastbuf; 421 data.size = strlen(yplastbuf); 422 423 if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 424 yp_error("failed to update timestamp in %s/%s", domain, 425 maps[i]); 426 (void)(dbp->close)(dbp); 427 return(1); 428 } 429 430 (void)(dbp->close)(dbp); 431 } 432 433 return(0); 434} 435 436int * 437yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp) 438{ 439 static int result; 440 struct sockaddr_in *rqhost; 441 DBT key, data; 442 int rval = 0; 443 int pfd, tfd; 444 int pid; 445 int passwd_changed = 0; 446 int shell_changed = 0; 447 int gecos_changed = 0; 448 char *oldshell = NULL; 449 char *oldgecos = NULL; 450 char *passfile_hold; 451 char passfile_buf[MAXPATHLEN + 2]; 452 char *domain = yppasswd_domain; 453 static struct sockaddr_in clntaddr; 454 static struct timeval t_saved, t_test; 455 456 /* 457 * Normal user updates always use the 'default' master.passwd file. 458 */ 459 460 passfile = passfile_default; 461 result = 1; 462 463 rqhost = svc_getcaller(rqstp->rq_xprt); 464 465 gettimeofday(&t_test, NULL); 466 if (!bcmp(rqhost, &clntaddr, sizeof *rqhost) && 467 t_test.tv_sec > t_saved.tv_sec && 468 t_test.tv_sec - t_saved.tv_sec < 300) { 469 470 bzero(&clntaddr, sizeof clntaddr); 471 bzero(&t_saved, sizeof t_saved); 472 return(NULL); 473 } 474 475 bcopy(rqhost, &clntaddr, sizeof clntaddr); 476 gettimeofday(&t_saved, NULL); 477 478 if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) { 479 yp_error("rejected update request from unauthorized host"); 480 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 481 return(&result); 482 } 483 484 /* 485 * Step one: find the user. (It's kinda pointless to 486 * proceed if the user doesn't exist.) We look for the 487 * user in the master.passwd.byname database, _NOT_ by 488 * using getpwent() and friends! We can't use getpwent() 489 * since the NIS master server is not guaranteed to be 490 * configured as an NIS client. 491 */ 492 493 if (multidomain) { 494 if ((domain = find_domain(&argp->newpw)) == NULL) { 495 yp_error("multidomain lookup failed - aborting update"); 496 return(&result); 497 } else 498 yp_error("updating user %s in domain %s", 499 argp->newpw.pw_name, domain); 500 } 501 502 key.data = argp->newpw.pw_name; 503 key.size = strlen(argp->newpw.pw_name); 504 505 if ((rval = yp_get_record(domain,"master.passwd.byname", 506 &key, &data, 0)) != YP_TRUE) { 507 if (rval == YP_NOKEY) { 508 yp_error("user %s not found in passwd database", 509 argp->newpw.pw_name); 510 } else { 511 yp_error("database access error: %s", 512 yperr_string(rval)); 513 } 514 return(&result); 515 } 516 517 /* Nul terminate, please. */ 518 *((char *)data.data + data.size) = '\0'; 519 520 copy_yp_pass(data.data, 1, data.size); 521 522 /* Step 2: check that the supplied oldpass is valid. */ 523 524 if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd), 525 yp_password.pw_passwd)) { 526 yp_error("rejected change attempt -- bad password"); 527 yp_error("client address: %s username: %s", 528 inet_ntoa(rqhost->sin_addr), 529 argp->newpw.pw_name); 530 return(&result); 531 } 532 533 /* Step 3: validate the arguments passed to us by the client. */ 534 535 if (validate(&yp_password, &argp->newpw)) { 536 yp_error("rejecting change attempt: bad arguments"); 537 yp_error("client address: %s username: %s", 538 inet_ntoa(rqhost->sin_addr), 539 argp->newpw.pw_name); 540 svcerr_decode(rqstp->rq_xprt); 541 return(&result); 542 } 543 544 /* Step 4: update the user's passwd structure. */ 545 546 if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) { 547 oldshell = yp_password.pw_shell; 548 yp_password.pw_shell = argp->newpw.pw_shell; 549 shell_changed++; 550 } 551 552 553 if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) { 554 oldgecos = yp_password.pw_gecos; 555 yp_password.pw_gecos = argp->newpw.pw_gecos; 556 gecos_changed++; 557 } 558 559 if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) { 560 yp_password.pw_passwd = argp->newpw.pw_passwd; 561 yp_password.pw_change = 0; 562 passwd_changed++; 563 } 564 565 /* 566 * If the caller specified a domain other than our 'default' 567 * domain, change the path to master.passwd accordingly. 568 */ 569 570 if (strcmp(domain, yppasswd_domain)) { 571 snprintf(passfile_buf, sizeof(passfile_buf), 572 "%s/%s/master.passwd", yp_dir, domain); 573 passfile = (char *)&passfile_buf; 574 } 575 576 /* Step 5: make a new password file with the updated info. */ 577 578 if (pw_init(dirname(passfile), passfile)) { 579 yp_error("pw_init() failed"); 580 return &result; 581 } 582 if ((pfd = pw_lock()) == -1) { 583 pw_fini(); 584 yp_error("pw_lock() failed"); 585 return &result; 586 } 587 if ((tfd = pw_tmp(-1)) == -1) { 588 pw_fini(); 589 yp_error("pw_tmp() failed"); 590 return &result; 591 } 592 if (pw_copy(pfd, tfd, &yp_password, NULL) == -1) { 593 pw_fini(); 594 yp_error("pw_copy() failed"); 595 return &result; 596 } 597 if (pw_mkdb(yp_password.pw_name) == -1) { 598 pw_fini(); 599 yp_error("pw_mkdb() failed"); 600 return &result; 601 } 602 pw_fini(); 603 604 if (inplace) { 605 if ((rval = update_inplace(&yp_password, domain))) { 606 yp_error("inplace update failed -- rebuilding maps"); 607 } 608 } 609 610 switch ((pid = fork())) { 611 case 0: 612 if (inplace && !rval) { 613 execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 614 yppasswd_domain, "pushpw", (char *)NULL); 615 } else { 616 execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 617 yppasswd_domain, (char *)NULL); 618 } 619 yp_error("couldn't exec map update process: %s", 620 strerror(errno)); 621 unlink(passfile); 622 rename(passfile_hold, passfile); 623 exit(1); 624 break; 625 case -1: 626 yp_error("fork() failed: %s", strerror(errno)); 627 unlink(passfile); 628 rename(passfile_hold, passfile); 629 return(&result); 630 break; 631 default: 632 unlink(passfile_hold); 633 break; 634 } 635 636 if (verbose) { 637 yp_error("update completed for user %s (uid %d):", 638 argp->newpw.pw_name, 639 argp->newpw.pw_uid); 640 641 if (passwd_changed) 642 yp_error("password changed"); 643 644 if (gecos_changed) 645 yp_error("gecos changed ('%s' -> '%s')", 646 oldgecos, argp->newpw.pw_gecos); 647 648 if (shell_changed) 649 yp_error("shell changed ('%s' -> '%s')", 650 oldshell, argp->newpw.pw_shell); 651 } 652 653 result = 0; 654 return (&result); 655} 656 657/* 658 * Note that this function performs a little less sanity checking 659 * than the last one. Since only the superuser is allowed to use it, 660 * it is assumed that the caller knows what he's doing. 661 */ 662int * 663yppasswdproc_update_master_1_svc(master_yppasswd *argp, 664 struct svc_req *rqstp) 665{ 666 static int result; 667 int pfd, tfd; 668 int pid; 669 uid_t uid; 670 int rval = 0; 671 DBT key, data; 672 char *passfile_hold; 673 char passfile_buf[MAXPATHLEN + 2]; 674 struct sockaddr_in *rqhost; 675 SVCXPRT *transp; 676 677 result = 1; 678 transp = rqstp->rq_xprt; 679 680 /* 681 * NO AF_INET CONNETCIONS ALLOWED! 682 */ 683 rqhost = svc_getcaller(transp); 684 if (rqhost->sin_family != AF_UNIX) { 685 yp_error("Alert! %s/%d attempted to use superuser-only \ 686procedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port); 687 svcerr_auth(transp, AUTH_BADCRED); 688 return(&result); 689 } 690 691 if (rqstp->rq_cred.oa_flavor != AUTH_SYS) { 692 yp_error("caller didn't send proper credentials"); 693 svcerr_auth(transp, AUTH_BADCRED); 694 return(&result); 695 } 696 697 if (__rpc_get_local_uid(transp, &uid) < 0) { 698 yp_error("caller didn't send proper credentials"); 699 svcerr_auth(transp, AUTH_BADCRED); 700 return(&result); 701 } 702 703 if (uid) { 704 yp_error("caller euid is %d, expecting 0 -- rejecting request", 705 uid); 706 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 707 return(&result); 708 } 709 710 passfile = passfile_default; 711 712 key.data = argp->newpw.pw_name; 713 key.size = strlen(argp->newpw.pw_name); 714 715 /* 716 * The superuser may add entries to the passwd maps if 717 * rpc.yppasswdd is started with the -a flag. Paranoia 718 * prevents me from allowing additions by default. 719 */ 720 if ((rval = yp_get_record(argp->domain, "master.passwd.byname", 721 &key, &data, 0)) != YP_TRUE) { 722 if (rval == YP_NOKEY) { 723 yp_error("user %s not found in passwd database", 724 argp->newpw.pw_name); 725 if (allow_additions) 726 yp_error("notice: adding user %s to \ 727master.passwd database for domain %s", argp->newpw.pw_name, argp->domain); 728 else 729 yp_error("restart rpc.yppasswdd with the -a flag to \ 730allow additions to be made to the password database"); 731 } else { 732 yp_error("database access error: %s", 733 yperr_string(rval)); 734 } 735 if (!allow_additions) 736 return(&result); 737 } else { 738 739 /* Nul terminate, please. */ 740 *((char *)data.data + data.size) = '\0'; 741 742 copy_yp_pass(data.data, 1, data.size); 743 } 744 745 /* 746 * Perform a small bit of sanity checking. 747 */ 748 if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){ 749 yp_error("rejecting update attempt for %s: bad arguments", 750 argp->newpw.pw_name); 751 return(&result); 752 } 753 754 /* 755 * If the caller specified a domain other than our 'default' 756 * domain, change the path to master.passwd accordingly. 757 */ 758 759 if (strcmp(argp->domain, yppasswd_domain)) { 760 snprintf(passfile_buf, sizeof(passfile_buf), 761 "%s/%s/master.passwd", yp_dir, argp->domain); 762 passfile = (char *)&passfile_buf; 763 } 764 765 if (pw_init(dirname(passfile), passfile)) { 766 yp_error("pw_init() failed"); 767 return &result; 768 } 769 if ((pfd = pw_lock()) == -1) { 770 pw_fini(); 771 yp_error("pw_lock() failed"); 772 return &result; 773 } 774 if ((tfd = pw_tmp(-1)) == -1) { 775 pw_fini(); 776 yp_error("pw_tmp() failed"); 777 return &result; 778 } 779 if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw, NULL) == -1) { 780 pw_fini(); 781 yp_error("pw_copy() failed"); 782 return &result; 783 } 784 if (pw_mkdb(argp->newpw.pw_name) == -1) { 785 pw_fini(); 786 yp_error("pw_mkdb() failed"); 787 return &result; 788 } 789 pw_fini(); 790 791 if (inplace) { 792 if ((rval = update_inplace((struct passwd *)&argp->newpw, 793 argp->domain))) { 794 yp_error("inplace update failed -- rebuilding maps"); 795 } 796 } 797 798 switch ((pid = fork())) { 799 case 0: 800 if (inplace && !rval) { 801 execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 802 argp->domain, "pushpw", (char *)NULL); 803 } else { 804 execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 805 argp->domain, (char *)NULL); 806 } 807 yp_error("couldn't exec map update process: %s", 808 strerror(errno)); 809 unlink(passfile); 810 rename(passfile_hold, passfile); 811 exit(1); 812 break; 813 case -1: 814 yp_error("fork() failed: %s", strerror(errno)); 815 unlink(passfile); 816 rename(passfile_hold, passfile); 817 return(&result); 818 break; 819 default: 820 unlink(passfile_hold); 821 break; 822 } 823 824 yp_error("performed update of user %s (uid %d) domain %s", 825 argp->newpw.pw_name, 826 argp->newpw.pw_uid, 827 argp->domain); 828 829 result = 0; 830 return(&result); 831} 832