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