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