]> granicus.if.org Git - apache/blob - modules/generators/mod_cgid.c
Move all APR functions related to strings to their own directory, and
[apache] / modules / generators / mod_cgid.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 /* 
60  * http_script: keeps all script-related ramblings together. 
61  * 
62  * Compliant to cgi/1.1 spec 
63  * 
64  * Adapted by rst from original NCSA code by Rob McCool 
65  * 
66  * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for 
67  * custom error responses, and DOCUMENT_ROOT because we found it useful. 
68  * It also adds SERVER_ADMIN - useful for scripts to know who to mail when 
69  * they fail. 
70  */ 
71
72
73
74 #define CORE_PRIVATE 
75
76 #include "apr_lib.h"
77 #include "apr_strings.h"
78 #include "apr_general.h"
79 #include "apr_file_io.h"
80 #include "apr_portable.h"
81 #include "httpd.h" 
82 #include "http_config.h" 
83 #include "http_request.h" 
84 #include "http_core.h" 
85 #include "http_protocol.h" 
86 #include "http_main.h" 
87 #include "http_log.h" 
88 #include "util_script.h" 
89 #include "http_conf_globals.h" 
90 #include "buff.h" 
91 #include "ap_mpm.h"
92 #include "iol_socket.h"
93 #include "unixd.h"
94 #include <sys/stat.h>
95 #ifdef HAVE_SYS_SOCKET_H
96 #include <sys/socket.h>
97 #endif
98 #ifdef HAVE_UNISTD_H
99 #include <unistd.h>
100 #endif
101 #include <sys/un.h> /* for sockaddr_un */
102 #include <sys/types.h>
103
104 module MODULE_VAR_EXPORT cgid_module; 
105
106 static void cgid_init(ap_pool_t *p, ap_pool_t *plog, ap_pool_t *ptemp, server_rec *main_server); 
107 static int once_through = 0; 
108
109 static ap_pool_t *pcgi; 
110
111 /* KLUDGE --- for back-combatibility, we don't have to check Execcgid 
112  * in ScriptAliased directories, which means we need to know if this 
113  * request came through ScriptAlias or not... so the Alias module 
114  * leaves a note for us. 
115  */ 
116
117 static int is_scriptaliased(request_rec *r) 
118
119     const char *t = ap_table_get(r->notes, "alias-forced-type"); 
120     return t && (!strcasecmp(t, "cgi-script")); 
121
122
123 /* Configuration stuff */ 
124
125 #define DEFAULT_LOGBYTES 10385760 
126 #define DEFAULT_BUFBYTES 1024 
127 #define DEFAULT_SOCKET "logs/cgisock"
128
129 #define SHELL_PATH "/bin/sh"
130
131 /* DEFAULT_CGID_LISTENBACKLOG controls the max depth on the unix socket's
132  * pending connection queue.  If a bunch of cgi requests arrive at about
133  * the same time, connections from httpd threads/processes will back up
134  * in the queue while the cgid process slowly forks off a child to process
135  * each connection on the unix socket.  If the queue is too short, the
136  * httpd process will get ECONNREFUSED when trying to connect.
137  */
138 #ifndef DEFAULT_CGID_LISTENBACKLOG
139 #define DEFAULT_CGID_LISTENBACKLOG 100
140 #endif
141
142 typedef struct { 
143     const char *sockname;
144     const char *logname; 
145     long logbytes; 
146     int bufbytes; 
147     BUFF *bin; 
148     BUFF *bout; 
149     BUFF *berror; 
150 } cgid_server_conf; 
151
152 /* If a request includes query info in the URL (stuff after "?"), and
153  * the query info does not contain "=" (indicative of a FORM submission),
154  * then this routine is called to create the argument list to be passed
155  * to the CGI script.  When suexec is enabled, the suexec path, user, and
156  * group are the first three arguments to be passed; if not, all three
157  * must be NULL.  The query info is split into separate arguments, where
158  * "+" is the separator between keyword arguments.
159  *
160  * XXXX: note that the WIN32 code uses one of the suexec strings
161  * to pass an interpreter name.  Remember this if changing the way they
162  * are handled in create_argv.
163  *
164  */
165 static char **create_argv(ap_pool_t *p, char *path, char *user, char *group,
166                           char *av0, const char *args)
167 {
168     int x, numwords;
169     char **av;
170     char *w;
171     int idx = 0;
172
173     /* count the number of keywords */
174
175     for (x = 0, numwords = 1; args[x]; x++) {
176         if (args[x] == '+') {
177             ++numwords;
178         }
179     }
180
181     if (numwords > APACHE_ARG_MAX - 5) {
182         numwords = APACHE_ARG_MAX - 5;  /* Truncate args to prevent overrun */
183     }
184     av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *));
185
186     if (path) {
187         av[idx++] = path;
188     }
189     if (user) {
190         av[idx++] = user;
191     }
192     if (group) {
193         av[idx++] = group;
194      }
195
196     av[idx++] = av0;
197
198     for (x = 1; x <= numwords; x++) {
199         w = ap_getword_nulls(p, &args, '+');
200         ap_unescape_url(w);
201         av[idx++] = ap_escape_shell_cmd(p, w);
202     }
203     av[idx] = NULL;
204     return av;
205 }
206
207 static int call_exec(request_rec *r, char *argv0, char **env, int shellcmd)
208 {
209     int pid = 0;
210     int errfileno = STDERR_FILENO;
211     /* the fd on r->server->error_log is closed, but we need somewhere to            
212      * put the error messages from the log_* functions. So, we use stderr,
213      * since that is better than allowing errors to go unnoticed. 
214      */
215     ap_put_os_file(&r->server->error_log, &errfileno, r->pool);
216     /* TODO: reimplement suexec */
217 #if 0
218     if (ap_suexec_enabled
219         && ((r->server->server_uid != ap_user_id)
220             || (r->server->server_gid != ap_group_id)
221             || (!strncmp("/~", r->uri, 2)))) {
222
223         char *execuser, *grpname;
224         struct passwd *pw;
225         struct group *gr;
226
227         if (!strncmp("/~", r->uri, 2)) {
228             gid_t user_gid;
229             char *username = ap_pstrdup(r->pool, r->uri + 2);
230             char *pos = strchr(username, '/');
231
232             if (pos) {
233                 *pos = '\0';
234             }
235
236             if ((pw = getpwnam(username)) == NULL) {
237                 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
238                              "getpwnam: invalid username %s", username);
239                 return (pid);
240             }
241             execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL);
242             user_gid = pw->pw_gid;
243
244             if ((gr = getgrgid(user_gid)) == NULL) {
245                 if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
246                     return (pid);
247                 }
248                 else {
249                     ap_snprintf(grpname, 16, "%ld", (long) user_gid);
250                 }
251             }
252             else {
253                 grpname = gr->gr_name;
254             }
255         }
256         else {
257             if ((pw = getpwuid(r->server->server_uid)) == NULL) {
258                 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
259                              "getpwuid: invalid userid %ld",
260                              (long) r->server->server_uid);
261                 return (pid);
262             }
263             execuser = ap_pstrdup(r->pool, pw->pw_name);
264
265             if ((gr = getgrgid(r->server->server_gid)) == NULL) {
266                 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
267                              "getgrgid: invalid groupid %ld",
268                              (long) r->server->server_gid);
269                 return (pid);
270             }
271             grpname = gr->gr_name;
272         }
273
274         if (shellcmd) {
275             execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
276                    NULL, env);
277         }
278
279         else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
280             execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
281                    NULL, env);
282         }
283
284         else {
285             execve(SUEXEC_BIN,
286                    create_argv(r->pool, SUEXEC_BIN, execuser, grpname,
287                                argv0, r->args),
288                    env);
289         }
290     }
291     else {
292 #endif
293         if (shellcmd) {
294             execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
295         }
296
297         else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
298             execle(r->filename, argv0, NULL, env);
299         }
300
301         else {
302             execve(r->filename,
303                    create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
304                    env);
305         }
306 #if 0
307     }
308 #endif
309     return (pid);
310 }
311
312 static void cgid_maint(int reason, void *data, ap_wait_t status)
313 {
314 #if APR_HAS_OTHER_CHILD
315     int *sd = data;
316     switch (reason) {
317         case APR_OC_REASON_DEATH:
318         case APR_OC_REASON_LOST:
319             /* stop gap to make sure everything else works.  In the end,
320              * we'll just restart the cgid server. */
321             ap_destroy_pool(pcgi);
322             kill(getppid(), SIGWINCH);
323             break;
324         case APR_OC_REASON_RESTART:
325         case APR_OC_REASON_UNREGISTER:
326             ap_destroy_pool(pcgi);
327             kill(*sd, SIGHUP);
328             break;
329     }
330 #endif
331 }
332
333 static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env) 
334
335     int i, len, j; 
336     unsigned char *data; 
337     char **environ; 
338     core_dir_config *temp_core; 
339     void **dconf; 
340
341     r->server = ap_pcalloc(r->pool, sizeof(server_rec)); 
342
343     read(fd, &j, sizeof(int)); 
344     read(fd, &len, sizeof(int)); 
345     data = ap_pcalloc(r->pool, len + 1); /* get a cleared byte for final '\0' */
346     i = read(fd, data, len); 
347
348     r->filename = ap_getword(r->pool, (const char **)&data, '\n'); 
349     *argv0 = ap_getword(r->pool, (const char **)&data, '\n'); 
350
351     r->uri = ap_getword(r->pool, (const char **)&data, '\n'); 
352     
353     environ = ap_pcalloc(r->pool, (j + 2) *sizeof(char *)); 
354     i = 0; 
355     for (i = 0; i < j; i++) { 
356         environ[i] = ap_getword(r->pool, (const char **)&data, '\n'); 
357     } 
358     *env = environ; 
359     r->args = ap_getword(r->pool, (const char **)&data, '\n'); 
360   
361     read(fd, &r->server->server_uid, sizeof(uid_t)); 
362     read(fd, &r->server->server_gid, sizeof(gid_t)); 
363
364     read(fd, &i, sizeof(int)); 
365      
366     /* add 1, so that if i == 0, we still malloc something. */ 
367     dconf = (void **)malloc(sizeof(void *) * i + 1); 
368
369     temp_core = (core_dir_config *)malloc(sizeof(core_module)); 
370 #if 0
371 #ifdef RLIMIT_CPU 
372     read(fd, &j, sizeof(int)); 
373     if (j) { 
374         temp_core->limit_cpu = (struct rlimit *)malloc (sizeof(struct rlimit)); 
375         read(fd, temp_core->limit_cpu, sizeof(struct rlimit)); 
376     } 
377     else { 
378         temp_core->limit_cpu = NULL; 
379     } 
380 #endif 
381
382 #if defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 
383     read(fd, &j, sizeof(int)); 
384     if (j) { 
385         temp_core->limit_mem = (struct rlimit *)malloc (sizeof(struct rlimit)); 
386         read(fd, temp_core->limit_mem, sizeof(struct rlimit)); 
387     } 
388     else { 
389         temp_core->limit_mem = NULL; 
390     } 
391 #endif 
392
393 #ifdef RLIMIT_NPROC 
394     read(fd, &j, sizeof(int)); 
395     if (j) { 
396         temp_core->limit_nproc = (struct rlimit *)malloc (sizeof(struct rlimit)); 
397         read(fd, temp_core->limit_nproc, sizeof(struct rlimit)); 
398     } 
399     else { 
400         temp_core->limit_nproc = NULL; 
401     } 
402 #endif 
403 #endif
404     dconf[i] = (void *)temp_core; 
405     r->per_dir_config = dconf; 
406
407
408
409
410 static void send_req(int fd, request_rec *r, char *argv0, char **env) 
411
412     int len; 
413     int i = 0; 
414     char *data; 
415
416     data = ap_pstrcat(r->pool, r->filename, "\n", argv0, "\n", r->uri, "\n", 
417                      NULL); 
418
419     for (i =0; env[i]; i++) { 
420         continue; 
421     } 
422
423     if (write(fd, &i, sizeof(int)) < 0) {
424         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
425                      "write to cgi daemon process"); 
426         }     
427
428     for (i = 0; env[i]; i++) { 
429         data = ap_pstrcat(r->pool, data, env[i], "\n", NULL); 
430     } 
431     data = ap_pstrcat(r->pool, data, r->args, NULL); 
432     len = strlen(data); 
433     if (write(fd, &len, sizeof(int)) < 0) { 
434         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
435                      "write to cgi daemon process"); 
436         }     
437     if (write(fd, data, len) < 0) {
438         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
439                      "write to cgi daemon process"); 
440         }     
441     if (write(fd, &r->server->server_uid, sizeof(uid_t)) < 0) {
442         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
443                      "write to cgi daemon process"); 
444         }     
445     if (write(fd, &r->server->server_gid, sizeof(gid_t)) < 0) { 
446         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
447                      "write to cgi daemon process"); 
448         }     
449     if (write(fd, &core_module.module_index, sizeof(int)) < 0) { 
450         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
451                      "write to cgi daemon process"); 
452         }     
453 #if 0
454 #ifdef RLIMIT_CPU 
455     if (conf->limit_cpu) { 
456         len = 1; 
457         write(fd, &len, sizeof(int)); 
458         write(fd, conf->limit_cpu, sizeof(struct rlimit)); 
459     } 
460     else { 
461         len = 0; 
462         write(fd, &len, sizeof(int)); 
463     } 
464 #endif 
465
466 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 
467     if (conf->limit_mem) { 
468         len = 1; 
469         write(fd, &len, sizeof(int)); 
470         write(fd, conf->limit_mem, sizeof(struct rlimit)); 
471     } 
472     else { 
473         len = 0; 
474         write(fd, &len, sizeof(int)); 
475     } 
476 #endif 
477   
478 #ifdef RLIMIT_NPROC 
479     if (conf->limit_nproc) { 
480         len = 1; 
481         write(fd, &len, sizeof(int)); 
482         write(fd, conf->limit_nproc, sizeof(struct rlimit)); 
483     } 
484     else { 
485         len = 0; 
486         write(fd, &len, sizeof(int)); 
487     } 
488 #endif
489 #endif 
490
491
492 static int cgid_server_child(int sd) 
493
494     char *argv0; 
495     char *filename; 
496     char **env; 
497     ap_pool_t *p; 
498     request_rec *r; 
499
500     ap_create_pool(&p, pcgi); 
501     r = ap_pcalloc(p, sizeof(request_rec)); 
502     r->pool = p; 
503     dup2(sd, STDIN_FILENO); 
504     dup2(sd, STDOUT_FILENO); 
505     get_req(sd, r, &filename, &argv0, &env); 
506     call_exec(r, argv0, env, 0); 
507     exit(-1);   /* We should NEVER get here */
508
509
510 static int cgid_server(void *data) 
511
512     struct sockaddr_un unix_addr;
513     int pid; 
514     int sd, sd2, len, rc;
515     int errfile;
516     mode_t omask;
517     server_rec *main_server = data;
518     cgid_server_conf *sconf = (cgid_server_conf *)ap_get_module_config( 
519                        main_server->module_config, &cgid_module); 
520
521     ap_signal(SIGCHLD, SIG_IGN); 
522     if (unlink(sconf->sockname) < 0 &&
523         errno != ENOENT) {
524         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
525                      "Couldn't unlink unix domain socket %s",
526                      sconf->sockname);
527         /* just a warning; don't bail out */
528     }
529
530     if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
531         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
532                      "Couldn't create unix domain socket");
533         return errno;
534     } 
535
536     memset(&unix_addr, 0, sizeof(unix_addr));
537     unix_addr.sun_family = AF_UNIX;
538     strcpy(unix_addr.sun_path, sconf->sockname);
539
540     omask = umask(0077); /* so that only Apache can use socket */
541     rc = bind(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
542     umask(omask); /* can't fail, so can't clobber errno */
543     if (rc < 0) {
544         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
545                      "Couldn't bind unix domain socket %s",
546                      sconf->sockname); 
547         return errno;
548     } 
549
550     if (listen(sd, DEFAULT_CGID_LISTENBACKLOG) < 0) {
551         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
552                      "Couldn't listen on unix domain socket"); 
553         return errno;
554     } 
555
556     if (!geteuid()) {
557         if (chown(sconf->sockname, unixd_config.user_id, -1) < 0) {
558             ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
559                          "Couldn't change owner of unix domain socket %s",
560                          sconf->sockname); 
561             return errno;
562         }
563     }
564     
565     unixd_setup_child(); /* if running as root, switch to configured user/group */
566
567     while (1) {
568         len = sizeof(unix_addr);
569         sd2 = accept(sd, (struct sockaddr *)&unix_addr, &len);
570         if (sd2 < 0) {
571             ap_log_error(APLOG_MARK, APLOG_ERR, errno, (server_rec *)data,
572                          "Error accepting on cgid socket.");
573             continue;
574         }
575        
576         if ((pid = fork()) > 0) {
577             close(sd2);
578         } 
579         else if (pid == 0) { 
580             /* setup the STDERR here, because I have all the info
581              * for it.  I'll do the STDIN and STDOUT later, but I can't
582              * do STDERR as easily.
583              */
584             if (sconf->logname) {
585                 dup2(open(sconf->logname, O_WRONLY), STDERR_FILENO);
586             }
587             else {
588                 ap_get_os_file(&errfile, main_server->error_log);
589                 dup2(errfile, STDERR_FILENO);
590             }
591             cgid_server_child(sd2); 
592         } 
593         else { 
594             ap_log_error(APLOG_MARK, APLOG_ERR, errno, (server_rec *)data, 
595                          "Couldn't fork cgi script"); 
596         } 
597     } 
598     return -1; 
599
600
601 static void cgid_init(ap_pool_t *p, ap_pool_t *plog, ap_pool_t *ptemp, server_rec *main_server) 
602
603     pid_t pid; 
604     ap_proc_t ap_pid;
605
606     if (once_through > 0) { 
607         ap_create_pool(&pcgi, p); 
608
609         if ((pid = fork()) < 0) {
610             ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
611                          "Couldn't spawn cgid daemon process"); 
612         }
613         else if (pid == 0) {
614             cgid_server(main_server);
615             exit(-1);
616         } 
617 #if APR_HAS_OTHER_CHILD
618         ap_pid.pid = pid;
619         ap_pid.err = ap_pid.in = ap_pid.out = NULL;
620         ap_register_other_child(&ap_pid, cgid_maint, NULL, NULL, p);
621 #endif
622     } 
623     else once_through++; 
624
625
626 static void *create_cgid_config(ap_pool_t *p, server_rec *s) 
627
628     cgid_server_conf *c = 
629     (cgid_server_conf *) ap_pcalloc(p, sizeof(cgid_server_conf)); 
630
631     c->logname = NULL; 
632     c->logbytes = DEFAULT_LOGBYTES; 
633     c->bufbytes = DEFAULT_BUFBYTES; 
634     c->sockname = ap_server_root_relative(p, DEFAULT_SOCKET); 
635     c->bin = c->bout = c->berror = NULL; 
636     return c; 
637
638
639 static void *merge_cgid_config(ap_pool_t *p, void *basev, void *overridesv) 
640
641     cgid_server_conf *base = (cgid_server_conf *) basev, *overrides = (cgid_server_conf *) overridesv; 
642
643     return overrides->logname ? overrides : base; 
644
645
646 static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) 
647
648     server_rec *s = cmd->server; 
649     cgid_server_conf *conf = 
650     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
651
652     conf->logname = arg; 
653     return NULL; 
654
655
656 static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, const char *arg) 
657
658     server_rec *s = cmd->server; 
659     cgid_server_conf *conf = 
660     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
661
662     conf->logbytes = atol(arg); 
663     return NULL; 
664
665
666 static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, const char *arg) 
667
668     server_rec *s = cmd->server; 
669     cgid_server_conf *conf = 
670     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
671
672     conf->bufbytes = atoi(arg); 
673     return NULL; 
674
675
676 static const char *set_script_socket(cmd_parms *cmd, void *dummy, const char *arg) 
677
678     server_rec *s = cmd->server; 
679     cgid_server_conf *conf = 
680     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
681
682     conf->sockname = ap_server_root_relative(cmd->pool, arg); 
683     return NULL; 
684
685
686 static const command_rec cgid_cmds[] = 
687
688     AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
689                   "the name of a log for script debugging info"), 
690     AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF,
691                   "the maximum length (in bytes) of the script debug log"), 
692     AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF,
693                   "the maximum size (in bytes) to record of a POST request"), 
694     AP_INIT_TAKE1("Scriptsock", set_script_socket, NULL, RSRC_CONF,
695                   "the name of the socket to use for communication with "
696                   "the cgi daemon."), 
697     {NULL} 
698 }; 
699
700 static int log_scripterror(request_rec *r, cgid_server_conf * conf, int ret, 
701                            int show_errno, char *error) 
702
703     ap_file_t *f = NULL; 
704     struct stat finfo; 
705     char time_str[AP_CTIME_LEN];
706
707     ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, errno, r, 
708                 "%s: %s", error, r->filename); 
709
710     if (!conf->logname || 
711         ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) 
712          && (finfo.st_size > conf->logbytes)) || 
713          (ap_open(&f, ap_server_root_relative(r->pool, conf->logname),
714                   APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { 
715         return ret; 
716     } 
717
718     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ 
719     ap_ctime(time_str, ap_now());
720     ap_fprintf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, 
721             r->args ? "?" : "", r->args ? r->args : "", r->protocol); 
722     /* "%% 500 /usr/local/apache/cgid-bin */ 
723     ap_fprintf(f, "%%%% %d %s\n", ret, r->filename); 
724
725     ap_fprintf(f, "%%error\n%s\n", error); 
726
727     ap_close(f); 
728     return ret; 
729
730
731 static int log_script(request_rec *r, cgid_server_conf * conf, int ret, 
732                   char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err) 
733
734     ap_array_header_t *hdrs_arr = ap_table_elts(r->headers_in); 
735     ap_table_entry_t *hdrs = (ap_table_entry_t *) hdrs_arr->elts; 
736     char argsbuffer[HUGE_STRING_LEN]; 
737     ap_file_t *f = NULL; 
738     int i; 
739     struct stat finfo; 
740     char time_str[AP_CTIME_LEN];
741
742     if (!conf->logname || 
743         ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) 
744          && (finfo.st_size > conf->logbytes)) || 
745          (ap_open(&f, ap_server_root_relative(r->pool, conf->logname), 
746                   APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { 
747         /* Soak up script output */ 
748         while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) 
749             continue; 
750         if (script_err) {
751             while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) 
752                 continue; 
753         }
754         return ret; 
755     } 
756
757     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ 
758     ap_ctime(time_str, ap_now());
759     ap_fprintf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, 
760             r->args ? "?" : "", r->args ? r->args : "", r->protocol); 
761     /* "%% 500 /usr/local/apache/cgid-bin" */ 
762     ap_fprintf(f, "%%%% %d %s\n", ret, r->filename); 
763
764     ap_puts("%request\n", f); 
765     for (i = 0; i < hdrs_arr->nelts; ++i) { 
766         if (!hdrs[i].key) 
767             continue; 
768         ap_fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); 
769     } 
770     if ((r->method_number == M_POST || r->method_number == M_PUT) 
771         && *dbuf) { 
772         ap_fprintf(f, "\n%s\n", dbuf); 
773     } 
774
775     ap_puts("%response\n", f); 
776     hdrs_arr = ap_table_elts(r->err_headers_out); 
777     hdrs = (ap_table_entry_t *) hdrs_arr->elts; 
778
779     for (i = 0; i < hdrs_arr->nelts; ++i) { 
780         if (!hdrs[i].key) 
781             continue; 
782         ap_fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); 
783     } 
784
785     if (sbuf && *sbuf) 
786         ap_fprintf(f, "%s\n", sbuf); 
787
788     if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) { 
789         ap_puts("%stdout\n", f); 
790         ap_puts(argsbuffer, f); 
791         while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) 
792             ap_puts(argsbuffer, f); 
793         ap_puts("\n", f); 
794     } 
795
796     if (script_err) {
797         if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) { 
798             ap_puts("%stderr\n", f); 
799             ap_puts(argsbuffer, f); 
800             while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) 
801                 ap_puts(argsbuffer, f); 
802             ap_puts("\n", f); 
803         } 
804     }
805
806     ap_bclose(script_in); 
807     if (script_err) {
808         ap_bclose(script_err); 
809     }
810
811     ap_close(f); 
812     return ret; 
813
814
815
816
817 /**************************************************************** 
818  * 
819  * Actual cgid handling... 
820  */ 
821 static int cgid_handler(request_rec *r) 
822
823     int retval, nph, dbpos = 0; 
824     char *argv0, *dbuf = NULL; 
825     BUFF *script = NULL; 
826     char argsbuffer[HUGE_STRING_LEN]; 
827     void *sconf = r->server->module_config; 
828     cgid_server_conf *conf = (cgid_server_conf *) ap_get_module_config(sconf, &cgid_module); 
829     int is_included = !strcmp(r->protocol, "INCLUDED"); 
830     int sd;
831     char **env; 
832     struct sockaddr_un unix_addr;
833     ap_socket_t *tempsock = NULL;
834     int nbytes;
835     ap_iol *iol;
836     script = ap_bcreate(r->pool, B_RDWR); 
837
838     if (r->method_number == M_OPTIONS) { 
839         /* 99 out of 100 cgid scripts, this is all they support */ 
840         r->allowed |= (1 << M_GET); 
841         r->allowed |= (1 << M_POST); 
842         return DECLINED; 
843     } 
844
845     if ((argv0 = strrchr(r->filename, '/')) != NULL)
846         argv0++;
847     else
848         argv0 = r->filename;
849  
850     nph = !(strncmp(argv0, "nph-", 4)); 
851
852     if ((argv0 = strrchr(r->filename, '/')) != NULL) 
853         argv0++; 
854     else 
855         argv0 = r->filename; 
856
857     if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) 
858         return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
859                                "Options ExecCGI is off in this directory"); 
860     if (nph && is_included) 
861         return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
862                                "attempt to include NPH CGI script"); 
863
864 #if defined(OS2) || defined(WIN32) 
865     /* Allow for cgid files without the .EXE extension on them under OS/2 */ 
866     if (r->finfo.st_mode == 0) { 
867         struct stat statbuf; 
868         char *newfile; 
869
870         newfile = ap_pstrcat(r->pool, r->filename, ".EXE", NULL); 
871
872         if ((stat(newfile, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) { 
873             return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, 
874                                    "script not found or unable to stat"); 
875         } else { 
876             r->filename = newfile; 
877         } 
878     } 
879 #else 
880     if (r->finfo.protection == 0) 
881         return log_scripterror(r, conf, HTTP_NOT_FOUND, APLOG_NOERRNO, 
882                                "script not found or unable to stat"); 
883 #endif 
884     if (r->finfo.filetype == APR_DIR) 
885         return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
886                                "attempt to invoke directory as script"); 
887 /*
888     if (!ap_suexec_enabled) { 
889         if (!ap_can_exec(&r->finfo)) 
890             return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
891                                    "file permissions deny server execution"); 
892     } 
893 */
894     ap_add_common_vars(r); 
895     ap_add_cgi_vars(r); 
896     env = ap_create_environment(r->pool, r->subprocess_env); 
897
898     if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
899             return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, 
900                                    "unable to create socket to cgi daemon");
901     } 
902     memset(&unix_addr, 0, sizeof(unix_addr));
903     unix_addr.sun_family = AF_UNIX;
904     strcpy(unix_addr.sun_path, conf->sockname);
905
906     if (connect(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) {
907             return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, 
908                                    "unable to connect to cgi daemon");
909     } 
910
911     send_req(sd, r, argv0, env); 
912
913     ap_put_os_sock(&tempsock, &sd, pcgi);
914
915     iol = ap_iol_attach_socket(pcgi, tempsock);
916
917     ap_bpush_iol(script, iol); 
918
919     if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) 
920         return retval; 
921      
922     if ((argv0 = strrchr(r->filename, '/')) != NULL) 
923         argv0++; 
924     else 
925         argv0 = r->filename; 
926
927     /* Transfer any put/post args, CERN style... 
928      * Note that we already ignore SIGPIPE in the core server. 
929      */ 
930
931     if (ap_should_client_block(r)) { 
932         int dbsize, len_read; 
933
934         if (conf->logname) { 
935             dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1); 
936             dbpos = 0; 
937         } 
938
939
940
941         while ((len_read = 
942                 ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) { 
943             if (conf->logname) { 
944                 if ((dbpos + len_read) > conf->bufbytes) { 
945                     dbsize = conf->bufbytes - dbpos; 
946                 } 
947                 else { 
948                     dbsize = len_read; 
949                 } 
950                 memcpy(dbuf + dbpos, argsbuffer, dbsize); 
951                 dbpos += dbsize; 
952             } 
953             ap_bwrite(script, argsbuffer, len_read, &nbytes);
954             if (nbytes < len_read) { 
955                 /* silly script stopped reading, soak up remaining message */ 
956                 while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) { 
957                     /* dump it */ 
958                 } 
959                 break; 
960             } 
961         } 
962
963         ap_bflush(script); 
964
965     } 
966
967     /* Handle script return... */ 
968     if (script && !nph) { 
969         const char *location; 
970         char sbuf[MAX_STRING_LEN]; 
971         int ret; 
972
973         if ((ret = ap_scan_script_header_err_buff(r, script, sbuf))) { 
974             return log_script(r, conf, ret, dbuf, sbuf, script, NULL); 
975         } 
976
977         location = ap_table_get(r->headers_out, "Location"); 
978
979         if (location && location[0] == '/' && r->status == 200) { 
980
981             /* Soak up all the script output */ 
982             while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script) > 0) { 
983                 continue; 
984             } 
985             /* This redirect needs to be a GET no matter what the original 
986              * method was. 
987              */ 
988             r->method = ap_pstrdup(r->pool, "GET"); 
989             r->method_number = M_GET; 
990
991             /* We already read the message body (if any), so don't allow 
992              * the redirected request to think it has one. We can ignore 
993              * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. 
994              */ 
995             ap_table_unset(r->headers_in, "Content-Length"); 
996
997             ap_internal_redirect_handler(location, r); 
998             return OK; 
999         } 
1000         else if (location && r->status == 200) { 
1001             /* XX Note that if a script wants to produce its own Redirect 
1002              * body, it now has to explicitly *say* "Status: 302" 
1003              */ 
1004             return HTTP_MOVED_TEMPORARILY; 
1005         } 
1006
1007         ap_send_http_header(r); 
1008         if (!r->header_only) { 
1009             ap_send_fb(script, r); 
1010         } 
1011         ap_bclose(script); 
1012     } 
1013
1014     if (script && nph) { 
1015         ap_send_fb(script, r); 
1016     } 
1017
1018     return OK; /* NOT r->status, even if it has changed. */ 
1019
1020
1021 static const handler_rec cgid_handlers[] = 
1022
1023     {CGI_MAGIC_TYPE, cgid_handler}, 
1024     {"cgi-script", cgid_handler}, 
1025     {NULL} 
1026 };
1027
1028 static void register_hook(void)
1029 {
1030     ap_hook_post_config(cgid_init, NULL, NULL, AP_HOOK_MIDDLE);
1031 }
1032
1033 module MODULE_VAR_EXPORT cgid_module = { 
1034     STANDARD20_MODULE_STUFF, 
1035     NULL, /* dir config creater */ 
1036     NULL, /* dir merger --- default is to override */ 
1037     create_cgid_config, /* server config */ 
1038     merge_cgid_config, /* merge server config */ 
1039     cgid_cmds, /* command table */ 
1040     cgid_handlers, /* handlers */ 
1041     register_hook /* register_handlers */ 
1042 }; 
1043