Hello,

Great ! I was actually speaking about it this morning at work ! Would love
to see this feature integrated (can it be backported to 1.5 too ?)

2015-06-23 20:07 GMT+02:00 Dave Zhu (yanbzhu) <[email protected]>:

>  Hello all,
>
>  I have a proposed enhancement that I have coded up and would like your
> comments.
>
>  The idea behind this is that when HAProxy is used to terminate SSL, and
> is configured with multiple certificates/keys with different key types
> (RSA, ECDSA, DSA), it only serves up the first cert/key loaded in the
> config (unless SNI is used). This means that if a client were to prefer
> ECDSA over RSA, even if HAProxy has an ECDSA certificate, it will use the
> RSA certificate. My proposed enhancement is that HAProxy switch the CTX
> that’s used, based on the clients’ choice of cipher-suites as well as the
> locally available certificates/keys.
>
>  Currently, I’ve coded it so that this only happens when the client does
> not specify an SNI, but I’m looking for guidance on what you would consider
> to be the best solution. This approach can certainly be taken to be
> compatible with SNI.
>
>  Is this something that you would be interested in folding into the
> codebase?
>
>  Here’s my diff:
>  diff --git a/include/types/listener.h b/include/types/listener.h
> index 895cd00..4a544d1 100644
> --- a/include/types/listener.h
> +++ b/include/types/listener.h
> @@ -133,6 +133,9 @@ struct bind_conf {
>   struct eb_root sni_ctx;    /* sni_ctx tree of all known certs
> full-names sorted by name */
>   struct eb_root sni_w_ctx;  /* sni_ctx tree of all known certs wildcards
> sorted by name */
>   struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */
> + SSL_CTX *default_rsa_ctx;  /* SSL Context of first RSA Certificate*/
> + SSL_CTX *default_dsa_ctx;  /* SSL Context of first DSA Certificate*/
> + SSL_CTX *default_ecdsa_ctx;/* SSL Context of first ECDSA Certificate*/
>  #endif
>   int is_ssl;                /* SSL is required for these listeners */
>   unsigned long bind_proc;   /* bitmask of processes allowed to use these
> listeners */
> diff --git a/src/ssl_sock.c b/src/ssl_sock.c
> index 3bd6fa2..0343c69 100644
> --- a/src/ssl_sock.c
> +++ b/src/ssl_sock.c
> @@ -978,6 +978,61 @@ static int ssl_sock_advertise_alpn_protos(SSL *s,
> const unsigned char **out,
>  }
>  #endif
>
> +
> +
> +/* This CB is techinically for using pre-shared secret for this
> + * handshake for a non-resumption case
> + *
> + * However, it is currnetly the only place where we can access the peer's
> + * cipher list before a cipher is picked
> + *
> + * Therefore, we use this CB in order to swap the SSL_CTX based on the
> + * client's preferred ciphersuite if we have multiple key types in the
> config
> + */
> +#ifdef USE_OPENSSL
> +int ssl_sock_switchctx_by_peer_cipher_cbk(SSL *ssl, void *secret, int
> *secret_len,
> +    STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void* arg)
> +{
> +    int i;
> +    SSL_CTX* new_ctx = NULL;
> +    SSL_CIPHER *cur_cipher;
> +    struct bind_conf* s = (struct bind_conf*) arg;
> +
> +    for (i=0; i<sk_SSL_CIPHER_num(peer_ciphers); i++)
> +    {
> +   new_ctx = NULL;
> +   cur_cipher = sk_SSL_CIPHER_value(peer_ciphers,i);
> +
> +   /* This information is located in ssl_locl.h,
> +    * but that file is not visibile outside the package
> +    * #define SSL_aRSA 0x00000001L - RSA auth
> +  #define SSL_aDSS 0x00000002L - DSS auth
> +  #define SSL_aECDSA   0x00000040L - ECDSA auth
> +    */
> +
> +   if(cur_cipher->algorithm_auth == 0x40L && s->default_ecdsa_ctx){
> +  new_ctx = s->default_ecdsa_ctx;
> +   } else if (cur_cipher->algorithm_auth == 0x02L && s->default_dsa_ctx){
> +  new_ctx = s->default_dsa_ctx;
> +   } else if(cur_cipher->algorithm_auth == 0x01L && s->default_rsa_ctx){
> +  new_ctx = s->default_rsa_ctx;
> +   }
> +
> +   if(new_ctx){
> +  /* Found a new CTX, switch it now*/
> +  SSL_set_SSL_CTX(ssl,new_ctx);
> +  break;
> +   }
> +    }
> +
> +    /* 0 means that a session secret was not found,
> + * which is always the case for us
> + */
> +    return 0;
> +}
> +#endif
> +
> +
>  #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
>  /* Sets the SSL ctx of <ssl> to match the advertised server name. Returns
> a
>   * warning when no match is found, which implies the default (first) cert
> @@ -993,6 +1048,7 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al,
> struct bind_conf *s)
>
>   servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
>   if (!servername) {
> +
>  
> SSL_set_session_secret_cb(ssl,ssl_sock_switchctx_by_peer_cipher_cbk,(void*)s);
>   return (s->strict_sni ?
>   SSL_TLSEXT_ERR_ALERT_FATAL :
>   SSL_TLSEXT_ERR_NOACK);
> @@ -1033,6 +1089,8 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al,
> struct bind_conf *s)
>  }
>  #endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
>
> +
> +
>  #ifndef OPENSSL_NO_DH
>
>  static DH * ssl_get_dh_1024(void)
> @@ -1414,6 +1472,9 @@ static int ssl_sock_load_cert_file(const char *path,
> struct bind_conf *bind_conf
>  {
>   int ret;
>   SSL_CTX *ctx;
> +    EVP_PKEY *pkey=NULL;
> +    FILE* file = NULL;
> +    int key_type = 0;
>
>   ctx = SSL_CTX_new(SSLv23_server_method());
>   if (!ctx) {
> @@ -1422,13 +1483,35 @@ static int ssl_sock_load_cert_file(const char
> *path, struct bind_conf *bind_conf
>   return 1;
>   }
>
> - 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;
> + file = fopen(path,"r");
> + PEM_read_PrivateKey(file,&pkey,NULL,NULL);
> + if(file){
> +    fclose(file);
> + }
> +
> + if (pkey == NULL) {
> +    memprintf(err, "%sunable to load SSL private key from PEM file
> '%s'.\n",
> +            err && *err ? *err : "", path);
> +    SSL_CTX_free(ctx);
> +    return 1;
> + }
> +
> + key_type=EVP_PKEY_id(pkey);
> + ret=SSL_CTX_use_PrivateKey(ctx,pkey);
> +
> + if (ret <= 0) {
> +    memprintf(err,
> +            "%sunable to set SSL_CTX private key from PEM file '%s'.\n",
> +            err && *err ? *err : "", path);
> +    EVP_PKEY_free(pkey);
> +    SSL_CTX_free(ctx);
> +    return 1;
>   }
>
> + /* We no longer need the key anymore */
> + EVP_PKEY_free(pkey);
> +
> +
>   ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf, sni_filter,
> fcount);
>   if (ret <= 0) {
>   memprintf(err, "%sunable to load SSL certificate from PEM file '%s'.\n",
> @@ -1493,7 +1576,22 @@ static int ssl_sock_load_cert_file(const char
> *path, struct bind_conf *bind_conf
>   }
>  #endif
>   if (!bind_conf->default_ctx)
> - bind_conf->default_ctx = ctx;
> +    bind_conf->default_ctx = ctx;
> +
> + switch(key_type){
> + case EVP_PKEY_RSA:
> +    if(!bind_conf->default_rsa_ctx)
> +   bind_conf->default_rsa_ctx = ctx;
> +    break;
> + case EVP_PKEY_DSA:
> +    if(!bind_conf->default_dsa_ctx)
> +   bind_conf->default_dsa_ctx = ctx;
> +    break;
> + case EVP_PKEY_EC:
> +    if(!bind_conf->default_ecdsa_ctx)
> +   bind_conf->default_ecdsa_ctx = ctx;
> +    break;
> + }
>
>   return 0;
>  }
> @@ -1870,6 +1968,8 @@ int ssl_sock_prepare_ctx(struct bind_conf
> *bind_conf, SSL_CTX *ctx, struct proxy
>   SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_cbk);
>   SSL_CTX_set_tlsext_servername_arg(ctx, bind_conf);
>  #endif
> +
> +
>  #if defined(SSL_CTX_set_tmp_ecdh) && !defined(OPENSSL_NO_ECDH)
>   {
>   int i;
> @@ -2243,6 +2343,9 @@ void ssl_sock_free_all_ctx(struct bind_conf
> *bind_conf)
>   }
>
>   bind_conf->default_ctx = NULL;
> + bind_conf->default_rsa_ctx = NULL;
> + bind_conf->default_dsa_ctx = NULL;
> + bind_conf->default_ecdsa_ctx = NULL;
>  }
>
>  /*
>

Reply via email to