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