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