]> granicus.if.org Git - apache/blob - modules/arch/unix/mod_unixd.c
axe the remaining checks of AP_MPM_WANT_foo to see what code to generate
[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 "mpm_common.h"
23 #include "os.h"
24 #include "ap_mpm.h"
25 #include "mod_unixd.h"
26 #include "apr_thread_proc.h"
27 #include "apr_strings.h"
28 #include "apr_portable.h"
29 #ifdef HAVE_PWD_H
30 #include <pwd.h>
31 #endif
32 #ifdef HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
34 #endif
35 /* XXX */
36 #include <sys/stat.h>
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #ifdef HAVE_GRP_H
41 #include <grp.h>
42 #endif
43 #ifdef HAVE_STRINGS_H
44 #include <strings.h>
45 #endif
46 #ifdef HAVE_SYS_SEM_H
47 #include <sys/sem.h>
48 #endif
49 #ifdef HAVE_SYS_PRCTL_H
50 #include <sys/prctl.h>
51 #endif
52
53 #ifndef DEFAULT_USER
54 #define DEFAULT_USER "#-1"
55 #endif
56 #ifndef DEFAULT_GROUP
57 #define DEFAULT_GROUP "#-1"
58 #endif
59
60 #if 0
61 typedef struct {
62   const char *user_name;
63   uid_t user_id;
64   gid_t group_id;
65   const char *chroot_dir;
66 } unixd_config_t;
67 #else
68 /*
69  * TODO: clean up the separation between this code
70  *       and its data structures and unixd.c, as shown
71  *       by the fact that we include unixd.h. Create
72  *       mod_unixd.h which does what we need and
73  *       clean up unixd.h for what it no longer needs
74  */
75 #include "unixd.h"
76 #endif
77
78
79 /* Set group privileges.
80  *
81  * Note that we use the username as set in the config files, rather than
82  * the lookup of to uid --- the same uid may have multiple passwd entries,
83  * with different sets of groups for each.
84  */
85
86 static int set_group_privs(void)
87 {
88     if (!geteuid()) {
89         const char *name;
90
91         /* Get username if passed as a uid */
92
93         if (ap_unixd_config.user_name[0] == '#') {
94             struct passwd *ent;
95             uid_t uid = atol(&ap_unixd_config.user_name[1]);
96
97             if ((ent = getpwuid(uid)) == NULL) {
98                 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
99                          "getpwuid: couldn't determine user name from uid %ld, "
100                          "you probably need to modify the User directive",
101                          (long)uid);
102                 return -1;
103             }
104
105             name = ent->pw_name;
106         }
107         else
108             name = ap_unixd_config.user_name;
109
110 #if !defined(OS2) && !defined(TPF)
111         /* OS/2 and TPF don't support groups. */
112
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 %u",
120                         (unsigned)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 %u", name, (unsigned)ap_unixd_config.group_id);
130             return -1;
131         }
132 #endif /* !defined(OS2) && !defined(TPF) */
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 #ifdef MPE
178     /* Only try to switch if we're running as MANAGER.SYS */
179     if (geteuid() == 1 && ap_unixd_config.user_id > 1) {
180         GETPRIVMODE();
181         if (setuid(ap_unixd_config.user_id) == -1) {
182             GETUSERMODE();
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         GETUSERMODE();
190     }
191 #else
192     /* Only try to switch if we're running as root */
193     if (!geteuid() && (
194 #ifdef _OSD_POSIX
195         os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
196 #endif
197         setuid(ap_unixd_config.user_id) == -1)) {
198         rv = errno;
199         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
200                     "setuid: unable to change to uid: %ld",
201                     (long) ap_unixd_config.user_id);
202         return rv;
203     }
204 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
205     /* this applies to Linux 2.4+ */
206     if (ap_coredumpdir_configured) {
207         if (prctl(PR_SET_DUMPABLE, 1)) {
208             rv = errno;
209             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
210                          "set dumpable failed - this child will not coredump"
211                          " after software errors");
212             return rv;
213         }
214     }
215 #endif
216 #endif
217
218     return OK;
219 }
220
221
222 static const char *
223 unixd_set_user(cmd_parms *cmd, void *dummy,
224                const char *arg)
225 {
226     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
227     if (err != NULL) {
228         return err;
229     }
230
231     ap_unixd_config.user_name = arg;
232     ap_unixd_config.user_id = ap_uname2id(arg);
233 #if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
234     if (ap_unixd_config.user_id == 0) {
235         return "Error:\tApache has not been designed to serve pages while\n"
236                 "\trunning as root.  There are known race conditions that\n"
237                 "\twill allow any local user to read any file on the system.\n"
238                 "\tIf you still desire to serve pages as root then\n"
239                 "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
240                 "\tand then rebuild the server.\n"
241                 "\tIt is strongly suggested that you instead modify the User\n"
242                 "\tdirective in your httpd.conf file to list a non-root\n"
243                 "\tuser.\n";
244     }
245 #endif
246
247     return NULL;
248 }
249
250 static const char* 
251 unixd_set_group(cmd_parms *cmd, void *dummy,
252                                          const char *arg)
253 {
254     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
255     if (err != NULL) {
256         return err;
257     }
258
259     ap_unixd_config.group_id = ap_gname2id(arg);
260
261     return NULL;
262 }
263
264 static const char* 
265 unixd_set_chroot_dir(cmd_parms *cmd, void *dummy,
266                     const char *arg)
267 {
268     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
269     if (err != NULL) {
270         return err;
271     }
272     if (!ap_is_directory(cmd->pool, arg)) {
273         return "ChrootDir must be a valid directory";
274     }
275
276     ap_unixd_config.chroot_dir = arg;
277     return NULL;
278 }
279
280 static int 
281 unixd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
282                  apr_pool_t *ptemp)
283 {
284     apr_finfo_t wrapper;
285     ap_unixd_config.user_name = DEFAULT_USER;
286     ap_unixd_config.user_id = ap_uname2id(DEFAULT_USER);
287     ap_unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
288
289     ap_unixd_config.chroot_dir = NULL; /* none */
290
291     /* Check for suexec */
292     ap_unixd_config.suexec_enabled = 0;
293     if ((apr_stat(&wrapper, SUEXEC_BIN, APR_FINFO_NORM, ptemp))
294          == APR_SUCCESS) {
295         if ((wrapper.protection & APR_USETID) && wrapper.user == 0) {
296             ap_unixd_config.suexec_enabled = 1;
297         }
298     }
299
300     ap_sys_privileges_handlers(1);
301     return OK;
302 }
303
304 AP_DECLARE(int) ap_unixd_setup_child(void)
305 {
306     if (set_group_privs()) {
307         return -1;
308     }
309
310     if (NULL != ap_unixd_config.chroot_dir) {
311         if (geteuid()) {
312             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
313                          "Cannot chroot when not started as root");
314             return -1;
315         }
316         if (chdir(ap_unixd_config.chroot_dir) != 0) {
317             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
318                          "Can't chdir to %s", ap_unixd_config.chroot_dir);
319             return -1;
320         }
321         if (chroot(ap_unixd_config.chroot_dir) != 0) {
322             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
323                          "Can't chroot to %s", ap_unixd_config.chroot_dir);
324             return -1;
325         }
326         if (chdir("/") != 0) {
327             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
328                          "Can't chdir to new root");
329             return -1;
330         }
331     }
332
333 #ifdef MPE
334     /* Only try to switch if we're running as MANAGER.SYS */
335     if (geteuid() == 1 && ap_unixd_config.user_id > 1) {
336         GETPRIVMODE();
337         if (setuid(ap_unixd_config.user_id) == -1) {
338             GETUSERMODE();
339             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
340                         "setuid: unable to change to uid: %ld",
341                         (long) ap_unixd_config.user_id);
342             exit(1);
343         }
344         GETUSERMODE();
345     }
346 #else
347     /* Only try to switch if we're running as root */
348     if (!geteuid() && (
349 #ifdef _OSD_POSIX
350         os_init_job_environment(NULL, ap_unixd_config.user_name, ap_exists_config_define("DEBUG")) != 0 ||
351 #endif
352         setuid(ap_unixd_config.user_id) == -1)) {
353         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
354                     "setuid: unable to change to uid: %ld",
355                     (long) ap_unixd_config.user_id);
356         return -1;
357     }
358 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
359     /* this applies to Linux 2.4+ */
360     if (ap_coredumpdir_configured) {
361         if (prctl(PR_SET_DUMPABLE, 1)) {
362             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
363                          "set dumpable failed - this child will not coredump"
364                          " after software errors");
365         }
366     }
367 #endif
368 #endif
369     return 0;
370 }
371
372 static void unixd_hooks(apr_pool_t *pool)
373 {
374     ap_hook_pre_config(unixd_pre_config,
375                        NULL, NULL, APR_HOOK_FIRST);
376
377     ap_hook_drop_privileges(unixd_drop_privileges,
378                             NULL, NULL, APR_HOOK_MIDDLE);
379 }
380
381 static const command_rec unixd_cmds[] = {
382     AP_INIT_TAKE1("User", unixd_set_user, NULL, RSRC_CONF,
383                   "Effective user id for this server"),
384     AP_INIT_TAKE1("Group", unixd_set_group, NULL, RSRC_CONF,
385                   "Effective group id for this server"),
386     AP_INIT_TAKE1("ChrootDir", unixd_set_chroot_dir, NULL, RSRC_CONF,
387                   "The directory to chroot(2) into"),
388     {NULL}
389 };
390
391 module AP_MODULE_DECLARE_DATA unixd_module = {
392     STANDARD20_MODULE_STUFF,
393     NULL,
394     NULL,
395     NULL,
396     NULL,
397     unixd_cmds,
398     unixd_hooks
399 };
400