From 119244d9b13e84eb898f9fb97e83cac260c1a0b5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 4 Jan 2013 18:28:10 +0100 Subject: [PATCH] add basic tls support, todo: error handling --- CMakeLists.txt | 12 +++++-- client.c | 47 +++++++++++++++++--------- listen.c | 2 +- main.c | 27 +++++++++++++++ tls.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tls.h | 46 ++++++++++++++++++++++++++ uhttpd.h | 10 ++++-- 7 files changed, 227 insertions(+), 20 deletions(-) create mode 100644 tls.c create mode 100644 tls.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bc8b1d3..8d0c1b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,8 @@ PROJECT(uhttpd C) SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") ADD_DEFINITIONS(-Os -Wall -Werror -Wmissing-declarations --std=gnu99 -g3) +OPTION(TLS_SUPPORT "TLS support" ON) + IF(APPLE) INCLUDE_DIRECTORIES(/opt/local/include) LINK_DIRECTORIES(/opt/local/lib) @@ -14,5 +16,11 @@ IF(LIBS STREQUAL "LIBS-NOTFOUND") SET(LIBS "") ENDIF() -ADD_EXECUTABLE(uhttpd main.c listen.c client.c utils.c file.c auth.c cgi.c relay.c proc.c) -TARGET_LINK_LIBRARIES(uhttpd ubox ${LIBS}) +SET(SOURCES main.c listen.c client.c utils.c file.c auth.c cgi.c relay.c proc.c) +IF(TLS_SUPPORT) + SET(SOURCES ${SOURCES} tls.c) + ADD_DEFINITIONS(-DHAVE_TLS) +ENDIF() + +ADD_EXECUTABLE(uhttpd ${SOURCES}) +TARGET_LINK_LIBRARIES(uhttpd ubox dl ${LIBS}) diff --git a/client.c b/client.c index dd3f912..e2772dd 100644 --- a/client.c +++ b/client.c @@ -21,6 +21,7 @@ #include #include "uhttpd.h" +#include "tls.h" static LIST_HEAD(clients); @@ -356,7 +357,7 @@ static read_cb_t read_cbs[] = { [CLIENT_STATE_DATA] = client_data_cb, }; -static void client_read_cb(struct client *cl) +void uh_client_read_cb(struct client *cl) { struct ustream *us = cl->us; char *str; @@ -384,6 +385,8 @@ static void client_close(struct client *cl) n_clients--; uh_dispatch_done(cl); uloop_timeout_cancel(&cl->timeout); + if (cl->tls) + uh_tls_client_detach(cl); ustream_free(&cl->sfd.stream); close(cl->sfd.fd.fd); list_del(&cl->list); @@ -393,11 +396,26 @@ static void client_close(struct client *cl) uh_unblock_listeners(); } +void uh_client_notify_state(struct client *cl) +{ + struct ustream *s = cl->us; + + if (!s->write_error) { + if (cl->state == CLIENT_STATE_DATA) + return; + + if (!s->eof || s->w.data_bytes) + return; + } + + return client_close(cl); +} + static void client_ustream_read_cb(struct ustream *s, int bytes) { struct client *cl = container_of(s, struct client, sfd); - client_read_cb(cl); + uh_client_read_cb(cl); } static void client_ustream_write_cb(struct ustream *s, int bytes) @@ -412,15 +430,7 @@ static void client_notify_state(struct ustream *s) { struct client *cl = container_of(s, struct client, sfd); - if (!s->write_error) { - if (cl->state == CLIENT_STATE_DATA) - return; - - if (!s->eof || s->w.data_bytes) - return; - } - - return client_close(cl); + uh_client_notify_state(cl); } static void set_addr(struct uh_addr *addr, void *src) @@ -438,7 +448,7 @@ static void set_addr(struct uh_addr *addr, void *src) } } -bool uh_accept_client(int fd) +bool uh_accept_client(int fd, bool tls) { static struct client *next_client; struct client *cl; @@ -461,11 +471,17 @@ bool uh_accept_client(int fd) sl = sizeof(addr); getsockname(fd, (struct sockaddr *) &addr, &sl); set_addr(&cl->srv_addr, &addr); + cl->us = &cl->sfd.stream; + if (tls) { + uh_tls_client_attach(cl); + } else { + cl->us->notify_read = client_ustream_read_cb; + cl->us->notify_write = client_ustream_write_cb; + cl->us->notify_state = client_notify_state; + } + cl->us->string_data = true; - cl->us->notify_read = client_ustream_read_cb; - cl->us->notify_write = client_ustream_write_cb; - cl->us->notify_state = client_notify_state; ustream_fd_init(&cl->sfd, sfd); cl->timeout.cb = client_timeout; @@ -476,6 +492,7 @@ bool uh_accept_client(int fd) next_client = NULL; n_clients++; cl->id = client_id++; + return true; } diff --git a/listen.c b/listen.c index f11768a..251a60e 100644 --- a/listen.c +++ b/listen.c @@ -79,7 +79,7 @@ static void listener_cb(struct uloop_fd *fd, unsigned int events) struct listener *l = container_of(fd, struct listener, fd); while (1) { - if (!uh_accept_client(fd->fd)) + if (!uh_accept_client(fd->fd, l->tls)) break; } diff --git a/main.c b/main.c index ce5123b..ab6fbb0 100644 --- a/main.c +++ b/main.c @@ -32,6 +32,7 @@ #include #include "uhttpd.h" +#include "tls.h" char uh_buf[4096]; @@ -190,11 +191,13 @@ static void fixup_prefix(char *str) int main(int argc, char **argv) { + const char *tls_key, *tls_crt; bool nofork = false; char *port; int opt, ch; int cur_fd; int bound = 0; + int n_tls = 0; BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX); @@ -207,6 +210,7 @@ int main(int argc, char **argv) switch(ch) { case 's': + n_tls++; tls = true; /* fall through */ case 'p': @@ -324,6 +328,13 @@ int main(int argc, char **argv) conf.file = optarg; break; + case 'C': + tls_crt = optarg; + break; + + case 'K': + tls_key = optarg; + break; default: return usage(argv[0]); } @@ -336,6 +347,22 @@ int main(int argc, char **argv) return 1; } + if (n_tls) { + if (!tls_crt || !tls_key) { + fprintf(stderr, "Please specify a certificate and " + "a key file to enable SSL support\n"); + return 1; + } + +#ifdef HAVE_TLS + if (uh_tls_init(tls_key, tls_crt)) + return 1; +#else + fprintf(stderr, "Error: TLS support not compiled in.\n"); + return 1; +#endif + } + /* fork (if not disabled) */ if (!nofork) { switch (fork()) { diff --git a/tls.c b/tls.c new file mode 100644 index 0000000..02886f2 --- /dev/null +++ b/tls.c @@ -0,0 +1,103 @@ +/* + * uhttpd - Tiny single-threaded httpd - Main header + * + * Copyright (C) 2010-2012 Jo-Philipp Wich + * Copyright (C) 2012 Felix Fietkau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "uhttpd.h" +#include "tls.h" + +#ifdef __APPLE__ +#define LIB_EXT "dylib" +#else +#define LIB_EXT "so" +#endif + +static struct ustream_ssl_ops *ops; +static void *dlh; +static void *ctx; + +int uh_tls_init(const char *key, const char *crt) +{ + static bool _init = false; + + if (_init) + return 0; + + _init = true; + dlh = dlopen("libustream-ssl." LIB_EXT, RTLD_LAZY | RTLD_LOCAL); + if (!dlh) { + fprintf(stderr, "Failed to load ustream-ssl library: %s\n", dlerror()); + return -ENOENT; + } + + ops = dlsym(dlh, "ustream_ssl_ops"); + if (!ops) { + fprintf(stderr, "Could not find required symbol 'ustream_ssl_ops' in ustream-ssl library\n"); + return -ENOENT; + } + + ctx = ops->context_new(true); + if (!ctx) { + fprintf(stderr, "Failed to initialize ustream-ssl\n"); + return -EINVAL; + } + + if (ops->context_set_crt_file(ctx, crt) || + ops->context_set_key_file(ctx, key)) { + fprintf(stderr, "Failed to load certificate/key files\n"); + return -EINVAL; + } + + return 0; +} + +static void tls_ustream_read_cb(struct ustream *s, int bytes) +{ + struct client *cl = container_of(s, struct client, ssl); + + uh_client_read_cb(cl); +} + +static void tls_ustream_write_cb(struct ustream *s, int bytes) +{ + struct client *cl = container_of(s, struct client, ssl); + + if (cl->dispatch.write_cb) + cl->dispatch.write_cb(cl); +} + +static void tls_notify_state(struct ustream *s) +{ + struct client *cl = container_of(s, struct client, ssl); + + uh_client_notify_state(cl); +} + +void uh_tls_client_attach(struct client *cl) +{ + cl->us = &cl->ssl.stream; + ops->init(&cl->ssl, &cl->sfd.stream, ctx, true); + cl->us->notify_read = tls_ustream_read_cb; + cl->us->notify_write = tls_ustream_write_cb; + cl->us->notify_state = tls_notify_state; +} + +void uh_tls_client_detach(struct client *cl) +{ + ustream_free(&cl->ssl.stream); +} diff --git a/tls.h b/tls.h new file mode 100644 index 0000000..276f306 --- /dev/null +++ b/tls.h @@ -0,0 +1,46 @@ +/* + * uhttpd - Tiny single-threaded httpd - Main header + * + * Copyright (C) 2010-2012 Jo-Philipp Wich + * Copyright (C) 2012 Felix Fietkau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UHTTPD_TLS_H +#define __UHTTPD_TLS_H + +#ifdef HAVE_TLS + +int uh_tls_init(const char *key, const char *crt); +void uh_tls_client_attach(struct client *cl); +void uh_tls_client_detach(struct client *cl); + +#else + +static inline int uh_tls_init(const char *key, const char *crt) +{ + return -1; +} + +static inline void uh_tls_client_attach(struct client *cl) +{ +} + +static inline void uh_tls_client_detach(struct client *cl) +{ +} + +#endif + +#endif diff --git a/uhttpd.h b/uhttpd.h index d67e48c..9ed6439 100644 --- a/uhttpd.h +++ b/uhttpd.h @@ -29,6 +29,9 @@ #include #include #include +#ifdef HAVE_TLS +#include +#endif #include "utils.h" @@ -170,11 +173,12 @@ struct client { struct ustream *us; struct ustream_fd sfd; #ifdef HAVE_TLS - struct ustream_ssl stream_ssl; + struct ustream_ssl ssl; #endif struct uloop_timeout timeout; enum client_state state; + bool tls; struct http_request request; struct uh_addr srv_addr, peer_addr; @@ -192,7 +196,7 @@ extern struct dispatch_handler cgi_dispatch; void uh_index_add(const char *filename); -bool uh_accept_client(int fd); +bool uh_accept_client(int fd, bool tls); void uh_unblock_listeners(void); void uh_setup_listeners(void); @@ -214,6 +218,8 @@ uh_client_error(struct client *cl, int code, const char *summary, const char *fm void uh_handle_request(struct client *cl); void client_poll_post_data(struct client *cl); +void uh_client_read_cb(struct client *cl); +void uh_client_notify_state(struct client *cl); void uh_auth_add(const char *path, const char *user, const char *pass); bool uh_auth_check(struct client *cl, struct path_info *pi); -- 2.11.0