>Emeric responded here:
>http://marc.info/?l=haproxy&m=143643724320705&w=2
>
>Not sure what you mean by pushing this to master...?
>
>
>
>Lukas
>

I¹ve corrected the indentation to use tabs instead of spaces. Here¹s the
new diff:

Is there a better method to do code reviews other than using the mailing
list? I¹ll use whatever is easiest for you guys.

Again, sorry for the delayed response. I¹ll track the website from now on,
-Dave

diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h
index e71ba79..dcf00fc 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -29,6 +29,9 @@ struct sni_ctx {
        SSL_CTX *ctx;             /* context associated to the certificate */
        int order;                /* load order for the certificate */
        int neg;                  /* reject if match */
+       short has_rsa_cert;       /* 1 if CTX contains an RSA certificate */
+       short has_dsa_cert;       /* 1 if CTX contains an DSA certificate */
+       short has_ecc_cert;       /* 1 if CTX contains an ECC certificate */
        struct ebmb_node name;    /* node holding the servername value */
 };
 
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 4e75549..ccf3251 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -1505,7 +1505,23 @@ end:
 }
 #endif
 
-static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char
*name, int order)
+static void ssl_sock_update_sni_ctx_keytype(struct sni_ctx *sc, int
cert_keytype)
+{
+       switch(cert_keytype){
+       case EVP_PKEY_RSA:
+               sc->has_rsa_cert = 1;
+               break;
+       case EVP_PKEY_DSA:
+               sc->has_dsa_cert = 1;
+               break;
+       case EVP_PKEY_EC:
+               sc->has_ecc_cert = 1;
+               break;
+       }
+
+}
+
+static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char
*name, int order, int ctx_cert_keytype)
 {
        struct sni_ctx *sc;
        int wild = 0, neg = 0;
@@ -1531,6 +1547,12 @@ static int ssl_sock_add_cert_sni(SSL_CTX *ctx,
struct bind_conf *s, char *name,
                sc->ctx = ctx;
                sc->order = order++;
                sc->neg = neg;
+               sc->has_rsa_cert = 0;
+               sc->has_dsa_cert = 0;
+               sc->has_ecc_cert = 0;
+
+               ssl_sock_update_sni_ctx_keytype(sc,ctx_cert_keytype);
+
                if (wild)
                        ebst_insert(&s->sni_w_ctx, &sc->name);
                else
@@ -1539,21 +1561,17 @@ static int ssl_sock_add_cert_sni(SSL_CTX *ctx,
struct bind_conf *s, char *name,
        return order;
 }
 
+#if OPENSSL_VERSION_NUMBER < 0x1000200fL
+
 /* Loads a certificate key and CA chain from a file. Returns 0 on error,
-1 if
  * an early error happens and the caller must call SSL_CTX_free() by
itelf.
  */
-static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file,
struct bind_conf *s, char **sni_filter, int fcount)
+static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file,
struct bind_conf *s)
 {
        BIO *in;
        X509 *x = NULL, *ca;
-       int i, err;
+       int err;
        int ret = -1;
-       int order = 0;
-       X509_NAME *xname;
-       char *str;
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-       STACK_OF(GENERAL_NAME) *names;
-#endif
 
        in = BIO_new(BIO_s_file());
        if (in == NULL)
@@ -1566,37 +1584,6 @@ static int ssl_sock_load_cert_chain_file(SSL_CTX
*ctx, const char *file, struct
        if (x == NULL)
                goto end;
 
-       if (fcount) {
-               while (fcount--)
-                       order = ssl_sock_add_cert_sni(ctx, s, 
sni_filter[fcount], order);
-       }
-       else {
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-               names = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
-               if (names) {
-                       for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
-                               GENERAL_NAME *name = 
sk_GENERAL_NAME_value(names, i);
-                               if (name->type == GEN_DNS) {
-                                       if (ASN1_STRING_to_UTF8((unsigned char 
**)&str, name->d.dNSName) >=
0) {
-                                               order = 
ssl_sock_add_cert_sni(ctx, s, str, order);
-                                               OPENSSL_free(str);
-                                       }
-                               }
-                       }
-                       sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
-               }
-#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
-               xname = X509_get_subject_name(x);
-               i = -1;
-               while ((i = X509_NAME_get_index_by_NID(xname, NID_commonName, 
i)) !=
-1) {
-                       X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, i);
-                       if (ASN1_STRING_to_UTF8((unsigned char **)&str, 
entry->value) >= 0) {
-                               order = ssl_sock_add_cert_sni(ctx, s, str, 
order);
-                               OPENSSL_free(str);
-                       }
-               }
-       }
-
        ret = 0; /* the caller must not free the SSL_CTX argument anymore */
        if (!SSL_CTX_use_certificate(ctx, x))
                goto end;
@@ -1630,31 +1617,64 @@ end:
        return ret;
 }
 
