1/*
2 * Copyright (c) 1985, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if defined(LIBC_SCCS) && !defined(lint)
31static char sccsid[] = "@(#)getusershell.c	8.1 (Berkeley) 6/4/93";
32#endif /* LIBC_SCCS and not lint */
33/*	$NetBSD: getusershell.c,v 1.17 1999/01/25 01:09:34 lukem Exp $	*/
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD$");
36
37#include "namespace.h"
38#include <sys/param.h>
39#include <sys/file.h>
40
41#include <ctype.h>
42#include <errno.h>
43#include <nsswitch.h>
44#include <paths.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <stringlist.h>
49#include <unistd.h>
50
51#ifdef HESIOD
52#include <hesiod.h>
53#endif
54#ifdef YP
55#include <rpc/rpc.h>
56#include <rpcsvc/ypclnt.h>
57#include <rpcsvc/yp_prot.h>
58#endif
59#include "un-namespace.h"
60
61static const char *const *curshell;
62static StringList	 *sl;
63
64static const char *const *initshells(void);
65
66/*
67 * Get a list of shells from "shells" nsswitch database
68 */
69char *
70getusershell(void)
71{
72	char *ret;
73
74	if (curshell == NULL)
75		curshell = initshells();
76	/*LINTED*/
77	ret = (char *)*curshell;
78	if (ret != NULL)
79		curshell++;
80	return (ret);
81}
82
83void
84endusershell(void)
85{
86	if (sl) {
87		sl_free(sl, 1);
88		sl = NULL;
89	}
90	curshell = NULL;
91}
92
93void
94setusershell(void)
95{
96
97	curshell = initshells();
98}
99
100
101static int	_local_initshells(void *, void *, va_list);
102
103/*ARGSUSED*/
104static int
105_local_initshells(void	*rv, void *cb_data, va_list ap)
106{
107	char	*sp, *cp;
108	FILE	*fp;
109	char	 line[MAXPATHLEN + 2];
110
111	if (sl)
112		sl_free(sl, 1);
113	sl = sl_init();
114
115	if ((fp = fopen(_PATH_SHELLS, "re")) == NULL)
116		return NS_UNAVAIL;
117
118	cp = line;
119	while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
120		while (*cp != '#' && *cp != '/' && *cp != '\0')
121			cp++;
122		if (*cp == '#' || *cp == '\0')
123			continue;
124		sp = cp;
125		while (!isspace(*cp) && *cp != '#' && *cp != '\0')
126			cp++;
127		*cp++ = '\0';
128		sl_add(sl, strdup(sp));
129	}
130	(void)fclose(fp);
131	return NS_SUCCESS;
132}
133
134#ifdef HESIOD
135static int	_dns_initshells(void *, void *, va_list);
136
137/*ARGSUSED*/
138static int
139_dns_initshells(void *rv, void *cb_data, va_list ap)
140{
141	char	  shellname[] = "shells-XXXXX";
142	int	  hsindex, hpi, r;
143	char	**hp;
144	void	 *context;
145
146	if (sl)
147		sl_free(sl, 1);
148	sl = sl_init();
149	r = NS_UNAVAIL;
150	if (hesiod_init(&context) == -1)
151		return (r);
152
153	for (hsindex = 0; ; hsindex++) {
154		snprintf(shellname, sizeof(shellname)-1, "shells-%d", hsindex);
155		hp = hesiod_resolve(context, shellname, "shells");
156		if (hp == NULL) {
157			if (errno == ENOENT) {
158				if (hsindex == 0)
159					r = NS_NOTFOUND;
160				else
161					r = NS_SUCCESS;
162			}
163			break;
164		} else {
165			for (hpi = 0; hp[hpi]; hpi++)
166				sl_add(sl, hp[hpi]);
167			free(hp);
168		}
169	}
170	hesiod_end(context);
171	return (r);
172}
173#endif /* HESIOD */
174
175#ifdef YP
176static int	_nis_initshells(void *, void *, va_list);
177
178/*ARGSUSED*/
179static int
180_nis_initshells(void *rv, void *cb_data, va_list ap)
181{
182	static char *ypdomain;
183	char	*key, *data;
184	char	*lastkey;
185	int	 keylen, datalen;
186	int	 r;
187
188	if (sl)
189		sl_free(sl, 1);
190	sl = sl_init();
191
192	if (ypdomain == NULL) {
193		switch (yp_get_default_domain(&ypdomain)) {
194		case 0:
195			break;
196		case YPERR_RESRC:
197			return NS_TRYAGAIN;
198		default:
199			return NS_UNAVAIL;
200		}
201	}
202
203	/*
204	 * `key' and `data' point to strings dynamically allocated by
205	 * the yp_... functions.
206	 * `data' is directly put into the stringlist of shells.
207	 */
208	key = data = NULL;
209	if (yp_first(ypdomain, "shells", &key, &keylen, &data, &datalen))
210		return NS_UNAVAIL;
211	do {
212		data[datalen] = '\0';		/* clear trailing \n */
213		sl_add(sl, data);
214
215		lastkey = key;
216		r = yp_next(ypdomain, "shells", lastkey, keylen,
217		    &key, &keylen, &data, &datalen);
218		free(lastkey);
219	} while (r == 0);
220
221	if (r == YPERR_NOMORE) {
222		/*
223		 * `data' and `key' ought to be NULL - do not try to free them.
224		 */
225		return NS_SUCCESS;
226	}
227
228	return NS_UNAVAIL;
229}
230#endif /* YP */
231
232static const char *const *
233initshells(void)
234{
235	static const ns_dtab dtab[] = {
236		NS_FILES_CB(_local_initshells, NULL)
237		NS_DNS_CB(_dns_initshells, NULL)
238		NS_NIS_CB(_nis_initshells, NULL)
239		{ 0 }
240	};
241	if (sl)
242		sl_free(sl, 1);
243	sl = sl_init();
244
245	if (_nsdispatch(NULL, dtab, NSDB_SHELLS, "initshells", __nsdefaultsrc)
246	    != NS_SUCCESS) {
247		if (sl)
248			sl_free(sl, 1);
249		sl = sl_init();
250		/*
251		 * Local shells should NOT be added here.  They should be
252		 * added in /etc/shells.
253		 */
254		sl_add(sl, strdup(_PATH_BSHELL));
255		sl_add(sl, strdup(_PATH_CSHELL));
256	}
257	sl_add(sl, NULL);
258
259	return (const char *const *)(sl->sl_str);
260}
261