bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / os / unix / 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 #define CORE_PRIVATE
19 #include "httpd.h"
20 #include "http_config.h"
21 #include "http_main.h"
22 #include "http_log.h"
23 #include "unixd.h"
24 #include "mpm_common.h"
25 #include "os.h"
26 #include "ap_mpm.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 unixd_config_rec unixd_config;
55
56 /* Set group privileges.
57  *
58  * Note that we use the username as set in the config files, rather than
59  * the lookup of to uid --- the same uid may have multiple passwd entries,
60  * with different sets of groups for each.
61  */
62
63 static int set_group_privs(void)
64 {
65     if (!geteuid()) {
66         const char *name;
67
68         /* Get username if passed as a uid */
69
70         if (unixd_config.user_name[0] == '#') {
71             struct passwd *ent;
72             uid_t uid = atoi(&unixd_config.user_name[1]);
73
74             if ((ent = getpwuid(uid)) == NULL) {
75                 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
76                          "getpwuid: couldn't determine user name from uid %u, "
77                          "you probably need to modify the User directive",
78                          (unsigned)uid);
79                 return -1;
80             }
81
82             name = ent->pw_name;
83         }
84         else
85             name = unixd_config.user_name;
86
87 #if !defined(OS2) && !defined(TPF)
88         /* OS/2 and TPF don't support groups. */
89
90         /*
91          * Set the GID before initgroups(), since on some platforms
92          * setgid() is known to zap the group list.
93          */
94         if (setgid(unixd_config.group_id) == -1) {
95             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
96                         "setgid: unable to set group id to Group %u",
97                         (unsigned)unixd_config.group_id);
98             return -1;
99         }
100
101         /* Reset `groups' attributes. */
102
103         if (initgroups(name, unixd_config.group_id) == -1) {
104             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
105                         "initgroups: unable to set groups for User %s "
106                         "and Group %u", name, (unsigned)unixd_config.group_id);
107             return -1;
108         }
109 #endif /* !defined(OS2) && !defined(TPF) */
110     }
111     return 0;
112 }
113
114
115 AP_DECLARE(int) unixd_setup_child(void)
116 {
117     if (set_group_privs()) {
118         return -1;
119     }
120 #ifdef MPE
121     /* Only try to switch if we're running as MANAGER.SYS */
122     if (geteuid() == 1 && unixd_config.user_id > 1) {
123         GETPRIVMODE();
124         if (setuid(unixd_config.user_id) == -1) {
125             GETUSERMODE();
126             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
127                         "setuid: unable to change to uid: %ld",
128                         (long) unixd_config.user_id);
129             exit(1);
130         }
131         GETUSERMODE();
132     }
133 #else
134     /* Only try to switch if we're running as root */
135     if (!geteuid() && (
136 #ifdef _OSD_POSIX
137         os_init_job_environment(server_conf, unixd_config.user_name, one_process) != 0 || 
138 #endif
139         setuid(unixd_config.user_id) == -1)) {
140         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
141                     "setuid: unable to change to uid: %ld",
142                     (long) unixd_config.user_id);
143         return -1;
144     }
145 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) 
146     /* this applies to Linux 2.4+ */
147 #ifdef AP_MPM_WANT_SET_COREDUMPDIR
148     if (ap_coredumpdir_configured) {
149         if (prctl(PR_SET_DUMPABLE, 1)) {
150             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL,
151                          "set dumpable failed - this child will not coredump"
152                          " after software errors");
153         }
154     }
155 #endif
156 #endif
157 #endif
158     return 0;
159 }
160
161
162 AP_DECLARE(const char *) unixd_set_user(cmd_parms *cmd, void *dummy, 
163                                         const char *arg)
164 {
165     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
166     if (err != NULL) {
167         return err;
168     }
169
170     unixd_config.user_name = arg;
171     unixd_config.user_id = ap_uname2id(arg);
172 #if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
173     if (unixd_config.user_id == 0) {
174         return "Error:\tApache has not been designed to serve pages while\n"
175                 "\trunning as root.  There are known race conditions that\n"
176                 "\twill allow any local user to read any file on the system.\n"
177                 "\tIf you still desire to serve pages as root then\n"
178                 "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
179                 "\tand then rebuild the server.\n"
180                 "\tIt is strongly suggested that you instead modify the User\n"
181                 "\tdirective in your httpd.conf file to list a non-root\n"
182                 "\tuser.\n";
183     }
184 #endif
185
186     return NULL;
187 }
188
189 AP_DECLARE(const char *) unixd_set_group(cmd_parms *cmd, void *dummy, 
190                                          const char *arg)
191 {
192     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
193     if (err != NULL) {
194         return err;
195     }
196
197     unixd_config.group_id = ap_gname2id(arg);
198
199     return NULL;
200 }
201
202 AP_DECLARE(void) unixd_pre_config(apr_pool_t *ptemp)
203 {
204     apr_finfo_t wrapper;
205
206     unixd_config.user_name = DEFAULT_USER;
207     unixd_config.user_id = ap_uname2id(DEFAULT_USER);
208     unixd_config.group_id = ap_gname2id(DEFAULT_GROUP);
209
210     /* Check for suexec */
211     unixd_config.suexec_enabled = 0;
212     if ((apr_stat(&wrapper, SUEXEC_BIN, 
213                   APR_FINFO_NORM, ptemp)) != APR_SUCCESS) {
214         return;
215     }
216
217 /* since APR 0.9.5 */
218 #ifdef APR_USETID
219     if ((wrapper.protection & APR_USETID) && wrapper.user == 0) {
220 #endif
221         unixd_config.suexec_enabled = 1;
222 #ifdef APR_USETID
223     }
224 #endif
225 }
226
227
228 AP_DECLARE(void) unixd_set_rlimit(cmd_parms *cmd, struct rlimit **plimit, 
229                            const char *arg, const char * arg2, int type)
230 {
231 #if (defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)) && APR_HAVE_STRUCT_RLIMIT && APR_HAVE_GETRLIMIT
232     char *str;
233     struct rlimit *limit;
234     /* If your platform doesn't define rlim_t then typedef it in ap_config.h */
235     rlim_t cur = 0;
236     rlim_t max = 0;
237
238     *plimit = (struct rlimit *)apr_pcalloc(cmd->pool, sizeof(**plimit));
239     limit = *plimit;
240     if ((getrlimit(type, limit)) != 0)  {
241         *plimit = NULL;
242         ap_log_error(APLOG_MARK, APLOG_ERR, errno, cmd->server,
243                      "%s: getrlimit failed", cmd->cmd->name);
244         return;
245     }
246
247     if ((str = ap_getword_conf(cmd->pool, &arg))) {
248         if (!strcasecmp(str, "max")) {
249             cur = limit->rlim_max;
250         }
251         else {
252             cur = atol(str);
253         }
254     }
255     else {
256         ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
257                      "Invalid parameters for %s", cmd->cmd->name);
258         return;
259     }
260
261     if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) {
262         max = atol(str);
263     }
264
265     /* if we aren't running as root, cannot increase max */
266     if (geteuid()) {
267         limit->rlim_cur = cur;
268         if (max) {
269             ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
270                          "Must be uid 0 to raise maximum %s", cmd->cmd->name);
271         }
272     }
273     else {
274         if (cur) {
275             limit->rlim_cur = cur;
276         }
277         if (max) {
278             limit->rlim_max = max;
279         }
280     }
281 #else
282
283     ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
284                  "Platform does not support rlimit for %s", cmd->cmd->name);
285 #endif
286 }
287
288 APR_HOOK_STRUCT(
289                APR_HOOK_LINK(get_suexec_identity)
290 )
291
292 AP_IMPLEMENT_HOOK_RUN_FIRST(ap_unix_identity_t *, get_suexec_identity,
293                          (const request_rec *r), (r), NULL)
294
295 static apr_status_t ap_unix_create_privileged_process(
296                               apr_proc_t *newproc, const char *progname,
297                               const char * const *args,
298                               const char * const *env,
299                               apr_procattr_t *attr, ap_unix_identity_t *ugid,
300                               apr_pool_t *p)
301 {
302     int i = 0;
303     const char **newargs;
304     char *newprogname;
305     char *execuser, *execgroup;
306     const char *argv0;
307
308     if (!unixd_config.suexec_enabled) {
309         return apr_proc_create(newproc, progname, args, env, attr, p);
310     }
311
312     argv0 = ap_strrchr_c(progname, '/');
313     /* Allow suexec's "/" check to succeed */
314     if (argv0 != NULL) {
315         argv0++;
316     }
317     else {
318         argv0 = progname;
319     }
320
321
322     if (ugid->userdir) {
323         execuser = apr_psprintf(p, "~%ld", (long) ugid->uid);
324     }
325     else {
326         execuser = apr_psprintf(p, "%ld", (long) ugid->uid);
327     }
328     execgroup = apr_psprintf(p, "%ld", (long) ugid->gid);
329
330     if (!execuser || !execgroup) {
331         return APR_ENOMEM;
332     }
333
334     i = 0;
335     if (args) {
336         while (args[i]) {
337             i++;
338             }
339     }
340     /* allocate space for 4 new args, the input args, and a null terminator */
341     newargs = apr_palloc(p, sizeof(char *) * (i + 4));
342     newprogname = SUEXEC_BIN;
343     newargs[0] = SUEXEC_BIN;
344     newargs[1] = execuser;
345     newargs[2] = execgroup;
346     newargs[3] = apr_pstrdup(p, argv0);
347
348     /*
349     ** using a shell to execute suexec makes no sense thus
350     ** we force everything to be APR_PROGRAM, and never
351     ** APR_SHELLCMD
352     */
353     if(apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) {
354         return APR_EGENERAL;
355     }
356
357     i = 1;
358     do {
359         newargs[i + 3] = args[i];
360     } while (args[i++]);
361
362     return apr_proc_create(newproc, newprogname, newargs, env, attr, p);
363 }
364
365 AP_DECLARE(apr_status_t) ap_os_create_privileged_process(
366     const request_rec *r,
367     apr_proc_t *newproc, const char *progname,
368     const char * const *args,
369     const char * const *env,
370     apr_procattr_t *attr, apr_pool_t *p)
371 {
372     ap_unix_identity_t *ugid = ap_run_get_suexec_identity(r);
373
374     if (ugid == NULL) {
375         return apr_proc_create(newproc, progname, args, env, attr, p);
376     }
377
378     return ap_unix_create_privileged_process(newproc, progname, args, env,
379                                               attr, ugid, p);
380 }
381
382 /* XXX move to APR and externalize (but implement differently :) ) */
383 static apr_lockmech_e proc_mutex_mech(apr_proc_mutex_t *pmutex)
384 {
385     const char *mechname = apr_proc_mutex_name(pmutex);
386
387     if (!strcmp(mechname, "sysvsem")) {
388         return APR_LOCK_SYSVSEM;
389     }
390     else if (!strcmp(mechname, "flock")) {
391         return APR_LOCK_FLOCK;
392     }
393     return APR_LOCK_DEFAULT;
394 }
395
396 AP_DECLARE(apr_status_t) unixd_set_proc_mutex_perms(apr_proc_mutex_t *pmutex)
397 {
398     if (!geteuid()) {
399         apr_lockmech_e mech = proc_mutex_mech(pmutex);
400
401         switch(mech) {
402 #if APR_HAS_SYSVSEM_SERIALIZE
403         case APR_LOCK_SYSVSEM:
404         {
405             apr_os_proc_mutex_t ospmutex;
406 #if !APR_HAVE_UNION_SEMUN
407             union semun {
408                 long val;
409                 struct semid_ds *buf;
410                 unsigned short *array;
411             };
412 #endif
413             union semun ick;
414             struct semid_ds buf;
415         
416             apr_os_proc_mutex_get(&ospmutex, pmutex);
417             buf.sem_perm.uid = unixd_config.user_id;
418             buf.sem_perm.gid = unixd_config.group_id;
419             buf.sem_perm.mode = 0600;
420             ick.buf = &buf;
421             if (semctl(ospmutex.crossproc, 0, IPC_SET, ick) < 0) {
422                 return errno;
423             }
424         }
425         break;
426 #endif
427 #if APR_HAS_FLOCK_SERIALIZE
428         case APR_LOCK_FLOCK:
429         {
430             const char *lockfile = apr_proc_mutex_lockfile(pmutex);
431
432             if (lockfile) {
433                 if (chown(lockfile, unixd_config.user_id,
434                           -1 /* no gid change */) < 0) {
435                     return errno;
436                 }
437             }
438         }
439         break;
440 #endif
441         default:
442             /* do nothing */
443             break;
444         }
445     }
446     return APR_SUCCESS;
447 }
448
449 AP_DECLARE(apr_status_t) unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex)
450 {
451 #if !APR_PROC_MUTEX_IS_GLOBAL
452     apr_os_global_mutex_t osgmutex;
453     apr_os_global_mutex_get(&osgmutex, gmutex);
454     return unixd_set_proc_mutex_perms(osgmutex.proc_mutex);
455 #else  /* APR_PROC_MUTEX_IS_GLOBAL */
456     /* In this case, apr_proc_mutex_t and apr_global_mutex_t are the same. */
457     return unixd_set_proc_mutex_perms(gmutex);
458 #endif /* APR_PROC_MUTEX_IS_GLOBAL */
459 }
460
461 AP_DECLARE(apr_status_t) unixd_accept(void **accepted, ap_listen_rec *lr,
462                                         apr_pool_t *ptrans)
463 {
464     apr_socket_t *csd;
465     apr_status_t status;
466
467     *accepted = NULL;
468     status = apr_accept(&csd, lr->sd, ptrans);
469     if (status == APR_SUCCESS) { 
470         *accepted = csd;
471         return APR_SUCCESS;
472     }
473
474     if (APR_STATUS_IS_EINTR(status)) {
475         return status;
476     }
477     /* Our old behaviour here was to continue after accept()
478      * errors.  But this leads us into lots of troubles
479      * because most of the errors are quite fatal.  For
480      * example, EMFILE can be caused by slow descriptor
481      * leaks (say in a 3rd party module, or libc).  It's
482      * foolish for us to continue after an EMFILE.  We also
483      * seem to tickle kernel bugs on some platforms which
484      * lead to never-ending loops here.  So it seems best
485      * to just exit in most cases.
486      */
487     switch (status) {
488 #if defined(HPUX11) && defined(ENOBUFS)
489         /* On HPUX 11.x, the 'ENOBUFS, No buffer space available'
490          * error occurs because the accept() cannot complete.
491          * You will not see ENOBUFS with 10.20 because the kernel
492          * hides any occurrence from being returned to user space.
493          * ENOBUFS with 11.x's TCP/IP stack is possible, and could
494          * occur intermittently. As a work-around, we are going to
495          * ignore ENOBUFS.
496          */
497         case ENOBUFS:
498 #endif
499
500 #ifdef EPROTO
501         /* EPROTO on certain older kernels really means
502          * ECONNABORTED, so we need to ignore it for them.
503          * See discussion in new-httpd archives nh.9701
504          * search for EPROTO.
505          *
506          * Also see nh.9603, search for EPROTO:
507          * There is potentially a bug in Solaris 2.x x<6,
508          * and other boxes that implement tcp sockets in
509          * userland (i.e. on top of STREAMS).  On these
510          * systems, EPROTO can actually result in a fatal
511          * loop.  See PR#981 for example.  It's hard to
512          * handle both uses of EPROTO.
513          */
514         case EPROTO:
515 #endif
516 #ifdef ECONNABORTED
517         case ECONNABORTED:
518 #endif
519         /* Linux generates the rest of these, other tcp
520          * stacks (i.e. bsd) tend to hide them behind
521          * getsockopt() interfaces.  They occur when
522          * the net goes sour or the client disconnects
523          * after the three-way handshake has been done
524          * in the kernel but before userland has picked
525          * up the socket.
526          */
527 #ifdef ECONNRESET
528         case ECONNRESET:
529 #endif
530 #ifdef ETIMEDOUT
531         case ETIMEDOUT:
532 #endif
533 #ifdef EHOSTUNREACH
534         case EHOSTUNREACH:
535 #endif
536 #ifdef ENETUNREACH
537         case ENETUNREACH:
538 #endif
539         /* EAGAIN/EWOULDBLOCK can be returned on BSD-derived
540          * TCP stacks when the connection is aborted before
541          * we call connect, but only because our listener
542          * sockets are non-blocking (AP_NONBLOCK_WHEN_MULTI_LISTEN)
543          */
544 #ifdef EAGAIN
545         case EAGAIN:
546 #endif
547 #ifdef EWOULDBLOCK
548 #if !defined(EAGAIN) || EAGAIN != EWOULDBLOCK
549         case EWOULDBLOCK:
550 #endif
551 #endif
552             break;
553 #ifdef ENETDOWN
554         case ENETDOWN:
555             /*
556              * When the network layer has been shut down, there
557              * is not much use in simply exiting: the parent
558              * would simply re-create us (and we'd fail again).
559              * Use the CHILDFATAL code to tear the server down.
560              * @@@ Martin's idea for possible improvement:
561              * A different approach would be to define
562              * a new APEXIT_NETDOWN exit code, the reception
563              * of which would make the parent shutdown all
564              * children, then idle-loop until it detected that
565              * the network is up again, and restart the children.
566              * Ben Hyde noted that temporary ENETDOWN situations
567              * occur in mobile IP.
568              */
569             ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf,
570                          "apr_accept: giving up.");
571             return APR_EGENERAL;
572 #endif /*ENETDOWN*/
573
574 #ifdef TPF
575         case EINACT:
576             ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf,
577                          "offload device inactive");
578             return APR_EGENERAL;
579             break;
580         default:
581             ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
582                          "select/accept error (%d)", status);
583             return APR_EGENERAL;
584 #else
585         default:
586             ap_log_error(APLOG_MARK, APLOG_ERR, status, ap_server_conf,
587                          "apr_accept: (client socket)");
588             return APR_EGENERAL;
589 #endif
590     }
591     return status;
592 }
593