]> granicus.if.org Git - shadow/blob - libmisc/setupenv.c
* NEWS, libmisc/chowntty.c: Fix a race condition that could lead to
[shadow] / libmisc / setupenv.c
1 /*
2  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2008, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 /*
34  * Separated from setup.c.  --marekm
35  */
36
37 #include <config.h>
38
39 #ident "$Id$"
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <stdio.h>
44 #include <ctype.h>
45 #include "prototypes.h"
46 #include "defines.h"
47 #include <pwd.h>
48 #include "getdef.h"
49
50 #ifndef USE_PAM
51 static void
52 addenv_path (const char *varname, const char *dirname, const char *filename)
53 {
54         char *buf;
55
56         buf = xmalloc (strlen (dirname) + strlen (filename) + 2);
57         sprintf (buf, "%s/%s", dirname, filename);
58         addenv (varname, buf);
59         free (buf);
60 }
61
62 static void read_env_file (const char *filename)
63 {
64         FILE *fp;
65         char buf[1024];
66         char *cp, *name, *val;
67
68         fp = fopen (filename, "r");
69         if (!fp)
70                 return;
71         while (fgets (buf, sizeof buf, fp) == buf) {
72                 cp = strrchr (buf, '\n');
73                 if (!cp)
74                         break;
75                 *cp = '\0';
76
77                 cp = buf;
78                 /* ignore whitespace and comments */
79                 while (*cp && isspace (*cp))
80                         cp++;
81                 if (*cp == '\0' || *cp == '#')
82                         continue;
83                 /*
84                  * ignore lines which don't follow the name=value format
85                  * (for example, the "export NAME" shell commands)
86                  */
87                 name = cp;
88                 while (*cp && !isspace (*cp) && *cp != '=')
89                         cp++;
90                 if (*cp != '=')
91                         continue;
92                 /* NUL-terminate the name */
93                 *cp++ = '\0';
94                 val = cp;
95 #if 0                           /* XXX untested, and needs rewrite with fewer goto's :-) */
96 /*
97  (state, char_type) -> (state, action)
98
99  state: unquoted, single_quoted, double_quoted, escaped, double_quoted_escaped
100  char_type: normal, white, backslash, single, double
101  action: remove_curr, remove_curr_skip_next, remove_prev, finish XXX
102 */
103               no_quote:
104                 if (*cp == '\\') {
105                         /* remove the backslash */
106                         remove_char (cp);
107                         /* skip over the next character */
108                         if (*cp)
109                                 cp++;
110                         goto no_quote;
111                 } else if (*cp == '\'') {
112                         /* remove the quote */
113                         remove_char (cp);
114                         /* now within single quotes */
115                         goto s_quote;
116                 } else if (*cp == '"') {
117                         /* remove the quote */
118                         remove_char (cp);
119                         /* now within double quotes */
120                         goto d_quote;
121                 } else if (*cp == '\0') {
122                         /* end of string */
123                         goto finished;
124                 } else if (isspace (*cp)) {
125                         /* unescaped whitespace - end of string */
126                         *cp = '\0';
127                         goto finished;
128                 } else {
129                         cp++;
130                         goto no_quote;
131                 }
132               s_quote:
133                 if (*cp == '\'') {
134                         /* remove the quote */
135                         remove_char (cp);
136                         /* unquoted again */
137                         goto no_quote;
138                 } else if (*cp == '\0') {
139                         /* end of string */
140                         goto finished;
141                 } else {
142                         /* preserve everything within single quotes */
143                         cp++;
144                         goto s_quote;
145                 }
146               d_quote:
147                 if (*cp == '\"') {
148                         /* remove the quote */
149                         remove_char (cp);
150                         /* unquoted again */
151                         goto no_quote;
152                 } else if (*cp == '\\') {
153                         cp++;
154                         /* if backslash followed by double quote, remove backslash
155                            else skip over the backslash and following char */
156                         if (*cp == '"')
157                                 remove_char (cp - 1);
158                         else if (*cp)
159                                 cp++;
160                         goto d_quote;
161                 }
162                 eise if (*cp == '\0') {
163                         /* end of string */
164                         goto finished;
165                 } else {
166                         /* preserve everything within double quotes */
167                         goto d_quote;
168                 }
169               finished:
170 #endif                          /* 0 */
171                 /*
172                  * XXX - should handle quotes, backslash escapes, etc.
173                  * like the shell does.
174                  */
175                 addenv (name, val);
176         }
177         fclose (fp);
178 }
179 #endif                          /* USE_PAM */
180
181
182 /*
183  *      change to the user's home directory
184  *      set the HOME, SHELL, MAIL, PATH, and LOGNAME or USER environmental
185  *      variables.
186  */
187
188 void setup_env (struct passwd *info)
189 {
190 #ifndef USE_PAM
191         char *envf;
192 #endif
193         char *cp;
194
195         /*
196          * Change the current working directory to be the home directory
197          * of the user.  It is a fatal error for this process to be unable
198          * to change to that directory.  There is no "default" home
199          * directory.
200          *
201          * We no longer do it as root - should work better on NFS-mounted
202          * home directories.  Some systems default to HOME=/, so we make
203          * this a configurable option.  --marekm
204          */
205
206         if (chdir (info->pw_dir) == -1) {
207                 static char temp_pw_dir[] = "/";
208
209                 if (!getdef_bool ("DEFAULT_HOME") || chdir ("/") == -1) {
210                         fprintf (stderr, _("Unable to cd to '%s'\n"),
211                                  info->pw_dir);
212                         SYSLOG ((LOG_WARN,
213                                  "unable to cd to `%s' for user `%s'\n",
214                                  info->pw_dir, info->pw_name));
215                         closelog ();
216                         exit (1);
217                 }
218                 puts (_("No directory, logging in with HOME=/"));
219                 info->pw_dir = temp_pw_dir;
220         }
221
222         /*
223          * Create the HOME environmental variable and export it.
224          */
225
226         addenv ("HOME", info->pw_dir);
227
228         /*
229          * Create the SHELL environmental variable and export it.
230          */
231
232         if ((NULL == info->pw_shell) || ('\0' == *info->pw_shell)) {
233                 static char temp_pw_shell[] = "/bin/sh";
234
235                 info->pw_shell = temp_pw_shell;
236         }
237
238         addenv ("SHELL", info->pw_shell);
239
240         /*
241          * Export the user name.  For BSD derived systems, it's "USER", for
242          * all others it's "LOGNAME".  We set both of them.
243          */
244
245         addenv ("USER", info->pw_name);
246         addenv ("LOGNAME", info->pw_name);
247
248         /*
249          * Create the PATH environmental variable and export it.
250          */
251
252         cp = getdef_str ((info->pw_uid == 0) ? "ENV_SUPATH" : "ENV_PATH");
253
254         if (!cp) {
255                 /* not specified, use a minimal default */
256                 addenv ("PATH=/bin:/usr/bin", NULL);
257         } else if (strchr (cp, '=')) {
258                 /* specified as name=value (PATH=...) */
259                 addenv (cp, NULL);
260         } else {
261                 /* only value specified without "PATH=" */
262                 addenv ("PATH", cp);
263         }
264
265 #ifndef USE_PAM
266         /*
267          * Create the MAIL environmental variable and export it.  login.defs
268          * knows the prefix.
269          */
270
271         if (getdef_bool ("MAIL_CHECK_ENAB")) {
272                 if ((cp = getdef_str ("MAIL_DIR")))
273                         addenv_path ("MAIL", cp, info->pw_name);
274                 else if ((cp = getdef_str ("MAIL_FILE")))
275                         addenv_path ("MAIL", info->pw_dir, cp);
276                 else {
277 #if defined(MAIL_SPOOL_FILE)
278                         addenv_path ("MAIL", info->pw_dir, MAIL_SPOOL_FILE);
279 #elif defined(MAIL_SPOOL_DIR)
280                         addenv_path ("MAIL", MAIL_SPOOL_DIR, info->pw_name);
281 #endif
282                 }
283         }
284
285         /*
286          * Read environment from optional config file.  --marekm
287          */
288         if ((envf = getdef_str ("ENVIRON_FILE")))
289                 read_env_file (envf);
290 #endif                          /* !USE_PAM */
291 }