>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)