-static int ssl_sock_load_cert_file(const char *path, struct bind_conf
*bind_conf, struct proxy *curproxy, char **sni_filter, int fcount, char
**err)
-{
-       int ret;
-       SSL_CTX *ctx;
+#endif /* OPENSSL_VERSION_NUMBER < 0x1000200fL */
 
-       ctx = SSL_CTX_new(SSLv23_server_method());
-       if (!ctx) {
-               memprintf(err, "%sunable to allocate SSL context for cert 
'%s'.\n",
-                         err && *err ? *err : "", path);
-               return 1;
+#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
+static struct ebmb_node* ssl_sock_get_ctx_from_eb_tree(struct bind_conf*
bind_conf, const char* name){
+
+       struct ebmb_node *node = NULL;
+       int i;
+
+       for (i = 0; i < trash.size; i++) {
+               if (!name[i])
+                       break;
+               trash.str[i] = tolower(name[i]);
        }
+       trash.str[i] = 0;
+
+       node = ebst_lookup(&bind_conf->sni_ctx, trash.str);
+       if(node)
+               return node;
+
+       node = ebst_lookup(&bind_conf->sni_w_ctx, trash.str);
+
+       return node;
+}
+#endif /* OPENSSL_VERSION_NUMBER >= 0x1000200fL */
+
+static int ssl_sock_node_contains_keytype(struct ebmb_node *node, int
keytype){
+       switch(keytype){
+       case EVP_PKEY_RSA:
+               return container_of(node, struct sni_ctx, name)->has_rsa_cert;
+       case EVP_PKEY_DSA:
+               return container_of(node, struct sni_ctx, name)->has_dsa_cert;
+       case EVP_PKEY_EC:
+               return container_of(node, struct sni_ctx, name)->has_ecc_cert;
+       }
+
+       return 0;
+
+}
+
+static int ssl_sock_cert_file_into_ctx(const char *path, struct bind_conf
*bind_conf, SSL_CTX* ctx, char **err)
+{
+       int ret = 0;
 
        if (SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) <= 0) {
                memprintf(err, "%sunable to load SSL private key from PEM file 
'%s'.\n",
                          err && *err ? *err : "", path);
-               SSL_CTX_free(ctx);
                return 1;
        }
 
-       ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf, sni_filter,
fcount);
+#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
+       ret = SSL_CTX_use_certificate_chain_file(ctx,path);
+#else
+       ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf);
+#endif
+
        if (ret <= 0) {
                memprintf(err, "%sunable to load SSL certificate from PEM file 
'%s'.\n",
                          err && *err ? *err : "", path);
-               if (ret < 0) /* serious error, must do that ourselves */
-                       SSL_CTX_free(ctx);
                return 1;
        }
 
@@ -1664,9 +1684,6 @@ static int ssl_sock_load_cert_file(const char *path,
struct bind_conf *bind_conf
                return 1;
        }
 
-       /* we must not free the SSL_CTX anymore below, since it's already in
-        * the tree, so it will be discovered and cleaned in time.
-        */
 #ifndef OPENSSL_NO_DH
        /* store a NULL pointer to indicate we have not yet loaded
           a custom DH param file */
@@ -1705,17 +1722,206 @@ static int ssl_sock_load_cert_file(const char
*path, struct bind_conf *bind_conf
        }
 #endif
 
