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