1 /*- 2 * Copyright (c) 2003 Networks Associates Technology, Inc. 3 * All rights reserved. 4 * 5 * This software was developed for the FreeBSD Project by 6 * Jacques A. Vidrine, Safeport Network Services, and Network 7 * Associates Laboratories, the Security Research Division of Network 8 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 9 * ("CBOSS"), as part of the DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "namespace.h" 37 #include <sys/param.h> 38 #ifdef YP 39 #include <rpc/rpc.h> 40 #include <rpcsvc/yp_prot.h> 41 #include <rpcsvc/ypclnt.h> 42 #endif 43 #include <arpa/inet.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #ifdef HESIOD 47 #include <hesiod.h> 48 #endif 49 #include <netdb.h> 50 #include <nsswitch.h> 51 #include <pthread.h> 52 #include <pthread_np.h> 53 #include <pwd.h> 54 #include <stdlib.h> 55 #include <stdio.h> 56 #include <string.h> 57 #include <syslog.h> 58 #include <unistd.h> 59 #include "un-namespace.h" 60 #include <db.h> 61 #include "libc_private.h" 62 #include "pw_scan.h" 63 #include "nss_tls.h" 64 65 #ifndef CTASSERT 66 #define CTASSERT(x) _CTASSERT(x, __LINE__) 67 #define _CTASSERT(x, y) __CTASSERT(x, y) 68 #define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] 69 #endif 70 71 /* Counter as stored in /etc/pwd.db */ 72 typedef int pwkeynum; 73 74 CTASSERT(MAXLOGNAME > sizeof(uid_t)); 75 CTASSERT(MAXLOGNAME > sizeof(pwkeynum)); 76 77 enum constants { 78 PWD_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ 79 PWD_STORAGE_MAX = 1 << 20, /* 1 MByte */ 80 SETPWENT = 1, 81 ENDPWENT = 2, 82 HESIOD_NAME_MAX = 256 83 }; 84 85 static const ns_src defaultsrc[] = { 86 { NSSRC_COMPAT, NS_SUCCESS }, 87 { NULL, 0 } 88 }; 89 90 int __pw_match_entry(const char *, size_t, enum nss_lookup_type, 91 const char *, uid_t); 92 int __pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop); 93 94 static void pwd_init(struct passwd *); 95 96 union key { 97 const char *name; 98 uid_t uid; 99 }; 100 101 static struct passwd *getpw(int (*fn)(union key, struct passwd *, char *, 102 size_t, struct passwd **), union key); 103 static int wrap_getpwnam_r(union key, struct passwd *, char *, 104 size_t, struct passwd **); 105 static int wrap_getpwuid_r(union key, struct passwd *, char *, size_t, 106 struct passwd **); 107 static int wrap_getpwent_r(union key, struct passwd *, char *, size_t, 108 struct passwd **); 109 110 static int pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type, 111 const char *, uid_t); 112 static int pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *); 113 static int pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type, 114 const char *, uid_t); 115 static int pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *); 116 117 118 struct { 119 int (*match)(char *, size_t, enum nss_lookup_type, const char *, 120 uid_t); 121 int (*parse)(char *, size_t, struct passwd *, int *); 122 } pwdb_versions[] = { 123 { NULL, NULL }, /* version 0 */ 124 { NULL, NULL }, /* version 1 */ 125 { NULL, NULL }, /* version 2 */ 126 { pwdb_match_entry_v3, pwdb_parse_entry_v3 }, /* version 3 */ 127 { pwdb_match_entry_v4, pwdb_parse_entry_v4 }, /* version 4 */ 128 }; 129 130 131 struct files_state { 132 DB *db; 133 pwkeynum keynum; 134 int stayopen; 135 int version; 136 }; 137 static void files_endstate(void *); 138 NSS_TLS_HANDLING(files); 139 static DB *pwdbopen(int *); 140 static void files_endstate(void *); 141 static int files_setpwent(void *, void *, va_list); 142 static int files_passwd(void *, void *, va_list); 143 144 145 #ifdef HESIOD 146 struct dns_state { 147 long counter; 148 }; 149 static void dns_endstate(void *); 150 NSS_TLS_HANDLING(dns); 151 static int dns_setpwent(void *, void *, va_list); 152 static int dns_passwd(void *, void *, va_list); 153 #endif 154 155 156 #ifdef YP 157 struct nis_state { 158 char domain[MAXHOSTNAMELEN]; 159 int done; 160 char *key; 161 int keylen; 162 }; 163 static void nis_endstate(void *); 164 NSS_TLS_HANDLING(nis); 165 static int nis_setpwent(void *, void *, va_list); 166 static int nis_passwd(void *, void *, va_list); 167 static int nis_map(char *, enum nss_lookup_type, char *, size_t, int *); 168 static int nis_adjunct(char *, const char *, char *, size_t); 169 #endif 170 171 172 struct compat_state { 173 DB *db; 174 pwkeynum keynum; 175 int stayopen; 176 int version; 177 DB *exclude; 178 struct passwd template; 179 char *name; 180 enum _compat { 181 COMPAT_MODE_OFF = 0, 182 COMPAT_MODE_ALL, 183 COMPAT_MODE_NAME, 184 COMPAT_MODE_NETGROUP 185 } compat; 186 }; 187 static void compat_endstate(void *); 188 NSS_TLS_HANDLING(compat); 189 static int compat_setpwent(void *, void *, va_list); 190 static int compat_passwd(void *, void *, va_list); 191 static void compat_clear_template(struct passwd *); 192 static int compat_set_template(struct passwd *, struct passwd *); 193 static int compat_use_template(struct passwd *, struct passwd *, char *, 194 size_t); 195 static int compat_redispatch(struct compat_state *, enum nss_lookup_type, 196 enum nss_lookup_type, const char *, const char *, uid_t, 197 struct passwd *, char *, size_t, int *); 198 void 199 setpwent(void) 200 { 201 static const ns_dtab dtab[] = { 202 { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, 203 #ifdef HESIOD 204 { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, 205 #endif 206 #ifdef YP 207 { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, 208 #endif 209 { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, 210 { NULL, NULL, NULL } 211 }; 212 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0); 213 } 214 215 216 int 217 setpassent(int stayopen) 218 { 219 static const ns_dtab dtab[] = { 220 { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, 221 #ifdef HESIOD 222 { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, 223 #endif 224 #ifdef YP 225 { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, 226 #endif 227 { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, 228 { NULL, NULL, NULL } 229 }; 230 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 231 stayopen); 232 return (1); 233 } 234 235 236 void 237 endpwent(void) 238 { 239 static const ns_dtab dtab[] = { 240 { NSSRC_FILES, files_setpwent, (void *)ENDPWENT }, 241 #ifdef HESIOD 242 { NSSRC_DNS, dns_setpwent, (void *)ENDPWENT }, 243 #endif 244 #ifdef YP 245 { NSSRC_NIS, nis_setpwent, (void *)ENDPWENT }, 246 #endif 247 { NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT }, 248 { NULL, NULL, NULL } 249 }; 250 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc); 251 } 252 253 254 int 255 getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, 256 struct passwd **result) 257 { 258 static const ns_dtab dtab[] = { 259 { NSSRC_FILES, files_passwd, (void *)nss_lt_all }, 260 #ifdef HESIOD 261 { NSSRC_DNS, dns_passwd, (void *)nss_lt_all }, 262 #endif 263 #ifdef YP 264 { NSSRC_NIS, nis_passwd, (void *)nss_lt_all }, 265 #endif 266 { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all }, 267 { NULL, NULL, NULL } 268 }; 269 int rv, ret_errno; 270 271 pwd_init(pwd); 272 ret_errno = 0; 273 *result = NULL; 274 rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc, 275 pwd, buffer, bufsize, &ret_errno); 276 if (rv == NS_SUCCESS) 277 return (0); 278 else 279 return (ret_errno); 280 } 281 282 283 int 284 getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize, 285 struct passwd **result) 286 { 287 static const ns_dtab dtab[] = { 288 { NSSRC_FILES, files_passwd, (void *)nss_lt_name }, 289 #ifdef HESIOD 290 { NSSRC_DNS, dns_passwd, (void *)nss_lt_name }, 291 #endif 292 #ifdef YP 293 { NSSRC_NIS, nis_passwd, (void *)nss_lt_name }, 294 #endif 295 { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name }, 296 { NULL, NULL, NULL } 297 }; 298 int rv, ret_errno; 299 300 pwd_init(pwd); 301 ret_errno = 0; 302 *result = NULL; 303 rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc, 304 name, pwd, buffer, bufsize, &ret_errno); 305 if (rv == NS_SUCCESS) 306 return (0); 307 else 308 return (ret_errno); 309 } 310 311 312 int 313 getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, 314 struct passwd **result) 315 { 316 static const ns_dtab dtab[] = { 317 { NSSRC_FILES, files_passwd, (void *)nss_lt_id }, 318 #ifdef HESIOD 319 { NSSRC_DNS, dns_passwd, (void *)nss_lt_id }, 320 #endif 321 #ifdef YP 322 { NSSRC_NIS, nis_passwd, (void *)nss_lt_id }, 323 #endif 324 { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id }, 325 { NULL, NULL, NULL } 326 }; 327 int rv, ret_errno; 328 329 pwd_init(pwd); 330 ret_errno = 0; 331 *result = NULL; 332 rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc, 333 uid, pwd, buffer, bufsize, &ret_errno); 334 if (rv == NS_SUCCESS) 335 return (0); 336 else 337 return (ret_errno); 338 } 339 340 341 static void 342 pwd_init(struct passwd *pwd) 343 { 344 static char nul[] = ""; 345 346 memset(pwd, 0, sizeof(*pwd)); 347 pwd->pw_uid = (uid_t)-1; /* Considered least likely to lead to */ 348 pwd->pw_gid = (gid_t)-1; /* a security issue. */ 349 pwd->pw_name = nul; 350 pwd->pw_passwd = nul; 351 pwd->pw_class = nul; 352 pwd->pw_gecos = nul; 353 pwd->pw_dir = nul; 354 pwd->pw_shell = nul; 355 } 356 357 358 static struct passwd pwd; 359 static char *pwd_storage; 360 static size_t pwd_storage_size; 361 362 363 static struct passwd * 364 getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), 365 union key key) 366 { 367 int rv; 368 struct passwd *res; 369 370 if (pwd_storage == NULL) { 371 pwd_storage = malloc(PWD_STORAGE_INITIAL); 372 if (pwd_storage == NULL) 373 return (NULL); 374 pwd_storage_size = PWD_STORAGE_INITIAL; 375 } 376 do { 377 rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res); 378 if (res == NULL && rv == ERANGE) { 379 free(pwd_storage); 380 if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) { 381 pwd_storage = NULL; 382 return (NULL); 383 } 384 pwd_storage_size <<= 1; 385 pwd_storage = malloc(pwd_storage_size); 386 if (pwd_storage == NULL) 387 return (NULL); 388 } 389 } while (res == NULL && rv == ERANGE); 390 return (res); 391 } 392 393 394 static int 395 wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer, 396 size_t bufsize, struct passwd **res) 397 { 398 return (getpwnam_r(key.name, pwd, buffer, bufsize, res)); 399 } 400 401 402 static int 403 wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer, 404 size_t bufsize, struct passwd **res) 405 { 406 return (getpwuid_r(key.uid, pwd, buffer, bufsize, res)); 407 } 408 409 410 static int 411 wrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer, 412 size_t bufsize, struct passwd **res) 413 { 414 return (getpwent_r(pwd, buffer, bufsize, res)); 415 } 416 417 418 struct passwd * 419 getpwnam(const char *name) 420 { 421 union key key; 422 423 key.name = name; 424 return (getpw(wrap_getpwnam_r, key)); 425 } 426 427 428 struct passwd * 429 getpwuid(uid_t uid) 430 { 431 union key key; 432 433 key.uid = uid; 434 return (getpw(wrap_getpwuid_r, key)); 435 } 436 437 438 struct passwd * 439 getpwent(void) 440 { 441 union key key; 442 443 key.uid = 0; /* not used */ 444 return (getpw(wrap_getpwent_r, key)); 445 } 446 447 448 /* 449 * files backend 450 */ 451 static DB * 452 pwdbopen(int *version) 453 { 454 DB *res; 455 DBT key, entry; 456 int rv; 457 458 if (geteuid() != 0 || 459 (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL) 460 res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); 461 if (res == NULL) 462 return (NULL); 463 key.data = _PWD_VERSION_KEY; 464 key.size = strlen(_PWD_VERSION_KEY); 465 rv = res->get(res, &key, &entry, 0); 466 if (rv == 0) 467 *version = *(unsigned char *)entry.data; 468 else 469 *version = 3; 470 if (*version < 3 || 471 *version >= sizeof(pwdb_versions)/sizeof(pwdb_versions[0])) { 472 syslog(LOG_CRIT, "Unsupported password database version %d", 473 *version); 474 res->close(res); 475 res = NULL; 476 } 477 return (res); 478 } 479 480 481 static void 482 files_endstate(void *p) 483 { 484 DB *db; 485 486 if (p == NULL) 487 return; 488 db = ((struct files_state *)p)->db; 489 if (db != NULL) 490 db->close(db); 491 free(p); 492 } 493 494 495 static int 496 files_setpwent(void *retval, void *mdata, va_list ap) 497 { 498 struct files_state *st; 499 int rv, stayopen; 500 501 rv = files_getstate(&st); 502 if (rv != 0) 503 return (NS_UNAVAIL); 504 switch ((enum constants)mdata) { 505 case SETPWENT: 506 stayopen = va_arg(ap, int); 507 st->keynum = 0; 508 if (stayopen) 509 st->db = pwdbopen(&st->version); 510 st->stayopen = stayopen; 511 break; 512 case ENDPWENT: 513 if (st->db != NULL) { 514 (void)st->db->close(st->db); 515 st->db = NULL; 516 } 517 break; 518 default: 519 break; 520 } 521 return (NS_UNAVAIL); 522 } 523 524 525 static int 526 files_passwd(void *retval, void *mdata, va_list ap) 527 { 528 char keybuf[MAXLOGNAME + 1]; 529 DBT key, entry; 530 struct files_state *st; 531 enum nss_lookup_type how; 532 const char *name; 533 struct passwd *pwd; 534 char *buffer; 535 size_t bufsize, namesize; 536 uid_t uid; 537 uint32_t store; 538 int rv, stayopen, *errnop; 539 540 name = NULL; 541 uid = (uid_t)-1; 542 how = (enum nss_lookup_type)mdata; 543 switch (how) { 544 case nss_lt_name: 545 name = va_arg(ap, const char *); 546 keybuf[0] = _PW_KEYBYNAME; 547 break; 548 case nss_lt_id: 549 uid = va_arg(ap, uid_t); 550 keybuf[0] = _PW_KEYBYUID; 551 break; 552 case nss_lt_all: 553 keybuf[0] = _PW_KEYBYNUM; 554 break; 555 default: 556 rv = NS_NOTFOUND; 557 goto fin; 558 } 559 pwd = va_arg(ap, struct passwd *); 560 buffer = va_arg(ap, char *); 561 bufsize = va_arg(ap, size_t); 562 errnop = va_arg(ap, int *); 563 *errnop = files_getstate(&st); 564 if (*errnop != 0) 565 return (NS_UNAVAIL); 566 if (how == nss_lt_all && st->keynum < 0) { 567 rv = NS_NOTFOUND; 568 goto fin; 569 } 570 if (st->db == NULL && 571 (st->db = pwdbopen(&st->version)) == NULL) { 572 *errnop = errno; 573 rv = NS_UNAVAIL; 574 goto fin; 575 } 576 if (how == nss_lt_all) 577 stayopen = 1; 578 else 579 stayopen = st->stayopen; 580 key.data = keybuf; 581 do { 582 switch (how) { 583 case nss_lt_name: 584 /* MAXLOGNAME includes NUL byte, but we do not 585 * include the NUL byte in the key. 586 */ 587 namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1); 588 if (namesize >= sizeof(keybuf)-1) { 589 *errnop = EINVAL; 590 rv = NS_NOTFOUND; 591 goto fin; 592 } 593 key.size = namesize + 1; 594 break; 595 case nss_lt_id: 596 if (st->version < _PWD_CURRENT_VERSION) { 597 memcpy(&keybuf[1], &uid, sizeof(uid)); 598 key.size = sizeof(uid) + 1; 599 } else { 600 store = htonl(uid); 601 memcpy(&keybuf[1], &store, sizeof(store)); 602 key.size = sizeof(store) + 1; 603 } 604 break; 605 case nss_lt_all: 606 st->keynum++; 607 if (st->version < _PWD_CURRENT_VERSION) { 608 memcpy(&keybuf[1], &st->keynum, 609 sizeof(st->keynum)); 610 key.size = sizeof(st->keynum) + 1; 611 } else { 612 store = htonl(st->keynum); 613 memcpy(&keybuf[1], &store, sizeof(store)); 614 key.size = sizeof(store) + 1; 615 } 616 break; 617 } 618 keybuf[0] = _PW_VERSIONED(keybuf[0], st->version); 619 rv = st->db->get(st->db, &key, &entry, 0); 620 if (rv < 0 || rv > 1) { /* should never return > 1 */ 621 *errnop = errno; 622 rv = NS_UNAVAIL; 623 goto fin; 624 } else if (rv == 1) { 625 if (how == nss_lt_all) 626 st->keynum = -1; 627 rv = NS_NOTFOUND; 628 goto fin; 629 } 630 rv = pwdb_versions[st->version].match(entry.data, entry.size, 631 how, name, uid); 632 if (rv != NS_SUCCESS) 633 continue; 634 if (entry.size > bufsize) { 635 *errnop = ERANGE; 636 rv = NS_RETURN; 637 break; 638 } 639 memcpy(buffer, entry.data, entry.size); 640 rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, 641 errnop); 642 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 643 fin: 644 if (!stayopen && st->db != NULL) { 645 (void)st->db->close(st->db); 646 st->db = NULL; 647 } 648 if (rv == NS_SUCCESS) { 649 pwd->pw_fields &= ~_PWF_SOURCE; 650 pwd->pw_fields |= _PWF_FILES; 651 if (retval != NULL) 652 *(struct passwd **)retval = pwd; 653 } 654 return (rv); 655 } 656 657 658 static int 659 pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how, 660 const char *name, uid_t uid) 661 { 662 const char *p, *eom; 663 uid_t uid2; 664 665 eom = &entry[entrysize]; 666 for (p = entry; p < eom; p++) 667 if (*p == '\0') 668 break; 669 if (*p != '\0') 670 return (NS_NOTFOUND); 671 if (how == nss_lt_all) 672 return (NS_SUCCESS); 673 if (how == nss_lt_name) 674 return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); 675 for (p++; p < eom; p++) 676 if (*p == '\0') 677 break; 678 if (*p != '\0' || (++p) + sizeof(uid) >= eom) 679 return (NS_NOTFOUND); 680 memcpy(&uid2, p, sizeof(uid2)); 681 return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND); 682 } 683 684 685 static int 686 pwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd, 687 int *errnop) 688 { 689 char *p, *eom; 690 int32_t pw_change, pw_expire; 691 692 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ 693 p = buffer; 694 eom = &buffer[bufsize]; 695 #define STRING(field) do { \ 696 (field) = p; \ 697 while (p < eom && *p != '\0') \ 698 p++; \ 699 if (p >= eom) \ 700 return (NS_NOTFOUND); \ 701 p++; \ 702 } while (0) 703 #define SCALAR(field) do { \ 704 if (p + sizeof(field) > eom) \ 705 return (NS_NOTFOUND); \ 706 memcpy(&(field), p, sizeof(field)); \ 707 p += sizeof(field); \ 708 } while (0) 709 STRING(pwd->pw_name); 710 STRING(pwd->pw_passwd); 711 SCALAR(pwd->pw_uid); 712 SCALAR(pwd->pw_gid); 713 SCALAR(pw_change); 714 STRING(pwd->pw_class); 715 STRING(pwd->pw_gecos); 716 STRING(pwd->pw_dir); 717 STRING(pwd->pw_shell); 718 SCALAR(pw_expire); 719 SCALAR(pwd->pw_fields); 720 #undef STRING 721 #undef SCALAR 722 pwd->pw_change = pw_change; 723 pwd->pw_expire = pw_expire; 724 return (NS_SUCCESS); 725 } 726 727 728 static int 729 pwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how, 730 const char *name, uid_t uid) 731 { 732 const char *p, *eom; 733 uint32_t uid2; 734 735 eom = &entry[entrysize]; 736 for (p = entry; p < eom; p++) 737 if (*p == '\0') 738 break; 739 if (*p != '\0') 740 return (NS_NOTFOUND); 741 if (how == nss_lt_all) 742 return (NS_SUCCESS); 743 if (how == nss_lt_name) 744 return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); 745 for (p++; p < eom; p++) 746 if (*p == '\0') 747 break; 748 if (*p != '\0' || (++p) + sizeof(uid) >= eom) 749 return (NS_NOTFOUND); 750 memcpy(&uid2, p, sizeof(uid2)); 751 uid2 = ntohl(uid2); 752 return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND); 753 } 754 755 756 static int 757 pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd, 758 int *errnop) 759 { 760 char *p, *eom; 761 uint32_t n; 762 763 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ 764 p = buffer; 765 eom = &buffer[bufsize]; 766 #define STRING(field) do { \ 767 (field) = p; \ 768 while (p < eom && *p != '\0') \ 769 p++; \ 770 if (p >= eom) \ 771 return (NS_NOTFOUND); \ 772 p++; \ 773 } while (0) 774 #define SCALAR(field) do { \ 775 if (p + sizeof(n) > eom) \ 776 return (NS_NOTFOUND); \ 777 memcpy(&n, p, sizeof(n)); \ 778 (field) = ntohl(n); \ 779 p += sizeof(n); \ 780 } while (0) 781 STRING(pwd->pw_name); 782 STRING(pwd->pw_passwd); 783 SCALAR(pwd->pw_uid); 784 SCALAR(pwd->pw_gid); 785 SCALAR(pwd->pw_change); 786 STRING(pwd->pw_class); 787 STRING(pwd->pw_gecos); 788 STRING(pwd->pw_dir); 789 STRING(pwd->pw_shell); 790 SCALAR(pwd->pw_expire); 791 SCALAR(pwd->pw_fields); 792 #undef STRING 793 #undef SCALAR 794 return (NS_SUCCESS); 795 } 796 797 798 #ifdef HESIOD 799 /* 800 * dns backend 801 */ 802 static void 803 dns_endstate(void *p) 804 { 805 free(p); 806 } 807 808 809 static int 810 dns_setpwent(void *retval, void *mdata, va_list ap) 811 { 812 struct dns_state *st; 813 int rv; 814 815 rv = dns_getstate(&st); 816 if (rv != 0) 817 return (NS_UNAVAIL); 818 st->counter = 0; 819 return (NS_UNAVAIL); 820 } 821 822 823 static int 824 dns_passwd(void *retval, void *mdata, va_list ap) 825 { 826 char buf[HESIOD_NAME_MAX]; 827 struct dns_state *st; 828 struct passwd *pwd; 829 const char *name, *label; 830 void *ctx; 831 char *buffer, **hes; 832 size_t bufsize, linesize; 833 uid_t uid; 834 enum nss_lookup_type how; 835 int rv, *errnop; 836 837 ctx = NULL; 838 hes = NULL; 839 name = NULL; 840 uid = (uid_t)-1; 841 how = (enum nss_lookup_type)mdata; 842 switch (how) { 843 case nss_lt_name: 844 name = va_arg(ap, const char *); 845 break; 846 case nss_lt_id: 847 uid = va_arg(ap, uid_t); 848 break; 849 case nss_lt_all: 850 break; 851 } 852 pwd = va_arg(ap, struct passwd *); 853 buffer = va_arg(ap, char *); 854 bufsize = va_arg(ap, size_t); 855 errnop = va_arg(ap, int *); 856 *errnop = dns_getstate(&st); 857 if (*errnop != 0) 858 return (NS_UNAVAIL); 859 if (hesiod_init(&ctx) != 0) { 860 *errnop = errno; 861 rv = NS_UNAVAIL; 862 goto fin; 863 } 864 do { 865 rv = NS_NOTFOUND; 866 switch (how) { 867 case nss_lt_name: 868 label = name; 869 break; 870 case nss_lt_id: 871 if (snprintf(buf, sizeof(buf), "%lu", 872 (unsigned long)uid) >= sizeof(buf)) 873 goto fin; 874 label = buf; 875 break; 876 case nss_lt_all: 877 if (st->counter < 0) 878 goto fin; 879 if (snprintf(buf, sizeof(buf), "passwd-%ld", 880 st->counter++) >= sizeof(buf)) 881 goto fin; 882 label = buf; 883 break; 884 } 885 hes = hesiod_resolve(ctx, label, 886 how == nss_lt_id ? "uid" : "passwd"); 887 if (hes == NULL) { 888 if (how == nss_lt_all) 889 st->counter = -1; 890 if (errno != ENOENT) 891 *errnop = errno; 892 goto fin; 893 } 894 rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid); 895 if (rv != NS_SUCCESS) { 896 hesiod_free_list(ctx, hes); 897 hes = NULL; 898 continue; 899 } 900 linesize = strlcpy(buffer, hes[0], bufsize); 901 if (linesize >= bufsize) { 902 *errnop = ERANGE; 903 rv = NS_RETURN; 904 continue; 905 } 906 hesiod_free_list(ctx, hes); 907 hes = NULL; 908 rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop); 909 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 910 fin: 911 if (hes != NULL) 912 hesiod_free_list(ctx, hes); 913 if (ctx != NULL) 914 hesiod_end(ctx); 915 if (rv == NS_SUCCESS) { 916 pwd->pw_fields &= ~_PWF_SOURCE; 917 pwd->pw_fields |= _PWF_HESIOD; 918 if (retval != NULL) 919 *(struct passwd **)retval = pwd; 920 } 921 return (rv); 922 } 923 #endif /* HESIOD */ 924 925 926 #ifdef YP 927 /* 928 * nis backend 929 */ 930 static void 931 nis_endstate(void *p) 932 { 933 free(((struct nis_state *)p)->key); 934 free(p); 935 } 936 937 /* 938 * Test for the presence of special FreeBSD-specific master.passwd.by* 939 * maps. We do this using yp_order(). If it fails, then either the server 940 * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by 941 * the server (Sun NIS+ servers in YP compat mode behave this way). If 942 * the master.passwd.by* maps don't exist, then let the lookup routine try 943 * the regular passwd.by* maps instead. If the lookup routine fails, it 944 * can return an error as needed. 945 */ 946 static int 947 nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize, 948 int *master) 949 { 950 int rv, order; 951 952 *master = 0; 953 if (geteuid() == 0) { 954 if (snprintf(buffer, bufsize, "master.passwd.by%s", 955 (how == nss_lt_id) ? "uid" : "name") >= bufsize) 956 return (NS_UNAVAIL); 957 rv = yp_order(domain, buffer, &order); 958 if (rv == 0) { 959 *master = 1; 960 return (NS_SUCCESS); 961 } 962 } 963 964 if (snprintf(buffer, bufsize, "passwd.by%s", 965 (how == nss_lt_id) ? "uid" : "name") >= bufsize) 966 return (NS_UNAVAIL); 967 968 return (NS_SUCCESS); 969 } 970 971 972 static int 973 nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize) 974 { 975 int rv; 976 char *result, *p, *q, *eor; 977 int resultlen; 978 979 result = NULL; 980 rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name), 981 &result, &resultlen); 982 if (rv != 0) 983 rv = 1; 984 else { 985 eor = &result[resultlen]; 986 p = memchr(result, ':', eor - result); 987 if (p != NULL && ++p < eor && 988 (q = memchr(p, ':', eor - p)) != NULL) { 989 if (q - p >= bufsize) 990 rv = -1; 991 else { 992 memcpy(buffer, p, q - p); 993 buffer[q - p] ='\0'; 994 } 995 } else 996 rv = 1; 997 } 998 free(result); 999 return (rv); 1000 } 1001 1002 1003 static int 1004 nis_setpwent(void *retval, void *mdata, va_list ap) 1005 { 1006 struct nis_state *st; 1007 int rv; 1008 1009 rv = nis_getstate(&st); 1010 if (rv != 0) 1011 return (NS_UNAVAIL); 1012 st->done = 0; 1013 free(st->key); 1014 st->key = NULL; 1015 return (NS_UNAVAIL); 1016 } 1017 1018 1019 static int 1020 nis_passwd(void *retval, void *mdata, va_list ap) 1021 { 1022 char map[YPMAXMAP]; 1023 struct nis_state *st; 1024 struct passwd *pwd; 1025 const char *name; 1026 char *buffer, *key, *result; 1027 size_t bufsize; 1028 uid_t uid; 1029 enum nss_lookup_type how; 1030 int *errnop, keylen, resultlen, rv, master; 1031 1032 name = NULL; 1033 uid = (uid_t)-1; 1034 how = (enum nss_lookup_type)mdata; 1035 switch (how) { 1036 case nss_lt_name: 1037 name = va_arg(ap, const char *); 1038 break; 1039 case nss_lt_id: 1040 uid = va_arg(ap, uid_t); 1041 break; 1042 case nss_lt_all: 1043 break; 1044 } 1045 pwd = va_arg(ap, struct passwd *); 1046 buffer = va_arg(ap, char *); 1047 bufsize = va_arg(ap, size_t); 1048 errnop = va_arg(ap, int *); 1049 *errnop = nis_getstate(&st); 1050 if (*errnop != 0) 1051 return (NS_UNAVAIL); 1052 if (st->domain[0] == '\0') { 1053 if (getdomainname(st->domain, sizeof(st->domain)) != 0) { 1054 *errnop = errno; 1055 return (NS_UNAVAIL); 1056 } 1057 } 1058 rv = nis_map(st->domain, how, map, sizeof(map), &master); 1059 if (rv != NS_SUCCESS) 1060 return (rv); 1061 result = NULL; 1062 do { 1063 rv = NS_NOTFOUND; 1064 switch (how) { 1065 case nss_lt_name: 1066 if (strlcpy(buffer, name, bufsize) >= bufsize) 1067 goto erange; 1068 break; 1069 case nss_lt_id: 1070 if (snprintf(buffer, bufsize, "%lu", 1071 (unsigned long)uid) >= bufsize) 1072 goto erange; 1073 break; 1074 case nss_lt_all: 1075 if (st->done) 1076 goto fin; 1077 break; 1078 } 1079 result = NULL; 1080 if (how == nss_lt_all) { 1081 if (st->key == NULL) 1082 rv = yp_first(st->domain, map, &st->key, 1083 &st->keylen, &result, &resultlen); 1084 else { 1085 key = st->key; 1086 keylen = st->keylen; 1087 st->key = NULL; 1088 rv = yp_next(st->domain, map, key, keylen, 1089 &st->key, &st->keylen, &result, 1090 &resultlen); 1091 free(key); 1092 } 1093 if (rv != 0) { 1094 free(result); 1095 free(st->key); 1096 st->key = NULL; 1097 if (rv == YPERR_NOMORE) 1098 st->done = 1; 1099 else 1100 rv = NS_UNAVAIL; 1101 goto fin; 1102 } 1103 } else { 1104 rv = yp_match(st->domain, map, buffer, strlen(buffer), 1105 &result, &resultlen); 1106 if (rv == YPERR_KEY) { 1107 rv = NS_NOTFOUND; 1108 continue; 1109 } else if (rv != 0) { 1110 free(result); 1111 rv = NS_UNAVAIL; 1112 continue; 1113 } 1114 } 1115 if (resultlen >= bufsize) 1116 goto erange; 1117 memcpy(buffer, result, resultlen); 1118 buffer[resultlen] = '\0'; 1119 free(result); 1120 rv = __pw_match_entry(buffer, resultlen, how, name, uid); 1121 if (rv == NS_SUCCESS) 1122 rv = __pw_parse_entry(buffer, resultlen, pwd, master, 1123 errnop); 1124 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 1125 fin: 1126 if (rv == NS_SUCCESS) { 1127 if (strstr(pwd->pw_passwd, "##") != NULL) { 1128 rv = nis_adjunct(st->domain, pwd->pw_name, 1129 &buffer[resultlen+1], bufsize-resultlen-1); 1130 if (rv < 0) 1131 goto erange; 1132 else if (rv == 0) 1133 pwd->pw_passwd = &buffer[resultlen+1]; 1134 } 1135 pwd->pw_fields &= ~_PWF_SOURCE; 1136 pwd->pw_fields |= _PWF_NIS; 1137 if (retval != NULL) 1138 *(struct passwd **)retval = pwd; 1139 rv = NS_SUCCESS; 1140 } 1141 return (rv); 1142 erange: 1143 *errnop = ERANGE; 1144 return (NS_RETURN); 1145 } 1146 #endif /* YP */ 1147 1148 1149 /* 1150 * compat backend 1151 */ 1152 static void 1153 compat_clear_template(struct passwd *template) 1154 { 1155 1156 free(template->pw_passwd); 1157 free(template->pw_gecos); 1158 free(template->pw_dir); 1159 free(template->pw_shell); 1160 memset(template, 0, sizeof(*template)); 1161 } 1162 1163 1164 static int 1165 compat_set_template(struct passwd *src, struct passwd *template) 1166 { 1167 1168 compat_clear_template(template); 1169 #ifdef PW_OVERRIDE_PASSWD 1170 if ((src->pw_fields & _PWF_PASSWD) && 1171 (template->pw_passwd = strdup(src->pw_passwd)) == NULL) 1172 goto enomem; 1173 #endif 1174 if (src->pw_fields & _PWF_UID) 1175 template->pw_uid = src->pw_uid; 1176 if (src->pw_fields & _PWF_GID) 1177 template->pw_gid = src->pw_gid; 1178 if ((src->pw_fields & _PWF_GECOS) && 1179 (template->pw_gecos = strdup(src->pw_gecos)) == NULL) 1180 goto enomem; 1181 if ((src->pw_fields & _PWF_DIR) && 1182 (template->pw_dir = strdup(src->pw_dir)) == NULL) 1183 goto enomem; 1184 if ((src->pw_fields & _PWF_SHELL) && 1185 (template->pw_shell = strdup(src->pw_shell)) == NULL) 1186 goto enomem; 1187 template->pw_fields = src->pw_fields; 1188 return (0); 1189 enomem: 1190 syslog(LOG_ERR, "getpwent memory allocation failure"); 1191 return (-1); 1192 } 1193 1194 1195 static int 1196 compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer, 1197 size_t bufsize) 1198 { 1199 struct passwd hold; 1200 char *copy, *p, *q, *eob; 1201 size_t n; 1202 1203 /* We cannot know the layout of the password fields in `buffer', 1204 * so we have to copy everything. 1205 */ 1206 if (template->pw_fields == 0) /* nothing to fill-in */ 1207 return (0); 1208 n = 0; 1209 n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0; 1210 n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0; 1211 n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0; 1212 n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0; 1213 n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0; 1214 n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0; 1215 copy = malloc(n); 1216 if (copy == NULL) { 1217 syslog(LOG_ERR, "getpwent memory allocation failure"); 1218 return (ENOMEM); 1219 } 1220 p = copy; 1221 eob = ©[n]; 1222 #define COPY(field) do { \ 1223 if (pwd->field == NULL) \ 1224 hold.field = NULL; \ 1225 else { \ 1226 hold.field = p; \ 1227 p += strlcpy(p, pwd->field, eob-p) + 1; \ 1228 } \ 1229 } while (0) 1230 COPY(pw_name); 1231 COPY(pw_passwd); 1232 COPY(pw_class); 1233 COPY(pw_gecos); 1234 COPY(pw_dir); 1235 COPY(pw_shell); 1236 #undef COPY 1237 p = buffer; 1238 eob = &buffer[bufsize]; 1239 #define COPY(field, flag) do { \ 1240 q = (template->pw_fields & flag) ? template->field : hold.field; \ 1241 if (q == NULL) \ 1242 pwd->field = NULL; \ 1243 else { \ 1244 pwd->field = p; \ 1245 if ((n = strlcpy(p, q, eob-p)) >= eob-p) { \ 1246 free(copy); \ 1247 return (ERANGE); \ 1248 } \ 1249 p += n + 1; \ 1250 } \ 1251 } while (0) 1252 COPY(pw_name, 0); 1253 #ifdef PW_OVERRIDE_PASSWD 1254 COPY(pw_passwd, _PWF_PASSWD); 1255 #else 1256 COPY(pw_passwd, 0); 1257 #endif 1258 COPY(pw_class, 0); 1259 COPY(pw_gecos, _PWF_GECOS); 1260 COPY(pw_dir, _PWF_DIR); 1261 COPY(pw_shell, _PWF_SHELL); 1262 #undef COPY 1263 #define COPY(field, flag) do { \ 1264 if (template->pw_fields & flag) \ 1265 pwd->field = template->field; \ 1266 } while (0) 1267 COPY(pw_uid, _PWF_UID); 1268 COPY(pw_gid, _PWF_GID); 1269 #undef COPY 1270 free(copy); 1271 return (0); 1272 } 1273 1274 1275 static int 1276 compat_exclude(const char *name, DB **db) 1277 { 1278 DBT key, data; 1279 1280 if (*db == NULL && 1281 (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) 1282 return (errno); 1283 key.size = strlen(name); 1284 key.data = (char *)name; 1285 data.size = 0; 1286 data.data = NULL; 1287 1288 if ((*db)->put(*db, &key, &data, 0) == -1) 1289 return (errno); 1290 return (0); 1291 } 1292 1293 1294 static int 1295 compat_is_excluded(const char *name, DB *db) 1296 { 1297 DBT key, data; 1298 1299 if (db == NULL) 1300 return (0); 1301 key.size = strlen(name); 1302 key.data = (char *)name; 1303 return (db->get(db, &key, &data, 0) == 0); 1304 } 1305 1306 1307 static int 1308 compat_redispatch(struct compat_state *st, enum nss_lookup_type how, 1309 enum nss_lookup_type lookup_how, const char *name, const char *lookup_name, 1310 uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) 1311 { 1312 static const ns_src compatsrc[] = { 1313 #ifdef YP 1314 { NSSRC_NIS, NS_SUCCESS }, 1315 #endif 1316 { NULL, 0 } 1317 }; 1318 ns_dtab dtab[] = { 1319 #ifdef YP 1320 { NSSRC_NIS, nis_passwd, NULL }, 1321 #endif 1322 #ifdef HESIOD 1323 { NSSRC_DNS, dns_passwd, NULL }, 1324 #endif 1325 { NULL, NULL, NULL } 1326 }; 1327 void *discard; 1328 int rv, e, i; 1329 1330 for (i = 0; i < sizeof(dtab)/sizeof(dtab[0]) - 1; i++) 1331 dtab[i].mdata = (void *)lookup_how; 1332 more: 1333 pwd_init(pwd); 1334 switch (lookup_how) { 1335 case nss_lt_all: 1336 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1337 "getpwent_r", compatsrc, pwd, buffer, bufsize, 1338 errnop); 1339 break; 1340 case nss_lt_id: 1341 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1342 "getpwuid_r", compatsrc, uid, pwd, buffer, 1343 bufsize, errnop); 1344 break; 1345 case nss_lt_name: 1346 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1347 "getpwnam_r", compatsrc, lookup_name, pwd, buffer, 1348 bufsize, errnop); 1349 break; 1350 default: 1351 return (NS_UNAVAIL); 1352 } 1353 if (rv != NS_SUCCESS) 1354 return (rv); 1355 if (compat_is_excluded(pwd->pw_name, st->exclude)) { 1356 if (how == nss_lt_all) 1357 goto more; 1358 return (NS_NOTFOUND); 1359 } 1360 e = compat_use_template(pwd, &st->template, buffer, bufsize); 1361 if (e != 0) { 1362 *errnop = e; 1363 if (e == ERANGE) 1364 return (NS_RETURN); 1365 else 1366 return (NS_UNAVAIL); 1367 } 1368 switch (how) { 1369 case nss_lt_name: 1370 if (strcmp(name, pwd->pw_name) != 0) 1371 return (NS_NOTFOUND); 1372 break; 1373 case nss_lt_id: 1374 if (uid != pwd->pw_uid) 1375 return (NS_NOTFOUND); 1376 break; 1377 default: 1378 break; 1379 } 1380 return (NS_SUCCESS); 1381 } 1382 1383 1384 static void 1385 compat_endstate(void *p) 1386 { 1387 struct compat_state *st; 1388 1389 if (p == NULL) 1390 return; 1391 st = (struct compat_state *)p; 1392 if (st->db != NULL) 1393 st->db->close(st->db); 1394 if (st->exclude != NULL) 1395 st->exclude->close(st->exclude); 1396 compat_clear_template(&st->template); 1397 free(p); 1398 } 1399 1400 1401 static int 1402 compat_setpwent(void *retval, void *mdata, va_list ap) 1403 { 1404 static const ns_src compatsrc[] = { 1405 #ifdef YP 1406 { NSSRC_NIS, NS_SUCCESS }, 1407 #endif 1408 { NULL, 0 } 1409 }; 1410 ns_dtab dtab[] = { 1411 #ifdef YP 1412 { NSSRC_NIS, nis_setpwent, NULL }, 1413 #endif 1414 #ifdef HESIOD 1415 { NSSRC_DNS, dns_setpwent, NULL }, 1416 #endif 1417 { NULL, NULL, NULL } 1418 }; 1419 struct compat_state *st; 1420 int rv, stayopen; 1421 1422 #define set_setent(x, y) do { \ 1423 int i; \ 1424 \ 1425 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 1426 x[i].mdata = (void *)y; \ 1427 } while (0) 1428 1429 rv = compat_getstate(&st); 1430 if (rv != 0) 1431 return (NS_UNAVAIL); 1432 switch ((enum constants)mdata) { 1433 case SETPWENT: 1434 stayopen = va_arg(ap, int); 1435 st->keynum = 0; 1436 if (stayopen) 1437 st->db = pwdbopen(&st->version); 1438 st->stayopen = stayopen; 1439 set_setent(dtab, mdata); 1440 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent", 1441 compatsrc, 0); 1442 break; 1443 case ENDPWENT: 1444 if (st->db != NULL) { 1445 (void)st->db->close(st->db); 1446 st->db = NULL; 1447 } 1448 set_setent(dtab, mdata); 1449 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent", 1450 compatsrc, 0); 1451 break; 1452 default: 1453 break; 1454 } 1455 return (NS_UNAVAIL); 1456 #undef set_setent 1457 } 1458 1459 1460 static int 1461 compat_passwd(void *retval, void *mdata, va_list ap) 1462 { 1463 char keybuf[MAXLOGNAME + 1]; 1464 DBT key, entry; 1465 struct compat_state *st; 1466 enum nss_lookup_type how; 1467 const char *name; 1468 struct passwd *pwd; 1469 char *buffer, *pw_name; 1470 char *host, *user, *domain; 1471 size_t bufsize; 1472 uid_t uid; 1473 uint32_t store; 1474 int rv, from_compat, stayopen, *errnop; 1475 1476 from_compat = 0; 1477 name = NULL; 1478 uid = (uid_t)-1; 1479 how = (enum nss_lookup_type)mdata; 1480 switch (how) { 1481 case nss_lt_name: 1482 name = va_arg(ap, const char *); 1483 break; 1484 case nss_lt_id: 1485 uid = va_arg(ap, uid_t); 1486 break; 1487 case nss_lt_all: 1488 break; 1489 default: 1490 rv = NS_NOTFOUND; 1491 goto fin; 1492 } 1493 pwd = va_arg(ap, struct passwd *); 1494 buffer = va_arg(ap, char *); 1495 bufsize = va_arg(ap, size_t); 1496 errnop = va_arg(ap, int *); 1497 *errnop = compat_getstate(&st); 1498 if (*errnop != 0) 1499 return (NS_UNAVAIL); 1500 if (how == nss_lt_all && st->keynum < 0) { 1501 rv = NS_NOTFOUND; 1502 goto fin; 1503 } 1504 if (st->db == NULL && 1505 (st->db = pwdbopen(&st->version)) == NULL) { 1506 *errnop = errno; 1507 rv = NS_UNAVAIL; 1508 goto fin; 1509 } 1510 if (how == nss_lt_all) { 1511 if (st->keynum < 0) { 1512 rv = NS_NOTFOUND; 1513 goto fin; 1514 } 1515 stayopen = 1; 1516 } else { 1517 st->keynum = 0; 1518 stayopen = st->stayopen; 1519 } 1520 docompat: 1521 rv = NS_NOTFOUND; 1522 switch (st->compat) { 1523 case COMPAT_MODE_ALL: 1524 rv = compat_redispatch(st, how, how, name, name, uid, pwd, 1525 buffer, bufsize, errnop); 1526 if (rv != NS_SUCCESS) 1527 st->compat = COMPAT_MODE_OFF; 1528 break; 1529 case COMPAT_MODE_NETGROUP: 1530 /* XXX getnetgrent is not thread-safe. */ 1531 do { 1532 rv = getnetgrent(&host, &user, &domain); 1533 if (rv == 0) { 1534 endnetgrent(); 1535 st->compat = COMPAT_MODE_OFF; 1536 rv = NS_NOTFOUND; 1537 continue; 1538 } else if (user == NULL || user[0] == '\0') 1539 continue; 1540 rv = compat_redispatch(st, how, nss_lt_name, name, 1541 user, uid, pwd, buffer, bufsize, errnop); 1542 } while (st->compat == COMPAT_MODE_NETGROUP && 1543 !(rv & NS_TERMINATE)); 1544 break; 1545 case COMPAT_MODE_NAME: 1546 rv = compat_redispatch(st, how, nss_lt_name, name, st->name, 1547 uid, pwd, buffer, bufsize, errnop); 1548 free(st->name); 1549 st->name = NULL; 1550 st->compat = COMPAT_MODE_OFF; 1551 break; 1552 default: 1553 break; 1554 } 1555 if (rv & NS_TERMINATE) { 1556 from_compat = 1; 1557 goto fin; 1558 } 1559 key.data = keybuf; 1560 rv = NS_NOTFOUND; 1561 while (st->keynum >= 0) { 1562 st->keynum++; 1563 if (st->version < _PWD_CURRENT_VERSION) { 1564 memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); 1565 key.size = sizeof(st->keynum) + 1; 1566 } else { 1567 store = htonl(st->keynum); 1568 memcpy(&keybuf[1], &store, sizeof(store)); 1569 key.size = sizeof(store) + 1; 1570 } 1571 keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version); 1572 rv = st->db->get(st->db, &key, &entry, 0); 1573 if (rv < 0 || rv > 1) { /* should never return > 1 */ 1574 *errnop = errno; 1575 rv = NS_UNAVAIL; 1576 goto fin; 1577 } else if (rv == 1) { 1578 st->keynum = -1; 1579 rv = NS_NOTFOUND; 1580 goto fin; 1581 } 1582 pw_name = (char *)entry.data; 1583 switch (pw_name[0]) { 1584 case '+': 1585 switch (pw_name[1]) { 1586 case '\0': 1587 st->compat = COMPAT_MODE_ALL; 1588 break; 1589 case '@': 1590 setnetgrent(&pw_name[2]); 1591 st->compat = COMPAT_MODE_NETGROUP; 1592 break; 1593 default: 1594 st->name = strdup(&pw_name[1]); 1595 if (st->name == NULL) { 1596 syslog(LOG_ERR, 1597 "getpwent memory allocation failure"); 1598 *errnop = ENOMEM; 1599 rv = NS_UNAVAIL; 1600 break; 1601 } 1602 st->compat = COMPAT_MODE_NAME; 1603 } 1604 if (entry.size > bufsize) { 1605 *errnop = ERANGE; 1606 rv = NS_RETURN; 1607 goto fin; 1608 } 1609 memcpy(buffer, entry.data, entry.size); 1610 rv = pwdb_versions[st->version].parse(buffer, 1611 entry.size, pwd, errnop); 1612 if (rv != NS_SUCCESS) 1613 ; 1614 else if (compat_set_template(pwd, &st->template) < 0) { 1615 *errnop = ENOMEM; 1616 rv = NS_UNAVAIL; 1617 goto fin; 1618 } 1619 goto docompat; 1620 case '-': 1621 switch (pw_name[1]) { 1622 case '\0': 1623 /* XXX Maybe syslog warning */ 1624 continue; 1625 case '@': 1626 setnetgrent(&pw_name[2]); 1627 while (getnetgrent(&host, &user, &domain) != 1628 0) { 1629 if (user != NULL && user[0] != '\0') 1630 compat_exclude(user, 1631 &st->exclude); 1632 } 1633 endnetgrent(); 1634 continue; 1635 default: 1636 compat_exclude(&pw_name[1], &st->exclude); 1637 continue; 1638 } 1639 break; 1640 default: 1641 break; 1642 } 1643 if (compat_is_excluded((char *)entry.data, st->exclude)) 1644 continue; 1645 rv = pwdb_versions[st->version].match(entry.data, entry.size, 1646 how, name, uid); 1647 if (rv == NS_RETURN) 1648 break; 1649 else if (rv != NS_SUCCESS) 1650 continue; 1651 if (entry.size > bufsize) { 1652 *errnop = ERANGE; 1653 rv = NS_RETURN; 1654 break; 1655 } 1656 memcpy(buffer, entry.data, entry.size); 1657 rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, 1658 errnop); 1659 if (rv & NS_TERMINATE) 1660 break; 1661 } 1662 fin: 1663 if (!stayopen && st->db != NULL) { 1664 (void)st->db->close(st->db); 1665 st->db = NULL; 1666 } 1667 if (rv == NS_SUCCESS) { 1668 if (!from_compat) { 1669 pwd->pw_fields &= ~_PWF_SOURCE; 1670 pwd->pw_fields |= _PWF_FILES; 1671 } 1672 if (retval != NULL) 1673 *(struct passwd **)retval = pwd; 1674 } 1675 return (rv); 1676 } 1677 1678 1679 /* 1680 * common passwd line matching and parsing 1681 */ 1682 int 1683 __pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, 1684 const char *name, uid_t uid) 1685 { 1686 const char *p, *eom; 1687 char *q; 1688 size_t len; 1689 unsigned long m; 1690 1691 eom = entry + entrysize; 1692 for (p = entry; p < eom; p++) 1693 if (*p == ':') 1694 break; 1695 if (*p != ':') 1696 return (NS_NOTFOUND); 1697 if (how == nss_lt_all) 1698 return (NS_SUCCESS); 1699 if (how == nss_lt_name) { 1700 len = strlen(name); 1701 if (len == (p - entry) && memcmp(name, entry, len) == 0) 1702 return (NS_SUCCESS); 1703 else 1704 return (NS_NOTFOUND); 1705 } 1706 for (p++; p < eom; p++) 1707 if (*p == ':') 1708 break; 1709 if (*p != ':') 1710 return (NS_NOTFOUND); 1711 m = strtoul(++p, &q, 10); 1712 if (q[0] != ':' || (uid_t)m != uid) 1713 return (NS_NOTFOUND); 1714 else 1715 return (NS_SUCCESS); 1716 } 1717 1718 1719 /* XXX buffer must be NUL-terminated. errnop is not set correctly. */ 1720 int 1721 __pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd, 1722 int master, int *errnop __unused) 1723 { 1724 1725 if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0) 1726 return (NS_NOTFOUND); 1727 else 1728 return (NS_SUCCESS); 1729 } 1730