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  */
40 const 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 
80 static char **shells, *strings;
81 static char **curshell;
82 static char **initshells(void);
83 
84 /*
85  * Get a list of shells from SHELLS, if it exists.
86  */
87 char *
getusershell(void)88 getusershell(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 
100 void
endusershell(void)101 endusershell(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 
113 void
setusershell(void)114 setusershell(void)
115 {
116 
117 	curshell = initshells();
118 }
119 
120 static char **
initshells(void)121 initshells(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(3C):
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