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 errno = ERANGE; 383 return (NULL); 384 } 385 pwd_storage_size <<= 1; 386 pwd_storage = malloc(pwd_storage_size); 387 if (pwd_storage == NULL) 388 return (NULL); 389 } 390 } while (res == NULL && rv == ERANGE); 391 if (rv != 0) 392 errno = rv; 393 return (res); 394 } 395 396 397 static int 398 wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer, 399 size_t bufsize, struct passwd **res) 400 { 401 return (getpwnam_r(key.name, pwd, buffer, bufsize, res)); 402 } 403 404 405 static int 406 wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer, 407 size_t bufsize, struct passwd **res) 408 { 409 return (getpwuid_r(key.uid, pwd, buffer, bufsize, res)); 410 } 411 412 413 static int 414 wrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer, 415 size_t bufsize, struct passwd **res) 416 { 417 return (getpwent_r(pwd, buffer, bufsize, res)); 418 } 419 420 421 struct passwd * 422 getpwnam(const char *name) 423 { 424 union key key; 425 426 key.name = name; 427 return (getpw(wrap_getpwnam_r, key)); 428 } 429 430 431 struct passwd * 432 getpwuid(uid_t uid) 433 { 434 union key key; 435 436 key.uid = uid; 437 return (getpw(wrap_getpwuid_r, key)); 438 } 439 440 441 struct passwd * 442 getpwent(void) 443 { 444 union key key; 445 446 key.uid = 0; /* not used */ 447 return (getpw(wrap_getpwent_r, key)); 448 } 449 450 451 /* 452 * files backend 453 */ 454 static DB * 455 pwdbopen(int *version) 456 { 457 DB *res; 458 DBT key, entry; 459 int rv; 460 461 if (geteuid() != 0 || 462 (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL) 463 res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); 464 if (res == NULL) 465 return (NULL); 466 key.data = _PWD_VERSION_KEY; 467 key.size = strlen(_PWD_VERSION_KEY); 468 rv = res->get(res, &key, &entry, 0); 469 if (rv == 0) 470 *version = *(unsigned char *)entry.data; 471 else 472 *version = 3; 473 if (*version < 3 || 474 *version >= sizeof(pwdb_versions)/sizeof(pwdb_versions[0])) { 475 syslog(LOG_CRIT, "Unsupported password database version %d", 476 *version); 477 res->close(res); 478 res = NULL; 479 } 480 return (res); 481 } 482 483 484 static void 485 files_endstate(void *p) 486 { 487 DB *db; 488 489 if (p == NULL) 490 return; 491 db = ((struct files_state *)p)->db; 492 if (db != NULL) 493 db->close(db); 494 free(p); 495 } 496 497 498 static int 499 files_setpwent(void *retval, void *mdata, va_list ap) 500 { 501 struct files_state *st; 502 int rv, stayopen; 503 504 rv = files_getstate(&st); 505 if (rv != 0) 506 return (NS_UNAVAIL); 507 switch ((enum constants)mdata) { 508 case SETPWENT: 509 stayopen = va_arg(ap, int); 510 st->keynum = 0; 511 if (stayopen) 512 st->db = pwdbopen(&st->version); 513 st->stayopen = stayopen; 514 break; 515 case ENDPWENT: 516 if (st->db != NULL) { 517 (void)st->db->close(st->db); 518 st->db = NULL; 519 } 520 break; 521 default: 522 break; 523 } 524 return (NS_UNAVAIL); 525 } 526 527 528 static int 529 files_passwd(void *retval, void *mdata, va_list ap) 530 { 531 char keybuf[MAXLOGNAME + 1]; 532 DBT key, entry; 533 struct files_state *st; 534 enum nss_lookup_type how; 535 const char *name; 536 struct passwd *pwd; 537 char *buffer; 538 size_t bufsize, namesize; 539 uid_t uid; 540 uint32_t store; 541 int rv, stayopen, *errnop; 542 543 name = NULL; 544 uid = (uid_t)-1; 545 how = (enum nss_lookup_type)mdata; 546 switch (how) { 547 case nss_lt_name: 548 name = va_arg(ap, const char *); 549 keybuf[0] = _PW_KEYBYNAME; 550 break; 551 case nss_lt_id: 552 uid = va_arg(ap, uid_t); 553 keybuf[0] = _PW_KEYBYUID; 554 break; 555 case nss_lt_all: 556 keybuf[0] = _PW_KEYBYNUM; 557 break; 558 default: 559 rv = NS_NOTFOUND; 560 goto fin; 561 } 562 pwd = va_arg(ap, struct passwd *); 563 buffer = va_arg(ap, char *); 564 bufsize = va_arg(ap, size_t); 565 errnop = va_arg(ap, int *); 566 *errnop = files_getstate(&st); 567 if (*errnop != 0) 568 return (NS_UNAVAIL); 569 if (how == nss_lt_all && st->keynum < 0) { 570 rv = NS_NOTFOUND; 571 goto fin; 572 } 573 if (st->db == NULL && 574 (st->db = pwdbopen(&st->version)) == NULL) { 575 *errnop = errno; 576 rv = NS_UNAVAIL; 577 goto fin; 578 } 579 if (how == nss_lt_all) 580 stayopen = 1; 581 else 582 stayopen = st->stayopen; 583 key.data = keybuf; 584 do { 585 switch (how) { 586 case nss_lt_name: 587 /* MAXLOGNAME includes NUL byte, but we do not 588 * include the NUL byte in the key. 589 */ 590 namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1); 591 if (namesize >= sizeof(keybuf)-1) { 592 *errnop = EINVAL; 593 rv = NS_NOTFOUND; 594 goto fin; 595 } 596 key.size = namesize + 1; 597 break; 598 case nss_lt_id: 599 if (st->version < _PWD_CURRENT_VERSION) { 600 memcpy(&keybuf[1], &uid, sizeof(uid)); 601 key.size = sizeof(uid) + 1; 602 } else { 603 store = htonl(uid); 604 memcpy(&keybuf[1], &store, sizeof(store)); 605 key.size = sizeof(store) + 1; 606 } 607 break; 608 case nss_lt_all: 609 st->keynum++; 610 if (st->version < _PWD_CURRENT_VERSION) { 611 memcpy(&keybuf[1], &st->keynum, 612 sizeof(st->keynum)); 613 key.size = sizeof(st->keynum) + 1; 614 } else { 615 store = htonl(st->keynum); 616 memcpy(&keybuf[1], &store, sizeof(store)); 617 key.size = sizeof(store) + 1; 618 } 619 break; 620 } 621 keybuf[0] = _PW_VERSIONED(keybuf[0], st->version); 622 rv = st->db->get(st->db, &key, &entry, 0); 623 if (rv < 0 || rv > 1) { /* should never return > 1 */ 624 *errnop = errno; 625 rv = NS_UNAVAIL; 626 goto fin; 627 } else if (rv == 1) { 628 if (how == nss_lt_all) 629 st->keynum = -1; 630 rv = NS_NOTFOUND; 631 goto fin; 632 } 633 rv = pwdb_versions[st->version].match(entry.data, entry.size, 634 how, name, uid); 635 if (rv != NS_SUCCESS) 636 continue; 637 if (entry.size > bufsize) { 638 *errnop = ERANGE; 639 rv = NS_RETURN; 640 break; 641 } 642 memcpy(buffer, entry.data, entry.size); 643 rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, 644 errnop); 645 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 646 fin: 647 if (!stayopen && st->db != NULL) { 648 (void)st->db->close(st->db); 649 st->db = NULL; 650 } 651 if (rv == NS_SUCCESS) { 652 pwd->pw_fields &= ~_PWF_SOURCE; 653 pwd->pw_fields |= _PWF_FILES; 654 if (retval != NULL) 655 *(struct passwd **)retval = pwd; 656 } 657 return (rv); 658 } 659 660 661 static int 662 pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how, 663 const char *name, uid_t uid) 664 { 665 const char *p, *eom; 666 uid_t uid2; 667 668 eom = &entry[entrysize]; 669 for (p = entry; p < eom; p++) 670 if (*p == '\0') 671 break; 672 if (*p != '\0') 673 return (NS_NOTFOUND); 674 if (how == nss_lt_all) 675 return (NS_SUCCESS); 676 if (how == nss_lt_name) 677 return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); 678 for (p++; p < eom; p++) 679 if (*p == '\0') 680 break; 681 if (*p != '\0' || (++p) + sizeof(uid) >= eom) 682 return (NS_NOTFOUND); 683 memcpy(&uid2, p, sizeof(uid2)); 684 return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND); 685 } 686 687 688 static int 689 pwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd, 690 int *errnop) 691 { 692 char *p, *eom; 693 int32_t pw_change, pw_expire; 694 695 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ 696 p = buffer; 697 eom = &buffer[bufsize]; 698 #define STRING(field) do { \ 699 (field) = p; \ 700 while (p < eom && *p != '\0') \ 701 p++; \ 702 if (p >= eom) \ 703 return (NS_NOTFOUND); \ 704 p++; \ 705 } while (0) 706 #define SCALAR(field) do { \ 707 if (p + sizeof(field) > eom) \ 708 return (NS_NOTFOUND); \ 709 memcpy(&(field), p, sizeof(field)); \ 710 p += sizeof(field); \ 711 } while (0) 712 STRING(pwd->pw_name); 713 STRING(pwd->pw_passwd); 714 SCALAR(pwd->pw_uid); 715 SCALAR(pwd->pw_gid); 716 SCALAR(pw_change); 717 STRING(pwd->pw_class); 718 STRING(pwd->pw_gecos); 719 STRING(pwd->pw_dir); 720 STRING(pwd->pw_shell); 721 SCALAR(pw_expire); 722 SCALAR(pwd->pw_fields); 723 #undef STRING 724 #undef SCALAR 725 pwd->pw_change = pw_change; 726 pwd->pw_expire = pw_expire; 727 return (NS_SUCCESS); 728 } 729 730 731 static int 732 pwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how, 733 const char *name, uid_t uid) 734 { 735 const char *p, *eom; 736 uint32_t uid2; 737 738 eom = &entry[entrysize]; 739 for (p = entry; p < eom; p++) 740 if (*p == '\0') 741 break; 742 if (*p != '\0') 743 return (NS_NOTFOUND); 744 if (how == nss_lt_all) 745 return (NS_SUCCESS); 746 if (how == nss_lt_name) 747 return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND); 748 for (p++; p < eom; p++) 749 if (*p == '\0') 750 break; 751 if (*p != '\0' || (++p) + sizeof(uid) >= eom) 752 return (NS_NOTFOUND); 753 memcpy(&uid2, p, sizeof(uid2)); 754 uid2 = ntohl(uid2); 755 return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND); 756 } 757 758 759 static int 760 pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd, 761 int *errnop) 762 { 763 char *p, *eom; 764 uint32_t n; 765 766 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */ 767 p = buffer; 768 eom = &buffer[bufsize]; 769 #define STRING(field) do { \ 770 (field) = p; \ 771 while (p < eom && *p != '\0') \ 772 p++; \ 773 if (p >= eom) \ 774 return (NS_NOTFOUND); \ 775 p++; \ 776 } while (0) 777 #define SCALAR(field) do { \ 778 if (p + sizeof(n) > eom) \ 779 return (NS_NOTFOUND); \ 780 memcpy(&n, p, sizeof(n)); \ 781 (field) = ntohl(n); \ 782 p += sizeof(n); \ 783 } while (0) 784 STRING(pwd->pw_name); 785 STRING(pwd->pw_passwd); 786 SCALAR(pwd->pw_uid); 787 SCALAR(pwd->pw_gid); 788 SCALAR(pwd->pw_change); 789 STRING(pwd->pw_class); 790 STRING(pwd->pw_gecos); 791 STRING(pwd->pw_dir); 792 STRING(pwd->pw_shell); 793 SCALAR(pwd->pw_expire); 794 SCALAR(pwd->pw_fields); 795 #undef STRING 796 #undef SCALAR 797 return (NS_SUCCESS); 798 } 799 800 801 #ifdef HESIOD 802 /* 803 * dns backend 804 */ 805 static void 806 dns_endstate(void *p) 807 { 808 free(p); 809 } 810 811 812 static int 813 dns_setpwent(void *retval, void *mdata, va_list ap) 814 { 815 struct dns_state *st; 816 int rv; 817 818 rv = dns_getstate(&st); 819 if (rv != 0) 820 return (NS_UNAVAIL); 821 st->counter = 0; 822 return (NS_UNAVAIL); 823 } 824 825 826 static int 827 dns_passwd(void *retval, void *mdata, va_list ap) 828 { 829 char buf[HESIOD_NAME_MAX]; 830 struct dns_state *st; 831 struct passwd *pwd; 832 const char *name, *label; 833 void *ctx; 834 char *buffer, **hes; 835 size_t bufsize, linesize; 836 uid_t uid; 837 enum nss_lookup_type how; 838 int rv, *errnop; 839 840 ctx = NULL; 841 hes = NULL; 842 name = NULL; 843 uid = (uid_t)-1; 844 how = (enum nss_lookup_type)mdata; 845 switch (how) { 846 case nss_lt_name: 847 name = va_arg(ap, const char *); 848 break; 849 case nss_lt_id: 850 uid = va_arg(ap, uid_t); 851 break; 852 case nss_lt_all: 853 break; 854 } 855 pwd = va_arg(ap, struct passwd *); 856 buffer = va_arg(ap, char *); 857 bufsize = va_arg(ap, size_t); 858 errnop = va_arg(ap, int *); 859 *errnop = dns_getstate(&st); 860 if (*errnop != 0) 861 return (NS_UNAVAIL); 862 if (hesiod_init(&ctx) != 0) { 863 *errnop = errno; 864 rv = NS_UNAVAIL; 865 goto fin; 866 } 867 do { 868 rv = NS_NOTFOUND; 869 switch (how) { 870 case nss_lt_name: 871 label = name; 872 break; 873 case nss_lt_id: 874 if (snprintf(buf, sizeof(buf), "%lu", 875 (unsigned long)uid) >= sizeof(buf)) 876 goto fin; 877 label = buf; 878 break; 879 case nss_lt_all: 880 if (st->counter < 0) 881 goto fin; 882 if (snprintf(buf, sizeof(buf), "passwd-%ld", 883 st->counter++) >= sizeof(buf)) 884 goto fin; 885 label = buf; 886 break; 887 } 888 hes = hesiod_resolve(ctx, label, 889 how == nss_lt_id ? "uid" : "passwd"); 890 if (hes == NULL) { 891 if (how == nss_lt_all) 892 st->counter = -1; 893 if (errno != ENOENT) 894 *errnop = errno; 895 goto fin; 896 } 897 rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid); 898 if (rv != NS_SUCCESS) { 899 hesiod_free_list(ctx, hes); 900 hes = NULL; 901 continue; 902 } 903 linesize = strlcpy(buffer, hes[0], bufsize); 904 if (linesize >= bufsize) { 905 *errnop = ERANGE; 906 rv = NS_RETURN; 907 continue; 908 } 909 hesiod_free_list(ctx, hes); 910 hes = NULL; 911 rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop); 912 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 913 fin: 914 if (hes != NULL) 915 hesiod_free_list(ctx, hes); 916 if (ctx != NULL) 917 hesiod_end(ctx); 918 if (rv == NS_SUCCESS) { 919 pwd->pw_fields &= ~_PWF_SOURCE; 920 pwd->pw_fields |= _PWF_HESIOD; 921 if (retval != NULL) 922 *(struct passwd **)retval = pwd; 923 } 924 return (rv); 925 } 926 #endif /* HESIOD */ 927 928 929 #ifdef YP 930 /* 931 * nis backend 932 */ 933 static void 934 nis_endstate(void *p) 935 { 936 free(((struct nis_state *)p)->key); 937 free(p); 938 } 939 940 /* 941 * Test for the presence of special FreeBSD-specific master.passwd.by* 942 * maps. We do this using yp_order(). If it fails, then either the server 943 * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by 944 * the server (Sun NIS+ servers in YP compat mode behave this way). If 945 * the master.passwd.by* maps don't exist, then let the lookup routine try 946 * the regular passwd.by* maps instead. If the lookup routine fails, it 947 * can return an error as needed. 948 */ 949 static int 950 nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize, 951 int *master) 952 { 953 int rv, order; 954 955 *master = 0; 956 if (geteuid() == 0) { 957 if (snprintf(buffer, bufsize, "master.passwd.by%s", 958 (how == nss_lt_id) ? "uid" : "name") >= bufsize) 959 return (NS_UNAVAIL); 960 rv = yp_order(domain, buffer, &order); 961 if (rv == 0) { 962 *master = 1; 963 return (NS_SUCCESS); 964 } 965 } 966 967 if (snprintf(buffer, bufsize, "passwd.by%s", 968 (how == nss_lt_id) ? "uid" : "name") >= bufsize) 969 return (NS_UNAVAIL); 970 971 return (NS_SUCCESS); 972 } 973 974 975 static int 976 nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize) 977 { 978 int rv; 979 char *result, *p, *q, *eor; 980 int resultlen; 981 982 result = NULL; 983 rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name), 984 &result, &resultlen); 985 if (rv != 0) 986 rv = 1; 987 else { 988 eor = &result[resultlen]; 989 p = memchr(result, ':', eor - result); 990 if (p != NULL && ++p < eor && 991 (q = memchr(p, ':', eor - p)) != NULL) { 992 if (q - p >= bufsize) 993 rv = -1; 994 else { 995 memcpy(buffer, p, q - p); 996 buffer[q - p] ='\0'; 997 } 998 } else 999 rv = 1; 1000 } 1001 free(result); 1002 return (rv); 1003 } 1004 1005 1006 static int 1007 nis_setpwent(void *retval, void *mdata, va_list ap) 1008 { 1009 struct nis_state *st; 1010 int rv; 1011 1012 rv = nis_getstate(&st); 1013 if (rv != 0) 1014 return (NS_UNAVAIL); 1015 st->done = 0; 1016 free(st->key); 1017 st->key = NULL; 1018 return (NS_UNAVAIL); 1019 } 1020 1021 1022 static int 1023 nis_passwd(void *retval, void *mdata, va_list ap) 1024 { 1025 char map[YPMAXMAP]; 1026 struct nis_state *st; 1027 struct passwd *pwd; 1028 const char *name; 1029 char *buffer, *key, *result; 1030 size_t bufsize; 1031 uid_t uid; 1032 enum nss_lookup_type how; 1033 int *errnop, keylen, resultlen, rv, master; 1034 1035 name = NULL; 1036 uid = (uid_t)-1; 1037 how = (enum nss_lookup_type)mdata; 1038 switch (how) { 1039 case nss_lt_name: 1040 name = va_arg(ap, const char *); 1041 break; 1042 case nss_lt_id: 1043 uid = va_arg(ap, uid_t); 1044 break; 1045 case nss_lt_all: 1046 break; 1047 } 1048 pwd = va_arg(ap, struct passwd *); 1049 buffer = va_arg(ap, char *); 1050 bufsize = va_arg(ap, size_t); 1051 errnop = va_arg(ap, int *); 1052 *errnop = nis_getstate(&st); 1053 if (*errnop != 0) 1054 return (NS_UNAVAIL); 1055 if (st->domain[0] == '\0') { 1056 if (getdomainname(st->domain, sizeof(st->domain)) != 0) { 1057 *errnop = errno; 1058 return (NS_UNAVAIL); 1059 } 1060 } 1061 rv = nis_map(st->domain, how, map, sizeof(map), &master); 1062 if (rv != NS_SUCCESS) 1063 return (rv); 1064 result = NULL; 1065 do { 1066 rv = NS_NOTFOUND; 1067 switch (how) { 1068 case nss_lt_name: 1069 if (strlcpy(buffer, name, bufsize) >= bufsize) 1070 goto erange; 1071 break; 1072 case nss_lt_id: 1073 if (snprintf(buffer, bufsize, "%lu", 1074 (unsigned long)uid) >= bufsize) 1075 goto erange; 1076 break; 1077 case nss_lt_all: 1078 if (st->done) 1079 goto fin; 1080 break; 1081 } 1082 result = NULL; 1083 if (how == nss_lt_all) { 1084 if (st->key == NULL) 1085 rv = yp_first(st->domain, map, &st->key, 1086 &st->keylen, &result, &resultlen); 1087 else { 1088 key = st->key; 1089 keylen = st->keylen; 1090 st->key = NULL; 1091 rv = yp_next(st->domain, map, key, keylen, 1092 &st->key, &st->keylen, &result, 1093 &resultlen); 1094 free(key); 1095 } 1096 if (rv != 0) { 1097 free(result); 1098 free(st->key); 1099 st->key = NULL; 1100 if (rv == YPERR_NOMORE) 1101 st->done = 1; 1102 else 1103 rv = NS_UNAVAIL; 1104 goto fin; 1105 } 1106 } else { 1107 rv = yp_match(st->domain, map, buffer, strlen(buffer), 1108 &result, &resultlen); 1109 if (rv == YPERR_KEY) { 1110 rv = NS_NOTFOUND; 1111 continue; 1112 } else if (rv != 0) { 1113 free(result); 1114 rv = NS_UNAVAIL; 1115 continue; 1116 } 1117 } 1118 if (resultlen >= bufsize) 1119 goto erange; 1120 memcpy(buffer, result, resultlen); 1121 buffer[resultlen] = '\0'; 1122 free(result); 1123 rv = __pw_match_entry(buffer, resultlen, how, name, uid); 1124 if (rv == NS_SUCCESS) 1125 rv = __pw_parse_entry(buffer, resultlen, pwd, master, 1126 errnop); 1127 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 1128 fin: 1129 if (rv == NS_SUCCESS) { 1130 if (strstr(pwd->pw_passwd, "##") != NULL) { 1131 rv = nis_adjunct(st->domain, pwd->pw_name, 1132 &buffer[resultlen+1], bufsize-resultlen-1); 1133 if (rv < 0) 1134 goto erange; 1135 else if (rv == 0) 1136 pwd->pw_passwd = &buffer[resultlen+1]; 1137 } 1138 pwd->pw_fields &= ~_PWF_SOURCE; 1139 pwd->pw_fields |= _PWF_NIS; 1140 if (retval != NULL) 1141 *(struct passwd **)retval = pwd; 1142 rv = NS_SUCCESS; 1143 } 1144 return (rv); 1145 erange: 1146 *errnop = ERANGE; 1147 return (NS_RETURN); 1148 } 1149 #endif /* YP */ 1150 1151 1152 /* 1153 * compat backend 1154 */ 1155 static void 1156 compat_clear_template(struct passwd *template) 1157 { 1158 1159 free(template->pw_passwd); 1160 free(template->pw_gecos); 1161 free(template->pw_dir); 1162 free(template->pw_shell); 1163 memset(template, 0, sizeof(*template)); 1164 } 1165 1166 1167 static int 1168 compat_set_template(struct passwd *src, struct passwd *template) 1169 { 1170 1171 compat_clear_template(template); 1172 #ifdef PW_OVERRIDE_PASSWD 1173 if ((src->pw_fields & _PWF_PASSWD) && 1174 (template->pw_passwd = strdup(src->pw_passwd)) == NULL) 1175 goto enomem; 1176 #endif 1177 if (src->pw_fields & _PWF_UID) 1178 template->pw_uid = src->pw_uid; 1179 if (src->pw_fields & _PWF_GID) 1180 template->pw_gid = src->pw_gid; 1181 if ((src->pw_fields & _PWF_GECOS) && 1182 (template->pw_gecos = strdup(src->pw_gecos)) == NULL) 1183 goto enomem; 1184 if ((src->pw_fields & _PWF_DIR) && 1185 (template->pw_dir = strdup(src->pw_dir)) == NULL) 1186 goto enomem; 1187 if ((src->pw_fields & _PWF_SHELL) && 1188 (template->pw_shell = strdup(src->pw_shell)) == NULL) 1189 goto enomem; 1190 template->pw_fields = src->pw_fields; 1191 return (0); 1192 enomem: 1193 syslog(LOG_ERR, "getpwent memory allocation failure"); 1194 return (-1); 1195 } 1196 1197 1198 static int 1199 compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer, 1200 size_t bufsize) 1201 { 1202 struct passwd hold; 1203 char *copy, *p, *q, *eob; 1204 size_t n; 1205 1206 /* We cannot know the layout of the password fields in `buffer', 1207 * so we have to copy everything. 1208 */ 1209 if (template->pw_fields == 0) /* nothing to fill-in */ 1210 return (0); 1211 n = 0; 1212 n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0; 1213 n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0; 1214 n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0; 1215 n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0; 1216 n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0; 1217 n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0; 1218 copy = malloc(n); 1219 if (copy == NULL) { 1220 syslog(LOG_ERR, "getpwent memory allocation failure"); 1221 return (ENOMEM); 1222 } 1223 p = copy; 1224 eob = ©[n]; 1225 #define COPY(field) do { \ 1226 if (pwd->field == NULL) \ 1227 hold.field = NULL; \ 1228 else { \ 1229 hold.field = p; \ 1230 p += strlcpy(p, pwd->field, eob-p) + 1; \ 1231 } \ 1232 } while (0) 1233 COPY(pw_name); 1234 COPY(pw_passwd); 1235 COPY(pw_class); 1236 COPY(pw_gecos); 1237 COPY(pw_dir); 1238 COPY(pw_shell); 1239 #undef COPY 1240 p = buffer; 1241 eob = &buffer[bufsize]; 1242 #define COPY(field, flag) do { \ 1243 q = (template->pw_fields & flag) ? template->field : hold.field; \ 1244 if (q == NULL) \ 1245 pwd->field = NULL; \ 1246 else { \ 1247 pwd->field = p; \ 1248 if ((n = strlcpy(p, q, eob-p)) >= eob-p) { \ 1249 free(copy); \ 1250 return (ERANGE); \ 1251 } \ 1252 p += n + 1; \ 1253 } \ 1254 } while (0) 1255 COPY(pw_name, 0); 1256 #ifdef PW_OVERRIDE_PASSWD 1257 COPY(pw_passwd, _PWF_PASSWD); 1258 #else 1259 COPY(pw_passwd, 0); 1260 #endif 1261 COPY(pw_class, 0); 1262 COPY(pw_gecos, _PWF_GECOS); 1263 COPY(pw_dir, _PWF_DIR); 1264 COPY(pw_shell, _PWF_SHELL); 1265 #undef COPY 1266 #define COPY(field, flag) do { \ 1267 if (template->pw_fields & flag) \ 1268 pwd->field = template->field; \ 1269 } while (0) 1270 COPY(pw_uid, _PWF_UID); 1271 COPY(pw_gid, _PWF_GID); 1272 #undef COPY 1273 free(copy); 1274 return (0); 1275 } 1276 1277 1278 static int 1279 compat_exclude(const char *name, DB **db) 1280 { 1281 DBT key, data; 1282 1283 if (*db == NULL && 1284 (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) 1285 return (errno); 1286 key.size = strlen(name); 1287 key.data = (char *)name; 1288 data.size = 0; 1289 data.data = NULL; 1290 1291 if ((*db)->put(*db, &key, &data, 0) == -1) 1292 return (errno); 1293 return (0); 1294 } 1295 1296 1297 static int 1298 compat_is_excluded(const char *name, DB *db) 1299 { 1300 DBT key, data; 1301 1302 if (db == NULL) 1303 return (0); 1304 key.size = strlen(name); 1305 key.data = (char *)name; 1306 return (db->get(db, &key, &data, 0) == 0); 1307 } 1308 1309 1310 static int 1311 compat_redispatch(struct compat_state *st, enum nss_lookup_type how, 1312 enum nss_lookup_type lookup_how, const char *name, const char *lookup_name, 1313 uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) 1314 { 1315 static const ns_src compatsrc[] = { 1316 #ifdef YP 1317 { NSSRC_NIS, NS_SUCCESS }, 1318 #endif 1319 { NULL, 0 } 1320 }; 1321 ns_dtab dtab[] = { 1322 #ifdef YP 1323 { NSSRC_NIS, nis_passwd, NULL }, 1324 #endif 1325 #ifdef HESIOD 1326 { NSSRC_DNS, dns_passwd, NULL }, 1327 #endif 1328 { NULL, NULL, NULL } 1329 }; 1330 void *discard; 1331 int rv, e, i; 1332 1333 for (i = 0; i < sizeof(dtab)/sizeof(dtab[0]) - 1; i++) 1334 dtab[i].mdata = (void *)lookup_how; 1335 more: 1336 pwd_init(pwd); 1337 switch (lookup_how) { 1338 case nss_lt_all: 1339 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1340 "getpwent_r", compatsrc, pwd, buffer, bufsize, 1341 errnop); 1342 break; 1343 case nss_lt_id: 1344 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1345 "getpwuid_r", compatsrc, uid, pwd, buffer, 1346 bufsize, errnop); 1347 break; 1348 case nss_lt_name: 1349 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1350 "getpwnam_r", compatsrc, lookup_name, pwd, buffer, 1351 bufsize, errnop); 1352 break; 1353 default: 1354 return (NS_UNAVAIL); 1355 } 1356 if (rv != NS_SUCCESS) 1357 return (rv); 1358 if (compat_is_excluded(pwd->pw_name, st->exclude)) { 1359 if (how == nss_lt_all) 1360 goto more; 1361 return (NS_NOTFOUND); 1362 } 1363 e = compat_use_template(pwd, &st->template, buffer, bufsize); 1364 if (e != 0) { 1365 *errnop = e; 1366 if (e == ERANGE) 1367 return (NS_RETURN); 1368 else 1369 return (NS_UNAVAIL); 1370 } 1371 switch (how) { 1372 case nss_lt_name: 1373 if (strcmp(name, pwd->pw_name) != 0) 1374 return (NS_NOTFOUND); 1375 break; 1376 case nss_lt_id: 1377 if (uid != pwd->pw_uid) 1378 return (NS_NOTFOUND); 1379 break; 1380 default: 1381 break; 1382 } 1383 return (NS_SUCCESS); 1384 } 1385 1386 1387 static void 1388 compat_endstate(void *p) 1389 { 1390 struct compat_state *st; 1391 1392 if (p == NULL) 1393 return; 1394 st = (struct compat_state *)p; 1395 if (st->db != NULL) 1396 st->db->close(st->db); 1397 if (st->exclude != NULL) 1398 st->exclude->close(st->exclude); 1399 compat_clear_template(&st->template); 1400 free(p); 1401 } 1402 1403 1404 static int 1405 compat_setpwent(void *retval, void *mdata, va_list ap) 1406 { 1407 static const ns_src compatsrc[] = { 1408 #ifdef YP 1409 { NSSRC_NIS, NS_SUCCESS }, 1410 #endif 1411 { NULL, 0 } 1412 }; 1413 ns_dtab dtab[] = { 1414 #ifdef YP 1415 { NSSRC_NIS, nis_setpwent, NULL }, 1416 #endif 1417 #ifdef HESIOD 1418 { NSSRC_DNS, dns_setpwent, NULL }, 1419 #endif 1420 { NULL, NULL, NULL } 1421 }; 1422 struct compat_state *st; 1423 int rv, stayopen; 1424 1425 #define set_setent(x, y) do { \ 1426 int i; \ 1427 \ 1428 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 1429 x[i].mdata = (void *)y; \ 1430 } while (0) 1431 1432 rv = compat_getstate(&st); 1433 if (rv != 0) 1434 return (NS_UNAVAIL); 1435 switch ((enum constants)mdata) { 1436 case SETPWENT: 1437 stayopen = va_arg(ap, int); 1438 st->keynum = 0; 1439 if (stayopen) 1440 st->db = pwdbopen(&st->version); 1441 st->stayopen = stayopen; 1442 set_setent(dtab, mdata); 1443 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent", 1444 compatsrc, 0); 1445 break; 1446 case ENDPWENT: 1447 if (st->db != NULL) { 1448 (void)st->db->close(st->db); 1449 st->db = NULL; 1450 } 1451 set_setent(dtab, mdata); 1452 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent", 1453 compatsrc, 0); 1454 break; 1455 default: 1456 break; 1457 } 1458 return (NS_UNAVAIL); 1459 #undef set_setent 1460 } 1461 1462 1463 static int 1464 compat_passwd(void *retval, void *mdata, va_list ap) 1465 { 1466 char keybuf[MAXLOGNAME + 1]; 1467 DBT key, entry; 1468 struct compat_state *st; 1469 enum nss_lookup_type how; 1470 const char *name; 1471 struct passwd *pwd; 1472 char *buffer, *pw_name; 1473 char *host, *user, *domain; 1474 size_t bufsize; 1475 uid_t uid; 1476 uint32_t store; 1477 int rv, from_compat, stayopen, *errnop; 1478 1479 from_compat = 0; 1480 name = NULL; 1481 uid = (uid_t)-1; 1482 how = (enum nss_lookup_type)mdata; 1483 switch (how) { 1484 case nss_lt_name: 1485 name = va_arg(ap, const char *); 1486 break; 1487 case nss_lt_id: 1488 uid = va_arg(ap, uid_t); 1489 break; 1490 case nss_lt_all: 1491 break; 1492 default: 1493 rv = NS_NOTFOUND; 1494 goto fin; 1495 } 1496 pwd = va_arg(ap, struct passwd *); 1497 buffer = va_arg(ap, char *); 1498 bufsize = va_arg(ap, size_t); 1499 errnop = va_arg(ap, int *); 1500 *errnop = compat_getstate(&st); 1501 if (*errnop != 0) 1502 return (NS_UNAVAIL); 1503 if (how == nss_lt_all && st->keynum < 0) { 1504 rv = NS_NOTFOUND; 1505 goto fin; 1506 } 1507 if (st->db == NULL && 1508 (st->db = pwdbopen(&st->version)) == NULL) { 1509 *errnop = errno; 1510 rv = NS_UNAVAIL; 1511 goto fin; 1512 } 1513 if (how == nss_lt_all) { 1514 if (st->keynum < 0) { 1515 rv = NS_NOTFOUND; 1516 goto fin; 1517 } 1518 stayopen = 1; 1519 } else { 1520 st->keynum = 0; 1521 stayopen = st->stayopen; 1522 } 1523 docompat: 1524 rv = NS_NOTFOUND; 1525 switch (st->compat) { 1526 case COMPAT_MODE_ALL: 1527 rv = compat_redispatch(st, how, how, name, name, uid, pwd, 1528 buffer, bufsize, errnop); 1529 if (rv != NS_SUCCESS) 1530 st->compat = COMPAT_MODE_OFF; 1531 break; 1532 case COMPAT_MODE_NETGROUP: 1533 /* XXX getnetgrent is not thread-safe. */ 1534 do { 1535 rv = getnetgrent(&host, &user, &domain); 1536 if (rv == 0) { 1537 endnetgrent(); 1538 st->compat = COMPAT_MODE_OFF; 1539 rv = NS_NOTFOUND; 1540 continue; 1541 } else if (user == NULL || user[0] == '\0') 1542 continue; 1543 rv = compat_redispatch(st, how, nss_lt_name, name, 1544 user, uid, pwd, buffer, bufsize, errnop); 1545 } while (st->compat == COMPAT_MODE_NETGROUP && 1546 !(rv & NS_TERMINATE)); 1547 break; 1548 case COMPAT_MODE_NAME: 1549 rv = compat_redispatch(st, how, nss_lt_name, name, st->name, 1550 uid, pwd, buffer, bufsize, errnop); 1551 free(st->name); 1552 st->name = NULL; 1553 st->compat = COMPAT_MODE_OFF; 1554 break; 1555 default: 1556 break; 1557 } 1558 if (rv & NS_TERMINATE) { 1559 from_compat = 1; 1560 goto fin; 1561 } 1562 key.data = keybuf; 1563 rv = NS_NOTFOUND; 1564 while (st->keynum >= 0) { 1565 st->keynum++; 1566 if (st->version < _PWD_CURRENT_VERSION) { 1567 memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); 1568 key.size = sizeof(st->keynum) + 1; 1569 } else { 1570 store = htonl(st->keynum); 1571 memcpy(&keybuf[1], &store, sizeof(store)); 1572 key.size = sizeof(store) + 1; 1573 } 1574 keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version); 1575 rv = st->db->get(st->db, &key, &entry, 0); 1576 if (rv < 0 || rv > 1) { /* should never return > 1 */ 1577 *errnop = errno; 1578 rv = NS_UNAVAIL; 1579 goto fin; 1580 } else if (rv == 1) { 1581 st->keynum = -1; 1582 rv = NS_NOTFOUND; 1583 goto fin; 1584 } 1585 pw_name = (char *)entry.data; 1586 switch (pw_name[0]) { 1587 case '+': 1588 switch (pw_name[1]) { 1589 case '\0': 1590 st->compat = COMPAT_MODE_ALL; 1591 break; 1592 case '@': 1593 setnetgrent(&pw_name[2]); 1594 st->compat = COMPAT_MODE_NETGROUP; 1595 break; 1596 default: 1597 st->name = strdup(&pw_name[1]); 1598 if (st->name == NULL) { 1599 syslog(LOG_ERR, 1600 "getpwent memory allocation failure"); 1601 *errnop = ENOMEM; 1602 rv = NS_UNAVAIL; 1603 break; 1604 } 1605 st->compat = COMPAT_MODE_NAME; 1606 } 1607 if (entry.size > bufsize) { 1608 *errnop = ERANGE; 1609 rv = NS_RETURN; 1610 goto fin; 1611 } 1612 memcpy(buffer, entry.data, entry.size); 1613 rv = pwdb_versions[st->version].parse(buffer, 1614 entry.size, pwd, errnop); 1615 if (rv != NS_SUCCESS) 1616 ; 1617 else if (compat_set_template(pwd, &st->template) < 0) { 1618 *errnop = ENOMEM; 1619 rv = NS_UNAVAIL; 1620 goto fin; 1621 } 1622 goto docompat; 1623 case '-': 1624 switch (pw_name[1]) { 1625 case '\0': 1626 /* XXX Maybe syslog warning */ 1627 continue; 1628 case '@': 1629 setnetgrent(&pw_name[2]); 1630 while (getnetgrent(&host, &user, &domain) != 1631 0) { 1632 if (user != NULL && user[0] != '\0') 1633 compat_exclude(user, 1634 &st->exclude); 1635 } 1636 endnetgrent(); 1637 continue; 1638 default: 1639 compat_exclude(&pw_name[1], &st->exclude); 1640 continue; 1641 } 1642 break; 1643 default: 1644 break; 1645 } 1646 if (compat_is_excluded((char *)entry.data, st->exclude)) 1647 continue; 1648 rv = pwdb_versions[st->version].match(entry.data, entry.size, 1649 how, name, uid); 1650 if (rv == NS_RETURN) 1651 break; 1652 else if (rv != NS_SUCCESS) 1653 continue; 1654 if (entry.size > bufsize) { 1655 *errnop = ERANGE; 1656 rv = NS_RETURN; 1657 break; 1658 } 1659 memcpy(buffer, entry.data, entry.size); 1660 rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, 1661 errnop); 1662 if (rv & NS_TERMINATE) 1663 break; 1664 } 1665 fin: 1666 if (!stayopen && st->db != NULL) { 1667 (void)st->db->close(st->db); 1668 st->db = NULL; 1669 } 1670 if (rv == NS_SUCCESS) { 1671 if (!from_compat) { 1672 pwd->pw_fields &= ~_PWF_SOURCE; 1673 pwd->pw_fields |= _PWF_FILES; 1674 } 1675 if (retval != NULL) 1676 *(struct passwd **)retval = pwd; 1677 } 1678 return (rv); 1679 } 1680 1681 1682 /* 1683 * common passwd line matching and parsing 1684 */ 1685 int 1686 __pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, 1687 const char *name, uid_t uid) 1688 { 1689 const char *p, *eom; 1690 char *q; 1691 size_t len; 1692 unsigned long m; 1693 1694 eom = entry + entrysize; 1695 for (p = entry; p < eom; p++) 1696 if (*p == ':') 1697 break; 1698 if (*p != ':') 1699 return (NS_NOTFOUND); 1700 if (how == nss_lt_all) 1701 return (NS_SUCCESS); 1702 if (how == nss_lt_name) { 1703 len = strlen(name); 1704 if (len == (p - entry) && memcmp(name, entry, len) == 0) 1705 return (NS_SUCCESS); 1706 else 1707 return (NS_NOTFOUND); 1708 } 1709 for (p++; p < eom; p++) 1710 if (*p == ':') 1711 break; 1712 if (*p != ':') 1713 return (NS_NOTFOUND); 1714 m = strtoul(++p, &q, 10); 1715 if (q[0] != ':' || (uid_t)m != uid) 1716 return (NS_NOTFOUND); 1717 else 1718 return (NS_SUCCESS); 1719 } 1720 1721 1722 /* XXX buffer must be NUL-terminated. errnop is not set correctly. */ 1723 int 1724 __pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd, 1725 int master, int *errnop __unused) 1726 { 1727 1728 if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0) 1729 return (NS_NOTFOUND); 1730 else 1731 return (NS_SUCCESS); 1732 } 1733