1 | /* $NetBSD: getusershell.c,v 1.29 2012/03/13 21:13:36 christos 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.29 2012/03/13 21:13:36 christos 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, "re" ); |
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, (int)sizeof(curshell) - 1, _files_state.fp) |
193 | != NULL) { |
194 | sp = cp = curshell; |
195 | while (*cp != '#' && *cp != '/' && *cp != '\0') |
196 | cp++; |
197 | if (*cp == '#' || *cp == '\0') |
198 | continue; |
199 | sp = cp; |
200 | while (!isspace((unsigned char) *cp) && *cp != '#' |
201 | && *cp != '\0') |
202 | cp++; |
203 | *cp++ = '\0'; |
204 | *retval = sp; |
205 | return NS_SUCCESS; |
206 | } |
207 | |
208 | return NS_NOTFOUND; |
209 | } |
210 | |
211 | |
212 | #ifdef HESIOD |
213 | /* |
214 | * dns methods |
215 | */ |
216 | |
217 | /* state shared between dns methods */ |
218 | struct dns_state { |
219 | void *context; /* Hesiod context */ |
220 | int num; /* shell index, -1 if no more */ |
221 | }; |
222 | |
223 | static struct dns_state _dns_state; |
224 | |
225 | static int |
226 | _dns_start(struct dns_state *state) |
227 | { |
228 | |
229 | _DIAGASSERT(state != NULL); |
230 | |
231 | state->num = 0; |
232 | if (state->context == NULL) { /* setup Hesiod */ |
233 | if (hesiod_init(&state->context) == -1) |
234 | return NS_UNAVAIL; |
235 | } |
236 | |
237 | return NS_SUCCESS; |
238 | } |
239 | |
240 | static int |
241 | _dns_end(struct dns_state *state) |
242 | { |
243 | |
244 | _DIAGASSERT(state != NULL); |
245 | |
246 | state->num = 0; |
247 | if (state->context) { |
248 | hesiod_end(state->context); |
249 | state->context = NULL; |
250 | } |
251 | return NS_SUCCESS; |
252 | } |
253 | |
254 | /*ARGSUSED*/ |
255 | static int |
256 | _dns_setusershell(void *nsrv, void *nscb, va_list ap) |
257 | { |
258 | |
259 | return _dns_start(&_dns_state); |
260 | } |
261 | |
262 | /*ARGSUSED*/ |
263 | static int |
264 | _dns_endusershell(void *nsrv, void *nscb, va_list ap) |
265 | { |
266 | |
267 | return _dns_end(&_dns_state); |
268 | } |
269 | |
270 | /*ARGSUSED*/ |
271 | static int |
272 | _dns_getusershell(void *nsrv, void *nscb, va_list ap) |
273 | { |
274 | char **retval = va_arg(ap, char **); |
275 | |
276 | char shellname[] = "shells-NNNNNNNNNN" ; |
277 | char **hp, *ep; |
278 | int rv; |
279 | |
280 | _DIAGASSERT(retval != NULL); |
281 | |
282 | *retval = NULL; |
283 | |
284 | if (_dns_state.num == -1) /* exhausted search */ |
285 | return NS_NOTFOUND; |
286 | |
287 | if (_dns_state.context == NULL) { |
288 | /* only start if Hesiod not setup */ |
289 | rv = _dns_start(&_dns_state); |
290 | if (rv != NS_SUCCESS) |
291 | return rv; |
292 | } |
293 | |
294 | hp = NULL; |
295 | rv = NS_NOTFOUND; |
296 | |
297 | /* find shells-NNN */ |
298 | snprintf(shellname, sizeof(shellname), "shells-%d" , _dns_state.num); |
299 | _dns_state.num++; |
300 | |
301 | hp = hesiod_resolve(_dns_state.context, shellname, "shells" ); |
302 | if (hp == NULL) { |
303 | if (errno == ENOENT) |
304 | rv = NS_NOTFOUND; |
305 | else |
306 | rv = NS_UNAVAIL; |
307 | } else { |
308 | if ((ep = strchr(hp[0], '\n')) != NULL) |
309 | *ep = '\0'; /* clear trailing \n */ |
310 | /* only use first result */ |
311 | strlcpy(curshell, hp[0], sizeof(curshell)); |
312 | *retval = curshell; |
313 | rv = NS_SUCCESS; |
314 | } |
315 | |
316 | if (hp) |
317 | hesiod_free_list(_dns_state.context, hp); |
318 | if (rv != NS_SUCCESS) |
319 | _dns_state.num = -1; /* any failure halts search */ |
320 | return rv; |
321 | } |
322 | |
323 | #endif /* HESIOD */ |
324 | |
325 | |
326 | #ifdef YP |
327 | /* |
328 | * nis methods |
329 | */ |
330 | /* state shared between nis methods */ |
331 | struct nis_state { |
332 | char *domain; /* NIS domain */ |
333 | int done; /* non-zero if search exhausted */ |
334 | char *current; /* current first/next match */ |
335 | int currentlen; /* length of _nis_current */ |
336 | }; |
337 | |
338 | static struct nis_state _nis_state; |
339 | |
340 | static int |
341 | _nis_start(struct nis_state *state) |
342 | { |
343 | |
344 | _DIAGASSERT(state != NULL); |
345 | |
346 | state->done = 0; |
347 | if (state->current) { |
348 | free(state->current); |
349 | state->current = NULL; |
350 | } |
351 | if (state->domain == NULL) { /* setup NIS */ |
352 | switch (yp_get_default_domain(&state->domain)) { |
353 | case 0: |
354 | break; |
355 | case YPERR_RESRC: |
356 | return NS_TRYAGAIN; |
357 | default: |
358 | return NS_UNAVAIL; |
359 | } |
360 | } |
361 | return NS_SUCCESS; |
362 | } |
363 | |
364 | static int |
365 | _nis_end(struct nis_state *state) |
366 | { |
367 | |
368 | _DIAGASSERT(state != NULL); |
369 | |
370 | if (state->domain) |
371 | state->domain = NULL; |
372 | state->done = 0; |
373 | if (state->current) |
374 | free(state->current); |
375 | state->current = NULL; |
376 | return NS_SUCCESS; |
377 | } |
378 | |
379 | /*ARGSUSED*/ |
380 | static int |
381 | _nis_setusershell(void *nsrv, void *nscb, va_list ap) |
382 | { |
383 | |
384 | return _nis_start(&_nis_state); |
385 | } |
386 | |
387 | /*ARGSUSED*/ |
388 | static int |
389 | _nis_endusershell(void *nsrv, void *nscb, va_list ap) |
390 | { |
391 | |
392 | return _nis_end(&_nis_state); |
393 | } |
394 | |
395 | /*ARGSUSED*/ |
396 | static int |
397 | _nis_getusershell(void *nsrv, void *nscb, va_list ap) |
398 | { |
399 | char **retval = va_arg(ap, char **); |
400 | |
401 | char *key, *data; |
402 | int keylen, datalen, rv, nisr; |
403 | |
404 | _DIAGASSERT(retval != NULL); |
405 | |
406 | *retval = NULL; |
407 | |
408 | if (_nis_state.done) /* exhausted search */ |
409 | return NS_NOTFOUND; |
410 | if (_nis_state.domain == NULL) { |
411 | /* only start if NIS not setup */ |
412 | rv = _nis_start(&_nis_state); |
413 | if (rv != NS_SUCCESS) |
414 | return rv; |
415 | } |
416 | |
417 | key = NULL; |
418 | data = NULL; |
419 | rv = NS_NOTFOUND; |
420 | |
421 | if (_nis_state.current) { /* already searching */ |
422 | nisr = yp_next(_nis_state.domain, "shells" , |
423 | _nis_state.current, _nis_state.currentlen, |
424 | &key, &keylen, &data, &datalen); |
425 | free(_nis_state.current); |
426 | _nis_state.current = NULL; |
427 | switch (nisr) { |
428 | case 0: |
429 | _nis_state.current = key; |
430 | _nis_state.currentlen = keylen; |
431 | key = NULL; |
432 | break; |
433 | case YPERR_NOMORE: |
434 | rv = NS_NOTFOUND; |
435 | goto nisent_out; |
436 | default: |
437 | rv = NS_UNAVAIL; |
438 | goto nisent_out; |
439 | } |
440 | } else { /* new search */ |
441 | if (yp_first(_nis_state.domain, "shells" , |
442 | &_nis_state.current, &_nis_state.currentlen, |
443 | &data, &datalen)) { |
444 | rv = NS_UNAVAIL; |
445 | goto nisent_out; |
446 | } |
447 | } |
448 | |
449 | data[datalen] = '\0'; /* clear trailing \n */ |
450 | strlcpy(curshell, data, sizeof(curshell)); |
451 | *retval = curshell; |
452 | rv = NS_SUCCESS; |
453 | |
454 | nisent_out: |
455 | if (key) |
456 | free(key); |
457 | if (data) |
458 | free(data); |
459 | if (rv != NS_SUCCESS) /* any failure halts search */ |
460 | _nis_state.done = 1; |
461 | return rv; |
462 | } |
463 | |
464 | #endif /* YP */ |
465 | |
466 | |
467 | /* |
468 | * public functions |
469 | */ |
470 | |
471 | void |
472 | endusershell(void) |
473 | { |
474 | static const ns_dtab dtab[] = { |
475 | NS_FILES_CB(_files_endusershell, NULL) |
476 | NS_DNS_CB(_dns_endusershell, NULL) |
477 | NS_NIS_CB(_nis_endusershell, NULL) |
478 | NS_NULL_CB |
479 | }; |
480 | |
481 | mutex_lock(&__shellmutex); |
482 | |
483 | curokshell = okshells; /* reset okshells fallback state */ |
484 | shellsfound = 0; |
485 | |
486 | /* force all endusershell() methods */ |
487 | (void) nsdispatch(NULL, dtab, NSDB_SHELLS, "endusershell" , |
488 | __nsdefaultfiles_forceall); |
489 | mutex_unlock(&__shellmutex); |
490 | } |
491 | |
492 | __aconst char * |
493 | getusershell(void) |
494 | { |
495 | int rv; |
496 | __aconst char *retval; |
497 | |
498 | static const ns_dtab dtab[] = { |
499 | NS_FILES_CB(_files_getusershell, NULL) |
500 | NS_DNS_CB(_dns_getusershell, NULL) |
501 | NS_NIS_CB(_nis_getusershell, NULL) |
502 | NS_NULL_CB |
503 | }; |
504 | |
505 | mutex_lock(&__shellmutex); |
506 | |
507 | retval = NULL; |
508 | do { |
509 | rv = nsdispatch(NULL, dtab, NSDB_SHELLS, "getusershell" , |
510 | __nsdefaultsrc, &retval); |
511 | /* loop until failure or non-blank result */ |
512 | } while (rv == NS_SUCCESS && retval[0] == '\0'); |
513 | |
514 | if (rv == NS_SUCCESS) { |
515 | shellsfound++; |
516 | } else if (shellsfound == 0) { /* no shells; fall back to okshells */ |
517 | if (curokshell != NULL) { |
518 | retval = __UNCONST(*curokshell); |
519 | curokshell++; |
520 | rv = NS_SUCCESS; |
521 | } |
522 | } |
523 | |
524 | mutex_unlock(&__shellmutex); |
525 | return (rv == NS_SUCCESS) ? retval : NULL; |
526 | } |
527 | |
528 | void |
529 | setusershell(void) |
530 | { |
531 | static const ns_dtab dtab[] = { |
532 | NS_FILES_CB(_files_setusershell, NULL) |
533 | NS_DNS_CB(_dns_setusershell, NULL) |
534 | NS_NIS_CB(_nis_setusershell, NULL) |
535 | NS_NULL_CB |
536 | }; |
537 | |
538 | mutex_lock(&__shellmutex); |
539 | |
540 | curokshell = okshells; /* reset okshells fallback state */ |
541 | shellsfound = 0; |
542 | |
543 | /* force all setusershell() methods */ |
544 | (void) nsdispatch(NULL, dtab, NSDB_SHELLS, "setusershell" , |
545 | __nsdefaultfiles_forceall); |
546 | mutex_unlock(&__shellmutex); |
547 | } |
548 | |