r8482: gnutls_x509_crt_set_subject_key_id is not available in some versions
[bbaumbach/samba-autobuild/.git] / source4 / web_server / http.c
index e8dba5a754b0022a5e7fa3089e5f9b62ab57f3cd..d6d8196ced8d46fe1e5854565dd45af4388c37a9 100644 (file)
 #include "system/filesys.h"
 #include "system/iconv.h"
 #include "system/time.h"
-#include "web_server/esp/esp.h"
+#include "lib/appweb/esp/esp.h"
 #include "dlinklist.h"
+#include "lib/tls/tls.h"
 
-#define SWAT_SESSION_KEY "_swat_session_"
+#define SWAT_SESSION_KEY "SwatSessionId"
+#define HTTP_PREAUTH_URI "/scripting/preauth.esp"
 
 /* state of the esp subsystem for a specific request */
 struct esp_state {
@@ -103,8 +105,7 @@ static void http_output_headers(struct websrv_context *web)
        if (s == NULL) return;
 
        b = web->output.content;
-       web->output.content.data = s;
-       web->output.content.length = strlen(s);
+       web->output.content = data_blob_string_const(s);
        data_blob_append(web, &web->output.content, b.data, b.length);
        data_blob_free(&b);
 }
@@ -121,7 +122,7 @@ static const char *http_local_path(struct websrv_context *web, const char *url)
        if (url[0] != '/') return NULL;
 
        for (i=0;url[i];i++) {
-               if ((!isalnum(url[i]) && !strchr("./", url[i])) ||
+               if ((!isalnum((unsigned char)url[i]) && !strchr("./_", url[i])) ||
                    (url[i] == '.' && strchr("/.", url[i+1]))) {
                        return NULL;
                }
@@ -164,6 +165,7 @@ static int http_readFile(EspHandle handle, char **buf, int *len, const char *pat
        return 0;
 
 failed:
+       DEBUG(0,("Failed to read file %s - %s\n", path, strerror(errno)));
        if (fd != -1) close(fd);
        talloc_free(*buf);
        *buf = NULL;
@@ -244,12 +246,12 @@ static void http_redirect(EspHandle handle, int code, char *url)
                        char *p = strrchr(web->input.url, '/');
                        if (p == web->input.url) {
                                url = talloc_asprintf(web, "http%s://%s/%s", 
-                                                     web->tls_session?"s":"",
+                                                     tls_enabled(web->tls)?"s":"",
                                                      host, url);
                        } else {
                                int dirlen = p - web->input.url;
                                url = talloc_asprintf(web, "http%s://%s%*.*s/%s",
-                                                     web->tls_session?"s":"",
+                                                     tls_enabled(web->tls)?"s":"",
                                                      host, 
                                                      dirlen, dirlen, web->input.url,
                                                      url);
@@ -385,6 +387,7 @@ static void http_simple_request(struct websrv_context *web)
        /* looks ok */
        web->output.fd = open(path, O_RDONLY);
        if (web->output.fd == -1) {
+               DEBUG(0,("Failed to read file %s - %s\n", path, strerror(errno)));
                http_error_unix(web, path);
                return;
        }
@@ -394,9 +397,6 @@ static void http_simple_request(struct websrv_context *web)
                goto invalid;
        }
 
-       http_output_headers(web);
-       EVENT_FD_WRITEABLE(web->conn->event.fde);
-       web->output.output_pending = True;
        return;
 
 invalid:
@@ -426,14 +426,16 @@ static void http_setup_arrays(struct esp_state *esp)
        p = strrchr(web->input.url, '/');
        SETVAR(ESP_REQUEST_OBJ, "SCRIPT_NAME", p+1);
        SETVAR(ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
+       p = socket_get_peer_addr(web->conn->socket, esp);
+       SETVAR(ESP_REQUEST_OBJ, "REMOTE_ADDR", p);
        p = socket_get_peer_name(web->conn->socket, esp);
        SETVAR(ESP_REQUEST_OBJ, "REMOTE_HOST", p);
-       SETVAR(ESP_REQUEST_OBJ, "REMOTE_ADDR", p);
        SETVAR(ESP_REQUEST_OBJ, "REMOTE_USER", "");
        SETVAR(ESP_REQUEST_OBJ, "CONTENT_TYPE", web->input.content_type);
        if (web->session) {
                SETVAR(ESP_REQUEST_OBJ, "SESSION_ID", web->session->id);
        }
+       SETVAR(ESP_REQUEST_OBJ, "COOKIE_SUPPORT", web->input.cookie?"True":"False");
 
        SETVAR(ESP_HEADERS_OBJ, "HTT_REFERER", web->input.referer);
        SETVAR(ESP_HEADERS_OBJ, "HOST", web->input.host);
@@ -449,10 +451,10 @@ static void http_setup_arrays(struct esp_state *esp)
        SETVAR(ESP_SERVER_OBJ, "DOCUMENT_ROOT", lp_swat_directory());
        SETVAR(ESP_SERVER_OBJ, "SERVER_PORT", 
               talloc_asprintf(esp, "%u", socket_get_my_port(web->conn->socket)));
-       SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", web->tls_session?"https":"http");
+       SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", tls_enabled(web->tls)?"https":"http");
        SETVAR(ESP_SERVER_OBJ, "SERVER_SOFTWARE", "SWAT");
        SETVAR(ESP_SERVER_OBJ, "GATEWAY_INTERFACE", "CGI/1.1");
-       SETVAR(ESP_SERVER_OBJ, "TLS_SUPPORT", edata->tls_data?"True":"False");
+       SETVAR(ESP_SERVER_OBJ, "TLS_SUPPORT", tls_support(edata->tls_params)?"True":"False");
 }
 
 #if HAVE_SETJMP_H
@@ -481,16 +483,13 @@ void ejs_exception(const char *reason)
 /*
   process a esp request
 */
-static void esp_request(struct esp_state *esp)
+static void esp_request(struct esp_state *esp, const char *url)
 {
        struct websrv_context *web = esp->web;
-       const char *url = web->input.url;
-       size_t size;
+       ssize_t size;
        int res;
        char *emsg = NULL, *buf;
 
-       http_setup_arrays(esp);
-
        if (http_readFile(web, &buf, &size, url) != 0) {
                http_error_unix(web, url);
                return;
@@ -507,9 +506,40 @@ static void esp_request(struct esp_state *esp)
                http_writeBlock(web, emsg, strlen(emsg));
        }
        talloc_free(buf);
-       http_output_headers(web);
-       EVENT_FD_WRITEABLE(web->conn->event.fde);
-       web->output.output_pending = True;
+}
+
+
+/*
+  perform pre-authentication on every page is /scripting/preauth.esp
+  exists.  If this script generates any non-whitepace output at all,
+  then we don't run the requested URL.
+
+  note that the preauth is run even for static pages such as images.
+*/
+static BOOL http_preauth(struct esp_state *esp)
+{
+       const char *path = http_local_path(esp->web, HTTP_PREAUTH_URI);
+       int i;
+       if (path == NULL) {
+               http_error(esp->web, 500, "Internal server error");
+               return False;
+       }
+       if (!file_exist(path)) {
+               /* if the preath script is not installed then allow access */
+               return True;
+       }
+       esp_request(esp, HTTP_PREAUTH_URI);
+       for (i=0;i<esp->web->output.content.length;i++) {
+               if (!isspace(esp->web->output.content.data[i])) {
+                       /* if the preauth has generated content, then force it to be
+                          html, so that we can show the login page for failed
+                          access to images */
+                       http_setHeader(esp->web, "Content-Type: text/html", 0);
+                       return False;
+               }
+       }
+       data_blob_free(&esp->web->output.content);
+       return True;
 }
 
 
@@ -540,9 +570,15 @@ static const char *http_unescape(TALLOC_CTX *mem_ctx, const char *p)
 */
 static void esp_putvar(struct esp_state *esp, const char *var, const char *value)
 {
-       espSetStringVar(esp->req, ESP_FORM_OBJ, 
-                       http_unescape(esp, var),
-                       http_unescape(esp, value));
+       if (strcasecmp(var, SWAT_SESSION_KEY) == 0) {
+               /* special case support for browsers without cookie
+                support */
+               esp->web->input.session_key = talloc_strdup(esp, value);
+       } else {
+               mprSetPropertyValue(&esp->variables[ESP_FORM_OBJ], 
+                                   http_unescape(esp, var),
+                                   mprCreateStringVar(http_unescape(esp, value), 0));
+       }
 }
 
 
@@ -563,7 +599,7 @@ static NTSTATUS http_parse_post(struct esp_state *esp)
                } else {
                        len = p - (char *)b.data;
                }
-               line = talloc_strndup(esp, b.data, len);
+               line = talloc_strndup(esp, (char *)b.data, len);
                NT_STATUS_HAVE_NO_MEMORY(line);
                                     
                p = strchr(line,'=');
@@ -640,6 +676,7 @@ static void http_setup_session(struct esp_state *esp)
        const char *key = NULL;
        struct esp_data *edata = talloc_get_type(esp->web->task->private, struct esp_data);
        struct session_data *s;
+       BOOL generated_key = False;
 
        /* look for our session key */
        if (cookie && (p = strstr(cookie, session_key)) && 
@@ -648,13 +685,18 @@ static void http_setup_session(struct esp_state *esp)
                key = talloc_strndup(esp, p, strcspn(p, ";"));
        }
 
-       if (key == NULL) {
-               key = generate_random_str_list(esp, 64, "0123456789");
+       if (key == NULL && esp->web->input.session_key) {
+               key = esp->web->input.session_key;
+       } else if (key == NULL) {
+               key = generate_random_str_list(esp, 16, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+               generated_key = True;
        }
 
        /* try to find this session in the existing session list */
        for (s=edata->sessions;s;s=s->next) {
-               if (strcmp(key, s->id) == 0) break;
+               if (strcmp(key, s->id) == 0) {
+                       break;
+               }
        }
 
        if (s == NULL) {
@@ -667,6 +709,10 @@ static void http_setup_session(struct esp_state *esp)
                s->lifetime = lp_parm_int(-1, "web", "sessiontimeout", 300);
                DLIST_ADD(edata->sessions, s);
                talloc_set_destructor(s, session_destructor);
+               if (!generated_key) {
+                       mprSetPropertyValue(&esp->variables[ESP_REQUEST_OBJ], 
+                                           "SESSION_EXPIRED", mprCreateStringVar("True", 0));
+               }
        }
 
        http_setCookie(esp->web, session_key, key, s->lifetime, "/", 0);
@@ -716,6 +762,7 @@ void http_process_input(struct websrv_context *web)
                {"jpg",  "image/jpeg"},
                {"txt",  "text/plain"},
                {"ico",  "image/x-icon"},
+               {"css",  "text/css"},
                {"esp",  "text/html", True}
        };
 
@@ -745,15 +792,9 @@ void http_process_input(struct websrv_context *web)
                           edata->application_data, MPR_DEEP_COPY);
        }
 
-       http_setup_session(esp);
-
        talloc_set_destructor(esp, esp_destructor);
 
        smb_setup_ejs_functions();
-       http_setup_ejs_functions();
-
-       esp->req = espCreateRequest(web, web->input.url, esp->variables);
-       if (esp->req == NULL) goto internal_error;
 
        if (web->input.url == NULL) {
                http_error(web, 400, "You must specify a GET or POST request");
@@ -767,7 +808,8 @@ void http_process_input(struct websrv_context *web)
                        http_error(web, 400, "Malformed POST data");
                        return;
                }
-       } else if (strchr(web->input.url, '?')) {
+       } 
+       if (strchr(web->input.url, '?')) {
                status = http_parse_get(esp);
                if (!NT_STATUS_IS_OK(status)) {
                        http_error(web, 400, "Malformed GET data");
@@ -775,6 +817,13 @@ void http_process_input(struct websrv_context *web)
                }
        }
 
