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(rv, cb_data, ap)
106	void	*rv;
107	void	*cb_data;
108	va_list	 ap;
109{
110	char	*sp, *cp;
111	FILE	*fp;
112	char	 line[MAXPATHLEN + 2];
113
114	if (sl)
115		sl_free(sl, 1);
116	sl = sl_init();
117
118	if ((fp = fopen(_PATH_SHELLS, "re")) == NULL)
119		return NS_UNAVAIL;
120
121	cp = line;
122	while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
123		while (*cp != '#' && *cp != '/' && *cp != '\0')
124			cp++;
125		if (*cp == '#' || *cp == '\0')
126			continue;
127		sp = cp;
128		while (!isspace(*cp) && *cp != '#' && *cp != '\0')
129			cp++;
130		*cp++ = '\0';
131		sl_add(sl, strdup(sp));
132	}
133	(void)fclose(fp);
134	return NS_SUCCESS;
135}
136
137#ifdef HESIOD
138static int	_dns_initshells(void *, void *, va_list);
139
140/*ARGSUSED*/
141static int
142_dns_initshells(rv, cb_data, ap)
143	void	*rv;
144	void	*cb_data;
145	va_list	 ap;
146{
147	char	  shellname[] = "shells-XXXXX";
148	int	  hsindex, hpi, r;
149	char	**hp;
150	void	 *context;
151
152	if (sl)
153		sl_free(sl, 1);
154	sl = sl_init();
155	r = NS_UNAVAIL;
156	if (hesiod_init(&context) == -1)
157		return (r);
158
159	for (hsindex = 0; ; hsindex++) {
160		snprintf(shellname, sizeof(shellname)-1, "shells-%d", hsindex);
161		hp = hesiod_resolve(context, shellname, "shells");
162		if (hp == NULL) {
163			if (errno == ENOENT) {
164				if (hsindex == 0)
165					r = NS_NOTFOUND;
166				else
167					r = NS_SUCCESS;
168			}
169			break;
170		} else {
171			for (hpi = 0; hp[hpi]; hpi++)
172				sl_add(sl, hp[hpi]);
173			free(hp);
174		}
175	}
176	hesiod_end(context);
177	return (r);
178}
179#endif /* HESIOD */
180
181#ifdef YP
182static int	_nis_initshells(void *, void *, va_list);
183
184/*ARGSUSED*/
185static int
186_nis_initshells(rv, cb_data, ap)
187	void	*rv;
188	void	*cb_data;
189	va_list	 ap;
190{
191	static char *ypdomain;
192	char	*key, *data;
193	char	*lastkey;
194	int	 keylen, datalen;
195	int	 r;
196
197	if (sl)
198		sl_free(sl, 1);
199	sl = sl_init();
200
201	if (ypdomain == NULL) {
202		switch (yp_get_default_domain(&ypdomain)) {
203		case 0:
204			break;
205		case YPERR_RESRC:
206			return NS_TRYAGAIN;
207		default:
208			return NS_UNAVAIL;
209		}
210	}
211
212	/*
213	 * `key' and `data' point to strings dynamically allocated by
214	 * the yp_... functions.
215	 * `data' is directly put into the stringlist of shells.
216	 */
217	key = data = NULL;
218	if (yp_first(ypdomain, "shells", &key, &keylen, &data, &datalen))
219		return NS_UNAVAIL;
220	do {
221		data[datalen] = '\0';		/* clear trailing \n */
222		sl_add(sl, data);
223
224		lastkey = key;
225		r = yp_next(ypdomain, "shells", lastkey, keylen,
226		    &key, &keylen, &data, &datalen);
227		free(lastkey);
228	} while (r == 0);
229
230	if (r == YPERR_NOMORE) {
231		/*
232		 * `data' and `key' ought to be NULL - do not try to free them.
233		 */
234		return NS_SUCCESS;
235	}
236
237	return NS_UNAVAIL;
238}
239#endif /* YP */
240
241static const char *const *
242initshells()
243{
244	static const ns_dtab dtab[] = {
245		NS_FILES_CB(_local_initshells, NULL)
246		NS_DNS_CB(_dns_initshells, NULL)
247		NS_NIS_CB(_nis_initshells, NULL)
248		{ 0 }
249	};
250	if (sl)
251		sl_free(sl, 1);
252	sl = sl_init();
253
254	if (_nsdispatch(NULL, dtab, NSDB_SHELLS, "initshells", __nsdefaultsrc)
255	    != NS_SUCCESS) {
256		if (sl)
257			sl_free(sl, 1);
258		sl = sl_init();
259		/*
260		 * Local shells should NOT be added here.  They should be
261		 * added in /etc/shells.
262		 */
263		sl_add(sl, strdup(_PATH_BSHELL));
264		sl_add(sl, strdup(_PATH_CSHELL));
265	}
266	sl_add(sl, NULL);
267
268	return (const char *const *)(sl->sl_str);
269}
270