+       return 0;
+}
+
+/* Just wraps up the error line*/
+static SSL_CTX* ssl_sock_new_ssl_ctx(char** err){
+       SSL_CTX* ctx = NULL;
+       ctx = SSL_CTX_new(SSLv23_server_method());
+       if (!ctx) {
+               memprintf(err, "%sunable to allocate SSL context\n",
+                         err && *err ? *err : "");
+               return NULL;
+       }
+
+       return ctx;
+}
+
+/* Wraps the processing of an SSL CTX given a SNI entry
+ * Return Values:
+ * 0 = No Error
+ * 1 = Error in OpenSSL Calls
+ *
+ * Note that the default_ctx is passed in as a **,
+ * this function will allocate a new SSL_CTX if the
+ * SNI entry is not found in the tree and the *default_ctx is NULL.
+ * It will then update *default_ctx
+ * */
+static int ssl_sock_process_ctx_and_sni(const char *path, struct
bind_conf *bind_conf, X509* path_cert, SSL_CTX** default_ctx, char
*sni_name, char **err)
+{
+       struct ebmb_node *node = NULL;
+       int key_type = 0;
+       SSL_CTX* ctx = NULL;
+       int order = 0;
+
+       /* Without 1.0.2, node will always be NULL, which is fine */
+#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT
&& !defined OPENSSL_IS_BORINGSSL)
+       node  = ssl_sock_get_ctx_from_eb_tree(bind_conf, sni_name);
+# endif
+
+       if(node == NULL && *default_ctx == NULL)
+       {
+               /* No previous SNI in tree and no new ctx for this file yet */
+               ctx = ssl_sock_new_ssl_ctx(err);
+               if (ctx == NULL)
+                       return 1;
+
+               if (ssl_sock_cert_file_into_ctx(path,bind_conf,ctx,err) != 0) {
+                       memprintf(err, "%sUnable to load cert file into SSL 
Context '%s'.\n",
+                                 err && *err ? *err : "", path);
+                       SSL_CTX_free(ctx);
+                       return 1;
+               }
+
+               /* Ctx is now valid */
+               *default_ctx = ctx;
+
+       }
+       else if(node == NULL)
+               ctx = *default_ctx;
+       else
+               ctx = container_of(node, struct sni_ctx, name)->ctx;
+
+       key_type = EVP_PKEY_id(X509_get_pubkey(path_cert));
+       /* Check node to see if node has a cert of this keytype already */
+       if(node && ssl_sock_node_contains_keytype(node,key_type))
+               return 0;
+
+       /* we pulled a ctx from the SNI tree, load cert file into it */
+       if(ctx != *default_ctx)
+       {
+               if (ssl_sock_cert_file_into_ctx(path,bind_conf,ctx,err) != 0) {
+                       memprintf(err, "%sUnable to load cert file into SSL 
Context '%s'.\n",
+                                 err && *err ? *err : "", path);
+                       return 1;
+               }
+
+               /* Update node's keytypes */
+               ssl_sock_update_sni_ctx_keytype(container_of(node, struct 
sni_ctx,
name), key_type);
+       }
+       else
+       {
+               /* This is a new SNI, add it to the tree */
+               order = 0;
+               order = ssl_sock_add_cert_sni(ctx, bind_conf, sni_name, order,
key_type);
+       }
+
+       return 0;
+}
+
+static int ssl_sock_load_cert_file(const char *path, struct bind_conf
*bind_conf, struct proxy *curproxy, char **sni_filter, int fcount, char
**err)
+{
+       int ret = 1;
+       SSL_CTX *new_ctx = NULL;
+       FILE* file = NULL;
+       X509* cert = NULL;
+       X509_NAME *xname = NULL;
+       char *str=NULL;
+       int i;
+       int order = 0;
+       int key_type;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+       STACK_OF(GENERAL_NAME) *names = NULL;
+#endif
+       struct ebmb_node *n = NULL;
+
+       file = fopen(path,"r");
+       PEM_read_X509(file,&cert,NULL,NULL);
+       fclose(file);
+
+       if(!cert){
+               memprintf(err, "%sunable to read first certificate in '%s'.\n",
+                         err && *err ? *err : "", path);
+               goto end;
+       }
+
+       key_type = EVP_PKEY_id(X509_get_pubkey(cert));
+
+       /* fcount indicates that the user provided an SNI for the list of certs,
just use that one instead of CN/SAN*/
+       if (fcount) {
+               new_ctx = ssl_sock_new_ssl_ctx(err);
+               if(!new_ctx)
+                       goto end;
+
+               ret = ssl_sock_cert_file_into_ctx(path,bind_conf,new_ctx,err);
+
+               if (ret != 0) {
+                       memprintf(err, "%sUnable to load cert file into SSL 
Context '%s'.\n",
+                                 err && *err ? *err : "", path);
+                       SSL_CTX_free(new_ctx);
+                       goto end;
+               }
+
+               /* Keytype does not matter here */
+               while (fcount--)
+                       order = ssl_sock_add_cert_sni(new_ctx, bind_conf, 
sni_filter[fcount],
order, key_type);
+
+               ret = 0;
+               goto end;
+       }
+
+       /* If using OpenSSL 1.0.2+, find context to load cert from SNI EB tree
for each CN/SAN, creating a ctx if needed
+        * Otherwise, create a new ctx and insert it into the EB tree
+        */
+       /* Check if CN already exists in SNI Tree */
+       xname = X509_get_subject_name(cert);
+       i = -1;
+       while ((i = X509_NAME_get_index_by_NID(xname, NID_commonName, i)) != -1)
{
+               X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, i);
+               if (ASN1_STRING_to_UTF8((unsigned char **)&str, entry->value) 
>= 0) {
+
+                       
if(ssl_sock_process_ctx_and_sni(path,bind_conf,cert,&new_ctx,str,err)
!= 0)
+                               goto end;
+                       OPENSSL_free(str);
+                       str = NULL;
+
 #ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME
        if (bind_conf->default_ctx) {
                memprintf(err, "%sthis version of openssl cannot load multiple 
SSL
certificates.\n",
                          err && *err ? *err : "");
-               return 1;
+                               goto end;
        }
 #endif
+
        if (!bind_conf->default_ctx)
-               bind_conf->default_ctx = ctx;
+                               bind_conf->default_ctx = new_ctx;
+               }
+       }
 
-       return 0;
+       /* Do the above logic for each SAN */
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+       names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+       if (names) {
+               for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
+                       GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
+                       if (name->type == GEN_DNS) {
+                               if (ASN1_STRING_to_UTF8((unsigned char **)&str, 
name->d.dNSName) >=
0) {
+                                       
if(ssl_sock_process_ctx_and_sni(path,bind_conf,cert,&new_ctx,str,err)
 != 0)
+                                               goto end;
+
+                                       OPENSSL_free(str);
+                                       str = NULL;
+                               }
+                       }
+               }
+       }
+#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
+
+       ret = 0;
+
+end:
+
+       if(names)
+               sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+
+       if(cert)
+               X509_free(cert);
+
+       if(str)
+               OPENSSL_free(str);
+
+       return ret;
 }
 
 int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, struct
proxy *curproxy, char **err)


Reply via email to