+       http_setup_session(esp);
+
+       esp->req = espCreateRequest(web, web->input.url, esp->variables);
+       if (esp->req == NULL) goto internal_error;
+
+       smb_setup_ejs_constants(esp->req->eid);
+
        /* work out the mime type */
        p = strrchr(web->input.url, '.');
        if (p == NULL) {
@@ -798,17 +847,29 @@ void http_process_input(struct websrv_context *web)
        http_setHeader(web, "Connection: close", 0);
        http_setHeader(web, talloc_asprintf(esp, "Content-Type: %s", file_type), 0);
 
-       if (esp_enable) {
-               esp_request(esp);
-       } else {
-               http_simple_request(web);
+       http_setup_arrays(esp);
+
+       /* possibly do pre-authentication */
+       if (http_preauth(esp)) {
+               if (esp_enable) {
+                       esp_request(esp, web->input.url);
+               } else {
+                       http_simple_request(web);
+               }
+       }
+
+       if (!web->output.output_pending) {
+               http_output_headers(web);
+               EVENT_FD_WRITEABLE(web->conn->event.fde);
+               web->output.output_pending = True;
        }
 
        /* copy any application data to long term storage in edata */
        talloc_free(edata->application_data);
        edata->application_data = talloc_zero(edata, struct MprVar);
        mprSetCtx(edata->application_data);
-       mprCopyVar(edata->application_data, &esp->variables[ESP_APPLICATION_OBJ], MPR_DEEP_COPY);
+       mprCopyVar(edata->application_data, &esp->variables[ESP_APPLICATION_OBJ], 
+                  MPR_DEEP_COPY);
 
        /* copy any session data */
        if (web->session) {
@@ -890,5 +951,8 @@ NTSTATUS http_setup_esp(struct task_server *task)
 
        task->private = edata;
 
+       edata->tls_params = tls_initialise(edata);
+       NT_STATUS_HAVE_NO_MEMORY(edata->tls_params);
+
        return NT_STATUS_OK;
 }