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