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