summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/web_server/config.m412
-rw-r--r--source4/web_server/config.mk3
-rw-r--r--source4/web_server/http.c37
-rw-r--r--source4/web_server/tls.c358
-rw-r--r--source4/web_server/web_server.c14
-rw-r--r--source4/web_server/web_server.h22
6 files changed, 419 insertions, 27 deletions
diff --git a/source4/web_server/config.m4 b/source4/web_server/config.m4
index 4d8952b775..057f193179 100644
--- a/source4/web_server/config.m4
+++ b/source4/web_server/config.m4
@@ -1 +1,13 @@
AC_CHECK_HEADERS(setjmp.h)
+
+###############################
+# start SMB_EXT_LIB_GNUTLS
+# check for gnutls/gnutls.h and -lgnutls
+AC_CHECK_HEADERS(gnutls/gnutls.h)
+AC_CHECK_LIB_EXT(gnutls, GNUTLS_LIBS, gnutls_global_init)
+if test x"$ac_cv_header_gnutls_gnutls_h" = x"yes" -a x"$ac_cv_lib_ext_gnutls_gnutls_global_init" = x"yes";then
+ SMB_EXT_LIB_ENABLE(GNUTLS,YES)
+fi
+SMB_EXT_LIB(GNUTLS, $GNUTLS_LIBS)
+# end SMB_EXT_LIB_GNUTLS
+###############################
diff --git a/source4/web_server/config.mk b/source4/web_server/config.mk
index b287b4f153..00ae2ea8a0 100644
--- a/source4/web_server/config.mk
+++ b/source4/web_server/config.mk
@@ -34,7 +34,8 @@ INIT_OBJ_FILES = \
web_server/web_server.o
ADD_OBJ_FILES = \
web_server/http.o \
- web_server/calls.o
+ web_server/calls.o \
+ web_server/tls.o
REQUIRED_SUBSYSTEMS = ESP
# End SUBSYSTEM WEB
#######################
diff --git a/source4/web_server/http.c b/source4/web_server/http.c
index 6687ab7d16..11ddec552d 100644
--- a/source4/web_server/http.c
+++ b/source4/web_server/http.c
@@ -33,22 +33,6 @@
#define SWAT_SESSION_KEY "_swat_session_"
-/*
- context for long term storage in the web server, to support session[]
- and application[] data. Stored in task->private.
-*/
-struct esp_data {
- struct session_data {
- struct session_data *next, *prev;
- struct esp_data *edata;
- const char *id;
- struct MprVar *data;
- struct timed_event *te;
- int lifetime;
- } *sessions;
- struct MprVar *application_data;
-};
-
/* state of the esp subsystem for a specific request */
struct esp_state {
struct websrv_context *web;
@@ -250,10 +234,13 @@ static void http_redirect(EspHandle handle, int code, char *url)
if (url[0] != '/') {
char *p = strrchr(web->input.url, '/');
if (p == web->input.url) {
- url = talloc_asprintf(web, "http://%s/%s", host, url);
+ url = talloc_asprintf(web, "http%s://%s/%s",
+ web->tls_session?"s":"",
+ host, url);
} else {
int dirlen = p - web->input.url;
- url = talloc_asprintf(web, "http://%s%*.*s/%s",
+ url = talloc_asprintf(web, "http%s://%s%*.*s/%s",
+ web->tls_session?"s":"",
host,
dirlen, dirlen, web->input.url,
url);
@@ -351,6 +338,7 @@ void http_error(struct websrv_context *web, int code, const char *info)
http_output_headers(web);
EVENT_FD_NOT_READABLE(web->conn->event.fde);
EVENT_FD_WRITEABLE(web->conn->event.fde);
+ web->output.output_pending = True;
}
/*
@@ -399,6 +387,7 @@ static void http_simple_request(struct websrv_context *web)
http_output_headers(web);
EVENT_FD_WRITEABLE(web->conn->event.fde);
+ web->output.output_pending = True;
return;
invalid:
@@ -449,7 +438,7 @@ 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", "http");
+ SETVAR(ESP_SERVER_OBJ, "SERVER_PROTOCOL", web->tls_session?"https":"http");
SETVAR(ESP_SERVER_OBJ, "SERVER_SOFTWARE", "SWAT");
SETVAR(ESP_SERVER_OBJ, "GATEWAY_INTERFACE", "CGI/1.1");
SETVAR(ESP_REQUEST_OBJ, "SCRIPT_FILENAME", web->input.url);
@@ -509,6 +498,7 @@ static void esp_request(struct esp_state *esp)
talloc_free(buf);
http_output_headers(web);
EVENT_FD_WRITEABLE(web->conn->event.fde);
+ web->output.output_pending = True;
}
@@ -663,7 +653,7 @@ static void http_setup_session(struct esp_state *esp)
s->data = NULL;
s->te = NULL;
s->edata = edata;
- s->lifetime = lp_parm_int(-1, "http", "sessiontimeout", 300);
+ s->lifetime = lp_parm_int(-1, "web", "sessiontimeout", 300);
DLIST_ADD(edata->sessions, s);
talloc_set_destructor(s, session_destructor);
}
@@ -775,6 +765,9 @@ void http_process_input(struct websrv_context *web)
/* work out the mime type */
p = strrchr(web->input.url, '.');
+ if (p == NULL) {
+ esp_enable = True;
+ }
for (i=0;p && i<ARRAY_SIZE(mime_types);i++) {
if (strcmp(mime_types[i].extension, p+1) == 0) {
file_type = mime_types[i].mime_type;
@@ -880,12 +873,10 @@ NTSTATUS http_setup_esp(struct task_server *task)
{
struct esp_data *edata;
- edata = talloc(task, struct esp_data);
+ edata = talloc_zero(task, struct esp_data);
NT_STATUS_HAVE_NO_MEMORY(edata);
task->private = edata;
- edata->sessions = NULL;
- edata->application_data = NULL;
return NT_STATUS_OK;
}
diff --git a/source4/web_server/tls.c b/source4/web_server/tls.c
new file mode 100644
index 0000000000..4129d59355
--- /dev/null
+++ b/source4/web_server/tls.c
@@ -0,0 +1,358 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ transport layer security handling code
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "web_server/web_server.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+
+#if HAVE_LIBGNUTLS
+#include "gnutls/gnutls.h"
+
+#define DH_BITS 1024
+
+/* hold per connection tls data */
+struct tls_session {
+ gnutls_session session;
+ BOOL done_handshake;
+};
+
+/* hold persistent tls data */
+struct tls_data {
+ gnutls_certificate_credentials x509_cred;
+ gnutls_dh_params dh_params;
+};
+
+/*
+ initialise global tls state
+*/
+void tls_initialise(struct task_server *task)
+{
+ struct esp_data *edata = talloc_get_type(task->private, struct esp_data);
+ struct tls_data *tls;
+ int ret;
+ const char *keyfile = lp_web_keyfile();
+ const char *certfile = lp_web_certfile();
+ const char *cafile = lp_web_cafile();
+ const char *crlfile = lp_web_crlfile();
+
+ if (!lp_parm_bool(-1, "web", "tls", False)) {
+ return;
+ }
+
+ tls = talloc_zero(edata, struct tls_data);
+ edata->tls_data = tls;
+
+ ret = gnutls_global_init();
+ if (ret < 0) goto init_failed;
+
+ gnutls_certificate_allocate_credentials(&tls->x509_cred);
+ if (ret < 0) goto init_failed;
+
+ ret = gnutls_certificate_set_x509_trust_file(tls->x509_cred, cafile,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise cafile %s\n", cafile));
+ goto init_failed;
+ }
+
+ if (crlfile && *crlfile) {
+ ret = gnutls_certificate_set_x509_crl_file(tls->x509_cred,
+ crlfile,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise crlfile %s\n", cafile));
+ goto init_failed;
+ }
+ }
+
+ ret = gnutls_certificate_set_x509_key_file(tls->x509_cred,
+ certfile, keyfile,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise certfile %s and keyfile %s\n",
+ lp_web_certfile(), lp_web_keyfile()));
+ goto init_failed;
+ }
+
+ ret = gnutls_dh_params_init(&tls->dh_params);
+ if (ret < 0) goto init_failed;
+
+ ret = gnutls_dh_params_generate2(tls->dh_params, DH_BITS);
+ if (ret < 0) goto init_failed;
+
+ gnutls_certificate_set_dh_params(tls->x509_cred, tls->dh_params);
+ return;
+
+init_failed:
+ DEBUG(0,("GNUTLS failed to initialise with code %d - disabling\n", ret));
+ talloc_free(tls);
+ edata->tls_data = NULL;
+}
+
+
+/*
+ callback for reading from a socket
+*/
+static ssize_t tls_pull(gnutls_transport_ptr ptr, void *buf, size_t size)
+{
+ struct websrv_context *web = talloc_get_type(ptr, struct websrv_context);
+ NTSTATUS status;
+ size_t nread;
+
+ if (web->input.tls_first_char) {
+ *(uint8_t *)buf = web->input.first_byte;
+ web->input.tls_first_char = False;
+ return 1;
+ }
+
+ status = socket_recv(web->conn->socket, buf, size, &nread, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ EVENT_FD_READABLE(web->conn->event.fde);
+ EVENT_FD_NOT_WRITEABLE(web->conn->event.fde);
+ return -1;
+ }
+ if (web->output.output_pending) {
+ EVENT_FD_WRITEABLE(web->conn->event.fde);
+ }
+ if (size != nread) {
+ EVENT_FD_READABLE(web->conn->event.fde);
+ }
+ return nread;
+}
+
+/*
+ callback for writing to a socket
+*/
+static ssize_t tls_push(gnutls_transport_ptr ptr, const void *buf, size_t size)
+{
+ struct websrv_context *web = talloc_get_type(ptr, struct websrv_context);
+ NTSTATUS status;
+ size_t nwritten;
+ DATA_BLOB b;
+
+ if (web->tls_session == NULL) {
+ return size;
+ }
+
+ b.data = discard_const(buf);
+ b.length = size;
+
+ status = socket_send(web->conn->socket, &b, &nwritten, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ EVENT_FD_WRITEABLE(web->conn->event.fde);
+ return -1;
+ }
+ if (size != nwritten) {
+ EVENT_FD_WRITEABLE(web->conn->event.fde);
+ }
+ return nwritten;
+}
+
+/*
+ destroy a tls session
+ */
+static int tls_destructor(void *ptr)
+{
+ struct tls_session *tls_session = talloc_get_type(ptr, struct tls_session);
+ gnutls_bye(tls_session->session, GNUTLS_SHUT_WR);
+ return 0;
+}
+
+
+/*
+ setup for a new connection
+*/
+NTSTATUS tls_init_connection(struct websrv_context *web)
+{
+ struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
+ struct tls_data *tls_data = talloc_get_type(edata->tls_data, struct tls_data);
+ struct tls_session *tls_session;
+ int ret;
+
+ if (edata->tls_data == NULL) {
+ web->tls_session = NULL;
+ return NT_STATUS_OK;
+ }
+
+#define TLSCHECK(call) do { \
+ ret = call; \
+ if (ret < 0) { \
+ DEBUG(0,("TLS failed with code %d - %s\n", ret, #call)); \
+ goto failed; \
+ } \
+} while (0)
+
+ tls_session = talloc_zero(web, struct tls_session);
+ web->tls_session = tls_session;
+
+ TLSCHECK(gnutls_init(&tls_session->session, GNUTLS_SERVER));
+
+ talloc_set_destructor(tls_session, tls_destructor);
+
+ TLSCHECK(gnutls_set_default_priority(tls_session->session));
+ TLSCHECK(gnutls_credentials_set(tls_session->session, GNUTLS_CRD_CERTIFICATE, tls_data->x509_cred));
+ gnutls_certificate_server_set_request(tls_session->session, GNUTLS_CERT_REQUEST);
+ gnutls_dh_set_prime_bits(tls_session->session, DH_BITS);
+ gnutls_transport_set_ptr(tls_session->session, (gnutls_transport_ptr)web);
+ gnutls_transport_set_pull_function(tls_session->session, (gnutls_pull_func)tls_pull);
+ gnutls_transport_set_push_function(tls_session->session, (gnutls_push_func)tls_push);
+ gnutls_transport_set_lowat(tls_session->session, 0);
+
+ web->input.tls_detect = True;
+
+ return NT_STATUS_OK;
+
+failed:
+ web->tls_session = NULL;
+ talloc_free(tls_session);
+ return NT_STATUS_OK;
+}
+
+/*
+ possibly continue the handshake process
+*/
+static NTSTATUS tls_handshake(struct tls_session *tls_session)
+{
+ int ret;
+
+ if (tls_session->done_handshake) {
+ return NT_STATUS_OK;
+ }
+
+ ret = gnutls_handshake(tls_session->session);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return STATUS_MORE_ENTRIES;
+ }
+ if (ret < 0) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ tls_session->done_handshake = True;
+ return NT_STATUS_OK;
+}
+
+
+/*
+ receive data either by tls or normal socket_recv
+*/
+NTSTATUS tls_socket_recv(struct websrv_context *web, void *buf, size_t wantlen,
+ size_t *nread)
+{
+ int ret;
+ NTSTATUS status;
+ struct tls_session *tls_session = talloc_get_type(web->tls_session,
+ struct tls_session);
+
+ if (web->tls_session != NULL && web->input.tls_detect) {
+ status = socket_recv(web->conn->socket, &web->input.first_byte,
+ 1, nread, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (*nread == 0) return NT_STATUS_OK;
+ web->input.tls_detect = False;
+ /* look for the first byte of a valid HTTP operation */
+ if (strchr("GPHO", web->input.first_byte)) {
+ /* not a tls link */
+ web->tls_session = NULL;
+ talloc_free(tls_session);
+ *(uint8_t *)buf = web->input.first_byte;
+ return NT_STATUS_OK;
+ }
+ web->input.tls_first_char = True;
+ }
+
+ if (web->tls_session == NULL) {
+ return socket_recv(web->conn->socket, buf, wantlen, nread, 0);
+ }
+
+ status = tls_handshake(tls_session);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ret = gnutls_record_recv(tls_session->session, buf, wantlen);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return STATUS_MORE_ENTRIES;
+ }
+ if (ret < 0) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ *nread = ret;
+ return NT_STATUS_OK;
+}
+
+
+/*
+ send data either by tls or normal socket_recv
+*/
+NTSTATUS tls_socket_send(struct websrv_context *web, const DATA_BLOB *blob,
+ size_t *sendlen)
+{
+ NTSTATUS status;
+ int ret;
+ struct tls_session *tls_session = talloc_get_type(web->tls_session,
+ struct tls_session);
+
+ if (web->tls_session == NULL) {
+ return socket_send(web->conn->socket, blob, sendlen, 0);
+ }
+
+ status = tls_handshake(tls_session);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ret = gnutls_record_send(tls_session->session, blob->data, blob->length);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return STATUS_MORE_ENTRIES;
+ }
+ if (ret < 0) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ *sendlen = ret;
+ return NT_STATUS_OK;
+}
+#else
+
+/* for systems without tls */
+NTSTATUS tls_socket_recv(struct websrv_context *web, void *buf, size_t wantlen,
+ size_t *nread)
+{
+ return socket_recv(web->conn->socket, buf, wantlen, nread, 0);
+}
+
+NTSTATUS tls_socket_send(struct websrv_context *web, const DATA_BLOB *blob,
+ size_t *sendlen)
+{
+ return socket_send(web->conn->socket, blob, sendlen, 0);
+}
+
+NTSTATUS tls_init_connection(struct websrv_context *web)
+{
+ web->tls_session = NULL;
+ return NT_STATUS_OK;
+}
+
+void tls_initialise(struct task_server *task)
+{
+ struct esp_data *edata = talloc_get_type(task->private, struct esp_data);
+ edata->tls_data = NULL;
+}
+
+#endif
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
index 9bbf423d29..faa30fc55f 100644
--- a/source4/web_server/web_server.c
+++ b/source4/web_server/web_server.c
@@ -68,7 +68,7 @@ static void websrv_recv(struct stream_connection *conn, uint16_t flags)
DATA_BLOB b;
/* not the most efficient http parser ever, but good enough for us */
- status = socket_recv(conn->socket, buf, sizeof(buf), &nread, 0);
+ status = tls_socket_recv(web, buf, sizeof(buf), &nread);
if (NT_STATUS_IS_ERR(status)) goto failed;
if (!NT_STATUS_IS_OK(status)) return;
@@ -128,7 +128,7 @@ static void websrv_send(struct stream_connection *conn, uint16_t flags)
b.data += web->output.nsent;
b.length -= web->output.nsent;
- status = socket_send(conn->socket, &b, &nsent, 0);
+ status = tls_socket_send(web, &b, &nsent);
if (NT_STATUS_IS_ERR(status)) {
stream_terminate_connection(web->conn, "socket_send: failed");
return;
@@ -159,7 +159,8 @@ static void websrv_send(struct stream_connection *conn, uint16_t flags)
web->output.content = data_blob_talloc(web, buf, nread);
}
- if (web->output.content.length == web->output.nsent) {
+ if (web->output.content.length == web->output.nsent &&
+ web->output.fd == -1) {
stream_terminate_connection(web->conn, NULL);
}
}
@@ -171,6 +172,7 @@ static void websrv_accept(struct stream_connection *conn)
{
struct task_server *task = talloc_get_type(conn->private, struct task_server);
struct websrv_context *web;
+ NTSTATUS status;
web = talloc_zero(conn, struct websrv_context);
if (web == NULL) goto failed;
@@ -184,6 +186,10 @@ static void websrv_accept(struct stream_connection *conn)
event_add_timed(conn->event.ctx, web,
timeval_current_ofs(HTTP_TIMEOUT, 0),
websrv_timeout, web);
+
+ status = tls_init_connection(web);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
return;
failed:
@@ -235,6 +241,8 @@ static void websrv_task_init(struct task_server *task)
status = http_setup_esp(task);
if (!NT_STATUS_IS_OK(status)) goto failed;
+ tls_initialise(task);
+
return;
failed:
diff --git a/source4/web_server/web_server.h b/source4/web_server/web_server.h
index f23ea90d55..53f97964f1 100644
--- a/source4/web_server/web_server.h
+++ b/source4/web_server/web_server.h
@@ -28,6 +28,9 @@ struct websrv_context {
struct task_server *task;
struct stream_connection *conn;
struct {
+ BOOL tls_detect;
+ BOOL tls_first_char;
+ uint8_t first_byte;
DATA_BLOB partial;
BOOL end_of_headers;
char *url;
@@ -45,13 +48,32 @@ struct websrv_context {
const char *session_key;
} input;
struct {
+ BOOL output_pending;
DATA_BLOB content;
int fd;
unsigned nsent;
int response_code;
const char **headers;
} output;
+ void *tls_session;
struct session_data *session;
};
+/*
+ context for long term storage in the web server, to support session[]
+ and application[] data. Stored in task->private.
+*/
+struct esp_data {
+ struct session_data {
+ struct session_data *next, *prev;
+ struct esp_data *edata;
+ const char *id;
+ struct MprVar *data;
+ struct timed_event *te;
+ int lifetime;
+ } *sessions;
+ struct MprVar *application_data;
+ void *tls_data;
+};
+