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