1 /* $NetBSD: getusershell.c,v 1.27 2008/04/28 20:22:59 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1985, 1993 34 * The Regents of the University of California. All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61 #include <sys/cdefs.h> 62 #if defined(LIBC_SCCS) && !defined(lint) 63 #if 0 64 static char sccsid[] = "@(#)getusershell.c 8.1 (Berkeley) 6/4/93"; 65 #else 66 __RCSID("$NetBSD: getusershell.c,v 1.27 2008/04/28 20:22:59 martin Exp $"); 67 #endif 68 #endif /* LIBC_SCCS and not lint */ 69 70 #include "namespace.h" 71 #include "reentrant.h" 72 73 #include <sys/param.h> 74 #include <sys/file.h> 75 76 #include <assert.h> 77 #include <ctype.h> 78 #include <errno.h> 79 #include <nsswitch.h> 80 #include <paths.h> 81 #include <stdarg.h> 82 #include <stdio.h> 83 #include <stdlib.h> 84 #include <string.h> 85 #include <unistd.h> 86 87 #ifdef HESIOD 88 #include <hesiod.h> 89 #endif 90 #ifdef YP 91 #include <rpc/rpc.h> 92 #include <rpcsvc/ypclnt.h> 93 #include <rpcsvc/yp_prot.h> 94 #endif 95 96 #ifdef __weak_alias 97 __weak_alias(endusershell,_endusershell) 98 __weak_alias(getusershell,_getusershell) 99 __weak_alias(setusershell,_setusershell) 100 #endif 101 102 /* 103 * Local shells should NOT be added here. 104 * They should be added in /etc/shells. 105 */ 106 static const char *const okshells[] = { _PATH_BSHELL, _PATH_CSHELL, NULL }; 107 108 #ifdef _REENTRANT 109 static mutex_t __shellmutex = MUTEX_INITIALIZER; 110 #endif 111 112 static char curshell[MAXPATHLEN + 2]; 113 114 static const char *const *curokshell = okshells; 115 static int shellsfound = 0; 116 117 /* 118 * files methods 119 */ 120 121 /* state shared between files methods */ 122 struct files_state { 123 FILE *fp; 124 }; 125 126 static struct files_state _files_state; 127 128 129 static int 130 _files_start(struct files_state *state) 131 { 132 133 _DIAGASSERT(state != NULL); 134 135 if (state->fp == NULL) { 136 state->fp = fopen(_PATH_SHELLS, "r"); 137 if (state->fp == NULL) 138 return NS_UNAVAIL; 139 } else { 140 rewind(state->fp); 141 } 142 return NS_SUCCESS; 143 } 144 145 static int 146 _files_end(struct files_state *state) 147 { 148 149 _DIAGASSERT(state != NULL); 150 151 if (state->fp) { 152 (void) fclose(state->fp); 153 state->fp = NULL; 154 } 155 return NS_SUCCESS; 156 } 157 158 /*ARGSUSED*/ 159 static int 160 _files_setusershell(void *nsrv, void *nscb, va_list ap) 161 { 162 163 return _files_start(&_files_state); 164 } 165 166 /*ARGSUSED*/ 167 static int 168 _files_endusershell(void *nsrv, void *nscb, va_list ap) 169 { 170 171 return _files_end(&_files_state); 172 } 173 174 /*ARGSUSED*/ 175 static int 176 _files_getusershell(void *nsrv, void *nscb, va_list ap) 177 { 178 char **retval = va_arg(ap, char **); 179 180 char *sp, *cp; 181 int rv; 182 183 _DIAGASSERT(retval != NULL); 184 185 *retval = NULL; 186 if (_files_state.fp == NULL) { /* only start if file not open yet */ 187 rv = _files_start(&_files_state); 188 if (rv != NS_SUCCESS) 189 return rv; 190 } 191 192 while (fgets(curshell, sizeof(curshell) - 1, _files_state.fp) != NULL) { 193 sp = cp = curshell; 194 while (*cp != '#' && *cp != '/' && *cp != '\0') 195 cp++; 196 if (*cp == '#' || *cp == '\0') 197 continue; 198 sp = cp; 199 while (!isspace((unsigned char) *cp) && *cp != '#' 200 && *cp != '\0') 201 cp++; 202 *cp++ = '\0'; 203 *retval = sp; 204 return NS_SUCCESS; 205 } 206 207 return NS_NOTFOUND; 208 } 209 210 211 #ifdef HESIOD 212 /* 213 * dns methods 214 */ 215 216 /* state shared between dns methods */ 217 struct dns_state { 218 void *context; /* Hesiod context */ 219 int num; /* shell index, -1 if no more */ 220 }; 221 222 static struct dns_state _dns_state; 223 224 static int 225 _dns_start(struct dns_state *state) 226 { 227 228 _DIAGASSERT(state != NULL); 229 230 state->num = 0; 231 if (state->context == NULL) { /* setup Hesiod */ 232 if (hesiod_init(&state->context) == -1) 233 return NS_UNAVAIL; 234 } 235 236 return NS_SUCCESS; 237 } 238 239 static int 240 _dns_end(struct dns_state *state) 241 { 242 243 _DIAGASSERT(state != NULL); 244 245 state->num = 0; 246 if (state->context) { 247 hesiod_end(state->context); 248 state->context = NULL; 249 } 250 return NS_SUCCESS; 251 } 252 253 /*ARGSUSED*/ 254 static int 255 _dns_setusershell(void *nsrv, void *nscb, va_list ap) 256 { 257 258 return _dns_start(&_dns_state); 259 } 260 261 /*ARGSUSED*/ 262 static int 263 _dns_endusershell(void *nsrv, void *nscb, va_list ap) 264 { 265 266 return _dns_end(&_dns_state); 267 } 268 269 /*ARGSUSED*/ 270 static int 271 _dns_getusershell(void *nsrv, void *nscb, va_list ap) 272 { 273 char **retval = va_arg(ap, char **); 274 275 char shellname[] = "shells-NNNNNNNNNN"; 276 char **hp, *ep; 277 int rv; 278 279 _DIAGASSERT(retval != NULL); 280 281 *retval = NULL; 282 283 if (_dns_state.num == -1) /* exhausted search */ 284 return NS_NOTFOUND; 285 286 if (_dns_state.context == NULL) { 287 /* only start if Hesiod not setup */ 288 rv = _dns_start(&_dns_state); 289 if (rv != NS_SUCCESS) 290 return rv; 291 } 292 293 hp = NULL; 294 rv = NS_NOTFOUND; 295 296 /* find shells-NNN */ 297 snprintf(shellname, sizeof(shellname), "shells-%d", _dns_state.num); 298 _dns_state.num++; 299 300 hp = hesiod_resolve(_dns_state.context, shellname, "shells"); 301 if (hp == NULL) { 302 if (errno == ENOENT) 303 rv = NS_NOTFOUND; 304 else 305 rv = NS_UNAVAIL; 306 } else { 307 if ((ep = strchr(hp[0], '\n')) != NULL) 308 *ep = '\0'; /* clear trailing \n */ 309 /* only use first result */ 310 strlcpy(curshell, hp[0], sizeof(curshell)); 311 *retval = curshell; 312 rv = NS_SUCCESS; 313 } 314 315 if (hp) 316 hesiod_free_list(_dns_state.context, hp); 317 if (rv != NS_SUCCESS) 318 _dns_state.num = -1; /* any failure halts search */ 319 return rv; 320 } 321 322 #endif /* HESIOD */ 323 324 325 #ifdef YP 326 /* 327 * nis methods 328 */ 329 /* state shared between nis methods */ 330 struct nis_state { 331 char *domain; /* NIS domain */ 332 int done; /* non-zero if search exhausted */ 333 char *current; /* current first/next match */ 334 int currentlen; /* length of _nis_current */ 335 }; 336 337 static struct nis_state _nis_state; 338 339 static int 340 _nis_start(struct nis_state *state) 341 { 342 343 _DIAGASSERT(state != NULL); 344 345 state->done = 0; 346 if (state->current) { 347 free(state->current); 348 state->current = NULL; 349 } 350 if (state->domain == NULL) { /* setup NIS */ 351 switch (yp_get_default_domain(&state->domain)) { 352 case 0: 353 break; 354 case YPERR_RESRC: 355 return NS_TRYAGAIN; 356 default: 357 return NS_UNAVAIL; 358 } 359 } 360 return NS_SUCCESS; 361 } 362 363 static int 364 _nis_end(struct nis_state *state) 365 { 366 367 _DIAGASSERT(state != NULL); 368 369 if (state->domain) 370 state->domain = NULL; 371 state->done = 0; 372 if (state->current) 373 free(state->current); 374 state->current = NULL; 375 return NS_SUCCESS; 376 } 377 378 /*ARGSUSED*/ 379 static int 380 _nis_setusershell(void *nsrv, void *nscb, va_list ap) 381 { 382 383 return _nis_start(&_nis_state); 384 } 385 386 /*ARGSUSED*/ 387 static int 388 _nis_endusershell(void *nsrv, void *nscb, va_list ap) 389 { 390 391 return _nis_end(&_nis_state); 392 } 393 394 /*ARGSUSED*/ 395 static int 396 _nis_getusershell(void *nsrv, void *nscb, va_list ap) 397 { 398 char **retval = va_arg(ap, char **); 399 400 char *key, *data; 401 int keylen, datalen, rv, nisr; 402 403 _DIAGASSERT(retval != NULL); 404 405 *retval = NULL; 406 407 if (_nis_state.done) /* exhausted search */ 408 return NS_NOTFOUND; 409 if (_nis_state.domain == NULL) { 410 /* only start if NIS not setup */ 411 rv = _nis_start(&_nis_state); 412 if (rv != NS_SUCCESS) 413 return rv; 414 } 415 416 key = NULL; 417 data = NULL; 418 rv = NS_NOTFOUND; 419 420 if (_nis_state.current) { /* already searching */ 421 nisr = yp_next(_nis_state.domain, "shells", 422 _nis_state.current, _nis_state.currentlen, 423 &key, &keylen, &data, &datalen); 424 free(_nis_state.current); 425 _nis_state.current = NULL; 426 switch (nisr) { 427 case 0: 428 _nis_state.current = key; 429 _nis_state.currentlen = keylen; 430 key = NULL; 431 break; 432 case YPERR_NOMORE: 433 rv = NS_NOTFOUND; 434 goto nisent_out; 435 default: 436 rv = NS_UNAVAIL; 437 goto nisent_out; 438 } 439 } else { /* new search */ 440 if (yp_first(_nis_state.domain, "shells", 441 &_nis_state.current, &_nis_state.currentlen, 442 &data, &datalen)) { 443 rv = NS_UNAVAIL; 444 goto nisent_out; 445 } 446 } 447 448 data[datalen] = '\0'; /* clear trailing \n */ 449 strlcpy(curshell, data, sizeof(curshell)); 450 *retval = curshell; 451 rv = NS_SUCCESS; 452 453 nisent_out: 454 if (key) 455 free(key); 456 if (data) 457 free(data); 458 if (rv != NS_SUCCESS) /* any failure halts search */ 459 _nis_state.done = 1; 460 return rv; 461 } 462 463 #endif /* YP */ 464 465 466 /* 467 * public functions 468 */ 469 470 void 471 endusershell(void) 472 { 473 static const ns_dtab dtab[] = { 474 NS_FILES_CB(_files_endusershell, NULL) 475 NS_DNS_CB(_dns_endusershell, NULL) 476 NS_NIS_CB(_nis_endusershell, NULL) 477 NS_NULL_CB 478 }; 479 480 mutex_lock(&__shellmutex); 481 482 curokshell = okshells; /* reset okshells fallback state */ 483 shellsfound = 0; 484 485 /* force all endusershell() methods */ 486 (void) nsdispatch(NULL, dtab, NSDB_SHELLS, "endusershell", 487 __nsdefaultfiles_forceall); 488 mutex_unlock(&__shellmutex); 489 } 490 491 __aconst char * 492 getusershell(void) 493 { 494 int rv; 495 __aconst char *retval; 496 497 static const ns_dtab dtab[] = { 498 NS_FILES_CB(_files_getusershell, NULL) 499 NS_DNS_CB(_dns_getusershell, NULL) 500 NS_NIS_CB(_nis_getusershell, NULL) 501 NS_NULL_CB 502 }; 503 504 mutex_lock(&__shellmutex); 505 506 retval = NULL; 507 do { 508 rv = nsdispatch(NULL, dtab, NSDB_SHELLS, "getusershell", 509 __nsdefaultsrc, &retval); 510 /* loop until failure or non-blank result */ 511 } while (rv == NS_SUCCESS && retval[0] == '\0'); 512 513 if (rv == NS_SUCCESS) { 514 shellsfound++; 515 } else if (shellsfound == 0) { /* no shells; fall back to okshells */ 516 if (curokshell != NULL) { 517 retval = __UNCONST(*curokshell); 518 curokshell++; 519 rv = NS_SUCCESS; 520 } 521 } 522 523 mutex_unlock(&__shellmutex); 524 return (rv == NS_SUCCESS) ? retval : NULL; 525 } 526 527 void 528 setusershell(void) 529 { 530 static const ns_dtab dtab[] = { 531 NS_FILES_CB(_files_setusershell, NULL) 532 NS_DNS_CB(_dns_setusershell, NULL) 533 NS_NIS_CB(_nis_setusershell, NULL) 534 NS_NULL_CB 535 }; 536 537 mutex_lock(&__shellmutex); 538 539 curokshell = okshells; /* reset okshells fallback state */ 540 shellsfound = 0; 541 542 /* force all setusershell() methods */ 543 (void) nsdispatch(NULL, dtab, NSDB_SHELLS, "setusershell", 544 __nsdefaultfiles_forceall); 545 mutex_unlock(&__shellmutex); 546 } 547