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