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