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