1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Common code and structures used by name-service-switch "compat" backends. 26 * 27 * Most of the code in the "compat" backend is a perverted form of code from 28 * the "files" backend; this file is no exception. 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <bsm/libbsm.h> 36 #include <user_attr.h> 37 #include <pwd.h> 38 #include <shadow.h> 39 #include <grp.h> 40 #include <unistd.h> /* for GF_PATH */ 41 #include <dlfcn.h> 42 #include "compat_common.h" 43 44 /* 45 * This should be in a header. 46 */ 47 48 extern int yp_get_default_domain(char **domain); 49 50 /* from libc */ 51 extern int str2passwd(const char *instr, int lenstr, void *ent, 52 char *buffer, int buflen); 53 extern int str2spwd(const char *instr, int lenstr, void *ent, 54 char *buffer, int buflen); 55 extern int str2group(const char *instr, int lenstr, void *ent, 56 char *buffer, int buflen); 57 58 /* from libnsl */ 59 extern char *_strtok_escape(char *, char *, char **); 60 61 /* 62 * str2auuser_s and str2userattr_s are very simple version 63 * of the str2auuser() and str2userattr() that can be found in 64 * libnsl. They only copy the user name into the userstr_t 65 * or au_user_str_t structure (so check on user name can be 66 * performed). 67 */ 68 static int 69 str2auuser_s( 70 const char *instr, 71 int lenstr, 72 void *ent, 73 char *buffer, 74 int buflen) 75 { 76 char *last = NULL; 77 char *sep = KV_TOKEN_DELIMIT; 78 au_user_str_t *au_user = (au_user_str_t *)ent; 79 80 if (lenstr >= buflen) 81 return (NSS_STR_PARSE_ERANGE); 82 (void) strncpy(buffer, instr, buflen); 83 au_user->au_name = _strtok_escape(buffer, sep, &last); 84 return (0); 85 } 86 87 static int 88 str2userattr_s( 89 const char *instr, 90 int lenstr, 91 void *ent, 92 char *buffer, 93 int buflen) 94 { 95 char *last = NULL; 96 char *sep = KV_TOKEN_DELIMIT; 97 userstr_t *user = (userstr_t *)ent; 98 99 if (lenstr >= buflen) 100 return (NSS_STR_PARSE_ERANGE); 101 (void) strncpy(buffer, instr, buflen); 102 user->name = _strtok_escape(buffer, sep, &last); 103 return (0); 104 } 105 106 /* 107 * Routines to manage list of "-" users for get{pw, sp, gr}ent(). Current 108 * implementation is completely moronic; we use a linked list. But then 109 * that's what it's always done in 4.x... 110 */ 111 112 struct setofstrings { 113 char *name; 114 struct setofstrings *next; 115 /* 116 * === Should get smart and malloc the string and pointer as one 117 * object rather than two. 118 */ 119 }; 120 121 static void 122 strset_free(ssp) 123 strset_t *ssp; 124 { 125 strset_t cur, nxt; 126 127 for (cur = *ssp; cur != 0; cur = nxt) { 128 nxt = cur->next; 129 free(cur->name); 130 free(cur); 131 } 132 *ssp = 0; 133 } 134 135 static boolean_t 136 strset_add(ssp, nam) 137 strset_t *ssp; 138 const char *nam; 139 { 140 strset_t new; 141 142 if (0 == (new = (strset_t)malloc(sizeof (*new)))) { 143 return (B_FALSE); 144 } 145 if (0 == (new->name = malloc(strlen(nam) + 1))) { 146 free(new); 147 return (B_FALSE); 148 } 149 (void) strcpy(new->name, nam); 150 new->next = *ssp; 151 *ssp = new; 152 return (B_TRUE); 153 } 154 155 static boolean_t 156 strset_in(ssp, nam) 157 const strset_t *ssp; 158 const char *nam; 159 { 160 strset_t cur; 161 162 for (cur = *ssp; cur != 0; cur = cur->next) { 163 if (strcmp(cur->name, nam) == 0) { 164 return (B_TRUE); 165 } 166 } 167 return (B_FALSE); 168 } 169 170 /* 171 * Lookup and enumeration routines for +@group and -@group. 172 * 173 * This code knows a lot more about lib/libc/port/gen/getnetgrent.c than 174 * is really healthy. The set/get/end routines below duplicate code 175 * from that file, but keep the state information per-backend-instance 176 * instead of just per-process. 177 */ 178 179 extern void _nss_initf_netgroup(nss_db_params_t *); 180 /* 181 * Should really share the db_root in getnetgrent.c in order to get the 182 * resource-management quotas right, but this will have to do. 183 */ 184 static DEFINE_NSS_DB_ROOT(netgr_db_root); 185 186 static boolean_t 187 netgr_in(compat_backend_ptr_t be, const char *group, const char *user) 188 { 189 if (be->yp_domain == 0) { 190 if (yp_get_default_domain((char **)&be->yp_domain) != 0) { 191 return (B_FALSE); 192 } 193 } 194 return (innetgr(group, 0, user, be->yp_domain)); 195 } 196 197 static void 198 netgr_set(be, netgroup) 199 compat_backend_ptr_t be; 200 const char *netgroup; 201 { 202 /* 203 * ===> Need comment to explain that this first "if" is optimizing 204 * for the same-netgroup-as-last-time case 205 */ 206 if (be->getnetgrent_backend != 0 && 207 NSS_INVOKE_DBOP(be->getnetgrent_backend, 208 NSS_DBOP_SETENT, 209 (void *) netgroup) != NSS_SUCCESS) { 210 NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR, 211 0); 212 be->getnetgrent_backend = 0; 213 } 214 if (be->getnetgrent_backend == 0) { 215 struct nss_setnetgrent_args args; 216 217 args.netgroup = netgroup; 218 args.iterator = 0; 219 (void) nss_search(&netgr_db_root, _nss_initf_netgroup, 220 NSS_DBOP_NETGROUP_SET, &args); 221 be->getnetgrent_backend = args.iterator; 222 } 223 } 224 225 static boolean_t 226 netgr_next_u(be, up) 227 compat_backend_ptr_t be; 228 char **up; 229 { 230 if (be->netgr_buffer == 0 && 231 (be->netgr_buffer = malloc(NSS_BUFLEN_NETGROUP)) == 0) { 232 /* Out of memory */ 233 return (B_FALSE); 234 } 235 236 do { 237 struct nss_getnetgrent_args args; 238 239 args.buffer = be->netgr_buffer; 240 args.buflen = NSS_BUFLEN_NETGROUP; 241 args.status = NSS_NETGR_NO; 242 243 if (be->getnetgrent_backend != 0) { 244 NSS_INVOKE_DBOP(be->getnetgrent_backend, 245 NSS_DBOP_GETENT, &args); 246 } 247 248 if (args.status == NSS_NETGR_FOUND) { 249 *up = args.retp[NSS_NETGR_USER]; 250 } else { 251 return (B_FALSE); 252 } 253 } while (*up == 0); 254 return (B_TRUE); 255 } 256 257 static void 258 netgr_end(be) 259 compat_backend_ptr_t be; 260 { 261 if (be->getnetgrent_backend != 0) { 262 NSS_INVOKE_DBOP(be->getnetgrent_backend, 263 NSS_DBOP_DESTRUCTOR, 0); 264 be->getnetgrent_backend = 0; 265 } 266 if (be->netgr_buffer != 0) { 267 free(be->netgr_buffer); 268 be->netgr_buffer = 0; 269 } 270 } 271 272 273 #define MAXFIELDS 9 /* Sufficient for passwd (7), shadow (9), group (4) */ 274 275 static nss_status_t 276 do_merge(be, args, instr, linelen) 277 compat_backend_ptr_t be; 278 nss_XbyY_args_t *args; 279 const char *instr; 280 int linelen; 281 { 282 char *fields[MAXFIELDS]; 283 int i; 284 int overrides; 285 const char *p; 286 const char *end = instr + linelen; 287 nss_status_t res = NSS_NOTFOUND; 288 289 /* 290 * Potential optimization: only perform the field-splitting nonsense 291 * once per input line (at present, "+" and "+@netgroup" entries 292 * will cause us to do this multiple times in getent() requests). 293 */ 294 295 for (i = 0; i < MAXFIELDS; i++) { 296 fields[i] = 0; 297 } 298 for (p = instr, overrides = 0, i = 0; /* no test */; i++) { 299 const char *q = memchr(p, ':', end - p); 300 const char *r = (q == 0) ? end : q; 301 ssize_t len = r - p; 302 303 if (len > 0) { 304 char *s = malloc(len + 1); 305 if (s == 0) { 306 overrides = -1; /* Indicates "you lose" */ 307 break; 308 } 309 (void) memcpy(s, p, len); 310 s[len] = '\0'; 311 fields[i] = s; 312 overrides++; 313 } 314 if (q == 0) { 315 /* End of line */ 316 break; 317 } else { 318 /* Skip the colon at (*q) */ 319 p = q + 1; 320 } 321 } 322 if (overrides == 1) { 323 /* 324 * return result here if /etc file format is requested 325 */ 326 if (be->return_string_data != 1) { 327 /* No real overrides, return (*args) intact */ 328 res = NSS_SUCCESS; 329 } else { 330 free(fields[0]); 331 fields[0] = NULL; 332 } 333 } 334 335 if (overrides > 1 || be->return_string_data == 1) { 336 /* 337 * The zero'th field is always nonempty (+/-...), but at least 338 * one other field was also nonempty, i.e. wants to override 339 */ 340 switch ((*be->mergef)(be, args, (const char **)fields)) { 341 case NSS_STR_PARSE_SUCCESS: 342 if (be->return_string_data != 1) 343 args->returnval = args->buf.result; 344 else 345 args->returnval = args->buf.buffer; 346 args->erange = 0; 347 res = NSS_SUCCESS; 348 break; 349 case NSS_STR_PARSE_ERANGE: 350 args->returnval = 0; 351 args->erange = 1; 352 res = NSS_NOTFOUND; 353 break; 354 case NSS_STR_PARSE_PARSE: 355 args->returnval = 0; 356 args->erange = 0; 357 /* ===> Very likely the wrong thing to do... */ 358 res = NSS_NOTFOUND; 359 break; 360 } 361 } else if (res != NSS_SUCCESS) { 362 args->returnval = 0; 363 args->erange = 0; 364 res = NSS_UNAVAIL; /* ==> Right? */ 365 } 366 367 for (i = 0; i < MAXFIELDS; i++) { 368 if (fields[i] != 0) { 369 free(fields[i]); 370 } 371 } 372 373 return (res); 374 } 375 376 /*ARGSUSED*/ 377 nss_status_t 378 _nss_compat_setent(be, dummy) 379 compat_backend_ptr_t be; 380 void *dummy; 381 { 382 if (be->f == 0) { 383 if (be->filename == 0) { 384 /* Backend isn't initialized properly? */ 385 return (NSS_UNAVAIL); 386 } 387 if ((be->f = fopen(be->filename, "rF")) == 0) { 388 return (NSS_UNAVAIL); 389 } 390 } else { 391 rewind(be->f); 392 } 393 strset_free(&be->minuses); 394 /* ===> ??? nss_endent(be->db_rootp, be->db_initf, &be->db_context); */ 395 396 if ((strcmp(be->filename, USERATTR_FILENAME) == 0) || 397 (strcmp(be->filename, AUDITUSER_FILENAME) == 0)) 398 be->state = GETENT_ATTRDB; 399 else 400 be->state = GETENT_FILE; 401 402 be->return_string_data = 0; 403 404 /* ===> ?? netgroup stuff? */ 405 return (NSS_SUCCESS); 406 } 407 408 /*ARGSUSED*/ 409 nss_status_t 410 _nss_compat_endent(be, dummy) 411 compat_backend_ptr_t be; 412 void *dummy; 413 { 414 if (be->f != 0) { 415 (void) fclose(be->f); 416 be->f = 0; 417 } 418 if (be->buf != 0) { 419 free(be->buf); 420 be->buf = 0; 421 } 422 nss_endent(be->db_rootp, be->db_initf, &be->db_context); 423 424 be->state = GETENT_FILE; /* Probably superfluous but comforting */ 425 strset_free(&be->minuses); 426 netgr_end(be); 427 428 /* 429 * Question: from the point of view of resource-freeing vs. time to 430 * start up again, how much should we do in endent() and how much 431 * in the destructor? 432 */ 433 return (NSS_SUCCESS); 434 } 435 436 /*ARGSUSED*/ 437 nss_status_t 438 _nss_compat_destr(be, dummy) 439 compat_backend_ptr_t be; 440 void *dummy; 441 { 442 if (be != 0) { 443 if (be->f != 0) { 444 (void) _nss_compat_endent(be, 0); 445 } 446 nss_delete(be->db_rootp); 447 nss_delete(&netgr_db_root); 448 free(be->workarea); 449 free(be); 450 } 451 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ 452 } 453 454 static int 455 read_line(f, buffer, buflen) 456 FILE *f; 457 char *buffer; 458 int buflen; 459 { 460 /*CONSTCOND*/ 461 while (1) { 462 int linelen; 463 464 if (fgets(buffer, buflen, f) == 0) { 465 /* End of file */ 466 return (-1); 467 } 468 linelen = strlen(buffer); 469 /* linelen >= 1 (since fgets didn't return 0) */ 470 471 if (buffer[linelen - 1] == '\n') { 472 /* 473 * ===> The code below that calls read_line() doesn't 474 * play by the rules; it assumes in places that 475 * the line is null-terminated. For now we'll 476 * humour it. 477 */ 478 buffer[--linelen] = '\0'; 479 return (linelen); 480 } 481 if (feof(f)) { 482 /* Line is last line in file, and has no newline */ 483 return (linelen); 484 } 485 /* Line too long for buffer; toss it and loop for next line */ 486 /* ===== should syslog() in cases where previous code did */ 487 while (fgets(buffer, buflen, f) != 0 && 488 buffer[strlen(buffer) - 1] != '\n') { 489 ; 490 } 491 } 492 /*NOTREACHED*/ 493 } 494 495 static int 496 is_nss_lookup_by_name(int attrdb, nss_dbop_t op) 497 { 498 int result = 0; 499 500 if ((attrdb != 0) && 501 ((op == NSS_DBOP_AUDITUSER_BYNAME) || 502 (op == NSS_DBOP_USERATTR_BYNAME))) { 503 result = 1; 504 } else if ((attrdb == 0) && 505 ((op == NSS_DBOP_GROUP_BYNAME) || 506 (op == NSS_DBOP_PASSWD_BYNAME) || 507 (op == NSS_DBOP_SHADOW_BYNAME))) { 508 result = 1; 509 } 510 511 return (result); 512 } 513 514 /*ARGSUSED*/ 515 nss_status_t 516 _attrdb_compat_XY_all(be, argp, netdb, check, op_num) 517 compat_backend_ptr_t be; 518 nss_XbyY_args_t *argp; 519 int netdb; 520 compat_XY_check_func check; 521 nss_dbop_t op_num; 522 { 523 int parsestat; 524 int (*func)(); 525 const char *filter = argp->key.name; 526 nss_status_t res; 527 528 #ifdef DEBUG 529 (void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n"); 530 #endif /* DEBUG */ 531 532 if (be->buf == 0 && 533 (be->buf = malloc(be->minbuf)) == 0) { 534 return (NSS_UNAVAIL); 535 } 536 if (check != NULL) 537 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) 538 return (res); 539 540 res = NSS_NOTFOUND; 541 542 /* 543 * assume a NULL buf.result pointer is an indication 544 * that the lookup result should be returned in /etc 545 * file format (if called from _nss_compat_getent(), 546 * be->return_string_data and argp->buf.result 547 * would be set already if argp->buf.result is NULL) 548 */ 549 if (check != NULL) { 550 if (argp->buf.result == NULL) { 551 be->return_string_data = 1; 552 553 /* 554 * the code executed later needs the result struct 555 * as working area 556 */ 557 argp->buf.result = be->workarea; 558 } else 559 be->return_string_data = 0; 560 } 561 562 /* 563 * use an alternate str2ent function if necessary 564 */ 565 if (be->return_string_data == 1) 566 func = be->str2ent_alt; 567 else 568 func = argp->str2ent; 569 570 /*CONSTCOND*/ 571 while (1) { 572 int linelen; 573 char *instr = be->buf; 574 575 if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) { 576 /* End of file */ 577 argp->returnval = 0; 578 argp->erange = 0; 579 break; 580 } 581 if (filter != 0 && strstr(instr, filter) == 0) { 582 /* 583 * Optimization: if the entry doesn't contain the 584 * filter string then it can't be the entry we want, 585 * so don't bother looking more closely at it. 586 */ 587 continue; 588 } 589 if (netdb) { 590 char *first; 591 char *last; 592 593 if ((last = strchr(instr, '#')) == 0) { 594 last = instr + linelen; 595 } 596 *last-- = '\0'; /* Nuke '\n' or #comment */ 597 598 /* 599 * Skip leading whitespace. Normally there isn't 600 * any, so it's not worth calling strspn(). 601 */ 602 for (first = instr; isspace(*first); first++) { 603 ; 604 } 605 if (*first == '\0') { 606 continue; 607 } 608 /* 609 * Found something non-blank on the line. Skip back 610 * over any trailing whitespace; since we know 611 * there's non-whitespace earlier in the line, 612 * checking for termination is easy. 613 */ 614 while (isspace(*last)) { 615 --last; 616 } 617 linelen = last - first + 1; 618 if (first != instr) { 619 instr = first; 620 } 621 } 622 argp->returnval = 0; 623 parsestat = (*func)(instr, linelen, argp->buf.result, 624 argp->buf.buffer, argp->buf.buflen); 625 if (parsestat == NSS_STR_PARSE_SUCCESS) { 626 argp->returnval = argp->buf.result; 627 if (check == 0 || (*check)(argp)) { 628 int len; 629 630 if (be->return_string_data != 1) { 631 res = NSS_SUCCESS; 632 break; 633 } 634 635 /* copy string data to result buffer */ 636 argp->buf.result = NULL; 637 argp->returnval = argp->buf.buffer; 638 if ((len = strlcpy(argp->buf.buffer, instr, 639 argp->buf.buflen)) >= 640 argp->buf.buflen) { 641 argp->returnval = NULL; 642 res = NSS_NOTFOUND; 643 argp->erange = 1; 644 break; 645 } 646 647 argp->returnlen = len; 648 res = NSS_SUCCESS; 649 break; 650 } 651 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 652 res = NSS_NOTFOUND; 653 argp->erange = 1; 654 break; 655 } 656 } 657 /* 658 * stayopen is set to 0 by default in order to close the opened 659 * file. Some applications may break if it is set to 1. 660 */ 661 if (check != 0 && !argp->stayopen) { 662 (void) _nss_compat_endent(be, 0); 663 } 664 665 if (res != NSS_SUCCESS) { 666 /* 667 * tell the nss_search() and nss_getent() below 668 * if the result should be returned in the /etc 669 * file format 670 */ 671 if (be->return_string_data == 1) 672 argp->buf.result = NULL; 673 674 if ((op_num == NSS_DBOP_USERATTR_BYNAME) || 675 (op_num == NSS_DBOP_AUDITUSER_BYNAME)) { 676 res = nss_search(be->db_rootp, 677 be->db_initf, 678 op_num, 679 argp); 680 } else { 681 res = nss_getent(be->db_rootp, 682 be->db_initf, &be->db_context, argp); 683 } 684 if (res != NSS_SUCCESS) { 685 argp->returnval = 0; 686 argp->erange = 0; 687 } 688 } 689 690 return (res); 691 } 692 693 static int 694 validate_ids(compat_backend_ptr_t be, nss_XbyY_args_t *argp, 695 char *line, int *linelenp, int buflen, int extra_chars) 696 { 697 if (be->return_string_data != 1) { 698 struct passwd *p; 699 struct group *g; 700 /* 701 * The data is already marshalled into 702 * struct passwd or group. 703 */ 704 if (strcmp(be->filename, PASSWD) == 0) { 705 p = (struct passwd *)argp->returnval; 706 if (p->pw_uid > MAXUID) 707 p->pw_uid = UID_NOBODY; 708 if (p->pw_gid > MAXUID) 709 p->pw_gid = GID_NOBODY; 710 } else if (strcmp(be->filename, GF_PATH) == 0) { 711 g = (struct group *)argp->returnval; 712 if (g->gr_gid > MAXUID) 713 g->gr_gid = GID_NOBODY; 714 } 715 return (NSS_STR_PARSE_SUCCESS); 716 } 717 718 /* 719 * The data needs to be returned in string format therefore 720 * validate the return string. 721 */ 722 if (strcmp(be->filename, PASSWD) == 0) 723 return (validate_passwd_ids(line, linelenp, buflen, 724 extra_chars)); 725 else if (strcmp(be->filename, GF_PATH) == 0) 726 return (validate_group_ids(line, linelenp, buflen, 727 extra_chars)); 728 return (NSS_STR_PARSE_SUCCESS); 729 } 730 731 nss_status_t 732 _nss_compat_XY_all(be, args, check, op_num) 733 compat_backend_ptr_t be; 734 nss_XbyY_args_t *args; 735 compat_XY_check_func check; 736 nss_dbop_t op_num; 737 { 738 nss_status_t res; 739 int parsestat; 740 741 742 if (be->buf == 0 && 743 (be->buf = malloc(be->minbuf)) == 0) { 744 return (NSS_UNAVAIL); /* really panic, malloc failed */ 745 } 746 747 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) { 748 return (res); 749 } 750 751 res = NSS_NOTFOUND; 752 753 /* 754 * assume a NULL buf.result pointer is an indication 755 * that the lookup result should be returned in /etc 756 * file format 757 */ 758 if (args->buf.result == NULL) { 759 760 be->return_string_data = 1; 761 762 /* 763 * the code executed later needs the result struct 764 * as working area 765 */ 766 args->buf.result = be->workarea; 767 768 be->str2ent_save = args->str2ent; 769 args->str2ent = be->str2ent_alt; 770 } else 771 be->return_string_data = 0; 772 773 /*CONSTCOND*/ 774 while (1) { 775 int linelen; 776 char *instr = be->buf; 777 char *colon; 778 779 linelen = read_line(be->f, instr, be->minbuf); 780 if (linelen < 0) { 781 /* End of file */ 782 args->returnval = 0; 783 args->erange = 0; 784 break; 785 } 786 787 args->returnval = 0; /* reset for both types of entries */ 788 789 if (instr[0] != '+' && instr[0] != '-') { 790 /* Simple, wholesome, God-fearing entry */ 791 parsestat = (*args->str2ent)(instr, linelen, 792 args->buf.result, 793 args->buf.buffer, 794 args->buf.buflen); 795 if (parsestat == NSS_STR_PARSE_SUCCESS) { 796 args->returnval = args->buf.result; 797 if ((*check)(args) != 0) { 798 int len; 799 800 parsestat = validate_ids(be, args, 801 instr, &linelen, be->minbuf, 1); 802 if (parsestat == 803 NSS_STR_PARSE_ERANGE) { 804 args->erange = 1; 805 res = NSS_NOTFOUND; 806 break; 807 } else if (parsestat != 808 NSS_STR_PARSE_SUCCESS) { 809 continue; 810 } 811 812 if (be->return_string_data != 1) { 813 res = NSS_SUCCESS; 814 break; 815 } 816 817 /* 818 * copy string data to 819 * result buffer 820 */ 821 args->buf.result = NULL; 822 args->str2ent = be->str2ent_save; 823 if ((len = strlcpy(args->buf.buffer, 824 instr, args->buf.buflen)) >= 825 args->buf.buflen) 826 parsestat = 827 NSS_STR_PARSE_ERANGE; 828 else { 829 args->returnval = 830 args->buf.buffer; 831 args->returnlen = len; 832 res = NSS_SUCCESS; 833 break; 834 } 835 } else 836 continue; 837 } 838 839 /* ===> Check the Dani logic here... */ 840 841 if (parsestat == NSS_STR_PARSE_ERANGE) { 842 args->erange = 1; 843 res = NSS_NOTFOUND; 844 break; 845 /* should we just skip this one long line ? */ 846 } /* else if (parsestat == NSS_STR_PARSE_PARSE) */ 847 /* don't care ! */ 848 849 /* ==> ?? */ continue; 850 } 851 852 853 /* 854 * Process "+", "+name", "+@netgroup", "-name" or "-@netgroup" 855 * 856 * This code is optimized for lookups by name. 857 * 858 * For lookups by identifier search key cannot be matched with 859 * the name of the "+" or "-" entry. So nss_search() is to be 860 * called before extracting the name i.e. via (*be->getnamef)(). 861 * 862 * But for lookups by name, search key is compared with the name 863 * of the "+" or "-" entry to acquire a match and thus 864 * unnesessary calls to nss_search() is eliminated. Also for 865 * matching "-" entries, calls to nss_search() is eliminated. 866 */ 867 868 if ((colon = strchr(instr, ':')) != 0) { 869 *colon = '\0'; /* terminate field to extract name */ 870 } 871 872 if (instr[1] == '@') { 873 /* 874 * Case 1: 875 * The entry is of the form "+@netgroup" or 876 * "-@netgroup". If we're performing a lookup by name, 877 * we can simply extract the name from the search key 878 * (i.e. args->key.name). If not, then we must call 879 * nss_search() before extracting the name via the 880 * get_XXname() function. i.e. (*be->getnamef)(args). 881 */ 882 if (is_nss_lookup_by_name(0, op_num) != 0) { 883 /* compare then search */ 884 if (!be->permit_netgroups || 885 !netgr_in(be, instr + 2, args->key.name)) 886 continue; 887 if (instr[0] == '+') { 888 /* need to search for "+" entry */ 889 (void) nss_search(be->db_rootp, 890 be->db_initf, op_num, args); 891 if (args->returnval == 0) 892 continue; 893 } 894 } else { 895 /* search then compare */ 896 (void) nss_search(be->db_rootp, 897 be->db_initf, op_num, args); 898 if (args->returnval == 0) 899 continue; 900 if (!be->permit_netgroups || 901 !netgr_in(be, instr + 2, 902 (*be->getnamef)(args))) 903 continue; 904 } 905 } else if (instr[1] == '\0') { 906 /* 907 * Case 2: 908 * The entry is of the form "+" or "-". The former 909 * allows all entries from name services. The latter 910 * is illegal and ought to be ignored. 911 */ 912 if (instr[0] == '-') 913 continue; 914 /* need to search for "+" entry */ 915 (void) nss_search(be->db_rootp, be->db_initf, 916 op_num, args); 917 if (args->returnval == 0) 918 continue; 919 } else { 920 /* 921 * Case 3: 922 * The entry is of the form "+name" or "-name". 923 * If we're performing a lookup by name, we can simply 924 * extract the name from the search key 925 * (i.e. args->key.name). If not, then we must call 926 * nss_search() before extracting the name via the 927 * get_XXname() function. i.e. (*be->getnamef)(args). 928 */ 929 if (is_nss_lookup_by_name(0, op_num) != 0) { 930 /* compare then search */ 931 if (strcmp(instr + 1, args->key.name) != 0) 932 continue; 933 if (instr[0] == '+') { 934 /* need to search for "+" entry */ 935 (void) nss_search(be->db_rootp, 936 be->db_initf, op_num, args); 937 if (args->returnval == 0) 938 continue; 939 } 940 } else { 941 /* search then compare */ 942 (void) nss_search(be->db_rootp, 943 be->db_initf, op_num, args); 944 if (args->returnval == 0) 945 continue; 946 if (strcmp(instr + 1, (*be->getnamef)(args)) 947 != 0) 948 continue; 949 } 950 } 951 if (instr[0] == '-') { 952 /* no need to search for "-" entry */ 953 args->returnval = 0; 954 args->erange = 0; 955 res = NSS_NOTFOUND; 956 } else { 957 if (colon != 0) 958 *colon = ':'; /* restoration */ 959 res = do_merge(be, args, instr, linelen); 960 } 961 break; 962 } 963 964 /* 965 * stayopen is set to 0 by default in order to close the opened 966 * file. Some applications may break if it is set to 1. 967 */ 968 if (!args->stayopen) { 969 (void) _nss_compat_endent(be, 0); 970 } 971 972 if (be->return_string_data == 1) { 973 args->str2ent = be->str2ent_save; 974 } 975 976 return (res); 977 } 978 979 nss_status_t 980 _nss_compat_getent(be, a) 981 compat_backend_ptr_t be; 982 void *a; 983 { 984 nss_XbyY_args_t *args = (nss_XbyY_args_t *)a; 985 nss_status_t res; 986 char *colon = 0; /* <=== need comment re lifetime */ 987 988 if (be->f == 0) { 989 if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) { 990 return (res); 991 } 992 } 993 994 if (be->buf == 0 && 995 (be->buf = malloc(be->minbuf)) == 0) { 996 return (NSS_UNAVAIL); /* really panic, malloc failed */ 997 } 998 999 /* 1000 * assume a NULL buf.result pointer is an indication 1001 * that the lookup result should be returned in /etc 1002 * file format 1003 */ 1004 if (args->buf.result == NULL) { 1005 be->return_string_data = 1; 1006 1007 /* 1008 * the code executed later needs the result struct 1009 * as working area 1010 */ 1011 args->buf.result = be->workarea; 1012 } else 1013 be->return_string_data = 0; 1014 1015 /*CONSTCOND*/ 1016 while (1) { 1017 char *instr = be->buf; 1018 int linelen; 1019 char *name; /* === Need more distinctive label */ 1020 const char *savename; 1021 1022 /* 1023 * In the code below... 1024 * break means "I found one, I think" (i.e. goto the 1025 * code after the end of the switch statement), 1026 * continue means "Next candidate" 1027 * (i.e. loop around to the switch statement), 1028 * return means "I'm quite sure" (either Yes or No). 1029 */ 1030 switch (be->state) { 1031 1032 case GETENT_DONE: 1033 args->returnval = 0; 1034 args->erange = 0; 1035 return (NSS_NOTFOUND); 1036 1037 case GETENT_ATTRDB: 1038 args->key.name = NULL; 1039 res = _attrdb_compat_XY_all(be, 1040 args, 1, (compat_XY_check_func)NULL, 0); 1041 return (res); 1042 1043 case GETENT_FILE: 1044 linelen = read_line(be->f, instr, be->minbuf); 1045 if (linelen < 0) { 1046 /* End of file */ 1047 be->state = GETENT_DONE; 1048 continue; 1049 } 1050 if ((colon = strchr(instr, ':')) != 0) { 1051 *colon = '\0'; 1052 } 1053 if (instr[0] == '-') { 1054 if (instr[1] != '@') { 1055 (void) strset_add(&be->minuses, 1056 instr + 1); 1057 } else if (be->permit_netgroups) { 1058 netgr_set(be, instr + 2); 1059 while (netgr_next_u(be, &name)) { 1060 (void) strset_add(&be->minuses, 1061 name); 1062 } 1063 netgr_end(be); 1064 } /* Else (silently) ignore the entry */ 1065 continue; 1066 } else if (instr[0] != '+') { 1067 int parsestat; 1068 /* 1069 * Normal entry, no +/- nonsense 1070 */ 1071 if (colon != 0) { 1072 *colon = ':'; 1073 } 1074 args->returnval = 0; 1075 parsestat = (*args->str2ent)(instr, linelen, 1076 args->buf.result, 1077 args->buf.buffer, 1078 args->buf.buflen); 1079 if (parsestat == NSS_STR_PARSE_SUCCESS) { 1080 int len; 1081 1082 if (be->return_string_data != 1) { 1083 args->returnval = 1084 args->buf.result; 1085 return (NSS_SUCCESS); 1086 } 1087 1088 /* 1089 * copy string data to 1090 * result buffer 1091 */ 1092 args->buf.result = NULL; 1093 args->returnval = 1094 args->buf.buffer; 1095 if ((len = strlcpy(args->buf.buffer, 1096 instr, args->buf.buflen)) >= 1097 args->buf.buflen) 1098 parsestat = 1099 NSS_STR_PARSE_ERANGE; 1100 else { 1101 args->returnlen = len; 1102 return (NSS_SUCCESS); 1103 } 1104 } 1105 /* ==> ?? Treat ERANGE differently ?? */ 1106 if (parsestat == NSS_STR_PARSE_ERANGE) { 1107 args->returnval = 0; 1108 args->erange = 1; 1109 return (NSS_NOTFOUND); 1110 } 1111 /* Skip the offending entry, get next */ 1112 continue; 1113 } else if (instr[1] == '\0') { 1114 /* Plain "+" */ 1115 nss_setent(be->db_rootp, be->db_initf, 1116 &be->db_context); 1117 be->state = GETENT_ALL; 1118 be->linelen = linelen; 1119 1120 continue; 1121 } else if (instr[1] == '@') { 1122 /* "+@netgroup" */ 1123 netgr_set(be, instr + 2); 1124 be->state = GETENT_NETGROUP; 1125 be->linelen = linelen; 1126 continue; 1127 } else { 1128 /* "+name" */ 1129 name = instr + 1; 1130 break; 1131 } 1132 /* NOTREACHED */ 1133 1134 case GETENT_ALL: 1135 linelen = be->linelen; 1136 args->returnval = 0; 1137 if (be->return_string_data == 1) { 1138 be->str2ent_save = args->str2ent; 1139 args->str2ent = be->str2ent_alt; 1140 } 1141 1142 (void) nss_getent(be->db_rootp, be->db_initf, 1143 &be->db_context, args); 1144 if (args->returnval == 0) { 1145 /* ==> ?? Treat ERANGE differently ?? */ 1146 nss_endent(be->db_rootp, be->db_initf, 1147 &be->db_context); 1148 be->state = GETENT_FILE; 1149 if (be->return_string_data == 1) 1150 args->str2ent = be->str2ent_save; 1151 continue; 1152 } 1153 if (strset_in(&be->minuses, (*be->getnamef)(args))) 1154 continue; 1155 name = 0; /* tell code below we've done the lookup */ 1156 if (be->return_string_data == 1) 1157 args->str2ent = be->str2ent_save; 1158 break; 1159 1160 case GETENT_NETGROUP: 1161 linelen = be->linelen; 1162 if (!netgr_next_u(be, &name)) { 1163 netgr_end(be); 1164 be->state = GETENT_FILE; 1165 continue; 1166 } 1167 /* pass "name" variable to code below... */ 1168 break; 1169 } 1170 1171 if (name != 0) { 1172 if (strset_in(&be->minuses, name)) { 1173 continue; 1174 } 1175 /* 1176 * Do a getXXXnam(name). If we were being pure, 1177 * we'd introduce yet another function-pointer 1178 * that the database-specific code had to supply 1179 * to us. Instead we'll be grotty and hard-code 1180 * the knowledge that 1181 * (a) The username is always passwd in key.name, 1182 * (b) NSS_DBOP_PASSWD_BYNAME == 1183 * NSS_DBOP_SHADOW_BYNAME == 1184 * NSS_DBOP_next_iter. 1185 */ 1186 savename = args->key.name; 1187 args->key.name = name; 1188 args->returnval = 0; 1189 if (be->return_string_data == 1) { 1190 be->str2ent_save = args->str2ent; 1191 args->str2ent = be->str2ent_alt; 1192 } 1193 1194 (void) nss_search(be->db_rootp, be->db_initf, 1195 NSS_DBOP_next_iter, args); 1196 1197 if (be->return_string_data == 1) 1198 args->str2ent = be->str2ent_save; 1199 args->key.name = savename; /* In case anyone cares */ 1200 } 1201 /* 1202 * Found one via "+", "+name" or "@netgroup". 1203 * Override some fields if the /etc file says to do so. 1204 */ 1205 if (args->returnval == 0) { 1206 /* ==> ?? Should treat erange differently? */ 1207 continue; 1208 } 1209 /* 'colon' was set umpteen iterations ago in GETENT_FILE */ 1210 if (colon != 0) { 1211 *colon = ':'; 1212 colon = 0; 1213 } 1214 return (do_merge(be, args, instr, linelen)); 1215 } 1216 /*NOTREACHED*/ 1217 } 1218 1219 /* We don't use this directly; we just copy the bits when we want to */ 1220 /* initialize the variable (in the compat_backend struct) that we do use */ 1221 static DEFINE_NSS_GETENT(context_initval); 1222 1223 nss_backend_t * 1224 _nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups, 1225 getname_func, merge_func) 1226 compat_backend_op_t ops[]; 1227 int n_ops; 1228 const char *filename; 1229 int min_bufsize; 1230 nss_db_root_t *rootp; 1231 nss_db_initf_t initf; 1232 int netgroups; 1233 compat_get_name getname_func; 1234 compat_merge_func merge_func; 1235 { 1236 compat_backend_ptr_t be; 1237 1238 if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) { 1239 return (0); 1240 } 1241 be->ops = ops; 1242 be->n_ops = n_ops; 1243 be->filename = filename; 1244 be->f = 0; 1245 be->minbuf = min_bufsize; 1246 be->buf = 0; 1247 1248 be->db_rootp = rootp; 1249 be->db_initf = initf; 1250 be->db_context = context_initval; 1251 1252 be->getnamef = getname_func; 1253 be->mergef = merge_func; 1254 1255 be->state = GETENT_FILE; /* i.e. do Automatic setent(); */ 1256 if (strcmp(be->filename, USERATTR_FILENAME) == 0) { 1257 be->state = GETENT_ATTRDB; 1258 be->str2ent_alt = str2userattr_s; 1259 be->workarea = calloc(1, sizeof (userstr_t)); 1260 } else if (strcmp(be->filename, AUDITUSER_FILENAME) == 0) { 1261 be->state = GETENT_ATTRDB; 1262 be->str2ent_alt = str2auuser_s; 1263 be->workarea = calloc(1, sizeof (au_user_str_t)); 1264 } else if (strcmp(be->filename, PASSWD) == 0) { 1265 be->str2ent_alt = str2passwd; 1266 be->workarea = calloc(1, sizeof (struct passwd)); 1267 } else if (strcmp(be->filename, SHADOW) == 0) { 1268 be->str2ent_alt = str2spwd; 1269 be->workarea = calloc(1, sizeof (struct spwd)); 1270 } else { /* group */ 1271 be->str2ent_alt = str2group; 1272 be->workarea = calloc(1, sizeof (struct group)); 1273 } 1274 if (be->workarea == NULL) 1275 return (NULL); 1276 1277 be->minuses = 0; 1278 1279 be->permit_netgroups = netgroups; 1280 be->yp_domain = 0; 1281 be->getnetgrent_backend = 0; 1282 be->netgr_buffer = 0; 1283 be->return_string_data = 0; 1284 1285 return ((nss_backend_t *)be); 1286 } 1287