1/*
2 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
6/*	  All Rights Reserved  	*/
7
8/*
9 * Copyright (c) 1985 Regents of the University of California.
10 * All rights reserved.  The Berkeley software License Agreement
11 * specifies the terms and conditions for redistribution.
12 */
13
14#include "lint.h"
15#include <sys/types.h>
16#include <sys/param.h>
17#include <sys/stat.h>
18#include <ctype.h>
19#include <stdio.h>
20#include <limits.h>
21#include <stdlib.h>
22#include <sys/file.h>
23#include "libc.h"
24#include <unistd.h>
25
26#define	SHELLS "/etc/shells"
27
28/*
29 * Do not add local shells here.  They should be added in /etc/shells
30 *
31 * Do not add restricted shells:
32 * Shells returned by getusershell traditionally allow:
33 * - users to change away from (i.e., if you have an rksh in
34 *   getusershell(), then users can change their shell to ksh)
35 * - by default, ftp in is allowed only for shells returned by
36 *   getusershell(); since FTP has no restrictions on directory
37 *   movement, adding rksh to getusershell() would defeat that
38 *   protection.
39 */
40const char *okshells[] = {
41	"/usr/bin/sh",
42	"/usr/bin/csh",
43	"/usr/bin/ksh",
44	"/usr/bin/ksh93",
45	"/usr/bin/jsh",
46	"/bin/sh",
47	"/bin/csh",
48	"/bin/ksh",
49	"/bin/ksh93",
50	"/bin/jsh",
51	"/sbin/sh",
52	"/sbin/jsh",
53	"/usr/bin/pfsh",
54	"/usr/bin/pfcsh",
55	"/usr/bin/pfksh",
56	"/usr/bin/pfksh93",
57	"/usr/bin/bash",
58	"/usr/bin/tcsh",
59	"/usr/bin/zsh",
60	"/usr/bin/pfbash",
61	"/usr/bin/pftcsh",
62	"/usr/bin/pfzsh",
63	"/bin/pfsh",
64	"/bin/pfcsh",
65	"/bin/pfksh",
66	"/bin/pfksh93",
67	"/bin/bash",
68	"/bin/tcsh",
69	"/bin/zsh",
70	"/bin/pfbash",
71	"/bin/pftcsh",
72	"/bin/pfzsh",
73	"/usr/xpg4/bin/sh",
74	"/usr/xpg4/bin/pfsh",
75	"/sbin/pfsh",
76	"/usr/sfw/bin/zsh",
77	NULL
78};
79
80static char **shells, *strings;
81static char **curshell;
82static char **initshells(void);
83
84/*
85 * Get a list of shells from SHELLS, if it exists.
86 */
87char *
88getusershell(void)
89{
90	char *ret;
91
92	if (curshell == NULL)
93		curshell = initshells();
94	ret = *curshell;
95	if (ret != NULL)
96		curshell++;
97	return (ret);
98}
99
100void
101endusershell(void)
102{
103
104	if (shells != NULL)
105		(void) free((char *)shells);
106	shells = NULL;
107	if (strings != NULL)
108		(void) free(strings);
109	strings = NULL;
110	curshell = NULL;
111}
112
113void
114setusershell(void)
115{
116
117	curshell = initshells();
118}
119
120static char **
121initshells(void)
122{
123	char **sp, *cp;
124	FILE *fp;
125	struct stat statb;
126
127	if (shells != NULL)
128		(void) free((char *)shells);
129	shells = NULL;
130	if (strings != NULL)
131		(void) free(strings);
132	strings = NULL;
133	if ((fp = fopen(SHELLS, "rF")) == (FILE *)0)
134		return ((char **)okshells);
135	/*
136	 * The +1 in the malloc() below is needed to handle the final
137	 * fgets() NULL terminator.  From fgets(3S):
138	 *
139	 * char *fgets(char *s, int n, FILE *stream);
140	 *
141	 * The  fgets()  function reads characters from the stream into
142	 * the array pointed to by s, until n-1 characters are read, or
143	 * a newline character is read and transferred to s, or an end-
144	 * of-file condition is encountered.  The string is then termi-
145	 * nated with a null character.
146	 */
147	if ((fstat(fileno(fp), &statb) == -1) || (statb.st_size > LONG_MAX) ||
148	    ((strings = malloc((size_t)statb.st_size + 1)) == NULL)) {
149		(void) fclose(fp);
150		return ((char **)okshells);
151	}
152	shells = calloc((size_t)statb.st_size / 3, sizeof (char *));
153	if (shells == NULL) {
154		(void) fclose(fp);
155		(void) free(strings);
156		strings = NULL;
157		return ((char **)okshells);
158	}
159	sp = shells;
160	cp = strings;
161	while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
162		while (*cp != '#' && *cp != '/' && *cp != '\0')
163			cp++;
164		if (*cp == '#' || *cp == '\0')
165			continue;
166		*sp++ = cp;
167		while (!isspace(*cp) && *cp != '#' && *cp != '\0')
168			cp++;
169		*cp++ = '\0';
170	}
171	*sp = (char *)0;
172	(void) fclose(fp);
173	return (shells);
174}
175