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, *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 goto erange; 1397 memcpy(buffer, result, resultlen); 1398 buffer[resultlen] = '\0'; 1399 free(result); 1400 rv = __pw_match_entry(buffer, resultlen, how, name, uid); 1401 if (rv == NS_SUCCESS) 1402 rv = __pw_parse_entry(buffer, resultlen, pwd, master, 1403 errnop); 1404 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 1405 fin: 1406 if (rv == NS_SUCCESS) { 1407 if (strstr(pwd->pw_passwd, "##") != NULL) { 1408 rv = nis_adjunct(st->domain, pwd->pw_name, 1409 &buffer[resultlen+1], bufsize-resultlen-1); 1410 if (rv < 0) 1411 goto erange; 1412 else if (rv == 0) 1413 pwd->pw_passwd = &buffer[resultlen+1]; 1414 } 1415 pwd->pw_fields &= ~_PWF_SOURCE; 1416 pwd->pw_fields |= _PWF_NIS; 1417 if (retval != NULL) 1418 *(struct passwd **)retval = pwd; 1419 rv = NS_SUCCESS; 1420 } 1421 return (rv); 1422 erange: 1423 *errnop = ERANGE; 1424 return (NS_RETURN); 1425 } 1426 #endif /* YP */ 1427 1428 1429 /* 1430 * compat backend 1431 */ 1432 static void 1433 compat_clear_template(struct passwd *template) 1434 { 1435 1436 free(template->pw_passwd); 1437 free(template->pw_gecos); 1438 free(template->pw_dir); 1439 free(template->pw_shell); 1440 memset(template, 0, sizeof(*template)); 1441 } 1442 1443 1444 static int 1445 compat_set_template(struct passwd *src, struct passwd *template) 1446 { 1447 1448 compat_clear_template(template); 1449 #ifdef PW_OVERRIDE_PASSWD 1450 if ((src->pw_fields & _PWF_PASSWD) && 1451 (template->pw_passwd = strdup(src->pw_passwd)) == NULL) 1452 goto enomem; 1453 #endif 1454 if (src->pw_fields & _PWF_UID) 1455 template->pw_uid = src->pw_uid; 1456 if (src->pw_fields & _PWF_GID) 1457 template->pw_gid = src->pw_gid; 1458 if ((src->pw_fields & _PWF_GECOS) && 1459 (template->pw_gecos = strdup(src->pw_gecos)) == NULL) 1460 goto enomem; 1461 if ((src->pw_fields & _PWF_DIR) && 1462 (template->pw_dir = strdup(src->pw_dir)) == NULL) 1463 goto enomem; 1464 if ((src->pw_fields & _PWF_SHELL) && 1465 (template->pw_shell = strdup(src->pw_shell)) == NULL) 1466 goto enomem; 1467 template->pw_fields = src->pw_fields; 1468 return (0); 1469 enomem: 1470 syslog(LOG_ERR, "getpwent memory allocation failure"); 1471 return (-1); 1472 } 1473 1474 1475 static int 1476 compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer, 1477 size_t bufsize) 1478 { 1479 struct passwd hold; 1480 char *copy, *p, *q, *eob; 1481 size_t n; 1482 1483 /* We cannot know the layout of the password fields in `buffer', 1484 * so we have to copy everything. 1485 */ 1486 if (template->pw_fields == 0) /* nothing to fill-in */ 1487 return (0); 1488 n = 0; 1489 n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0; 1490 n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0; 1491 n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0; 1492 n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0; 1493 n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0; 1494 n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0; 1495 copy = malloc(n); 1496 if (copy == NULL) { 1497 syslog(LOG_ERR, "getpwent memory allocation failure"); 1498 return (ENOMEM); 1499 } 1500 p = copy; 1501 eob = ©[n]; 1502 #define COPY(field) do { \ 1503 if (pwd->field == NULL) \ 1504 hold.field = NULL; \ 1505 else { \ 1506 hold.field = p; \ 1507 p += strlcpy(p, pwd->field, eob-p) + 1; \ 1508 } \ 1509 } while (0) 1510 COPY(pw_name); 1511 COPY(pw_passwd); 1512 COPY(pw_class); 1513 COPY(pw_gecos); 1514 COPY(pw_dir); 1515 COPY(pw_shell); 1516 #undef COPY 1517 p = buffer; 1518 eob = &buffer[bufsize]; 1519 #define COPY(field, flag) do { \ 1520 q = (template->pw_fields & flag) ? template->field : hold.field; \ 1521 if (q == NULL) \ 1522 pwd->field = NULL; \ 1523 else { \ 1524 pwd->field = p; \ 1525 if ((n = strlcpy(p, q, eob-p)) >= eob-p) { \ 1526 free(copy); \ 1527 return (ERANGE); \ 1528 } \ 1529 p += n + 1; \ 1530 } \ 1531 } while (0) 1532 COPY(pw_name, 0); 1533 #ifdef PW_OVERRIDE_PASSWD 1534 COPY(pw_passwd, _PWF_PASSWD); 1535 #else 1536 COPY(pw_passwd, 0); 1537 #endif 1538 COPY(pw_class, 0); 1539 COPY(pw_gecos, _PWF_GECOS); 1540 COPY(pw_dir, _PWF_DIR); 1541 COPY(pw_shell, _PWF_SHELL); 1542 #undef COPY 1543 #define COPY(field, flag) do { \ 1544 if (template->pw_fields & flag) \ 1545 pwd->field = template->field; \ 1546 } while (0) 1547 COPY(pw_uid, _PWF_UID); 1548 COPY(pw_gid, _PWF_GID); 1549 #undef COPY 1550 free(copy); 1551 return (0); 1552 } 1553 1554 1555 static int 1556 compat_exclude(const char *name, DB **db) 1557 { 1558 DBT key, data; 1559 1560 if (*db == NULL && 1561 (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) 1562 return (errno); 1563 key.size = strlen(name); 1564 key.data = (char *)name; 1565 data.size = 0; 1566 data.data = NULL; 1567 1568 if ((*db)->put(*db, &key, &data, 0) == -1) 1569 return (errno); 1570 return (0); 1571 } 1572 1573 1574 static int 1575 compat_is_excluded(const char *name, DB *db) 1576 { 1577 DBT key, data; 1578 1579 if (db == NULL) 1580 return (0); 1581 key.size = strlen(name); 1582 key.data = (char *)name; 1583 return (db->get(db, &key, &data, 0) == 0); 1584 } 1585 1586 1587 static int 1588 compat_redispatch(struct compat_state *st, enum nss_lookup_type how, 1589 enum nss_lookup_type lookup_how, const char *name, const char *lookup_name, 1590 uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) 1591 { 1592 static const ns_src compatsrc[] = { 1593 #ifdef YP 1594 { NSSRC_NIS, NS_SUCCESS }, 1595 #endif 1596 { NULL, 0 } 1597 }; 1598 ns_dtab dtab[] = { 1599 #ifdef YP 1600 { NSSRC_NIS, nis_passwd, NULL }, 1601 #endif 1602 #ifdef HESIOD 1603 { NSSRC_DNS, dns_passwd, NULL }, 1604 #endif 1605 { NULL, NULL, NULL } 1606 }; 1607 void *discard; 1608 int rv, e, i; 1609 1610 for (i = 0; i < sizeof(dtab)/sizeof(dtab[0]) - 1; i++) 1611 dtab[i].mdata = (void *)lookup_how; 1612 more: 1613 pwd_init(pwd); 1614 switch (lookup_how) { 1615 case nss_lt_all: 1616 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1617 "getpwent_r", compatsrc, pwd, buffer, bufsize, 1618 errnop); 1619 break; 1620 case nss_lt_id: 1621 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1622 "getpwuid_r", compatsrc, uid, pwd, buffer, 1623 bufsize, errnop); 1624 break; 1625 case nss_lt_name: 1626 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, 1627 "getpwnam_r", compatsrc, lookup_name, pwd, buffer, 1628 bufsize, errnop); 1629 break; 1630 default: 1631 return (NS_UNAVAIL); 1632 } 1633 if (rv != NS_SUCCESS) 1634 return (rv); 1635 if (compat_is_excluded(pwd->pw_name, st->exclude)) { 1636 if (how == nss_lt_all) 1637 goto more; 1638 return (NS_NOTFOUND); 1639 } 1640 e = compat_use_template(pwd, &st->template, buffer, bufsize); 1641 if (e != 0) { 1642 *errnop = e; 1643 if (e == ERANGE) 1644 return (NS_RETURN); 1645 else 1646 return (NS_UNAVAIL); 1647 } 1648 switch (how) { 1649 case nss_lt_name: 1650 if (strcmp(name, pwd->pw_name) != 0) 1651 return (NS_NOTFOUND); 1652 break; 1653 case nss_lt_id: 1654 if (uid != pwd->pw_uid) 1655 return (NS_NOTFOUND); 1656 break; 1657 default: 1658 break; 1659 } 1660 return (NS_SUCCESS); 1661 } 1662 1663 1664 static void 1665 compat_endstate(void *p) 1666 { 1667 struct compat_state *st; 1668 1669 if (p == NULL) 1670 return; 1671 st = (struct compat_state *)p; 1672 if (st->db != NULL) 1673 st->db->close(st->db); 1674 if (st->exclude != NULL) 1675 st->exclude->close(st->exclude); 1676 compat_clear_template(&st->template); 1677 free(p); 1678 } 1679 1680 1681 static int 1682 compat_setpwent(void *retval, void *mdata, va_list ap) 1683 { 1684 static const ns_src compatsrc[] = { 1685 #ifdef YP 1686 { NSSRC_NIS, NS_SUCCESS }, 1687 #endif 1688 { NULL, 0 } 1689 }; 1690 ns_dtab dtab[] = { 1691 #ifdef YP 1692 { NSSRC_NIS, nis_setpwent, NULL }, 1693 #endif 1694 #ifdef HESIOD 1695 { NSSRC_DNS, dns_setpwent, NULL }, 1696 #endif 1697 { NULL, NULL, NULL } 1698 }; 1699 struct compat_state *st; 1700 int rv, stayopen; 1701 1702 #define set_setent(x, y) do { \ 1703 int i; \ 1704 \ 1705 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 1706 x[i].mdata = (void *)y; \ 1707 } while (0) 1708 1709 rv = compat_getstate(&st); 1710 if (rv != 0) 1711 return (NS_UNAVAIL); 1712 switch ((enum constants)mdata) { 1713 case SETPWENT: 1714 stayopen = va_arg(ap, int); 1715 st->keynum = 0; 1716 if (stayopen) 1717 st->db = pwdbopen(&st->version); 1718 st->stayopen = stayopen; 1719 set_setent(dtab, mdata); 1720 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent", 1721 compatsrc, 0); 1722 break; 1723 case ENDPWENT: 1724 if (st->db != NULL) { 1725 (void)st->db->close(st->db); 1726 st->db = NULL; 1727 } 1728 set_setent(dtab, mdata); 1729 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent", 1730 compatsrc, 0); 1731 break; 1732 default: 1733 break; 1734 } 1735 return (NS_UNAVAIL); 1736 #undef set_setent 1737 } 1738 1739 1740 static int 1741 compat_passwd(void *retval, void *mdata, va_list ap) 1742 { 1743 char keybuf[MAXLOGNAME + 1]; 1744 DBT key, entry; 1745 struct compat_state *st; 1746 enum nss_lookup_type how; 1747 const char *name; 1748 struct passwd *pwd; 1749 char *buffer, *pw_name; 1750 char *host, *user, *domain; 1751 size_t bufsize; 1752 uid_t uid; 1753 uint32_t store; 1754 int rv, from_compat, stayopen, *errnop; 1755 1756 from_compat = 0; 1757 name = NULL; 1758 uid = (uid_t)-1; 1759 how = (enum nss_lookup_type)mdata; 1760 switch (how) { 1761 case nss_lt_name: 1762 name = va_arg(ap, const char *); 1763 break; 1764 case nss_lt_id: 1765 uid = va_arg(ap, uid_t); 1766 break; 1767 case nss_lt_all: 1768 break; 1769 default: 1770 rv = NS_NOTFOUND; 1771 goto fin; 1772 } 1773 pwd = va_arg(ap, struct passwd *); 1774 buffer = va_arg(ap, char *); 1775 bufsize = va_arg(ap, size_t); 1776 errnop = va_arg(ap, int *); 1777 *errnop = compat_getstate(&st); 1778 if (*errnop != 0) 1779 return (NS_UNAVAIL); 1780 if (how == nss_lt_all && st->keynum < 0) { 1781 rv = NS_NOTFOUND; 1782 goto fin; 1783 } 1784 if (st->db == NULL && 1785 (st->db = pwdbopen(&st->version)) == NULL) { 1786 *errnop = errno; 1787 rv = NS_UNAVAIL; 1788 goto fin; 1789 } 1790 if (how == nss_lt_all) { 1791 if (st->keynum < 0) { 1792 rv = NS_NOTFOUND; 1793 goto fin; 1794 } 1795 stayopen = 1; 1796 } else { 1797 st->keynum = 0; 1798 stayopen = st->stayopen; 1799 } 1800 docompat: 1801 rv = NS_NOTFOUND; 1802 switch (st->compat) { 1803 case COMPAT_MODE_ALL: 1804 rv = compat_redispatch(st, how, how, name, name, uid, pwd, 1805 buffer, bufsize, errnop); 1806 if (rv != NS_SUCCESS) 1807 st->compat = COMPAT_MODE_OFF; 1808 break; 1809 case COMPAT_MODE_NETGROUP: 1810 /* XXX getnetgrent is not thread-safe. */ 1811 do { 1812 rv = getnetgrent(&host, &user, &domain); 1813 if (rv == 0) { 1814 endnetgrent(); 1815 st->compat = COMPAT_MODE_OFF; 1816 rv = NS_NOTFOUND; 1817 continue; 1818 } else if (user == NULL || user[0] == '\0') 1819 continue; 1820 rv = compat_redispatch(st, how, nss_lt_name, name, 1821 user, uid, pwd, buffer, bufsize, errnop); 1822 } while (st->compat == COMPAT_MODE_NETGROUP && 1823 !(rv & NS_TERMINATE)); 1824 break; 1825 case COMPAT_MODE_NAME: 1826 rv = compat_redispatch(st, how, nss_lt_name, name, st->name, 1827 uid, pwd, buffer, bufsize, errnop); 1828 free(st->name); 1829 st->name = NULL; 1830 st->compat = COMPAT_MODE_OFF; 1831 break; 1832 default: 1833 break; 1834 } 1835 if (rv & NS_TERMINATE) { 1836 from_compat = 1; 1837 goto fin; 1838 } 1839 key.data = keybuf; 1840 rv = NS_NOTFOUND; 1841 while (st->keynum >= 0) { 1842 st->keynum++; 1843 if (st->version < _PWD_CURRENT_VERSION) { 1844 memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); 1845 key.size = sizeof(st->keynum) + 1; 1846 } else { 1847 store = htonl(st->keynum); 1848 memcpy(&keybuf[1], &store, sizeof(store)); 1849 key.size = sizeof(store) + 1; 1850 } 1851 keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version); 1852 rv = st->db->get(st->db, &key, &entry, 0); 1853 if (rv < 0 || rv > 1) { /* should never return > 1 */ 1854 *errnop = errno; 1855 rv = NS_UNAVAIL; 1856 goto fin; 1857 } else if (rv == 1) { 1858 st->keynum = -1; 1859 rv = NS_NOTFOUND; 1860 goto fin; 1861 } 1862 pw_name = (char *)entry.data; 1863 switch (pw_name[0]) { 1864 case '+': 1865 switch (pw_name[1]) { 1866 case '\0': 1867 st->compat = COMPAT_MODE_ALL; 1868 break; 1869 case '@': 1870 setnetgrent(&pw_name[2]); 1871 st->compat = COMPAT_MODE_NETGROUP; 1872 break; 1873 default: 1874 st->name = strdup(&pw_name[1]); 1875 if (st->name == NULL) { 1876 syslog(LOG_ERR, 1877 "getpwent memory allocation failure"); 1878 *errnop = ENOMEM; 1879 rv = NS_UNAVAIL; 1880 break; 1881 } 1882 st->compat = COMPAT_MODE_NAME; 1883 } 1884 if (entry.size > bufsize) { 1885 *errnop = ERANGE; 1886 rv = NS_RETURN; 1887 goto fin; 1888 } 1889 memcpy(buffer, entry.data, entry.size); 1890 rv = pwdb_versions[st->version].parse(buffer, 1891 entry.size, pwd, errnop); 1892 if (rv != NS_SUCCESS) 1893 ; 1894 else if (compat_set_template(pwd, &st->template) < 0) { 1895 *errnop = ENOMEM; 1896 rv = NS_UNAVAIL; 1897 goto fin; 1898 } 1899 goto docompat; 1900 case '-': 1901 switch (pw_name[1]) { 1902 case '\0': 1903 /* XXX Maybe syslog warning */ 1904 continue; 1905 case '@': 1906 setnetgrent(&pw_name[2]); 1907 while (getnetgrent(&host, &user, &domain) != 1908 0) { 1909 if (user != NULL && user[0] != '\0') 1910 compat_exclude(user, 1911 &st->exclude); 1912 } 1913 endnetgrent(); 1914 continue; 1915 default: 1916 compat_exclude(&pw_name[1], &st->exclude); 1917 continue; 1918 } 1919 break; 1920 default: 1921 break; 1922 } 1923 if (compat_is_excluded((char *)entry.data, st->exclude)) 1924 continue; 1925 rv = pwdb_versions[st->version].match(entry.data, entry.size, 1926 how, name, uid); 1927 if (rv == NS_RETURN) 1928 break; 1929 else if (rv != NS_SUCCESS) 1930 continue; 1931 if (entry.size > bufsize) { 1932 *errnop = ERANGE; 1933 rv = NS_RETURN; 1934 break; 1935 } 1936 memcpy(buffer, entry.data, entry.size); 1937 rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, 1938 errnop); 1939 if (rv & NS_TERMINATE) 1940 break; 1941 } 1942 fin: 1943 if (!stayopen && st->db != NULL) { 1944 (void)st->db->close(st->db); 1945 st->db = NULL; 1946 } 1947 if (rv == NS_SUCCESS) { 1948 if (!from_compat) { 1949 pwd->pw_fields &= ~_PWF_SOURCE; 1950 pwd->pw_fields |= _PWF_FILES; 1951 } 1952 if (retval != NULL) 1953 *(struct passwd **)retval = pwd; 1954 } 1955 return (rv); 1956 } 1957 1958 1959 /* 1960 * common passwd line matching and parsing 1961 */ 1962 int 1963 __pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, 1964 const char *name, uid_t uid) 1965 { 1966 const char *p, *eom; 1967 char *q; 1968 size_t len; 1969 unsigned long m; 1970 1971 eom = entry + entrysize; 1972 for (p = entry; p < eom; p++) 1973 if (*p == ':') 1974 break; 1975 if (*p != ':') 1976 return (NS_NOTFOUND); 1977 if (how == nss_lt_all) 1978 return (NS_SUCCESS); 1979 if (how == nss_lt_name) { 1980 len = strlen(name); 1981 if (len == (p - entry) && memcmp(name, entry, len) == 0) 1982 return (NS_SUCCESS); 1983 else 1984 return (NS_NOTFOUND); 1985 } 1986 for (p++; p < eom; p++) 1987 if (*p == ':') 1988 break; 1989 if (*p != ':') 1990 return (NS_NOTFOUND); 1991 m = strtoul(++p, &q, 10); 1992 if (q[0] != ':' || (uid_t)m != uid) 1993 return (NS_NOTFOUND); 1994 else 1995 return (NS_SUCCESS); 1996 } 1997 1998 1999 /* XXX buffer must be NUL-terminated. errnop is not set correctly. */ 2000 int 2001 __pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd, 2002 int master, int *errnop __unused) 2003 { 2004 2005 if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0) 2006 return (NS_NOTFOUND); 2007 else 2008 return (NS_SUCCESS); 2009 } 2010