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