The following diff makes httpd stricter with respect to TLS configuration:

- Do not allow TLS and non-TLS to be configured on the same port.
- Do not allow TLS options to be specified without a TLS listener.
- Ensure that TLS options are the same when a server is specified on the
  same address/port.

Currently, these configurations are permitted but do not work as intended.

This also factors out (and reuses) the server matching code that was already
duplicated.

ok?

Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.78
diff -u -p -r1.78 parse.y
--- parse.y     21 Jun 2016 21:35:24 -0000      1.78
+++ parse.y     12 Aug 2016 16:48:28 -0000
@@ -107,8 +107,10 @@ int                 host_if(const char *, struct addre
 int             host(const char *, struct addresslist *,
                    int, struct portrange *, const char *, int);
 void            host_free(struct addresslist *);
+struct server  *server_match(struct server *, int);
 struct server  *server_inherit(struct server *, struct server_config *,
                    struct server_config *);
+int             server_tls_cmp(struct server *, struct server *);
 int             getservice(char *);
 int             is_if_in_group(const char *, const char *);
 
@@ -283,24 +285,13 @@ server            : SERVER optmatch STRING        {
 
                        TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry);
                } '{' optnl serveropts_l '}'    {
-                       struct server           *s = NULL, *sn;
+                       struct server           *s, *sn;
                        struct server_config    *a, *b;
 
                        srv_conf = &srv->srv_conf;
 
-                       TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
-                               if ((s->srv_conf.flags &
-                                   SRVFLAG_LOCATION) == 0 &&
-                                   strcmp(s->srv_conf.name,
-                                   srv->srv_conf.name) == 0 &&
-                                   s->srv_conf.port == srv->srv_conf.port &&
-                                   sockaddr_cmp(
-                                   (struct sockaddr *)&s->srv_conf.ss,
-                                   (struct sockaddr *)&srv->srv_conf.ss,
-                                   s->srv_conf.prefixlen) == 0)
-                                       break;
-                       }
-                       if (s != NULL) {
+                       /* Check if the new server already exists. */
+                       if (server_match(srv, 1) != NULL) {
                                yyerror("server \"%s\" defined twice",
                                    srv->srv_conf.name);
                                serverconfig_free(srv_conf);
@@ -315,16 +306,39 @@ server            : SERVER optmatch STRING        {
                                YYERROR;
                        }
 
+                       if ((s = server_match(srv, 0)) != NULL) {
+                               if ((s->srv_conf.flags & SRVFLAG_TLS) != 
+                                   (srv->srv_conf.flags & SRVFLAG_TLS)) {
+                                       yyerror("server \"%s\": TLS and "
+                                           "non-TLS on same address/port",
+                                           srv->srv_conf.name);
+                                       serverconfig_free(srv_conf);
+                                       free(srv);
+                                       YYERROR;
+                               }
+                               if (server_tls_cmp(s, srv) != 0) {
+                                       yyerror("server \"%s\": TLS "
+                                           "configuration mismatch on same "
+                                           "address/port",
+                                           srv->srv_conf.name);
+                                       serverconfig_free(srv_conf);
+                                       free(srv);
+                                       YYERROR;
+                               }
+                       }
+
                        if ((srv->srv_conf.flags & SRVFLAG_TLS) &&
                            srv->srv_conf.tls_protocols == 0) {
-                               yyerror("no TLS protocols");
+                               yyerror("server \"%s\": no TLS protocols",
+                                   srv->srv_conf.name);
+                               serverconfig_free(srv_conf);
                                free(srv);
                                YYERROR;
                        }
 
                        if (server_tls_load_keypair(srv) == -1) {
-                               yyerror("failed to load public/private keys "
-                                   "for server %s", srv->srv_conf.name);
+                               yyerror("server \"%s\": failed to load "
+                                   "public/private keys", srv->srv_conf.name);
                                serverconfig_free(srv_conf);
                                free(srv);
                                YYERROR;
@@ -398,7 +412,7 @@ serveroptsl : LISTEN ON STRING opttls po
                                    sizeof(*alias))) == NULL)
                                        fatal("out of memory");
 
-                               /* Add as an alias */
+                               /* Add as an IP-based alias. */
                                s_conf = alias;
                        } else
                                s_conf = &srv->srv_conf;
