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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * nis_common.c 29 * 30 * Common code and structures used by name-service-switch "nis" backends. 31 */ 32 33 #include "nis_common.h" 34 #include <string.h> 35 #include <synch.h> 36 #include <rpcsvc/ypclnt.h> 37 #include <rpcsvc/yp_prot.h> 38 #include <thread.h> 39 #include <ctype.h> 40 #include <stdlib.h> 41 #include <signal.h> 42 43 #ifndef MT_UNSAFE_YP /* Is the libnsl YP client code MT-unsafe? */ 44 #define MT_UNSAFE_YP 0 /* No, not any longer */ 45 #endif 46 47 #if MT_UNSAFE_YP 48 static mutex_t one_lane = DEFAULTMUTEX; 49 #endif 50 51 /* <rpcsvc/ypclnt.h> uses (char *) where it should use (const char *) */ 52 typedef char *grrr; 53 54 /* 55 * The YP client code thinks it's being helpful by appending '\n' and '\0' 56 * to the values returned by yp_match() et al. In order to do this it 57 * ends up doing more malloc()ing and data copying than would otherwise 58 * be necessary. If we're interested in performance we should provide 59 * alternative library interfaces that skip the helpfulness and instead 60 * let the XDR routines dump the value directly into the buffer where 61 * we really want it. For now, though, we just use the vanilla interface. 62 */ 63 64 static nss_status_t 65 switch_err(ypstatus, ismatch) 66 int ypstatus; 67 int ismatch; 68 { 69 switch (ypstatus) { 70 case 0: 71 errno = 0; 72 return (NSS_SUCCESS); 73 74 case YPERR_BADARGS: 75 case YPERR_KEY: 76 errno = 0; 77 return (NSS_NOTFOUND); 78 79 /* 80 * When the YP server is running in DNS forwarding mode, 81 * the forwarder will return YPERR_NOMORE to us if it 82 * is unable to contact a server (i.e., it has timed out). 83 * The NSS_NISSERVDNS_TRYAGAIN is returned for timeout errors. 84 */ 85 case YPERR_NOMORE: 86 if (ismatch) 87 return (NSS_NISSERVDNS_TRYAGAIN); 88 else 89 return (NSS_NOTFOUND); 90 91 case YPERR_DOMAIN: 92 case YPERR_YPSERV: 93 case YPERR_BUSY: 94 return (NSS_TRYAGAIN); 95 96 default: 97 return (NSS_UNAVAIL); 98 } 99 } 100 101 /*ARGSUSED*/ 102 nss_status_t 103 _nss_nis_setent(be, dummy) 104 nis_backend_ptr_t be; 105 void *dummy; 106 { 107 if (be->enum_key != 0) { 108 free(be->enum_key); 109 be->enum_key = 0; 110 } 111 be->enum_keylen = 0; 112 return (NSS_SUCCESS); 113 } 114 115 nss_status_t 116 _nss_nis_endent(be, dummy) 117 nis_backend_ptr_t be; 118 void *dummy; 119 { 120 return (_nss_nis_setent(be, dummy)); 121 /* Nothing else we can clean up, is there? */ 122 } 123 124 void 125 massage_netdb(const char **valp, int *vallenp) 126 { 127 const char *first; 128 const char *last; 129 const char *val = *valp; 130 int vallen = *vallenp; 131 132 if ((last = memchr(val, '#', vallen)) == 0) { 133 last = val + vallen; 134 } 135 for (first = val; first < last && isspace(*first); first++) { 136 ; 137 } 138 for (/* cstyle */; first < last && isspace(last[-1]); last--) { 139 ; 140 } 141 /* 142 * Don't check for an empty line because it shouldn't ever 143 * have made it into the YP map. 144 */ 145 *valp = first; 146 *vallenp = (int)(last - first); 147 } 148 149 nss_status_t 150 _nss_nis_ypmatch(domain, map, key, valp, vallenp, ypstatusp) 151 const char *domain; 152 const char *map; 153 const char *key; 154 char **valp; 155 int *vallenp; 156 int *ypstatusp; 157 { 158 int ypstatus; 159 160 #if MT_UNSAFE_YP 161 sigset_t oldmask, newmask; 162 163 (void) sigfillset(&newmask); 164 (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 165 (void) mutex_lock(&one_lane); 166 #endif 167 ypstatus = __yp_match_cflookup((grrr)domain, (grrr)map, 168 (grrr)key, (int)strlen(key), valp, vallenp, 0); 169 #if MT_UNSAFE_YP 170 (void) mutex_unlock(&one_lane); 171 (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 172 #endif 173 174 if (ypstatusp != 0) { 175 *ypstatusp = ypstatus; 176 } 177 return (switch_err(ypstatus, 1)); 178 } 179 180 /* 181 * XXX special version of _nss_nis_ypmatch() for handling C2 (passwd.adjunct) 182 * lookups when we need a reserved port. 183 */ 184 185 static nss_status_t 186 _nss_nis_ypmatch_rsvdport(domain, map, key, valp, vallenp, ypstatusp) 187 const char *domain; 188 const char *map; 189 const char *key; 190 char **valp; 191 int *vallenp; 192 int *ypstatusp; 193 { 194 int ypstatus; 195 196 #if MT_UNSAFE_YP 197 sigset_t oldmask, newmask; 198 199 (void) sigfillset(&newmask); 200 (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 201 (void) mutex_lock(&one_lane); 202 #endif 203 ypstatus = __yp_match_rsvdport_cflookup((grrr)domain, (grrr)map, 204 (grrr)key, strlen(key), valp, vallenp, 0); 205 #if MT_UNSAFE_YP 206 (void) mutex_unlock(&one_lane); 207 (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 208 #endif 209 210 if (ypstatusp != 0) { 211 *ypstatusp = ypstatus; 212 } 213 return (switch_err(ypstatus, 1)); 214 } 215 216 nss_status_t 217 _nss_nis_lookup(be, args, netdb, map, key, ypstatusp) 218 nis_backend_ptr_t be; 219 nss_XbyY_args_t *args; 220 int netdb; 221 const char *map; 222 const char *key; 223 int *ypstatusp; 224 { 225 nss_status_t res; 226 int vallen; 227 char *val; 228 char *free_ptr; 229 int parsestat; 230 231 if ((res = _nss_nis_ypmatch(be->domain, map, key, &val, &vallen, 232 ypstatusp)) != NSS_SUCCESS) { 233 return (res); 234 } 235 236 parsestat = NSS_STR_PARSE_SUCCESS; 237 if (strcmp(map, "passwd.byname") == 0 || 238 strcmp(map, "passwd.byuid") == 0) { 239 parsestat = validate_passwd_ids(&val, &vallen, 1); 240 } else if (strcmp(map, "group.byname") == 0) 241 parsestat = validate_group_ids(&val, &vallen, 1); 242 if (parsestat != NSS_STR_PARSE_SUCCESS) { 243 free(val); 244 return (NSS_NOTFOUND); 245 } 246 247 free_ptr = val; 248 249 if (netdb) { 250 massage_netdb((const char **)&val, &vallen); 251 } 252 253 args->returnval = NULL; 254 args->returnlen = 0; 255 parsestat = (*args->str2ent)(val, vallen, 256 args->buf.result, args->buf.buffer, args->buf.buflen); 257 if (parsestat == NSS_STR_PARSE_SUCCESS) { 258 args->returnval = args->buf.result; 259 args->returnlen = vallen; 260 res = NSS_SUCCESS; 261 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 262 args->erange = 1; 263 /* We won't find this otherwise, anyway */ 264 res = NSS_NOTFOUND; 265 } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ 266 267 free(free_ptr); 268 269 return (res); 270 } 271 272 nss_status_t 273 _nss_nis_lookup_rsvdport(be, args, netdb, map, key, ypstatusp) 274 nis_backend_ptr_t be; 275 nss_XbyY_args_t *args; 276 int netdb; 277 const char *map; 278 const char *key; 279 int *ypstatusp; 280 { 281 nss_status_t res; 282 int vallen; 283 char *val; 284 char *free_ptr; 285 int parsestat; 286 287 if ((res = _nss_nis_ypmatch_rsvdport(be->domain, map, key, &val, 288 &vallen, ypstatusp)) != NSS_SUCCESS) { 289 return (res); 290 } 291 292 free_ptr = val; 293 294 if (netdb) { 295 massage_netdb((const char **)&val, &vallen); 296 } 297 298 args->returnval = NULL; 299 args->returnlen = 0; 300 parsestat = (*args->str2ent)(val, vallen, 301 args->buf.result, args->buf.buffer, args->buf.buflen); 302 if (parsestat == NSS_STR_PARSE_SUCCESS) { 303 args->returnval = args->buf.result; 304 args->returnlen = vallen; 305 res = NSS_SUCCESS; 306 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 307 args->erange = 1; 308 /* We won't find this otherwise, anyway */ 309 res = NSS_NOTFOUND; 310 } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ 311 312 free(free_ptr); 313 314 return (res); 315 } 316 317 static nss_status_t 318 do_getent(be, args, netdb) 319 nis_backend_ptr_t be; 320 nss_XbyY_args_t *args; 321 int netdb; 322 { 323 nss_status_t res; 324 int ypstatus; 325 int outkeylen, outvallen; 326 char *outkey, *outval; 327 char *free_ptr; 328 int parsestat; 329 330 #if MT_UNSAFE_YP 331 sigset_t oldmask, newmask; 332 333 (void) sigfillset(&newmask); 334 (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 335 (void) mutex_lock(&one_lane); 336 #endif 337 if (be->enum_key == 0) { 338 ypstatus = __yp_first_cflookup((grrr)be->domain, 339 (grrr)be->enum_map, &outkey, 340 &outkeylen, &outval, 341 &outvallen, 0); 342 } else { 343 ypstatus = __yp_next_cflookup((grrr)be->domain, 344 (grrr)be->enum_map, be->enum_key, 345 be->enum_keylen, &outkey, 346 &outkeylen, &outval, 347 &outvallen, 0); 348 } 349 #if MT_UNSAFE_YP 350 (void) mutex_unlock(&one_lane); 351 (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 352 #endif 353 354 if ((res = switch_err(ypstatus, 0)) != NSS_SUCCESS) { 355 return (res); 356 } 357 358 free_ptr = outval; 359 360 if (netdb) { 361 massage_netdb((const char **)&outval, &outvallen); 362 } 363 364 args->returnval = NULL; 365 args->returnlen = 0; 366 parsestat = (*args->str2ent)(outval, outvallen, 367 args->buf.result, args->buf.buffer, args->buf.buflen); 368 if (parsestat == NSS_STR_PARSE_SUCCESS) { 369 args->returnval = args->buf.result; 370 args->returnlen = outvallen; 371 res = NSS_SUCCESS; 372 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 373 args->erange = 1; 374 /* We won't find this otherwise, anyway */ 375 res = NSS_NOTFOUND; 376 } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ 377 378 free(free_ptr); 379 380 if (be->enum_key != 0) { 381 free(be->enum_key); 382 } 383 be->enum_key = outkey; 384 be->enum_keylen = outkeylen; 385 386 return (res); 387 } 388 389 nss_status_t 390 _nss_nis_getent_rigid(be, args) 391 nis_backend_ptr_t be; 392 void *args; 393 { 394 return (do_getent(be, (nss_XbyY_args_t *)args, 0)); 395 } 396 397 nss_status_t 398 _nss_nis_getent_netdb(be, args) 399 nis_backend_ptr_t be; 400 void *args; 401 { 402 return (do_getent(be, (nss_XbyY_args_t *)args, 1)); 403 } 404 405 406 struct cb_data { 407 void *args; 408 const char *filter; 409 nis_do_all_func_t func; 410 nss_status_t result; 411 }; 412 413 enum { ITER_NEXT = 0, ITER_STOP = 1 }; /* Should be in <rpcsvc/ypclnt.h> */ 414 415 /*ARGSUSED*/ 416 static int 417 do_cback(instatus, inkey, inkeylen, inval, invallen, indata) 418 int instatus; 419 const char *inkey; 420 int inkeylen; 421 const char *inval; 422 int invallen; 423 struct cb_data *indata; 424 { 425 nss_status_t res; 426 427 if (instatus != YP_TRUE) { 428 return (ITER_NEXT); /* yp_all may decide otherwise... */ 429 } 430 431 if (indata->filter != 0 && strstr(inval, indata->filter) == 0) { 432 /* 433 * Optimization: if the entry doesn't contain the filter 434 * string then it can't be the entry we want, so don't 435 * bother looking more closely at it. 436 */ 437 return (ITER_NEXT); 438 } 439 440 res = (*indata->func)(inval, invallen, indata->args); 441 442 if (res == NSS_NOTFOUND) { 443 return (ITER_NEXT); 444 } else { 445 indata->result = res; 446 return (ITER_STOP); 447 } 448 } 449 450 nss_status_t 451 _nss_nis_do_all(be, args, filter, func) 452 nis_backend_ptr_t be; 453 void *args; 454 const char *filter; 455 nis_do_all_func_t func; 456 { 457 int ypall_status; 458 struct cb_data data; 459 struct ypall_callback cback; 460 461 data.args = args; 462 data.filter = filter; 463 data.func = func; 464 data.result = NSS_NOTFOUND; 465 466 cback.foreach = do_cback; 467 cback.data = (char *)&data; 468 469 #if MT_UNSAFE_YP 470 sigset_t oldmask, newmask; 471 472 (void) sigfillset(&newmask); 473 (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 474 (void) mutex_lock(&one_lane); 475 #endif 476 ypall_status = __yp_all_cflookup((grrr)be->domain, 477 (grrr) be->enum_map, &cback, 0); 478 #if MT_UNSAFE_YP 479 (void) mutex_unlock(&one_lane); 480 (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 481 #endif 482 483 switch (ypall_status) { 484 case 0: 485 return (data.result); 486 case YPERR_DOMAIN: 487 case YPERR_YPSERV: 488 case YPERR_BUSY: /* Probably never get this, but... */ 489 return (NSS_TRYAGAIN); 490 default: 491 return (NSS_UNAVAIL); 492 } 493 } 494 495 struct XbyY_data { 496 nss_XbyY_args_t *args; 497 nis_XY_check_func func; 498 int netdb; 499 }; 500 501 static nss_status_t 502 XbyY_iterator(instr, instr_len, a) 503 const char *instr; 504 int instr_len; 505 void *a; 506 { 507 struct XbyY_data *xydata = (struct XbyY_data *)a; 508 nss_XbyY_args_t *args = xydata->args; 509 nss_status_t res; 510 int parsestat; 511 512 if (xydata->netdb) { 513 massage_netdb(&instr, &instr_len); 514 } 515 516 args->returnval = NULL; 517 args->returnlen = 0; 518 parsestat = (*args->str2ent)(instr, instr_len, 519 args->buf.result, args->buf.buffer, args->buf.buflen); 520 if (parsestat == NSS_STR_PARSE_SUCCESS) { 521 args->returnval = args->buf.result; 522 if ((*xydata->func)(args)) { 523 res = NSS_SUCCESS; 524 args->returnlen = instr_len; 525 } else { 526 res = NSS_NOTFOUND; 527 args->returnval = 0; 528 } 529 } else if (parsestat == NSS_STR_PARSE_ERANGE) { 530 /* 531 * If we got here because (*str2ent)() found that the buffer 532 * wasn't big enough, maybe we should quit and return erange. 533 * Instead we'll keep looking and eventually return "not 534 * found" -- it's a bug, but not an earth-shattering one. 535 */ 536 args->erange = 1; /* <== Is this a good idea? */ 537 res = NSS_NOTFOUND; 538 } /* else if (parsestat == NSS_STR_PARSE_PARSE) won't happen ! */ 539 540 return (res); 541 } 542 543 nss_status_t 544 _nss_nis_XY_all(be, args, netdb, filter, func) 545 nis_backend_ptr_t be; 546 nss_XbyY_args_t *args; 547 int netdb; 548 const char *filter; 549 nis_XY_check_func func; 550 { 551 struct XbyY_data data; 552 553 data.args = args; 554 data.func = func; 555 data.netdb = netdb; 556 557 return (_nss_nis_do_all(be, &data, filter, XbyY_iterator)); 558 /* Now how many levels of callbacks was that? */ 559 } 560 561 562 /*ARGSUSED*/ 563 nss_status_t 564 _nss_nis_destr(be, dummy) 565 nis_backend_ptr_t be; 566 void *dummy; 567 { 568 if (be != 0) { 569 /* === Should change to invoke ops[ENDENT] ? */ 570 (void) _nss_nis_endent(be, 0); 571 free(be); 572 } 573 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ 574 } 575 576 /* We want to lock this even if the YP routines are MT-safe */ 577 static mutex_t yp_domain_lock = DEFAULTMUTEX; 578 static char *yp_domain; 579 580 const char * 581 _nss_nis_domain() 582 { 583 char *domain; 584 585 /* 586 * This much locking is probably more "by the book" than necessary... 587 */ 588 sigset_t oldmask, newmask; 589 590 (void) sigfillset(&newmask); 591 (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 592 (void) mutex_lock(&yp_domain_lock); 593 594 if ((domain = yp_domain) == 0) { 595 #if MT_UNSAFE_YP 596 (void) mutex_lock(&one_lane); 597 #endif 598 if (yp_get_default_domain(&yp_domain) == 0) { 599 domain = yp_domain; 600 } 601 #if MT_UNSAFE_YP 602 (void) mutex_unlock(&one_lane); 603 #endif 604 } 605 606 (void) mutex_unlock(&yp_domain_lock); 607 (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 608 609 return (domain); 610 } 611 612 nss_backend_t * 613 _nss_nis_constr(ops, n_ops, enum_map) 614 nis_backend_op_t ops[]; 615 int n_ops; 616 const char *enum_map; 617 { 618 const char *domain; 619 nis_backend_ptr_t be; 620 621 if ((domain = _nss_nis_domain()) == 0 || 622 (be = (nis_backend_ptr_t)malloc(sizeof (*be))) == 0) { 623 return (0); 624 } 625 be->ops = ops; 626 be->n_ops = n_ops; 627 be->domain = domain; 628 be->enum_map = enum_map; /* Don't strdup, assume valid forever */ 629 be->enum_key = 0; 630 be->enum_keylen = 0; 631 632 return ((nss_backend_t *)be); 633 } 634 635 /* 636 * This routine is used to parse lines of the form: 637 * name number aliases 638 * It returns 1 if the key in argp matches any one of the 639 * names in the line, otherwise 0 640 * Used by rpc 641 */ 642 int 643 _nss_nis_check_name_aliases(nss_XbyY_args_t *argp, const char *line, 644 int linelen) 645 { 646 const char *limit, *linep, *keyp; 647 648 linep = line; 649 limit = line + linelen; 650 keyp = argp->key.name; 651 652 /* compare name */ 653 while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) { 654 keyp++; 655 linep++; 656 } 657 if (*keyp == '\0' && linep < limit && isspace(*linep)) 658 return (1); 659 /* skip remainder of the name, if any */ 660 while (linep < limit && !isspace(*linep)) 661 linep++; 662 /* skip the delimiting spaces */ 663 while (linep < limit && isspace(*linep)) 664 linep++; 665 /* compare with the aliases */ 666 while (linep < limit) { 667 /* 668 * 1st pass: skip number 669 * Other passes: skip remainder of the alias name, if any 670 */ 671 while (linep < limit && !isspace(*linep)) 672 linep++; 673 /* skip the delimiting spaces */ 674 while (linep < limit && isspace(*linep)) 675 linep++; 676 /* compare with the alias name */ 677 keyp = argp->key.name; 678 while (*keyp && linep < limit && !isspace(*linep) && 679 *keyp == *linep) { 680 keyp++; 681 linep++; 682 } 683 if (*keyp == '\0' && (linep == limit || isspace(*linep))) 684 return (1); 685 } 686 return (0); 687 } 688