]> granicus.if.org Git - apache/blob - os/unix/unixd.c
3c13dd4e3e6b165aac0ebf606d1d0e8c9d6b8796
[apache] / os / unix / unixd.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 #include "ap_config.h"
60 #define CORE_PRIVATE
61 #include "httpd.h"
62 #include "http_config.h"
63 #include "http_main.h"
64 #include "http_log.h"
65 #include "unixd.h"
66 #include "mpm_common.h"
67 #include "os.h"
68 #include "ap_mpm.h"
69 #include "apr_thread_proc.h"
70 #include "apr_strings.h"
71 #ifdef HAVE_PWD_H
72 #include <pwd.h>
73 #endif
74 #ifdef HAVE_SYS_RESOURCE_H
75 #include <sys/resource.h>
76 #endif
77 /* XXX */
78 #include <sys/stat.h>
79 #ifdef HAVE_UNISTD_H
80 #include <unistd.h>
81 #endif
82 #ifdef HAVE_GRP_H
83 #include <grp.h>
84 #endif
85 #ifdef HAVE_STRINGS_H
86 #include <strings.h>
87 #endif
88
89 unixd_config_rec unixd_config;
90
91 /* Set group privileges.
92  *
93  * Note that we use the username as set in the config files, rather than
94  * the lookup of to uid --- the same uid may have multiple passwd entries,
95  * with different sets of groups for each.
96  */
97
98 static int set_group_privs(void)
99 {
100     if (!geteuid()) {
101         const char *name;
102
103         /* Get username if passed as a uid */
104
105         if (unixd_config.user_name[0] == '#') {
106             struct passwd *ent;
107             uid_t uid = atoi(&unixd_config.user_name[1]);
108
109             if ((ent = getpwuid(uid)) == NULL) {
110                 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
111                          "getpwuid: couldn't determine user name from uid %u, "
112                          "you probably need to modify the User directive",
113                          (unsigned)uid);
114                 return -1;
115             }
116
117             name = ent->pw_name;
118         }
119         else
120             name = unixd_config.user_name;
121
122 #if !defined(OS2) && !defined(TPF)
123         /* OS/2 and TPF don't support groups. */
124
125         /*
126          * Set the GID before initgroups(), since on some platforms
127          * setgid() is known to zap the group list.
128          */
129         if (setgid(unixd_config.group_id) == -1) {
130             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
131                         "setgid: unable to set group id to Group %u",
132                         (unsigned)unixd_config.group_id);
133             return -1;
134         }
135
136         /* Reset `groups' attributes. */
137
138         if (initgroups(name, unixd_config.group_id) == -1) {
139             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
140                         "initgroups: unable to set groups for User %s "
141                         "and Group %u", name, (unsigned)unixd_config.group_id);
142             return -1;
143         }
144 #endif /* !defined(OS2) && !defined(TPF) */
145     }
146     return 0;
147 }
148
149
150 int unixd_setup_child(void)
151 {
152     if (set_group_privs()) {
153         return -1;
154     }
155 #ifdef MPE
156     /* Only try to switch if we're running as MANAGER.SYS */
157     if (geteuid() == 1 && unixd_config.user_id > 1) {
158         GETPRIVMODE();
159         if (setuid(unixd_config.user_id) == -1) {
160             GETUSERMODE();
161             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
162                         "setuid: unable to change to uid: %ld",
163                         (long) unixd_config.user_id);
164             exit(1);
165         }
166         GETUSERMODE();
167     }
168 #else
169     /* Only try to switch if we're running as root */
170     if (!geteuid() && (
171 #ifdef _OSD_POSIX
172         os_init_job_environment(server_conf, unixd_config.user_name, one_process) != 0 || 
173 #endif
174         setuid(unixd_config.user_id) == -1)) {
175         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
176                     "setuid: unable to change to uid: %ld",
177                     (long) unixd_config.user_id);
178         return -1;
179     }
180 #endif
181     return 0;
182 }
183
184
185 const char *unixd_set_user(cmd_parms *cmd, void *dummy, const char *arg)
186 {
187     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
188     if (err != NULL) {
189         return err;
190     }
191
192     unixd_config.user_name = arg;
193     unixd_config.user_id = ap_uname2id(arg);
194 #if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
195     if (unixd_config.user_id == 0) {
196         return "Error:\tApache has not been designed to serve pages while\n"
197                 "\trunning as root.  There are known race conditions that\n"
198                 "\twill allow any local user to read any file on the system.\n"
199                 "\tIf you still desire to serve pages as root then\n"
200                 "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n"
201                 "\tsrc/Configuration file and rebuild the server.  It is\n"
202                 "\tstrongly suggested that you instead modify the User\n"
203                 "\tdirective in your httpd.conf file to list a non-root\n"
204                 "\tuser.\n";
205     }
206 #endif
207
208     return NULL;
209 }
210
211 const char *unixd_set_group(cmd_parms *cmd, void *dummy, const char *arg)
212 {
213     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
214     if (err != NULL) {
215         return err;
216     }
217
218     unixd_config.group_id = ap_gname2id(arg);
219
220     return NULL;
221 }
222
223 void unixd_pre_config(apr_pool_t *ptemp)
224 {
225     apr_finfo_t wrapper;
226
227     unixd_config.user_name = DEFAULT_USER;
228     unixd_config.user_id = ap_uname2id(DEFAULT_USER);
229     unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
230
231     /* Check for suexec */
232     unixd_config.suexec_enabled = 0;
233     if ((apr_stat(&wrapper, SUEXEC_BIN, 
234                   APR_FINFO_NORM, ptemp)) != APR_SUCCESS) {
235         return;
236     }
237
238     /* XXX - apr_stat is incapable of checking suid bits (grumble) */
239     /* if ((wrapper.filetype & S_ISUID) && wrapper.user == 0) { */
240         unixd_config.suexec_enabled = 1;
241     /* } */
242 }
243
244 #ifdef NEED_AP_SYS_SIGLIST
245
246 const char *ap_sys_siglist[NumSIG];
247
248 #define store_str(array,index,string) \
249 (ap_assert(index < (sizeof(array)/sizeof(array[0]))),array[index]=string)
250
251 void unixd_siglist_init(void)
252 {
253     int sig;
254
255     ap_sys_siglist[0] = "Signal 0";
256 #ifdef SIGHUP
257     store_str(ap_sys_siglist,SIGHUP,"Hangup");
258 #endif
259 #ifdef SIGINT
260     store_str(ap_sys_siglist,SIGINT,"Interrupt");
261 #endif
262 #ifdef SIGQUIT
263     store_str(ap_sys_siglist,SIGQUIT,"Quit");
264 #endif
265 #ifdef SIGILL
266     store_str(ap_sys_siglist,SIGILL,"Illegal instruction");
267 #endif
268 #ifdef SIGTRAP
269     store_str(ap_sys_siglist,SIGTRAP,"Trace/BPT trap");
270 #endif
271 #ifdef SIGIOT
272     store_str(ap_sys_siglist,SIGIOT,"IOT instruction");
273 #endif
274 #ifdef SIGABRT
275     store_str(ap_sys_siglist,SIGABRT,"Abort");
276 #endif
277 #ifdef SIGEMT
278     store_str(ap_sys_siglist,SIGEMT,"Emulator trap");
279 #endif
280 #ifdef SIGFPE
281     store_str(ap_sys_siglist,SIGFPE,"Arithmetic exception");
282 #endif
283 #ifdef SIGKILL
284     store_str(ap_sys_siglist,SIGKILL,"Killed");
285 #endif
286 #ifdef SIGBUS
287     store_str(ap_sys_siglist,SIGBUS,"Bus error");
288 #endif
289 #ifdef SIGSEGV
290     store_str(ap_sys_siglist,SIGSEGV,"Segmentation fault");
291 #endif
292 #ifdef SIGSYS
293     store_str(ap_sys_siglist,SIGSYS,"Bad system call");
294 #endif
295 #ifdef SIGPIPE
296     store_str(ap_sys_siglist,SIGPIPE,"Broken pipe");
297 #endif
298 #ifdef SIGALRM
299     store_str(ap_sys_siglist,SIGALRM,"Alarm clock");
300 #endif
301 #ifdef SIGTERM
302     store_str(ap_sys_siglist,SIGTERM,"Terminated");
303 #endif
304 #ifdef SIGUSR1
305     store_str(ap_sys_siglist,SIGUSR1,"User defined signal 1");
306 #endif
307 #ifdef SIGUSR2
308     store_str(ap_sys_siglist,SIGUSR2,"User defined signal 2");
309 #endif
310 #ifdef SIGCLD
311     store_str(ap_sys_siglist,SIGCLD,"Child status change");
312 #endif
313 #ifdef SIGCHLD
314     store_str(ap_sys_siglist,SIGCHLD,"Child status change");
315 #endif
316 #ifdef SIGPWR
317     store_str(ap_sys_siglist,SIGPWR,"Power-fail restart");
318 #endif
319 #ifdef SIGWINCH
320     store_str(ap_sys_siglist,SIGWINCH,"Window changed");
321 #endif
322 #ifdef SIGURG
323     store_str(ap_sys_siglist,SIGURG,"urgent socket condition");
324 #endif
325 #ifdef SIGPOLL
326     store_str(ap_sys_siglist,SIGPOLL,"Pollable event occurred");
327 #endif
328 #ifdef SIGIO
329     store_str(ap_sys_siglist,SIGIO,"socket I/O possible");
330 #endif
331 #ifdef SIGSTOP
332     store_str(ap_sys_siglist,SIGSTOP,"Stopped (signal)");
333 #endif
334 #ifdef SIGTSTP
335     store_str(ap_sys_siglist,SIGTSTP,"Stopped");
336 #endif
337 #ifdef SIGCONT
338     store_str(ap_sys_siglist,SIGCONT,"Continued");
339 #endif
340 #ifdef SIGTTIN
341     store_str(ap_sys_siglist,SIGTTIN,"Stopped (tty input)");
342 #endif
343 #ifdef SIGTTOU
344     store_str(ap_sys_siglist,SIGTTOU,"Stopped (tty output)");
345 #endif
346 #ifdef SIGVTALRM
347     store_str(ap_sys_siglist,SIGVTALRM,"virtual timer expired");
348 #endif
349 #ifdef SIGPROF
350     store_str(ap_sys_siglist,SIGPROF,"profiling timer expired");
351 #endif
352 #ifdef SIGXCPU
353     store_str(ap_sys_siglist,SIGXCPU,"exceeded cpu limit");
354 #endif
355 #ifdef SIGXFSZ
356     store_str(ap_sys_siglist,SIGXFSZ,"exceeded file size limit");
357 #endif
358     for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig)
359         if (ap_sys_siglist[sig] == NULL)
360             ap_sys_siglist[sig] = "";
361 }
362 #endif /* NEED_AP_SYS_SIGLIST */
363
364 AP_DECLARE(void) unixd_set_rlimit(cmd_parms *cmd, struct rlimit **plimit, 
365                            const char *arg, const char * arg2, int type)
366 {
367 #if (defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)) && APR_HAVE_STRUCT_RLIMIT && APR_HAVE_GETRLIMIT
368     char *str;
369     struct rlimit *limit;
370     /* If your platform doesn't define rlim_t then typedef it in ap_config.h */
371     rlim_t cur = 0;
372     rlim_t max = 0;
373
374     *plimit = (struct rlimit *)apr_pcalloc(cmd->pool, sizeof(**plimit));
375     limit = *plimit;
376     if ((getrlimit(type, limit)) != 0)  {
377         *plimit = NULL;
378         ap_log_error(APLOG_MARK, APLOG_ERR, errno, cmd->server,
379                      "%s: getrlimit failed", cmd->cmd->name);
380         return;
381     }
382
383     if ((str = ap_getword_conf(cmd->pool, &arg))) {
384         if (!strcasecmp(str, "max")) {
385             cur = limit->rlim_max;
386         }
387         else {
388             cur = atol(str);
389         }
390     }
391     else {
392         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
393                      "Invalid parameters for %s", cmd->cmd->name);
394         return;
395     }
396
397     if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) {
398         max = atol(str);
399     }
400
401     /* if we aren't running as root, cannot increase max */
402     if (geteuid()) {
403         limit->rlim_cur = cur;
404         if (max) {
405             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
406                          "Must be uid 0 to raise maximum %s", cmd->cmd->name);
407         }
408     }
409     else {
410         if (cur) {
411             limit->rlim_cur = cur;
412         }
413         if (max) {
414             limit->rlim_max = max;
415         }
416     }
417 #else
418
419     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
420                  "Platform does not support rlimit for %s", cmd->cmd->name);
421 #endif
422 }
423
424 APR_HOOK_STRUCT(
425                APR_HOOK_LINK(get_suexec_identity)
426 )
427
428 AP_IMPLEMENT_HOOK_RUN_FIRST(ap_unix_identity_t *, get_suexec_identity,
429                          (const request_rec *r), (r), NULL)
430
431 static apr_status_t ap_unix_create_privileged_process(
432                               apr_proc_t *newproc, const char *progname,
433                               const char * const *args,
434                               const char * const *env,
435                               apr_procattr_t *attr, ap_unix_identity_t *ugid,
436                               apr_pool_t *p)
437 {
438     int i = 0;
439     const char **newargs;
440     char *newprogname;
441     char *execuser, *execgroup;
442
443     if (!unixd_config.suexec_enabled) {
444         return apr_create_process(newproc, progname, args, env, attr, p);
445     }
446
447     execuser = apr_psprintf(p, "%ld", (long) ugid->uid);
448     execgroup = apr_psprintf(p, "%ld", (long) ugid->gid);
449
450     if (!execuser || !execgroup) {
451         return APR_ENOMEM;
452     }
453
454     i = 0;
455     if (args) {
456         while (args[i]) {
457             i++;
458             }
459     }
460     newargs = apr_palloc(p, sizeof(char *) * (i + 4));
461     newprogname = SUEXEC_BIN;
462     newargs[0] = SUEXEC_BIN;
463     newargs[1] = execuser;
464     newargs[2] = execgroup;
465     newargs[3] = apr_pstrdup(p, progname);
466
467     i = 0;
468     do {
469         newargs[i + 4] = args[i];
470     } while (args[i++]);
471
472     return apr_create_process(newproc, newprogname, newargs, env, attr, p);
473 }
474
475 AP_DECLARE(apr_status_t) ap_os_create_privileged_process(
476     const request_rec *r,
477     apr_proc_t *newproc, const char *progname,
478     const char * const *args,
479     const char * const *env,
480     apr_procattr_t *attr, apr_pool_t *p)
481 {
482     ap_unix_identity_t *ugid = ap_run_get_suexec_identity(r);
483
484     if (ugid == NULL) {
485         return apr_create_process(newproc, progname, args, env, attr, p);
486     }
487
488     return ap_unix_create_privileged_process(newproc, progname, args, env,
489                                               attr, ugid, p);
490 }
491