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