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