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