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