@@ -416,9 +430,8 @@ serveroptsl : LISTEN ON STRING opttls po
                        s_conf->prefixlen = h->prefixlen;
                        host_free(&al);
 
-                       if ($4) {
+                       if ($4)
                                s_conf->flags |= SRVFLAG_TLS;
-                       }
 
                        if (alias != NULL) {
                                /* IP-based; use name match flags from parent */
@@ -468,10 +481,22 @@ serveroptsl       : LISTEN ON STRING opttls po
                        }
                }
                | tls                   {
+                       struct server_config    *sc;
+                       int                      tls_flag = 0;
+
                        if (parentsrv != NULL) {
                                yyerror("tls configuration inside location");
                                YYERROR;
                        }
+
+                       /* Ensure that at least one server has TLS enabled. */
+                       TAILQ_FOREACH(sc, &srv->srv_hosts, entry) {
+                               tls_flag |= (sc->flags & SRVFLAG_TLS);
+                       }
+                       if (tls_flag == 0) {
+                               yyerror("tls options without tls listener");
+                               YYERROR;
+                       }
                }
                | root
                | directory
@@ -1968,6 +1993,33 @@ host_free(struct addresslist *al)
 }
 
 struct server *
+server_match(struct server *s2, int match_name)
+{
+       struct server   *s1;
+
+       /* Attempt to find matching server. */
+       TAILQ_FOREACH(s1, conf->sc_servers, srv_entry) {
+               if ((s1->srv_conf.flags & SRVFLAG_LOCATION) != 0)
+                       continue;
+               if (match_name) {
+                       if (strcmp(s1->srv_conf.name, s2->srv_conf.name) != 0)
+                               continue;
+               }
+               if (s1->srv_conf.port != s2->srv_conf.port)
+                       continue;
+               if (sockaddr_cmp(
+                   (struct sockaddr *)&s1->srv_conf.ss,
+                   (struct sockaddr *)&s2->srv_conf.ss,
+                   s1->srv_conf.prefixlen) != 0)
+                       continue;
+
+               return (s1);
+       }
+
+       return (NULL);
+}
+
+struct server *
 server_inherit(struct server *src, struct server_config *alias,
     struct server_config *addr)
 {
@@ -2028,19 +2080,7 @@ server_inherit(struct server *src, struc
        }
 
        /* Check if the new server already exists */
-       TAILQ_FOREACH(s, conf->sc_servers, srv_entry) {
-               if ((s->srv_conf.flags &
-                   SRVFLAG_LOCATION) == 0 &&
-                   strcmp(s->srv_conf.name,
-                   dst->srv_conf.name) == 0 &&
-                   s->srv_conf.port == dst->srv_conf.port &&
-                   sockaddr_cmp(
-                   (struct sockaddr *)&s->srv_conf.ss,
-                   (struct sockaddr *)&dst->srv_conf.ss,
-                   s->srv_conf.prefixlen) == 0)
-                       break;
-       }
-       if (s != NULL) {
+       if (server_match(dst, 1) != NULL) {
                yyerror("server \"%s\" defined twice",
                    dst->srv_conf.name);
                serverconfig_free(&dst->srv_conf);
@@ -2078,6 +2118,30 @@ server_inherit(struct server *src, struc
        }
 
        return (dst);
+}
+
+int
+server_tls_cmp(struct server *s1, struct server *s2)
+{
+       struct server_config    *sc1, *sc2;
+
+       sc1 = &s1->srv_conf;
+       sc2 = &s2->srv_conf;
+
+       if (sc1->tls_protocols != sc2->tls_protocols)
+               return (-1);
+       if (strcmp(sc1->tls_cert_file, sc2->tls_cert_file) != 0)
+               return (-1);
+       if (strcmp(sc1->tls_key_file, sc2->tls_key_file) != 0)
+               return (-1);
+       if (strcmp(sc1->tls_ciphers, sc2->tls_ciphers) != 0)
+               return (-1);
+       if (strcmp(sc1->tls_dhe_params, sc2->tls_dhe_params) != 0)
+               return (-1);
+       if (strcmp(sc1->tls_ecdhe_curve, sc2->tls_ecdhe_curve) != 0)
+               return (-1);
+
+       return (0);
 }
 
 int

Reply via email to