# HG changeset patch # User Olaf Wintermann # Date 1477490036 -7200 # Node ID 38bf6dd8f4e70a58be62912aea81d9d76d92d781 # Parent a94cf2e944926d6a150c11c3bafea06f61bdb921 adds minimal cgi implementation some env vars still missing request body not supported yet diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/daemon/http.c --- a/src/server/daemon/http.c Sun Oct 23 11:12:12 2016 +0200 +++ b/src/server/daemon/http.c Wed Oct 26 15:53:56 2016 +0200 @@ -246,3 +246,65 @@ } +/* ---------------------------- http_hdrs2env ----------------------------- */ + +NSAPI_PUBLIC char **http_hdrs2env(pblock *pb) +{ + char *t, *n, **env; + struct pb_entry *p; + pb_param *pp; + register int x, y, z; + int pos, ts, ln; + + /* Find out how many there are. */ + for(x = 0, y = 0; x < pb->hsize; x++) { + p = pb->ht[x]; + while(p) { + ++y; + p = p->next; + } + } + + env = util_env_create(NULL, y, &pos); + + ts = 1024; + t = (char *) MALLOC(ts); + + for(x = 0; x < pb->hsize; x++) { + p = pb->ht[x]; + + while(p) { + pp = p->param; + + ln = strlen(pp->name) + 6; + + if(ln >= ts) { + ts = ln; + t = (char *) REALLOC(t, ts); + } + n = pp->name; + + /* Skip authorization for CGI */ + if(strcasecmp(n, "authorization")) { + if(strcasecmp(n, "content-type") && + strcasecmp(n, "content-length")) + { + strncpy(t, "HTTP_", 5); + z = 5; + } + else + z = 0; + + for(y = 0; n[y]; ++y, ++z) + t[z] = (n[y] == '-' ? '_' : toupper(n[y])); + t[z] = '\0'; + + env[pos++] = util_env_str(t, pp->value); + } + p = p->next; + } + } + env[pos] = NULL; + FREE(t); + return env; +} diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/daemon/netsite.h --- a/src/server/daemon/netsite.h Sun Oct 23 11:12:12 2016 +0200 +++ b/src/server/daemon/netsite.h Wed Oct 26 15:53:56 2016 +0200 @@ -99,7 +99,6 @@ //define FREE(ptr) INTsystem_free(ptr) NSAPI_PUBLIC void INTsystem_free(void *ptr); -#define STRDUP(ptr) INTsystem_strdup(ptr) NSAPI_PUBLIC char *INTsystem_strdup(const char *ptr); /* diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/daemon/ws-fn.c --- a/src/server/daemon/ws-fn.c Sun Oct 23 11:12:12 2016 +0200 +++ b/src/server/daemon/ws-fn.c Wed Oct 26 15:53:56 2016 +0200 @@ -37,6 +37,7 @@ #include "../safs/init.h" #include "../safs/common.h" #include "../safs/addlog.h" +#include "../safs/cgi.h" #include "../webdav/webdav.h" #include "../admin/admin.h" @@ -62,5 +63,6 @@ { "check-acl", check_acl, NULL, NULL, 0}, { "print-message", print_message, NULL, NULL, 0}, { "common-log", common_log, NULL, NULL, 0}, + { "send-cgi", send_cgi, NULL, NULL, 0}, {NULL, NULL, NULL, NULL, 0} }; diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/public/nsapi.h --- a/src/server/public/nsapi.h Sun Oct 23 11:12:12 2016 +0200 +++ b/src/server/public/nsapi.h Wed Oct 26 15:53:56 2016 +0200 @@ -383,6 +383,13 @@ /* --- End native platform includes --- */ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + /* --- Begin type definitions --- */ /* NOTE: both SYS_FILE and SYS_NETFD are actually NSPR PRFileDesc * and can */ @@ -1359,6 +1366,7 @@ #define CALLOC(size) calloc(1, size) #define REALLOC realloc #define FREE free +#define STRDUP strdup // func util functions FuncStruct* func_resolve(pblock *pb, Session *sn, Request *rq); @@ -1384,6 +1392,7 @@ NSAPI_PUBLIC int http_check_preconditions(Session *sn, Request *rq, struct tm *mtm, const char *etag); NSAPI_PUBLIC int http_set_finfo(Session *sn, Request *rq, struct stat *finfo); +NSAPI_PUBLIC char **http_hdrs2env(pblock *pb); typedef void (*thrstartfunc)(void *); SYS_THREAD INTsysthread_start(int prio, int stksz, thrstartfunc fn, void *arg); diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/safs/cgi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/safs/cgi.c Wed Oct 26 15:53:56 2016 +0200 @@ -0,0 +1,270 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2016 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cgi.h" + +#include +#include +#include + +#include "../util/util.h" +#include "../../ucx/string.h" + +#include "cgiutils.h" + +#define CGI_VARS 32 + +#define CGI_RESPONSE_PARSER_BUFLEN 2048 +#define CGI_RESPONSE_MAX_LINE_LENGTH 512 + +int send_cgi(pblock *pb, Session *sn, Request *rq) { + char *path = pblock_findval("path", rq->vars); + + struct stat s; + if(stat(path, &s)) { + int statuscode = util_errno2status(errno); + protocol_status(sn, rq, statuscode, NULL); + return REQ_ABORTED; + } + if(S_ISDIR(s.st_mode)) { + protocol_status(sn, rq, 403, NULL); + return REQ_ABORTED; + } + + param_free(pblock_remove("content-type", rq->srvhdrs)); + + const char *args = pblock_findval("query", rq->reqpb); + char **argv = cgi_create_argv(path, NULL, args); + + char **env = http_hdrs2env(rq->headers); + env = cgi_common_vars(sn, rq, env); + env = cgi_specific_vars(sn, rq, args, env, 1); + + CGIProcess cgip; + int ret = cgi_start(&cgip, path, argv, env); + if(ret != REQ_PROCEED) { + return ret; + } + + // TODO: send http body + close(cgip.in[1]); + + // read from child + CGIResponseParser *parser = cgi_parser_new(sn, rq); + WSBool cgiheader = TRUE; + + char buf[4096]; + ssize_t r; + while((r = read(cgip.out[0], buf, 4096)) > 0) { + if(cgiheader) { + size_t pos; + int ret = cgi_parse_response(parser, buf, r, &pos); + if(ret == -1) { + protocol_status(sn, rq, 500, NULL); + return REQ_ABORTED; + } else if(ret == 0) { + + } else if(ret == 1) { + cgiheader = FALSE; + http_start_response(sn, rq); + if(pos < r) { + net_write(sn->csd, buf+pos, r-pos); + } + } + } else { + net_write(sn->csd, buf, r); + } + } + + return REQ_PROCEED; +} + +int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]) { + if(pipe(p->in) || pipe(p->out)) { + log_ereport( + LOG_FAILURE, + "send-cgi: cannot create pipe: %s", + strerror(errno)); + return REQ_ABORTED; + } + + p->pid = fork(); + if(p->pid == 0) { + // child + if(dup2(p->in[0], STDIN_FILENO) == -1) { + perror("cgi_start: dup2"); + exit(EXIT_FAILURE); + } + if(dup2(p->out[1], STDOUT_FILENO) == -1) { + perror("cgi_start: dup2"); + exit(EXIT_FAILURE); + } + + // we need to close this unused pipe + // otherwise stdin cannot reach EOF + close(p->in[1]); + + // execute program + exit(execve(path, argv, envp)); + } else { + // parent + close(p->out[1]); + } + + return REQ_PROCEED; +} + +CGIResponseParser* cgi_parser_new(Session *sn, Request *rq) { + CGIResponseParser* parser = pool_malloc(sn->pool, sizeof(CGIResponseParser)); + parser->sn = sn; + parser->rq = rq; + parser->tmp = ucx_buffer_new(NULL, 64, UCX_BUFFER_AUTOEXTEND); + return parser; +} + +/* + * parses a cgi response line and adds the response header to rq->srvhdrs + * returns 0: incomplete line + * 1: successfully parsed lines + * 2: cgi response header complete (empty line) + * -1: error + */ +static int parse_lines(CGIResponseParser *parser, char *buf, size_t len, int *pos) { + sstr_t name; + sstr_t value; + WSBool space = TRUE; + int i; + + int line_begin = 0; + int value_begin = 0; + for(i=0;i error + return -1; + } + } + value = sstrn(buf + value_begin, i - value_begin); + + name = sstrtrim(name); + value = sstrtrim(value); + + if(name.length == 0 || value.length == 0) { + return -1; + } + pblock_nvlinsert( + name.ptr, + name.length, + value.ptr, + value.length, + parser->rq->srvhdrs); + + line_begin = i+1; + value_begin = line_begin; + space = TRUE; + } else if(c != ' ') { + space = FALSE; + } + } + + if(i < len) { + *pos = i; + return 0; + } + return 1; +} + +/* + * returns -1: error + * 0: response header incomplete + * 1: complete + */ +int cgi_parse_response(CGIResponseParser *parser, char *buf, size_t len, size_t *bpos) { + *bpos = 0; + int pos = 0; + if(parser->tmp->pos > 0) { + // the tmp buffer contains an unfinished line + // fill up the buffer until the line is complete + WSBool nb = FALSE; + for(pos=0;postmp); + + if(nb) { + // line complete + int npos; + int r = parse_lines(parser, parser->tmp->space, parser->tmp->pos, &npos); + switch(r) { + case -1: return -1; + case 0: return -1; + case 1: break; + case 2: { + *bpos = pos + 1; + return 1; + } + } + // reset tmp buffer + parser->tmp->pos = 0; + } else { + if(parser->tmp->pos > CGI_RESPONSE_MAX_LINE_LENGTH) { + return -1; + } + } + } + + int npos = 0; + int r = parse_lines(parser, buf + pos, len - pos, &npos); + switch(r) { + default: return -1; + case 0: + case 1: { + int newlen = len - npos; + if(npos > 0) { + ucx_buffer_write(buf + npos, 1, newlen, parser->tmp); + } + return 0; + } + case 2: { + *bpos = pos + npos; + return 1; + } + } +} + diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/safs/cgi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/safs/cgi.h Wed Oct 26 15:53:56 2016 +0200 @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2016 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CGI_H +#define CGI_H + +#include "../public/nsapi.h" +#include "../../ucx/buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int in[2]; + int out[2]; + pid_t pid; +} CGIProcess; + +typedef struct { + Session *sn; + Request *rq; + UcxBuffer *tmp; +} CGIResponseParser; + +int send_cgi(pblock *pb, Session *sn, Request *rq); + +char** cgi_add_vars(char **env, Session *sn, Request *rq); + +int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]); + +CGIResponseParser* cgi_parser_new(Session *sn, Request *rq); +int cgi_parse_response(CGIResponseParser *parser, char *buf, size_t len, size_t *bpos); + +#ifdef __cplusplus +} +#endif + +#endif /* CGI_H */ + diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/safs/cgiutils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/safs/cgiutils.c Wed Oct 26 15:53:56 2016 +0200 @@ -0,0 +1,379 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// code from open webserver +// safs/cgi.cpp + +#include "cgiutils.h" + +#include +#include + +#include "../util/util.h" +#include "../util/pblock.h" + + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- + +#define CGI_VERSION "CGI/1.1" + +// CGI env sizing +#define MAX_CGI_COMMON_VARS 23 +#define MAX_CGI_SPECIFIC_VARS 10 +#define MAX_CGI_CLIENT_AUTH_VARS 64 + +//----------------------------------------------------------------------------- +// cgi_parse_query +//----------------------------------------------------------------------------- + +int cgi_parse_query(char **argv, int n, char *q) +{ + int i = 0; + + for (;;) { + // Parse an arg from the query string + char *arg = q; + while (*q != ' ' && *q != '\0') + q++; + + // Caller must ensure argv[] is appropriately sized + WS_ASSERT(i < n); + if (i >= n) + return -1; + + // Escape any shell characters (does a MALLOC on our behalf) + char c = *q; + *q = '\0'; + argv[i] = util_sh_escape(arg); // TODO + *q = c; + + // Unescape this arg, bailing on error + if (!argv[i] || !util_uri_unescape_strict(argv[i])) + return -1; + + // We successfully parsed another arg + i++; + + // We're done when we hit the end of the query string + if (*q == '\0') + break; + + q++; + } + + return i; +} + +//----------------------------------------------------------------------------- +// cgi_create_argv +//----------------------------------------------------------------------------- + +char **cgi_create_argv(const char *program, const char *script, const char *query) +{ + int nargs = 1; + + // The script name, if any, will be argv[1] + if (script) + nargs++; + + // Turn '+' into ' ' in query string, counting the number of arg as we go + char *qargs = NULL; + if (query && !strchr(query, '=')) { + qargs = STRDUP(query); + if (!qargs) + return NULL; + nargs++; + for (char *t = qargs; *t; t++) { + if (*t == '+') + *t = ' '; + if (*t == ' ') + nargs++; + } + } + + // Allocate the argv[] array, leaving room for the trailing NULL + char **argv = (char **) MALLOC((nargs + 1) * sizeof(char *)); + if (!argv) + return NULL; + + int i = 0; + + // Set argv[0] to the program name + argv[i] = STRDUP(program); + i++; + + // Set argv[1] to the script name + if (script) { + argv[i] = STRDUP(script); + i++; + } + + // Parse the query string into argv[] + if (qargs) { + int n = cgi_parse_query(&argv[i], nargs - i, qargs); + if (n > 0) + i += n; + } + + // Mark end of argv[] + argv[i] = NULL; + + return argv; +} + +//---------------------------------------------------------------------------- +// cgi_get_request_uri +//---------------------------------------------------------------------------- + +char* cgi_get_request_uri(Request *req) +{ + // Extract the encoded URI from the request line if possible + char *clf_request = pblock_findkeyval(pb_key_clf_request, req->reqpb); + if (clf_request) { + // Find the beginning of the method + char *method = clf_request; + while (*method && isspace(*method)) + method++; + + // Skip over the method + char *uri = method; + while (*uri && !isspace(*uri)) + uri++; + + // Find the beginning of the URI + while (*uri && isspace(*uri)) + uri++; + + // Find the end of the URI + char *end = uri; + while (*end && !isspace(*end)) + end++; + + // Make a copy of the uri + int len = end - uri; + char* request_uri = (char*) MALLOC(len+1); + memcpy(request_uri, uri, len); + request_uri[len] = '\0'; + + return request_uri; + } + + // No request line. + return NULL; +} + +//----------------------------------------------------------------------------- +// cgi_specific_vars +//----------------------------------------------------------------------------- + +char** cgi_specific_vars(Session *sn, Request *rq, const char *args, + char** env, int scriptVars) +{ + int y; + register pblock *pb; + char c, *t, *u; + + pb = rq->reqpb; + + int x; + env = util_env_create(env, MAX_CGI_SPECIFIC_VARS, &x); + env[x++] = util_env_str("GATEWAY_INTERFACE", CGI_VERSION); + + // Added error check for missing request information. + t = pblock_findval("protocol", pb); + if (t == NULL) { + log_ereport(LOG_FAILURE, "cgi_specific_vars", "missing \"protocol\" in rq->reqpb"); + return NULL; + } + env[x++] = util_env_str("SERVER_PROTOCOL", t); + + t = pblock_findval("method", pb); + if (t == NULL) { + log_ereport(LOG_FAILURE, "cgi_specific_vars", "missing \"request method\" in rq->reqpb"); + return NULL; + } + env[x++] = util_env_str("REQUEST_METHOD", t); + + if (args) + env[x++] = util_env_str("QUERY_STRING", args); + + // set REQUEST_URI + if (rq->orig_rq) { + t = cgi_get_request_uri(rq->orig_rq); + } else { + t = cgi_get_request_uri(rq); + } + if (t) { + env[x++] = util_env_str("REQUEST_URI", t); + FREE(t); + } + + if (scriptVars) { + t = pblock_findval("uri", pb); + + /* Simulate CGI URIs by truncating path info */ + if ((u = pblock_findval("path-info", rq->vars))) { + y = strlen(t) - strlen(u); + if (y >= 0) { + c = t[y]; + t[y] = '\0'; + } + env[x++] = util_env_str("SCRIPT_NAME", t); + if (y >= 0) { + t[y] = c; + } + + env[x++] = util_env_str("PATH_INFO", u); + + // TODO + /* + if((t = INTservact_translate_uri2(u, sn, rq))) { + env[x++] = util_env_str("PATH_TRANSLATED", t); + // keep path-translated in rq->vars since we may need it + // during fastcgi processing + pblock_nvinsert("path-translated", t, rq->vars); + FREE(t); + } + */ + } else { + env[x++] = util_env_str("SCRIPT_NAME", t); + } + + if (t = pblock_findval("path", rq->vars)) + env[x++] = util_env_str("SCRIPT_FILENAME", t); + } + + env[x] = NULL; + return env; +} + +//----------------------------------------------------------------------------- +// cgi_common_vars +//----------------------------------------------------------------------------- + +char** cgi_common_vars(Session *sn, Request *rq, char **env) +{ + // TODO: enable code and add cgi_client_auth_vars() + return env; +#if 0 + char *t; + int x; + + env = util_env_create(env, MAX_CGI_COMMON_VARS, &x); + + if (!_env_initialized) cgi_env_init(); + if (_env_path) env[x++] = util_env_str("PATH", _env_path); + if (_env_tz) env[x++] = util_env_str("TZ", _env_tz); + if (_env_lang) env[x++] = util_env_str("LANG", _env_lang); + if (_env_ldpath) env[x++] = util_env_str(LIBRARY_PATH, _env_ldpath); + env[x++] = util_env_str("SERVER_SOFTWARE", PRODUCT_HEADER_ID"/"PRODUCT_VERSION_ID); + + NSString srvName, portStr; + char buf1[256], buf2[64]; + srvName.useStatic(buf1, sizeof(buf1), 0); + portStr.useStatic(buf2, sizeof(buf2), 0); + GetServerHostnameAndPort(*rq, *sn, srvName, portStr); + env[x++] = util_env_str("SERVER_PORT", (char*)(const char*)portStr); + env[x++] = util_env_str("SERVER_NAME", (char*)(const char*)srvName); + + t = http_uri2url_dynamic("","",sn,rq); + env[x++] = util_env_str("SERVER_URL", t); + FREE(t); + + char *ip; + ip = pblock_findval("ip", sn->client); + t = session_dns(sn); + env[x++] = util_env_str("REMOTE_HOST", (t ? t : ip)); + env[x++] = util_env_str("REMOTE_ADDR", ip); + + if((t = pblock_findval("auth-user", rq->vars))) { + env[x++] = util_env_str("REMOTE_USER", t); + if((t = pblock_findval("auth-type", rq->vars))) { + env[x++] = util_env_str("AUTH_TYPE", t); + } + if((t = pblock_findval("auth-userdn", rq->vars))) { + env[x++] = util_env_str("REMOTE_USERDN", t); + } + } + if((t = pblock_findval("password-policy", rq->vars))) { + /* chrisk made up this variable name */ + env[x++] = util_env_str("PASSWORD_POLICY", t); + } + + // Handle Apache ErrorDocument-style variables from the send-error SAF + if (rq->orig_rq != rq) { + if (t = pblock_findval("uri", rq->orig_rq->reqpb)) { + env[x++] = util_env_str("REDIRECT_URL", t); + } + if (t = pblock_findval("status", rq->orig_rq->srvhdrs)) { + env[x++] = util_env_str("REDIRECT_STATUS", t); + } + } + + if (GetSecurity(sn)) { + env[x++] = util_env_str("HTTPS", "ON"); + + if (t = pblock_findval("keysize", sn->client)) + env[x++] = util_env_str("HTTPS_KEYSIZE", t); + + if (t = pblock_findval("secret-keysize", sn->client)) + env[x++] = util_env_str("HTTPS_SECRETKEYSIZE", t); + + t = pblock_findval("ssl-id", sn->client); + env[x++] = util_env_str("HTTPS_SESSIONID", t ? t : (char *)""); + + unsigned char random_bytes[NUM_RANDOM_BYTES + 2]; + char random_string[NUM_RANDOM_BYTES*2 + 2]; + PK11_GenerateRandom(random_bytes, NUM_RANDOM_BYTES); + + int i; + for(i = 0; i < NUM_RANDOM_BYTES; i++) { + sprintf(&random_string[i*2], "%02x", + (unsigned int)(random_bytes[i] & 0xff)); + } + random_string[NUM_RANDOM_BYTES*2] = '\0'; + env[x++] = util_env_str("HTTPS_RANDOM", random_string); + + } else { + env[x++] = util_env_str("HTTPS", "OFF"); + } + + env[x] = NULL; + + env = cgi_client_auth_vars(sn, rq, env); + + return env; +#endif +} diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/safs/cgiutils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/safs/cgiutils.h Wed Oct 26 15:53:56 2016 +0200 @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2016 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CGIUTILS_H +#define CGIUTILS_H + +#include "../public/nsapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int cgi_parse_query(char **argv, int n, char *q); +char **cgi_create_argv(const char *program, const char *script, const char *query); +char* cgi_get_request_uri(Request *req); +char** cgi_specific_vars(Session *sn, Request *rq, const char *args, + char** env, int scriptVars); +char** cgi_common_vars(Session *sn, Request *rq, char **env); + +#ifdef __cplusplus +} +#endif + +#endif /* CGIUTILS_H */ + diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/safs/objs.mk --- a/src/server/safs/objs.mk Sun Oct 23 11:12:12 2016 +0200 +++ b/src/server/safs/objs.mk Wed Oct 26 15:53:56 2016 +0200 @@ -39,6 +39,8 @@ SAFOBJ += pcheck.o SAFOBJ += common.o SAFOBJ += addlog.o +SAFOBJ += cgi.o +SAFOBJ += cgiutils.o SAFOBJS = $(SAFOBJ:%=$(SAFS_OBJPRE)%) SAFSOURCE = $(SAFOBJ:%.o=safs/%.c) diff -r a94cf2e94492 -r 38bf6dd8f4e7 src/server/util/util.c --- a/src/server/util/util.c Sun Oct 23 11:12:12 2016 +0200 +++ b/src/server/util/util.c Wed Oct 26 15:53:56 2016 +0200 @@ -149,10 +149,11 @@ NSAPI_PUBLIC char *util_env_str(const char *name, const char *value) { char *t; + + size_t len = strlen(name) + strlen(value) + 2; + t = (char *) MALLOC(len); /* 2: '=' and '\0' */ - t = (char *) MALLOC(strlen(name)+strlen(value)+2); /* 2: '=' and '\0' */ - - sprintf(t, "%s=%s", name, value); + snprintf(t, len, "%s=%s", name, value); return t; } @@ -182,6 +183,24 @@ } +/* ---------------------------- util_sh_escape ---------------------------- */ + + +NSAPI_PUBLIC char *util_sh_escape(char *s) +{ + char *ns = (char *) MALLOC(strlen(s) * 2 + 1); /* worst case */ + register char *t, *u; + + for(t = s, u = ns; *t; ++t, ++u) { + if(strchr("&;`'\"|*?~<>^()[]{}$\\ #!", *t)) + *u++ = '\\'; + *u = *t; + } + *u = '\0'; + return ns; +} + + /* ---------------------------- util_env_find ----------------------------- */ diff -r a94cf2e94492 -r 38bf6dd8f4e7 templates/config/obj.conf --- a/templates/config/obj.conf Sun Oct 23 11:12:12 2016 +0200 +++ b/templates/config/obj.conf Wed Oct 26 15:53:56 2016 +0200 @@ -22,3 +22,7 @@ Service fn="admin-service" + + +Service fn="send-cgi" +