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