1 /* $MirOS: src/usr.sbin/httpd/src/main/util_script.c,v 1.5 2008/03/19 23:07:21 tg Exp $ */
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" must
29 * not be used to endorse or promote products derived from this
30 * software without prior written permission. For written
31 * permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * nor may "Apache" appear in their name, without prior written
35 * permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 *
56 * Portions of this software are based upon public domain software
57 * originally written at the National Center for Supercomputing Applications,
58 * University of Illinois, Urbana-Champaign.
59 */
60
61 #define CORE_PRIVATE
62 #include "httpd.h"
63 #include "http_config.h"
64 #include "http_conf_globals.h"
65 #include "http_main.h"
66 #include "http_log.h"
67 #include "http_protocol.h"
68 #include "http_core.h" /* For document_root. Sigh... */
69 #include "http_request.h" /* for sub_req_lookup_uri() */
70 #include "util_script.h"
71 #include "util_date.h" /* For parseHTTPdate() */
72 #include "sa_len.h"
73
74
75 /*
76 * Various utility functions which are common to a whole lot of
77 * script-type extensions mechanisms, and might as well be gathered
78 * in one place (if only to avoid creating inter-module dependancies
79 * where there don't have to be).
80 */
81
82 #define MALFORMED_MESSAGE "malformed header from script. Bad header="
83 #define MALFORMED_HEADER_LENGTH_TO_SHOW 30
84
85 /* If a request includes query info in the URL (stuff after "?"), and
86 * the query info does not contain "=" (indicative of a FORM submission),
87 * then this routine is called to create the argument list to be passed
88 * to the CGI script. When suexec is enabled, the suexec path, user, and
89 * group are the first three arguments to be passed; if not, all three
90 * must be NULL. The query info is split into separate arguments, where
91 * "+" is the separator between keyword arguments.
92 */
create_argv(pool * p,char * path,char * user,char * group,char * av0,const char * args)93 static char **create_argv(pool *p, char *path, char *user, char *group,
94 char *av0, const char *args)
95 {
96 int x, numwords;
97 char **av;
98 char *w;
99 int idx = 0;
100
101 /* count the number of keywords */
102
103 for (x = 0, numwords = 1; args[x]; x++) {
104 if (args[x] == '+') {
105 ++numwords;
106 }
107 }
108
109 if (numwords > APACHE_ARG_MAX - 5) {
110 numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */
111 }
112 av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *));
113
114 if (path) {
115 av[idx++] = path;
116 }
117 if (user) {
118 av[idx++] = user;
119 }
120 if (group) {
121 av[idx++] = group;
122 }
123
124 av[idx++] = av0;
125
126 for (x = 1; x <= numwords; x++) {
127 w = ap_getword_nulls(p, &args, '+');
128 ap_unescape_url(w);
129 av[idx++] = ap_escape_shell_cmd(p, w);
130 }
131 av[idx] = NULL;
132 return av;
133 }
134
135
http2env(pool * a,char * w)136 static char *http2env(pool *a, char *w)
137 {
138 char *res = ap_pstrcat(a, "HTTP_", w, NULL);
139 char *cp = res;
140
141 while (*++cp) {
142 if (!ap_isalnum(*cp) && *cp != '_') {
143 *cp = '_';
144 }
145 else {
146 *cp = ap_toupper(*cp);
147 }
148 }
149
150 return res;
151 }
152
ap_create_environment(pool * p,table * t)153 API_EXPORT(char **) ap_create_environment(pool *p, table *t)
154 {
155 array_header *env_arr = ap_table_elts(t);
156 table_entry *elts = (table_entry *) env_arr->elts;
157 char **env = (char **) ap_palloc(p, (env_arr->nelts + 2) * sizeof(char *));
158 int i, j;
159 char *tz;
160 char *whack;
161
162 j = 0;
163 if (!ap_table_get(t, "TZ")) {
164 tz = getenv("TZ");
165 if (tz != NULL) {
166 env[j++] = ap_pstrcat(p, "TZ=", tz, NULL);
167 }
168 }
169 for (i = 0; i < env_arr->nelts; ++i) {
170 if (!elts[i].key) {
171 continue;
172 }
173 env[j] = ap_pstrcat(p, elts[i].key, "=", elts[i].val, NULL);
174 whack = env[j];
175 if (isdigit((unsigned char)*whack)) {
176 *whack++ = '_';
177 }
178 while (*whack != '=') {
179 if (!ap_isalnum(*whack) && *whack != '_') {
180 *whack = '_';
181 }
182 ++whack;
183 }
184 ++j;
185 }
186
187 env[j] = NULL;
188 return env;
189 }
190
ap_add_common_vars(request_rec * r)191 API_EXPORT(void) ap_add_common_vars(request_rec *r)
192 {
193 table *e;
194 server_rec *s = r->server;
195 conn_rec *c = r->connection;
196 const char *rem_logname;
197 char *env_path;
198 const char *host;
199 array_header *hdrs_arr = ap_table_elts(r->headers_in);
200 table_entry *hdrs = (table_entry *) hdrs_arr->elts;
201 int i;
202 char servbuf[NI_MAXSERV];
203
204 /* use a temporary table which we'll overlap onto
205 * r->subprocess_env later
206 */
207 e = ap_make_table(r->pool, 25 + hdrs_arr->nelts);
208
209 /* First, add environment vars from headers... this is as per
210 * CGI specs, though other sorts of scripting interfaces see
211 * the same vars...
212 */
213
214 for (i = 0; i < hdrs_arr->nelts; ++i) {
215 if (!hdrs[i].key) {
216 continue;
217 }
218
219 /* A few headers are special cased --- Authorization to prevent
220 * rogue scripts from capturing passwords; content-type and -length
221 * for no particular reason.
222 */
223
224 if (!strcasecmp(hdrs[i].key, "Content-type")) {
225 ap_table_addn(e, "CONTENT_TYPE", hdrs[i].val);
226 }
227 else if (!strcasecmp(hdrs[i].key, "Content-length")) {
228 ap_table_addn(e, "CONTENT_LENGTH", hdrs[i].val);
229 }
230 /*
231 * You really don't want to disable this check, since it leaves you
232 * wide open to CGIs stealing passwords and people viewing them
233 * in the environment with "ps -e". But, if you must...
234 */
235 else if (!strcasecmp(hdrs[i].key, "Authorization")
236 || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
237 continue;
238 }
239 else {
240 ap_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
241 }
242 }
243
244 if (!(env_path = ap_pstrdup(r->pool, getenv("PATH")))) {
245 env_path = DEFAULT_PATH;
246 }
247
248 ap_table_addn(e, "PATH", env_path);
249 ap_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r));
250 ap_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version());
251 ap_table_addn(e, "SERVER_NAME",
252 ap_escape_html(r->pool,ap_get_server_name(r)));
253 ap_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */
254 ap_table_addn(e, "SERVER_PORT",
255 ap_psprintf(r->pool, "%u", ap_get_server_port(r)));
256 host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST);
257 if (host) {
258 ap_table_addn(e, "REMOTE_HOST", host);
259 }
260 ap_table_addn(e, "REMOTE_ADDR", c->remote_ip);
261 ap_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */
262 ap_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */
263 ap_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */
264
265 servbuf[0] = '\0';
266 if (!getnameinfo((struct sockaddr *)&c->remote_addr,
267 #ifndef HAVE_SOCKADDR_LEN
268 SA_LEN((struct sockaddr *)&c->remote_addr),
269 #else
270 c->remote_addr.ss_len,
271 #endif
272 NULL, 0, servbuf, sizeof(servbuf), NI_NUMERICSERV)){
273 ap_table_addn(e, "REMOTE_PORT", ap_pstrdup(r->pool, servbuf));
274 }
275
276 if (c->user) {
277 ap_table_addn(e, "REMOTE_USER", c->user);
278 }
279 if (c->ap_auth_type) {
280 ap_table_addn(e, "AUTH_TYPE", c->ap_auth_type);
281 }
282 rem_logname = ap_get_remote_logname(r);
283 if (rem_logname) {
284 ap_table_addn(e, "REMOTE_IDENT", ap_pstrdup(r->pool, rem_logname));
285 }
286
287 /* Apache custom error responses. If we have redirected set two new vars */
288
289 if (r->prev) {
290 if (r->prev->args) {
291 ap_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args);
292 }
293 if (r->prev->uri) {
294 ap_table_addn(e, "REDIRECT_URL", r->prev->uri);
295 }
296 }
297
298 ap_overlap_tables(r->subprocess_env, e, AP_OVERLAP_TABLES_SET);
299 }
300
301 /* This "cute" little function comes about because the path info on
302 * filenames and URLs aren't always the same. So we take the two,
303 * and find as much of the two that match as possible.
304 */
305
ap_find_path_info(const char * uri,const char * path_info)306 API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info)
307 {
308 int lu = strlen(uri);
309 int lp = strlen(path_info);
310
311 while (lu-- && lp-- && uri[lu] == path_info[lp]);
312
313 if (lu == -1) {
314 lu = 0;
315 }
316
317 while (uri[lu] != '\0' && uri[lu] != '/') {
318 lu++;
319 }
320 return lu;
321 }
322
323 /* Obtain the Request-URI from the original request-line, returning
324 * a new string from the request pool containing the URI or "".
325 */
original_uri(request_rec * r)326 static char *original_uri(request_rec *r)
327 {
328 char *first, *last;
329
330 if (r->the_request == NULL) {
331 return (char *) ap_pcalloc(r->pool, 1);
332 }
333
334 first = r->the_request; /* use the request-line */
335
336 while (*first && !ap_isspace(*first)) {
337 ++first; /* skip over the method */
338 }
339 while (ap_isspace(*first)) {
340 ++first; /* and the space(s) */
341 }
342
343 last = first;
344 while (*last && !ap_isspace(*last)) {
345 ++last; /* end at next whitespace */
346 }
347
348 return ap_pstrndup(r->pool, first, last - first);
349 }
350
ap_add_cgi_vars(request_rec * r)351 API_EXPORT(void) ap_add_cgi_vars(request_rec *r)
352 {
353 table *e = r->subprocess_env;
354
355 ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
356 ap_table_setn(e, "SERVER_PROTOCOL", r->protocol);
357 ap_table_setn(e, "REQUEST_METHOD", r->method);
358 ap_table_setn(e, "QUERY_STRING", r->args ? r->args : "");
359 ap_table_setn(e, "REQUEST_URI", original_uri(r));
360
361 /* Note that the code below special-cases scripts run from includes,
362 * because it "knows" that the sub_request has been hacked to have the
363 * args and path_info of the original request, and not any that may have
364 * come with the script URI in the include command. Ugh.
365 */
366
367 if (!strcmp(r->protocol, "INCLUDED")) {
368 ap_table_setn(e, "SCRIPT_NAME", r->uri);
369 if (r->path_info && *r->path_info) {
370 ap_table_setn(e, "PATH_INFO", r->path_info);
371 }
372 }
373 else if (!r->path_info || !*r->path_info) {
374 ap_table_setn(e, "SCRIPT_NAME", r->uri);
375 }
376 else {
377 int path_info_start = ap_find_path_info(r->uri, r->path_info);
378
379 ap_table_setn(e, "SCRIPT_NAME",
380 ap_pstrndup(r->pool, r->uri, path_info_start));
381
382 ap_table_setn(e, "PATH_INFO", r->path_info);
383 }
384
385 if (r->path_info && r->path_info[0]) {
386 /*
387 * To get PATH_TRANSLATED, treat PATH_INFO as a URI path.
388 * Need to re-escape it for this, since the entire URI was
389 * un-escaped before we determined where the PATH_INFO began.
390 */
391 request_rec *pa_req;
392
393 pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
394
395 if (pa_req->filename) {
396 char *pt = ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
397 NULL);
398 ap_table_setn(e, "PATH_TRANSLATED", pt);
399 }
400 ap_destroy_sub_req(pa_req);
401 }
402 }
403
404
set_cookie_doo_doo(void * v,const char * key,const char * val)405 static int set_cookie_doo_doo(void *v, const char *key, const char *val)
406 {
407 ap_table_addn(v, key, val);
408 return 1;
409 }
410
ap_scan_script_header_err_core(request_rec * r,char * buffer,int (* getsfunc)(char *,int,void *),void * getsfunc_data)411 API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
412 int (*getsfunc) (char *, int, void *),
413 void *getsfunc_data)
414 {
415 char x[MAX_STRING_LEN];
416 char *w, *l;
417 int p;
418 int cgi_status = HTTP_OK;
419 table *merge;
420 table *cookie_table;
421
422 if (buffer) {
423 *buffer = '\0';
424 }
425 w = buffer ? buffer : x;
426
427 ap_hard_timeout("read script header", r);
428
429 /* temporary place to hold headers to merge in later */
430 merge = ap_make_table(r->pool, 10);
431
432 /* The HTTP specification says that it is legal to merge duplicate
433 * headers into one. Some browsers that support Cookies don't like
434 * merged headers and prefer that each Set-Cookie header is sent
435 * separately. Lets humour those browsers by not merging.
436 * Oh what a pain it is.
437 */
438 cookie_table = ap_make_table(r->pool, 2);
439 ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL);
440
441 while (1) {
442
443 if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) {
444 ap_kill_timeout(r);
445 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
446 "Premature end of script headers: %s", r->filename);
447 return HTTP_INTERNAL_SERVER_ERROR;
448 }
449
450 /* Delete terminal (CR?)LF */
451
452 p = strlen(w);
453 /* Indeed, the host's '\n':
454 '\012' for UNIX; '\015' for MacOS; '\025' for OS/390
455 -- whatever the script generates.
456 */
457 if (p > 0 && w[p - 1] == '\n') {
458 if (p > 1 && w[p - 2] == CR) {
459 w[p - 2] = '\0';
460 }
461 else {
462 w[p - 1] = '\0';
463 }
464 }
465
466 /*
467 * If we've finished reading the headers, check to make sure any
468 * HTTP/1.1 conditions are met. If so, we're done; normal processing
469 * will handle the script's output. If not, just return the error.
470 * The appropriate thing to do would be to send the script process a
471 * SIGPIPE to let it know we're ignoring it, close the channel to the
472 * script process, and *then* return the failed-to-meet-condition
473 * error. Otherwise we'd be waiting for the script to finish
474 * blithering before telling the client the output was no good.
475 * However, we don't have the information to do that, so we have to
476 * leave it to an upper layer.
477 */
478 if (w[0] == '\0') {
479 int cond_status = OK;
480
481 ap_kill_timeout(r);
482 if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) {
483 cond_status = ap_meets_conditions(r);
484 }
485 ap_overlap_tables(r->err_headers_out, merge,
486 AP_OVERLAP_TABLES_MERGE);
487 if (!ap_is_empty_table(cookie_table)) {
488 /* the cookies have already been copied to the cookie_table */
489 ap_table_unset(r->err_headers_out, "Set-Cookie");
490 r->err_headers_out = ap_overlay_tables(r->pool,
491 r->err_headers_out, cookie_table);
492 }
493 return cond_status;
494 }
495
496 /* if we see a bogus header don't ignore it. Shout and scream */
497
498 if (!(l = strchr(w, ':'))) {
499 char malformed[(sizeof MALFORMED_MESSAGE) + 1
500 + MALFORMED_HEADER_LENGTH_TO_SHOW];
501
502 strlcpy(malformed, MALFORMED_MESSAGE, sizeof(malformed));
503 strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);
504
505 if (!buffer) {
506 /* Soak up all the script output - may save an outright kill */
507 while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) {
508 continue;
509 }
510 }
511
512 ap_kill_timeout(r);
513 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
514 "%s: %s", malformed, r->filename);
515 return HTTP_INTERNAL_SERVER_ERROR;
516 }
517
518 *l++ = '\0';
519 while (ap_isspace(*l)) {
520 ++l;
521 }
522
523 if (!strcasecmp(w, "Content-type")) {
524 char *tmp;
525
526 /* Nuke trailing whitespace */
527
528 char *endp = l + strlen(l) - 1;
529 while (endp > l && ap_isspace(*endp)) {
530 *endp-- = '\0';
531 }
532
533 tmp = ap_pstrdup(r->pool, l);
534 ap_content_type_tolower(tmp);
535 r->content_type = tmp;
536 }
537 /*
538 * If the script returned a specific status, that's what
539 * we'll use - otherwise we assume 200 OK.
540 */
541 else if (!strcasecmp(w, "Status")) {
542 r->status = cgi_status = atoi(l);
543 r->status_line = ap_pstrdup(r->pool, l);
544 }
545 else if (!strcasecmp(w, "Location")) {
546 ap_table_set(r->headers_out, w, l);
547 }
548 else if (!strcasecmp(w, "Content-Length")) {
549 ap_table_set(r->headers_out, w, l);
550 }
551 else if (!strcasecmp(w, "Transfer-Encoding")) {
552 ap_table_set(r->headers_out, w, l);
553 }
554 /*
555 * If the script gave us a Last-Modified header, we can't just
556 * pass it on blindly because of restrictions on future values.
557 */
558 else if (!strcasecmp(w, "Last-Modified")) {
559 time_t mtime = ap_parseHTTPdate(l);
560
561 ap_update_mtime(r, mtime);
562 ap_set_last_modified(r);
563 }
564 else if (!strcasecmp(w, "Set-Cookie")) {
565 ap_table_add(cookie_table, w, l);
566 }
567 else {
568 ap_table_add(merge, w, l);
569 }
570 }
571 }
572
getsfunc_FILE(char * buf,int len,void * f)573 static int getsfunc_FILE(char *buf, int len, void *f)
574 {
575 return fgets(buf, len, (FILE *) f) != NULL;
576 }
577
ap_scan_script_header_err(request_rec * r,FILE * f,char * buffer)578 API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f,
579 char *buffer)
580 {
581 return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f);
582 }
583
getsfunc_BUFF(char * w,int len,void * fb)584 static int getsfunc_BUFF(char *w, int len, void *fb)
585 {
586 return ap_bgets(w, len, (BUFF *) fb) > 0;
587 }
588
ap_scan_script_header_err_buff(request_rec * r,BUFF * fb,char * buffer)589 API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb,
590 char *buffer)
591 {
592 return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb);
593 }
594
595 struct vastrs {
596 va_list args;
597 int arg;
598 const char *curpos;
599 };
600
getsfunc_STRING(char * w,int len,void * pvastrs)601 static int getsfunc_STRING(char *w, int len, void *pvastrs)
602 {
603 struct vastrs *strs = (struct vastrs*) pvastrs;
604 char *p;
605 int t;
606
607 if (!strs->curpos || !*strs->curpos)
608 return 0;
609 p = strchr(strs->curpos, '\n');
610 if (p)
611 ++p;
612 else
613 p = strchr(strs->curpos, '\0');
614 t = p - strs->curpos;
615 if (t > len)
616 t = len;
617 strncpy (w, strs->curpos, t);
618 w[t] = '\0';
619 if (!strs->curpos[t]) {
620 ++strs->arg;
621 strs->curpos = va_arg(strs->args, const char *);
622 }
623 else
624 strs->curpos += t;
625 return t;
626 }
627
628 /* ap_scan_script_header_err_strs() accepts additional const char* args...
629 * each is treated as one or more header lines, and the first non-header
630 * character is returned to **arg, **data. (The first optional arg is
631 * counted as 0.)
632 */
ap_scan_script_header_err_strs(request_rec * r,char * buffer,const char ** termch,int * termarg,...)633 API_EXPORT_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r,
634 char *buffer,
635 const char **termch,
636 int *termarg, ...)
637 {
638 struct vastrs strs;
639 int res;
640
641 va_start(strs.args, termarg);
642 strs.arg = 0;
643 strs.curpos = va_arg(strs.args, char*);
644 res = ap_scan_script_header_err_core(r, buffer, getsfunc_STRING, (void *) &strs);
645 if (termch)
646 *termch = strs.curpos;
647 if (termarg)
648 *termarg = strs.arg;
649 va_end(strs.args);
650 return res;
651 }
652
ap_send_size(size_t size,request_rec * r)653 API_EXPORT(void) ap_send_size(size_t size, request_rec *r)
654 {
655 /* XXX: this -1 thing is a gross hack */
656 if (size == (size_t)-1) {
657 ap_rputs(" -", r);
658 }
659 else if (!size) {
660 ap_rputs(" 0k", r);
661 }
662 else if (size < 1024) {
663 ap_rputs(" 1k", r);
664 }
665 else if (size < 1048576) {
666 ap_rprintf(r, "%4dk", (int)((size + 512) / 1024));
667 }
668 else if (size < 103809024) {
669 ap_rprintf(r, "%4.1fM", size / 1048576.0);
670 }
671 else {
672 ap_rprintf(r, "%4dM", (int)((size + 524288) / 1048576));
673 }
674 }
675
ap_call_exec(request_rec * r,child_info * pinfo,char * argv0,char ** env,int shellcmd)676 API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0,
677 char **env, int shellcmd)
678 {
679 int pid = 0;
680 core_dir_config *conf;
681 conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
682 &core_module);
683
684 /* the fd on r->server->error_log is closed, but we need somewhere to
685 * put the error messages from the log_* functions. So, we use stderr,
686 * since that is better than allowing errors to go unnoticed. Don't do
687 * this on Win32, though, since we haven't fork()'d.
688 */
689 r->server->error_log = stderr;
690
691 if (conf->limit_cpu != NULL) {
692 if ((setrlimit(RLIMIT_CPU, conf->limit_cpu)) != 0) {
693 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
694 "setrlimit: failed to set CPU usage limit");
695 }
696 }
697 #ifdef RLIMIT_TIME
698 if (conf->limit_time != NULL) {
699 if ((setrlimit(RLIMIT_TIME, conf->limit_time)) != 0) {
700 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
701 "setrlimit: failed to set max real time limit");
702 }
703 }
704 #endif
705 if (conf->limit_nproc != NULL) {
706 if ((setrlimit(RLIMIT_NPROC, conf->limit_nproc)) != 0) {
707 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
708 "setrlimit: failed to set process limit");
709 }
710 }
711 if (conf->limit_mem != NULL) {
712 if ((setrlimit(RLIMIT_DATA, conf->limit_mem)) != 0) {
713 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
714 "setrlimit(RLIMIT_DATA): failed to set memory "
715 "usage limit");
716 }
717 }
718 if (ap_suexec_enabled
719 && ((r->server->server_uid != ap_user_id)
720 || (r->server->server_gid != ap_group_id)
721 || (!strncmp("/~", r->uri, 2)))) {
722
723 char *execuser, *grpname;
724 struct passwd *pw;
725 struct group *gr;
726
727 if (!strncmp("/~", r->uri, 2)) {
728 gid_t user_gid;
729 char *username = ap_pstrdup(r->pool, r->uri + 2);
730 char *pos = strchr(username, '/');
731
732 if (pos) {
733 *pos = '\0';
734 }
735
736 if ((pw = getpwnam(username)) == NULL) {
737 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
738 "getpwnam: invalid username %s", username);
739 return (pid);
740 }
741 execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL);
742 user_gid = pw->pw_gid;
743
744 if ((gr = getgrgid(user_gid)) == NULL) {
745 if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
746 return (pid);
747 }
748 else {
749 snprintf(grpname, 16, "%ld", (long) user_gid);
750 }
751 }
752 else {
753 grpname = gr->gr_name;
754 }
755 }
756 else {
757 if ((pw = getpwuid(r->server->server_uid)) == NULL) {
758 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
759 "getpwuid: invalid userid %ld",
760 (long) r->server->server_uid);
761 return (pid);
762 }
763 execuser = ap_pstrdup(r->pool, pw->pw_name);
764
765 if ((gr = getgrgid(r->server->server_gid)) == NULL) {
766 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
767 "getgrgid: invalid groupid %ld",
768 (long) r->server->server_gid);
769 return (pid);
770 }
771 grpname = gr->gr_name;
772 }
773
774 if (shellcmd) {
775 execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
776 (char *)NULL, env);
777 }
778
779 else if ((conf->cgi_command_args == AP_FLAG_OFF)
780 || (!r->args) || (!r->args[0])
781 || strchr(r->args, '=')) {
782 execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
783 (char *)NULL, env);
784 }
785
786 else {
787 execve(SUEXEC_BIN,
788 create_argv(r->pool, SUEXEC_BIN, execuser, grpname,
789 argv0, r->args),
790 env);
791 }
792 }
793 else {
794 if (shellcmd) {
795 execle(SHELL_PATH, SHELL_PATH, "-c", argv0, (char *)NULL, env);
796 }
797
798 else if ((conf->cgi_command_args == AP_FLAG_OFF)
799 || (!r->args) || (!r->args[0])
800 || strchr(r->args, '=')) {
801 execle(r->filename, argv0, (void*)NULL, env);
802 }
803
804 else {
805 execve(r->filename,
806 create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
807 env);
808 }
809 }
810 return (pid);
811 }
812