From 3d035a6f6ab63c2e3031678cda194435526da918 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sat, 5 Nov 2011 03:19:07 +0000 Subject: uhttpd: rework CyaSSL and OpenSSL integration; move protected recv() and send() operations below the ssl layer - fixes hangs when accessing via https SVN-Revision: 28761 --- package/uhttpd/Makefile | 4 +- package/uhttpd/src/Makefile | 32 +++++---- package/uhttpd/src/uhttpd-tls.c | 144 +++++++++++++++++++++++++++++++++++++- package/uhttpd/src/uhttpd-utils.c | 79 ++++++++++++--------- package/uhttpd/src/uhttpd-utils.h | 2 + 5 files changed, 210 insertions(+), 51 deletions(-) diff --git a/package/uhttpd/Makefile b/package/uhttpd/Makefile index 4f29284883..daf7333cd6 100644 --- a/package/uhttpd/Makefile +++ b/package/uhttpd/Makefile @@ -65,14 +65,16 @@ endef UHTTPD_TLS:= TLS_CFLAGS:= +TLS_LDFLAGS:= ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_cyassl),) UHTTPD_TLS:=cyassl - TLS_CFLAGS:=-I$(STAGING_DIR)/usr/include/cyassl + TLS_CFLAGS:=-I$(STAGING_DIR)/usr/include/cyassl -DTLS_IS_CYASSL endif ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_openssl),) UHTTPD_TLS:=openssl + TLS_CFLAGS:=-DTLS_IS_OPENSSL endif diff --git a/package/uhttpd/src/Makefile b/package/uhttpd/src/Makefile index e18833e8f3..9c3cc7f02c 100644 --- a/package/uhttpd/src/Makefile +++ b/package/uhttpd/src/Makefile @@ -3,19 +3,17 @@ LUA_SUPPORT ?= 1 TLS_SUPPORT ?= 1 UHTTPD_TLS ?= cyassl -CFLAGS ?= -I./lua-5.1.4/src -I$(TLS_INCLUDE_DIR) -O0 -ggdb3 -LDFLAGS ?= -L./lua-5.1.4/src -L$(TLS_LIB_DIR) +CFLAGS ?= -I./lua-5.1.4/src $(TLS_CFLAGS) -O0 -ggdb3 +LDFLAGS ?= -L./lua-5.1.4/src $(TLS_LDFLAGS) CFLAGS += -Wall --std=gnu99 ifeq ($(UHTTPD_TLS),openssl) - TLS_LDFLAGS := -lssl - TLS_INCLUDE_DIR := ./openssl-0.9.8m/include - TLS_LIB_DIR := ./openssl-0.9.8m + TLS_LDFLAGS := -L./openssl-0.9.8m -lssl + TLS_CFLAGS := -I./openssl-0.9.8m/include -DTLS_IS_OPENSSL else - TLS_LDFLAGS := -lcyassl - TLS_INCLUDE_DIR := ./cyassl-1.4.0/include - TLS_LIB_DIR := ./cyassl-1.4.0/src/.libs + TLS_LDFLAGS := -L./cyassl-1.4.0/src/.libs -lcyassl + TLS_CFLAGS := -I./cyassl-1.4.0/include -DTLS_IS_CYASSL endif OBJ := uhttpd.o uhttpd-file.o uhttpd-utils.o @@ -31,15 +29,26 @@ ifeq ($(HAVE_SHADOW),yes) CFLAGS += -DHAVE_SHADOW endif -world: compile +ifeq ($(TLS_SUPPORT),1) + CFLAGS += -DHAVE_TLS +endif ifeq ($(CGI_SUPPORT),1) - OBJ += uhttpd-cgi.o CFLAGS += -DHAVE_CGI endif ifeq ($(LUA_SUPPORT),1) CFLAGS += -DHAVE_LUA +endif + + +world: compile + +ifeq ($(CGI_SUPPORT),1) + OBJ += uhttpd-cgi.o +endif + +ifeq ($(LUA_SUPPORT),1) LUALIB := uhttpd_lua.so $(LUALIB): uhttpd-lua.c @@ -49,12 +58,11 @@ ifeq ($(LUA_SUPPORT),1) endif ifeq ($(TLS_SUPPORT),1) - CFLAGS += -DHAVE_TLS TLSLIB := uhttpd_tls.so $(TLSLIB): uhttpd-tls.c $(CC) $(CFLAGS) $(LDFLAGS) $(FPIC) \ - -shared $(TLS_LDFLAGS) \ + -shared \ -o $(TLSLIB) uhttpd-tls.c endif diff --git a/package/uhttpd/src/uhttpd-tls.c b/package/uhttpd/src/uhttpd-tls.c index 6beae25aa1..4a9e907922 100644 --- a/package/uhttpd/src/uhttpd-tls.c +++ b/package/uhttpd/src/uhttpd-tls.c @@ -20,6 +20,143 @@ #include "uhttpd-tls.h" #include "uhttpd-utils.h" +#include +#define dbg(...) syslog(LOG_INFO, __VA_ARGS__) + +#ifdef TLS_IS_CYASSL +static int uh_cyassl_recv_cb(char *buf, int sz, void *ctx) +{ + int rv; + int socket = *(int *)ctx; + struct client *cl; + + if (!(cl = uh_client_lookup(socket))) + return -1; /* unexpected error */ + + rv = uh_tcp_recv_lowlevel(cl, buf, sz); + + if (rv < 0) + return -4; /* interrupted */ + + if (rv == 0) + return -5; /* connection closed */ + + return rv; +} + +static int uh_cyassl_send_cb(char *buf, int sz, void *ctx) +{ + int rv; + int socket = *(int *)ctx; + struct client *cl; + + if (!(cl = uh_client_lookup(socket))) + return -1; /* unexpected error */ + + rv = uh_tcp_send_lowlevel(cl, buf, sz); + + if (rv <= 0) + return -5; /* connection dead */ + + return rv; +} + +void SetCallbackIORecv_Ctx(SSL_CTX*, int (*)(char *, int, void *)); +void SetCallbackIOSend_Ctx(SSL_CTX*, int (*)(char *, int, void *)); + +static void uh_tls_ctx_setup(SSL_CTX *ctx) +{ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + SetCallbackIORecv_Ctx(ctx, uh_cyassl_recv_cb); + SetCallbackIOSend_Ctx(ctx, uh_cyassl_send_cb); + return; +} + +static int uh_tls_client_ctx_setup(SSL *ssl, int socket) +{ + return SSL_set_fd(ssl, socket); +} +#endif /* TLS_IS_CYASSL */ + +#ifdef TLS_IS_OPENSSL +static long uh_openssl_bio_ctrl_cb(BIO *b, int cmd, long num, void *ptr) +{ + long rv = 1; + + switch (cmd) + { + case BIO_C_SET_FD: + b->num = *((int *)ptr); + b->shutdown = (int)num; + b->init = 1; + break; + + case BIO_C_GET_FD: + if (!b->init) + return -1; + + if (ptr) + *((int *)ptr) = b->num; + + rv = b->num; + break; + } + + return rv; +} + +static int uh_openssl_bio_read_cb(BIO *b, char *out, int outl) +{ + int rv = 0; + struct client *cl; + + if (!(cl = uh_client_lookup(b->num))) + return -1; + + if (out != NULL) + rv = uh_tcp_recv_lowlevel(cl, out, outl); + + return rv; +} + +static int uh_openssl_bio_write_cb(BIO *b, const char *in, int inl) +{ + struct client *cl; + + if (!(cl = uh_client_lookup(b->num))) + return -1; + + return uh_tcp_send_lowlevel(cl, in, inl); +} + +static BIO_METHOD uh_openssl_bio_methods = { + .type = BIO_TYPE_SOCKET, + .name = "uhsocket", + .ctrl = uh_openssl_bio_ctrl_cb, + .bwrite = uh_openssl_bio_write_cb, + .bread = uh_openssl_bio_read_cb +}; + +static void uh_tls_ctx_setup(SSL_CTX *ctx) +{ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + return; +} + +static int uh_tls_client_ctx_setup(SSL *ssl, int socket) +{ + BIO *b; + + if (!(b = BIO_new(&uh_openssl_bio_methods))) + return 0; + + BIO_set_fd(b, socket, BIO_NOCLOSE); + SSL_set_bio(ssl, b, b); + + return 1; +} +#endif /* TLS_IS_OPENSSL */ + SSL_CTX * uh_tls_ctx_init() { @@ -28,8 +165,8 @@ SSL_CTX * uh_tls_ctx_init() SSL_load_error_strings(); SSL_library_init(); - if( (c = SSL_CTX_new(TLSv1_server_method())) != NULL ) - SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL); + if ((c = SSL_CTX_new(TLSv1_server_method())) != NULL) + uh_tls_ctx_setup(c); return c; } @@ -69,8 +206,9 @@ int uh_tls_client_accept(struct client *c) c->tls = SSL_new(c->server->tls); if( c->tls ) { - if( (rv = SSL_set_fd(c->tls, c->socket)) < 1 ) + if( (rv = uh_tls_client_ctx_setup(c->tls, c->socket)) < 1 ) goto cleanup; + if( (rv = SSL_accept(c->tls)) < 1 ) goto cleanup; } diff --git a/package/uhttpd/src/uhttpd-utils.c b/package/uhttpd/src/uhttpd-utils.c index ac00af824e..d48f6bcf11 100644 --- a/package/uhttpd/src/uhttpd-utils.c +++ b/package/uhttpd/src/uhttpd-utils.c @@ -124,7 +124,7 @@ int select_intr(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) } -int uh_tcp_send(struct client *cl, const char *buf, int len) +int uh_tcp_send_lowlevel(struct client *cl, const char *buf, int len) { fd_set writer; struct timeval timeout; @@ -135,21 +135,28 @@ int uh_tcp_send(struct client *cl, const char *buf, int len) timeout.tv_sec = cl->server->conf->network_timeout; timeout.tv_usec = 0; - if( select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 0 ) - { -#ifdef HAVE_TLS - if( cl->tls ) - return cl->server->conf->tls_send(cl, (void *)buf, len); - else -#endif - return send(cl->socket, buf, len, 0); - } + if (select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 0) + return send(cl->socket, buf, len, 0); return -1; } +int uh_tcp_send(struct client *cl, const char *buf, int len) +{ +#ifdef HAVE_TLS + if (cl->tls) + return cl->server->conf->tls_send(cl, (void *)buf, len); + else +#endif + return uh_tcp_send_lowlevel(cl, buf, len); +} + int uh_tcp_peek(struct client *cl, char *buf, int len) { + /* sanity check, prevent overflowing peek buffer */ + if (len > sizeof(cl->peekbuf)) + return -1; + int sz = uh_tcp_recv(cl, buf, len); /* store received data in peek buffer */ @@ -162,49 +169,51 @@ int uh_tcp_peek(struct client *cl, char *buf, int len) return sz; } +int uh_tcp_recv_lowlevel(struct client *cl, char *buf, int len) +{ + fd_set reader; + struct timeval timeout; + + FD_ZERO(&reader); + FD_SET(cl->socket, &reader); + + timeout.tv_sec = cl->server->conf->network_timeout; + timeout.tv_usec = 0; + + if (select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0) + return recv(cl->socket, buf, len, 0); + + return -1; +} + int uh_tcp_recv(struct client *cl, char *buf, int len) { int sz = 0; int rsz = 0; - fd_set reader; - struct timeval timeout; - /* first serve data from peek buffer */ - if( cl->peeklen > 0 ) + if (cl->peeklen > 0) { sz = min(cl->peeklen, len); len -= sz; cl->peeklen -= sz; - memcpy(buf, cl->peekbuf, sz); memmove(cl->peekbuf, &cl->peekbuf[sz], cl->peeklen); } /* caller wants more */ - if( len > 0 ) + if (len > 0) { - FD_ZERO(&reader); - FD_SET(cl->socket, &reader); - - timeout.tv_sec = cl->server->conf->network_timeout; - timeout.tv_usec = 0; - - if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0 ) - { #ifdef HAVE_TLS - if( cl->tls ) - rsz = cl->server->conf->tls_recv(cl, (void *)&buf[sz], len); - else + if (cl->tls) + rsz = cl->server->conf->tls_recv(cl, (void *)&buf[sz], len); + else #endif - rsz = recv(cl->socket, (void *)&buf[sz], len, 0); + rsz = uh_tcp_recv_lowlevel(cl, (void *)&buf[sz], len); - if( (sz == 0) || (rsz > 0) ) - sz += rsz; - } - else if( sz == 0 ) - { - sz = -1; - } + if (rsz < 0) + return rsz; + + sz += rsz; } return sz; diff --git a/package/uhttpd/src/uhttpd-utils.h b/package/uhttpd/src/uhttpd-utils.h index 769e5b45d6..a2cac35ac5 100644 --- a/package/uhttpd/src/uhttpd-utils.h +++ b/package/uhttpd/src/uhttpd-utils.h @@ -67,8 +67,10 @@ char *strfind(char *haystack, int hslen, const char *needle, int ndlen); int select_intr(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t); int uh_tcp_send(struct client *cl, const char *buf, int len); +int uh_tcp_send_lowlevel(struct client *cl, const char *buf, int len); int uh_tcp_peek(struct client *cl, char *buf, int len); int uh_tcp_recv(struct client *cl, char *buf, int len); +int uh_tcp_recv_lowlevel(struct client *cl, char *buf, int len); int uh_http_sendhf( struct client *cl, int code, const char *summary, -- cgit v1.2.3