]> granicus.if.org Git - apache/blob - modules/arch/unix/mod_unixd.c
d30ac4f1abc408b515dd8ee38a941381282e78a0
[apache] / modules / arch / unix / mod_unixd.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "ap_config.h"
18 #include "httpd.h"
19 #include "http_config.h"
20 #include "http_main.h"
21 #include "http_log.h"
22 #include "http_core.h"
23 #include "mpm_common.h"
24 #include "os.h"
25 #include "ap_mpm.h"
26 #include "mod_unixd.h"
27 #include "apr_thread_proc.h"
28 #include "apr_strings.h"
29 #include "apr_portable.h"
30 #ifdef HAVE_PWD_H
31 #include <pwd.h>
32 #endif
33 #ifdef HAVE_SYS_RESOURCE_H
34 #include <sys/resource.h>
35 #endif
36 /* XXX */
37 #include <sys/stat.h>
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #ifdef HAVE_GRP_H
42 #include <grp.h>
43 #endif
44 #ifdef HAVE_STRINGS_H
45 #include <strings.h>
46 #endif
47 #ifdef HAVE_SYS_SEM_H
48 #include <sys/sem.h>
49 #endif
50 #ifdef HAVE_SYS_PRCTL_H
51 #include <sys/prctl.h>
52 #endif
53
54 #ifndef DEFAULT_USER
55 #define DEFAULT_USER "#-1"
56 #endif
57 #ifndef DEFAULT_GROUP
58 #define DEFAULT_GROUP "#-1"
59 #endif
60
61 #if 0
62 typedef struct {
63   const char *user_name;
64   uid_t user_id;
65   gid_t group_id;
66   const char *chroot_dir;
67 } unixd_config_t;
68 #else
69 /*
70  * TODO: clean up the separation between this code
71  *       and its data structures and unixd.c, as shown
72  *       by the fact that we include unixd.h. Create
73  *       mod_unixd.h which does what we need and
74  *       clean up unixd.h for what it no longer needs
75  */
76 #include "unixd.h"
77 #endif
78
79
80 /* Set group privileges.
81  *
82  * Note that we use the username as set in the config files, rather than
83  * the lookup of to uid --- the same uid may have multiple passwd entries,
84  * with different sets of groups for each.
85  */
86
87 static int set_group_privs(void)
88 {
89     if (!geteuid()) {
90         const char *name;
91
92         /* Get username if passed as a uid */
93
94         if (ap_unixd_config.user_name[0] == '#') {
95             struct passwd *ent;
96             uid_t uid = atol(&ap_unixd_config.user_name[1]);
97
98             if ((ent = getpwuid(uid)) == NULL) {
99                 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
100                          "getpwuid: couldn't determine user name from uid %ld, "
101                          "you probably need to modify the User directive",
102                          (long)uid);
103                 return -1;
104             }
105
106             name = ent->pw_name;
107         }
108         else
109             name = ap_unixd_config.user_name;
110
111 #if !defined(OS2)
112         /* OS/2 doesn't support groups. */
113         /*
114          * Set the GID before initgroups(), since on some platforms
115          * setgid() is known to zap the group list.
116          */
117         if (setgid(ap_unixd_config.group_id) == -1) {
118             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
119                         "setgid: unable to set group id to Group %ld",
120                         (long)ap_unixd_config.group_id);
121             return -1;
122         }
123
124         /* Reset `groups' attributes. */
125
126         if (initgroups(name, ap_unixd_config.group_id) == -1) {
127             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
128                         "initgroups: unable to set groups for User %s "
129                         "and Group %ld", name, (long)ap_unixd_config.group_id);
130             return -1;
131         }
132 #endif /* !defined(OS2) */
133     }
134     return 0;
135 }
136
137
138 static int
139 unixd_drop_privileges(apr_pool_t *pool, server_rec *s)
140 {
141     int rv = set_group_privs();
142
143     if (rv) {
144         return rv;
145     }
146
147     if (NULL != ap_unixd_config.chroot_dir) {
148         if (geteuid()) {
149             rv = errno;
150             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
151                          "Cannot chroot when not started as root");
152             return rv;
153         }
154
155         if (chdir(ap_unixd_config.chroot_dir) != 0) {
156             rv = errno;
157             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
158                          "Can't chdir to %s", ap_unixd_config.chroot_dir);
159             return rv;
160         }
161
162         if (chroot(ap_unixd_config.chroot_dir) != 0) {
163             rv = errno;
164             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
165                          "Can't chroot to %s", ap_unixd_config.chroot_dir);
166             return rv;
167         }
168
169         if (chdir("/") != 0) {
170             rv = errno;
171             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
172                          "Can't chdir to new root");
173             return rv;
174         }
175     }
176
177     /* Only try to switch if we're running as root */
178     if (!geteuid() && (
179 #ifdef _OSD_POSIX
180         os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
181 #endif
182         setuid(ap_unixd_config.user_id) == -1)) {
183         rv = errno;
184         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
185                     "setuid: unable to change to uid: %ld",
186                     (long) ap_unixd_config.user_id);
187         return rv;
188     }
189 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
190     /* this applies to Linux 2.4+ */
191     if (ap_coredumpdir_configured) {
192         if (prctl(PR_SET_DUMPABLE, 1)) {
193             rv = errno;
194             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
195                          "set dumpable failed - this child will not coredump"
196                          " after software errors");
197             return rv;
198         }
199     }
200 #endif
201
202     return OK;
203 }
204
205
206 static const char *
207 unixd_set_user(cmd_parms *cmd, void *dummy,
208                const char *arg)
209 {
210     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
211     if (err != NULL) {
212         return err;
213     }
214
215     ap_unixd_config.user_name = arg;
216     ap_unixd_config.user_id = ap_uname2id(arg);
217 #if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
218     if (ap_unixd_config.user_id == 0) {
219         return "Error:\tApache has not been designed to serve pages while\n"
220                 "\trunning as root.  There are known race conditions that\n"
221                 "\twill allow any local user to read any file on the system.\n"
222                 "\tIf you still desire to serve pages as root then\n"
223                 "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
224                 "\tand then rebuild the server.\n"
225                 "\tIt is strongly suggested that you instead modify the User\n"
226                 "\tdirective in your httpd.conf file to list a non-root\n"
227                 "\tuser.\n";
228     }
229 #endif
230
231     return NULL;
232 }
233
234 static const char*
235 unixd_set_group(cmd_parms *cmd, void *dummy,
236                                          const char *arg)
237 {
238     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
239     if (err != NULL) {
240         return err;
241     }
242
243     ap_unixd_config.group_name = arg;
244     ap_unixd_config.group_id = ap_gname2id(arg);
245
246     return NULL;
247 }
248
249 static const char*
250 unixd_set_chroot_dir(cmd_parms *cmd, void *dummy,
251                     const char *arg)
252 {
253     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
254     if (err != NULL) {
255         return err;
256     }
257     if (!ap_is_directory(cmd->pool, arg)) {
258         return "ChrootDir must be a valid directory";
259     }
260
261     ap_unixd_config.chroot_dir = arg;
262     return NULL;
263 }
264
265 static const char *
266 unixd_set_suexec(cmd_parms *cmd, void *dummy, int arg)
267 {
268     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
269
270     if (err != NULL) {
271         return err;
272     }
273
274     if (!ap_unixd_config.suexec_enabled && arg) {
275         return apr_pstrcat(cmd->pool, "suEXEC isn't supported: ",
276                            ap_unixd_config.suexec_disabled_reason, NULL);
277     }
278
279     if (!arg) {
280         ap_unixd_config.suexec_disabled_reason = "Suexec directive is Off";
281     }
282
283     ap_unixd_config.suexec_enabled = arg;
284     return NULL;
285 }
286
287 static int
288 unixd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
289                  apr_pool_t *ptemp)
290 {
291     apr_finfo_t wrapper;
292     ap_unixd_config.user_name = DEFAULT_USER;
293     ap_unixd_config.user_id = ap_uname2id(DEFAULT_USER);
294     ap_unixd_config.group_name = DEFAULT_GROUP;
295     ap_unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
296
297     ap_unixd_config.chroot_dir = NULL; /* none */
298
299     /* Check for suexec */
300     ap_unixd_config.suexec_enabled = 0;
301     if ((apr_stat(&wrapper, SUEXEC_BIN, APR_FINFO_NORM, ptemp))
302          == APR_SUCCESS) {
303         if ((wrapper.protection & APR_USETID) && wrapper.user == 0
304             && (access(SUEXEC_BIN, R_OK|X_OK) == 0)) {
305             ap_unixd_config.suexec_enabled = 1;
306             ap_unixd_config.suexec_disabled_reason = "";
307         }
308         else {
309             ap_unixd_config.suexec_disabled_reason =
310                 "Invalid owner or file mode for " SUEXEC_BIN;
311         }
312     }
313     else {
314         ap_unixd_config.suexec_disabled_reason =
315             "Missing suexec binary " SUEXEC_BIN;
316     }
317
318     ap_sys_privileges_handlers(1);
319     return OK;
320 }
321
322 AP_DECLARE(int) ap_unixd_setup_child(void)
323 {
324     if (set_group_privs()) {
325         return -1;
326     }
327
328     if (NULL != ap_unixd_config.chroot_dir) {
329         if (geteuid()) {
330             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
331                          "Cannot chroot when not started as root");
332             return -1;
333         }
334         if (chdir(ap_unixd_config.chroot_dir) != 0) {
335             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
336                          "Can't chdir to %s", ap_unixd_config.chroot_dir);
337             return -1;
338         }
339         if (chroot(ap_unixd_config.chroot_dir) != 0) {
340             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
341                          "Can't chroot to %s", ap_unixd_config.chroot_dir);
342             return -1;
343         }
344         if (chdir("/") != 0) {
345             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
346                          "Can't chdir to new root");
347             return -1;
348         }
349     }
350
351     /* Only try to switch if we're running as root */
352     if (!geteuid() && (
353 #ifdef _OSD_POSIX
354         os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
355 #endif
356         setuid(ap_unixd_config.user_id) == -1)) {
357         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
358                     "setuid: unable to change to uid: %ld",
359                     (long) ap_unixd_config.user_id);
360         return -1;
361     }
362 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
363     /* this applies to Linux 2.4+ */
364     if (ap_coredumpdir_configured) {
365         if (prctl(PR_SET_DUMPABLE, 1)) {
366             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
367                          "set dumpable failed - this child will not coredump"
368                          " after software errors");
369         }
370     }
371 #endif
372     return 0;
373 }
374
375 static void unixd_dump_config(apr_pool_t *p, server_rec *s)
376 {
377     apr_file_t *out = NULL;
378     apr_uid_t uid = ap_unixd_config.user_id;
379     apr_gid_t gid = ap_unixd_config.group_id;
380     char *no_root = "";
381     if (!ap_exists_config_define("DUMP_RUN_CFG"))
382         return;
383     if (geteuid() != 0)
384         no_root = " not_used";
385     apr_file_open_stdout(&out, p);
386     apr_file_printf(out, "User: name=\"%s\" id=%lu%s\n",
387                     ap_unixd_config.user_name, (unsigned long)uid, no_root);
388     apr_file_printf(out, "Group: name=\"%s\" id=%lu%s\n",
389                     ap_unixd_config.group_name, (unsigned long)gid, no_root);
390     if (ap_unixd_config.chroot_dir)
391         apr_file_printf(out, "ChrootDir: \"%s\"%s\n",
392                         ap_unixd_config.chroot_dir, no_root);
393 }
394
395 static void unixd_hooks(apr_pool_t *pool)
396 {
397     ap_hook_pre_config(unixd_pre_config,
398                        NULL, NULL, APR_HOOK_FIRST);
399     ap_hook_test_config(unixd_dump_config,
400                         NULL, NULL, APR_HOOK_FIRST);
401     ap_hook_drop_privileges(unixd_drop_privileges,
402                             NULL, NULL, APR_HOOK_MIDDLE);
403 }
404
405 static const command_rec unixd_cmds[] = {
406     AP_INIT_TAKE1("User", unixd_set_user, NULL, RSRC_CONF,
407                   "Effective user id for this server"),
408     AP_INIT_TAKE1("Group", unixd_set_group, NULL, RSRC_CONF,
409                   "Effective group id for this server"),
410     AP_INIT_TAKE1("ChrootDir", unixd_set_chroot_dir, NULL, RSRC_CONF,
411                   "The directory to chroot(2) into"),
412     AP_INIT_FLAG("Suexec", unixd_set_suexec, NULL, RSRC_CONF,
413                  "Enable or disable suEXEC support"),
414     {NULL}
415 };
416
417 AP_DECLARE_MODULE(unixd) = {
418     STANDARD20_MODULE_STUFF,
419     NULL,
420     NULL,
421     NULL,
422     NULL,
423     unixd_cmds,
424     unixd_hooks
425 };
426