add support for enforcing HTTPS
authorJo-Philipp Wich <jow@openwrt.org>
Sat, 30 May 2015 16:25:39 +0000 (18:25 +0200)
committerJo-Philipp Wich <jow@openwrt.org>
Sat, 30 May 2015 16:43:33 +0000 (18:43 +0200)
Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
client.c
listen.c
main.c
uhttpd.h

index 85d4625ed694802147008a1e3c74e5ce095298eb..3dbeb6d24c3cff95dd5a0732ab58598b76e4fb37 100644 (file)
--- a/client.c
+++ b/client.c
@@ -230,6 +230,52 @@ static bool rfc1918_filter_check(struct client *cl)
        return false;
 }
 
+static bool tls_redirect_check(struct client *cl)
+{
+       int rem, port;
+       struct blob_attr *cur;
+       char *ptr, *url = NULL, *host = NULL;
+
+       if (cl->tls || !conf.tls_redirect)
+               return true;
+
+       if ((port = uh_first_tls_port(cl->srv_addr.family)) == -1)
+               return true;
+
+       blob_for_each_attr(cur, cl->hdr.head, rem) {
+               if (!strcmp(blobmsg_name(cur), "host"))
+                       host = blobmsg_get_string(cur);
+
+               if (!strcmp(blobmsg_name(cur), "URL"))
+                       url = blobmsg_get_string(cur);
+
+               if (url && host)
+                       break;
+       }
+
+       if (!url || !host)
+               return true;
+
+       if ((ptr = strchr(host, ']')) != NULL)
+               *(ptr+1) = 0;
+       else if ((ptr = strchr(host, ':')) != NULL)
+               *ptr = 0;
+
+       cl->request.respond_chunked = false;
+       cl->request.connection_close = true;
+
+       uh_http_header(cl, 302, "Found");
+
+       if (port != 443)
+               ustream_printf(cl->us, "Location: https://%s:%d%s\r\n\r\n", host, port, url);
+       else
+               ustream_printf(cl->us, "Location: https://%s%s\r\n\r\n", host, url);
+
+       uh_request_done(cl);
+
+       return false;
+}
+
 static void client_header_complete(struct client *cl)
 {
        struct http_request *r = &cl->request;
@@ -237,6 +283,9 @@ static void client_header_complete(struct client *cl)
        if (!rfc1918_filter_check(cl))
                return;
 
+       if (!tls_redirect_check(cl))
+               return;
+
        if (r->expect_cont)
                ustream_printf(cl->us, "HTTP/1.1 100 Continue\r\n\r\n");
 
index adf8b16243dde1d0568e8a5bd7ef6dc1383763c3..92ca680aa4d2b947b51c1f0be9c80b75df678091 100644 (file)
--- a/listen.c
+++ b/listen.c
@@ -187,6 +187,7 @@ int uh_socket_bind(const char *host, const char *port, bool tls)
 
                l->fd.fd = sock;
                l->tls = tls;
+               l->addr = *(struct sockaddr_in6 *)p->ai_addr;
                list_add_tail(&l->list, &listeners);
                bound++;
 
@@ -201,3 +202,21 @@ error:
 
        return bound;
 }
+
+int uh_first_tls_port(int family)
+{
+       struct listener *l;
+       int tls_port = -1;
+
+       list_for_each_entry(l, &listeners, list) {
+               if (!l->tls || l->addr.sin6_family != family)
+                       continue;
+
+               if (tls_port != -1 && ntohs(l->addr.sin6_port) != 443)
+                       continue;
+
+               tls_port = ntohs(l->addr.sin6_port);
+       }
+
+       return tls_port;
+}
diff --git a/main.c b/main.c
index fba5f804e0a1c2684d9d7c1a89fd55077119be40..ed47486840347e82bf4c956f7319652a2b5d256f 100644 (file)
--- a/main.c
+++ b/main.c
@@ -134,6 +134,7 @@ static int usage(const char *name)
                "       -s [addr:]port  Like -p but provide HTTPS on this port\n"
                "       -C file         ASN.1 server certificate file\n"
                "       -K file         ASN.1 server private key file\n"
+               "       -q              Redirect all HTTP requests to HTTPS\n"
 #endif
                "       -h directory    Specify the document root, default is '.'\n"
                "       -E string       Use given virtual URL as 404 error handler\n"
@@ -227,7 +228,7 @@ int main(int argc, char **argv)
        init_defaults_pre();
        signal(SIGPIPE, SIG_IGN);
 
-       while ((ch = getopt(argc, argv, "afSDRXC:K:E:I:p:s:h:c:l:L:d:r:m:n:N:x:i:t:k:T:A:u:U:")) != -1) {
+       while ((ch = getopt(argc, argv, "afqSDRXC:K:E:I:p:s:h:c:l:L:d:r:m:n:N:x:i:t:k:T:A:u:U:")) != -1) {
                switch(ch) {
 #ifdef HAVE_TLS
                case 'C':
@@ -238,12 +239,17 @@ int main(int argc, char **argv)
                        tls_key = optarg;
                        break;
 
+               case 'q':
+                       conf.tls_redirect = 1;
+                       break;
+
                case 's':
                        n_tls++;
                        /* fall through */
 #else
                case 'C':
                case 'K':
+               case 'q':
                case 's':
                        fprintf(stderr, "uhttpd: TLS support not compiled, "
                                        "ignoring -%c\n", ch);
index 42385a6557af4f9a3fcf08ff03d29a99e188be32..fbcb1ed7a0c968087f39e1eca4e65305752c7b8e 100644 (file)
--- a/uhttpd.h
+++ b/uhttpd.h
@@ -62,6 +62,7 @@ struct config {
        int no_dirlists;
        int network_timeout;
        int rfc1918_filter;
+       int tls_redirect;
        int tcp_keepalive;
        int max_script_requests;
        int max_connections;
@@ -267,6 +268,8 @@ void uh_unblock_listeners(void);
 void uh_setup_listeners(void);
 int uh_socket_bind(const char *host, const char *port, bool tls);
 
+int uh_first_tls_port(int family);
+
 bool uh_use_chunked(struct client *cl);
 void uh_chunk_write(struct client *cl, const void *data, int len);
 void uh_chunk_vprintf(struct client *cl, const char *format, va_list arg);