add basic tls support, todo: error handling
authorFelix Fietkau <nbd@openwrt.org>
Fri, 4 Jan 2013 17:28:10 +0000 (18:28 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Fri, 4 Jan 2013 17:29:03 +0000 (18:29 +0100)
CMakeLists.txt
client.c
listen.c
main.c
tls.c [new file with mode: 0644]
tls.h [new file with mode: 0644]
uhttpd.h

index bc8b1d31ef56895afd35062eea4a133ca44f89a1..8d0c1b093462ff550b22f4ae776b3c2390a6753f 100644 (file)
@@ -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})
index dd3f912247a500a58b3f9de0810ce25262444d93..e2772dd45f490d5002ae83a543b065692b7e9558 100644 (file)
--- a/client.c
+++ b/client.c
@@ -21,6 +21,7 @@
 #include <ctype.h>
 
 #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;
 }
 
index f11768ae1bfbba2ec2e72536fc817121228053b9..251a60e07eef53844056f075f33c5511368bc0e3 100644 (file)
--- 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 ce5123b2b94884422c2691e26a8db9a59da52945..ab6fbb061d8788b27f88f3c67d01f8864b435d5f 100644 (file)
--- a/main.c
+++ b/main.c
@@ -32,6 +32,7 @@
 #include <libubox/usock.h>
 
 #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 (file)
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 <xm@subsignal.org>
+ *   Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
+ *
+ *  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 <dlfcn.h>
+#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 (file)
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 <xm@subsignal.org>
+ *   Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
+ *
+ *  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
index d67e48c14712fd9fcb2dcf59e466d264ef312e1b..9ed6439aa17aa8664af341f38402157571e097dc 100644 (file)
--- a/uhttpd.h
+++ b/uhttpd.h
@@ -29,6 +29,9 @@
 #include <libubox/ustream.h>
 #include <libubox/blob.h>
 #include <libubox/utils.h>
+#ifdef HAVE_TLS
+#include <libubox/ustream-ssl.h>
+#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);