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
This commit is contained in:
Jo-Philipp Wich 2011-11-05 03:19:07 +00:00
parent 297ac9a7f1
commit 3d035a6f6a
5 changed files with 210 additions and 51 deletions

View file

@ -65,14 +65,16 @@ endef
UHTTPD_TLS:= UHTTPD_TLS:=
TLS_CFLAGS:= TLS_CFLAGS:=
TLS_LDFLAGS:=
ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_cyassl),) ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_cyassl),)
UHTTPD_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 endif
ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_openssl),) ifneq ($(CONFIG_PACKAGE_uhttpd-mod-tls_openssl),)
UHTTPD_TLS:=openssl UHTTPD_TLS:=openssl
TLS_CFLAGS:=-DTLS_IS_OPENSSL
endif endif

View file

@ -3,19 +3,17 @@ LUA_SUPPORT ?= 1
TLS_SUPPORT ?= 1 TLS_SUPPORT ?= 1
UHTTPD_TLS ?= cyassl UHTTPD_TLS ?= cyassl
CFLAGS ?= -I./lua-5.1.4/src -I$(TLS_INCLUDE_DIR) -O0 -ggdb3 CFLAGS ?= -I./lua-5.1.4/src $(TLS_CFLAGS) -O0 -ggdb3
LDFLAGS ?= -L./lua-5.1.4/src -L$(TLS_LIB_DIR) LDFLAGS ?= -L./lua-5.1.4/src $(TLS_LDFLAGS)
CFLAGS += -Wall --std=gnu99 CFLAGS += -Wall --std=gnu99
ifeq ($(UHTTPD_TLS),openssl) ifeq ($(UHTTPD_TLS),openssl)
TLS_LDFLAGS := -lssl TLS_LDFLAGS := -L./openssl-0.9.8m -lssl
TLS_INCLUDE_DIR := ./openssl-0.9.8m/include TLS_CFLAGS := -I./openssl-0.9.8m/include -DTLS_IS_OPENSSL
TLS_LIB_DIR := ./openssl-0.9.8m
else else
TLS_LDFLAGS := -lcyassl TLS_LDFLAGS := -L./cyassl-1.4.0/src/.libs -lcyassl
TLS_INCLUDE_DIR := ./cyassl-1.4.0/include TLS_CFLAGS := -I./cyassl-1.4.0/include -DTLS_IS_CYASSL
TLS_LIB_DIR := ./cyassl-1.4.0/src/.libs
endif endif
OBJ := uhttpd.o uhttpd-file.o uhttpd-utils.o OBJ := uhttpd.o uhttpd-file.o uhttpd-utils.o
@ -31,15 +29,26 @@ ifeq ($(HAVE_SHADOW),yes)
CFLAGS += -DHAVE_SHADOW CFLAGS += -DHAVE_SHADOW
endif endif
world: compile ifeq ($(TLS_SUPPORT),1)
CFLAGS += -DHAVE_TLS
endif
ifeq ($(CGI_SUPPORT),1) ifeq ($(CGI_SUPPORT),1)
OBJ += uhttpd-cgi.o
CFLAGS += -DHAVE_CGI CFLAGS += -DHAVE_CGI
endif endif
ifeq ($(LUA_SUPPORT),1) ifeq ($(LUA_SUPPORT),1)
CFLAGS += -DHAVE_LUA 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.so
$(LUALIB): uhttpd-lua.c $(LUALIB): uhttpd-lua.c
@ -49,12 +58,11 @@ ifeq ($(LUA_SUPPORT),1)
endif endif
ifeq ($(TLS_SUPPORT),1) ifeq ($(TLS_SUPPORT),1)
CFLAGS += -DHAVE_TLS
TLSLIB := uhttpd_tls.so TLSLIB := uhttpd_tls.so
$(TLSLIB): uhttpd-tls.c $(TLSLIB): uhttpd-tls.c
$(CC) $(CFLAGS) $(LDFLAGS) $(FPIC) \ $(CC) $(CFLAGS) $(LDFLAGS) $(FPIC) \
-shared $(TLS_LDFLAGS) \ -shared \
-o $(TLSLIB) uhttpd-tls.c -o $(TLSLIB) uhttpd-tls.c
endif endif

View file

@ -20,6 +20,143 @@
#include "uhttpd-tls.h" #include "uhttpd-tls.h"
#include "uhttpd-utils.h" #include "uhttpd-utils.h"
#include <syslog.h>
#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() SSL_CTX * uh_tls_ctx_init()
{ {
@ -28,8 +165,8 @@ SSL_CTX * uh_tls_ctx_init()
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
if( (c = SSL_CTX_new(TLSv1_server_method())) != NULL ) if ((c = SSL_CTX_new(TLSv1_server_method())) != NULL)
SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL); uh_tls_ctx_setup(c);
return c; return c;
} }
@ -69,8 +206,9 @@ int uh_tls_client_accept(struct client *c)
c->tls = SSL_new(c->server->tls); c->tls = SSL_new(c->server->tls);
if( c->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; goto cleanup;
if( (rv = SSL_accept(c->tls)) < 1 ) if( (rv = SSL_accept(c->tls)) < 1 )
goto cleanup; goto cleanup;
} }

View file

@ -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; fd_set writer;
struct timeval timeout; 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_sec = cl->server->conf->network_timeout;
timeout.tv_usec = 0; timeout.tv_usec = 0;
if( select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 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); return send(cl->socket, buf, len, 0);
}
return -1; 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) 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); int sz = uh_tcp_recv(cl, buf, len);
/* store received data in peek buffer */ /* store received data in peek buffer */
@ -162,50 +169,52 @@ int uh_tcp_peek(struct client *cl, char *buf, int len)
return sz; return sz;
} }
int uh_tcp_recv(struct client *cl, char *buf, int len) int uh_tcp_recv_lowlevel(struct client *cl, char *buf, int len)
{ {
int sz = 0;
int rsz = 0;
fd_set reader; fd_set reader;
struct timeval timeout; struct timeval timeout;
/* first serve data from peek buffer */
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 )
{
FD_ZERO(&reader); FD_ZERO(&reader);
FD_SET(cl->socket, &reader); FD_SET(cl->socket, &reader);
timeout.tv_sec = cl->server->conf->network_timeout; timeout.tv_sec = cl->server->conf->network_timeout;
timeout.tv_usec = 0; timeout.tv_usec = 0;
if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 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;
/* first serve data from peek buffer */
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)
{ {
#ifdef HAVE_TLS #ifdef HAVE_TLS
if( cl->tls ) if (cl->tls)
rsz = cl->server->conf->tls_recv(cl, (void *)&buf[sz], len); rsz = cl->server->conf->tls_recv(cl, (void *)&buf[sz], len);
else else
#endif #endif
rsz = recv(cl->socket, (void *)&buf[sz], len, 0); rsz = uh_tcp_recv_lowlevel(cl, (void *)&buf[sz], len);
if (rsz < 0)
return rsz;
if( (sz == 0) || (rsz > 0) )
sz += rsz; sz += rsz;
} }
else if( sz == 0 )
{
sz = -1;
}
}
return sz; return sz;
} }

View file

@ -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 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(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_peek(struct client *cl, char *buf, int len);
int uh_tcp_recv(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( int uh_http_sendhf(
struct client *cl, int code, const char *summary, struct client *cl, int code, const char *summary,