Add new ctrl to retrieve client certificate types, print out
authorDr. Stephen Henson <steve@openssl.org>
Sun, 8 Jul 2012 14:22:45 +0000 (14:22 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Sun, 8 Jul 2012 14:22:45 +0000 (14:22 +0000)
details in s_client.

Also add ctrl to set client certificate types. If not used sensible values
will be included based on supported signature algorithms: for example if
we don't include any DSA signing algorithms the DSA certificate type is
omitted.

Fix restriction in old code where certificate types would be truncated
if it exceeded TLS_CT_NUMBER.

CHANGES
apps/s_apps.h
apps/s_cb.c
apps/s_client.c
apps/s_server.c
ssl/s3_clnt.c
ssl/s3_lib.c
ssl/ssl.h
ssl/ssl_cert.c
ssl/ssl_locl.h

diff --git a/CHANGES b/CHANGES
index ac40cce67813e66ddf2718dcce60b1d349442ea3..9aaaa063be1f9f8d8323fe8e677b6c017bc2ad34 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,12 @@
 
  Changes between 1.0.1 and 1.1.0  [xx XXX xxxx]
 
+  *) New ctrls to retrieve and set certificate types in a certificate
+     request message. Print out received values in s_client. If certificate
+     types is not set with custom values set sensible values based on
+     supported signature algorithms.
+     [Steve Henson]
+
   *) Support for distinct client and server supported signature algorithms.
      [Steve Henson]
 
index 3491b1ab6904dfb3ed6817a35b9bed23ae4bc108..c04e2d3611f0dae4085789648d055955eb51f5c0 100644 (file)
@@ -160,7 +160,7 @@ int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key,
 int set_cert_key_and_authz(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key,
                            unsigned char *authz, size_t authz_length);
 # endif
-int ssl_print_sigalgs(BIO *out, SSL *s, int client);
+int ssl_print_sigalgs(BIO *out, SSL *s);
 int ssl_print_curves(BIO *out, SSL *s);
 #endif
 int init_client(int *sock, char *server, int port, int type);
index 6c4c4057921aafc2872f9708d0210e3c0cb9d951..afc30f2650955f43e7d458de6ffc270a84317fc0 100644 (file)
@@ -285,9 +285,75 @@ int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key,
        return 1;
        }
 
-static int do_print_sigalgs(BIO *out, SSL *s, int client, int shared)
+static void ssl_print_client_cert_types(BIO *bio, SSL *s)
        {
-       int i, nsig;
+       const unsigned char *p;
+       int i;
+       int cert_type_num = SSL_get0_certificate_types(s, &p);
+       if (!cert_type_num)
+               return;
+       BIO_puts(bio, "Client Certificate Types: ");
+       for (i = 0; i < cert_type_num; i++)
+               {
+               unsigned char cert_type = p[i];
+               char *cname;
+               switch(cert_type)
+                       {
+               case TLS_CT_RSA_SIGN:
+                       cname = "RSA sign";
+                       break;
+
+               case TLS_CT_DSS_SIGN:
+                       cname = "DSA sign";
+                       break;
+
+               case TLS_CT_RSA_FIXED_DH:
+                       cname = "RSA fixed DH";
+                       break;
+
+               case TLS_CT_DSS_FIXED_DH:
+                       cname = "DSS fixed DH";
+                       break;
+
+               case TLS_CT_ECDSA_SIGN:
+                       cname = "ECDSA sign";
+                       break;
+
+               case TLS_CT_RSA_FIXED_ECDH:
+                       cname = "RSA fixed ECDH";
+                       break;
+
+               case TLS_CT_ECDSA_FIXED_ECDH:
+                       cname = "ECDSA fixed ECDH";
+                       break;
+
+               case TLS_CT_GOST94_SIGN:
+                       cname = "GOST94 Sign";
+                       break;
+
+               case TLS_CT_GOST01_SIGN:
+                       cname = "GOST01 Sign";
+                       break;
+
+               default:
+                        cname = NULL;
+                       }
+
+               if (i)
+                       BIO_puts(bio, ", ");
+
+               if (cname)
+                       BIO_puts(bio, cname);
+               else
+                       BIO_printf(bio, "UNKNOWN (%d),", cert_type);
+               }
+       BIO_puts(bio, "\n");
+       }
+
+static int do_print_sigalgs(BIO *out, SSL *s, int shared)
+       {
+       int i, nsig, client;
+       client = SSL_is_server(s) ? 0 : 1;
        if (shared)
                nsig = SSL_get_shared_sigalgs(s, -1, NULL, NULL, NULL,
                                                        NULL, NULL);
@@ -334,10 +400,12 @@ static int do_print_sigalgs(BIO *out, SSL *s, int client, int shared)
        return 1;
        }
 
-int ssl_print_sigalgs(BIO *out, SSL *s, int client)
+int ssl_print_sigalgs(BIO *out, SSL *s)
        {
-       do_print_sigalgs(out, s, client, 0);
-       do_print_sigalgs(out, s, client, 1);
+       if (!SSL_is_server(s))
+               ssl_print_client_cert_types(out, s);
+       do_print_sigalgs(out, s, 0);
+       do_print_sigalgs(out, s, 1);
        return 1;
        }
 
index ef798e8c08457665b8d8a3d3f8e4bcafb3cc9f29..97f7cbd9225916126c790c0ef08a45affccb0baa 100644 (file)
@@ -2096,7 +2096,7 @@ static void print_stuff(BIO *bio, SSL *s, int full)
                        BIO_write(bio,"\n",1);
                        }
 
-               ssl_print_sigalgs(bio, s, 1);
+               ssl_print_sigalgs(bio, s);
 
                BIO_printf(bio,"---\nSSL handshake has read %ld bytes and written %ld bytes\n",
                        BIO_number_read(SSL_get_rbio(s)),
index 6be2b628a793b6fa1efa1475d8b4c2cce598b22e..5dd9e8e059b69ded3d301157222db6175146f49e 100644 (file)
@@ -2610,7 +2610,7 @@ static int init_ssl_connection(SSL *con)
        if (SSL_get_shared_ciphers(con,buf,sizeof buf) != NULL)
                BIO_printf(bio_s_out,"Shared ciphers:%s\n",buf);
        str=SSL_CIPHER_get_name(SSL_get_current_cipher(con));
-       ssl_print_sigalgs(bio_s_out, con, 0);
+       ssl_print_sigalgs(bio_s_out, con);
        ssl_print_curves(bio_s_out, con);
        BIO_printf(bio_s_out,"CIPHER is %s\n",(str != NULL)?str:"(NONE)");
 
@@ -2953,7 +2953,7 @@ static int www_body(char *hostname, int s, unsigned char *context)
                                        }
                                BIO_puts(io,"\n");
                                }
-                       ssl_print_sigalgs(io, con, 0);
+                       ssl_print_sigalgs(io, con);
                        ssl_print_curves(io, con);
                        BIO_printf(io,(SSL_cache_hit(con)
                                ?"---\nReused, "
index 8d7bcfef39e287ffc08aea9966e96ecdee7a83a8..812af8cb5ade1689447a1e22ae3aecfd4332444d 100644 (file)
@@ -1936,11 +1936,22 @@ int ssl3_get_certificate_request(SSL *s)
 
        /* get the certificate types */
        ctype_num= *(p++);
+       if (s->cert->ctypes)
+               {
+               OPENSSL_free(s->cert->ctypes);
+               s->cert->ctypes = NULL;
+               }
        if (ctype_num > SSL3_CT_NUMBER)
+               {
+               /* If we exceed static buffer copy all to cert structure */
+               s->cert->ctypes = OPENSSL_malloc(ctype_num);
+               memcpy(s->cert->ctypes, p, ctype_num);
+               s->cert->ctype_num = (size_t)ctype_num;
                ctype_num=SSL3_CT_NUMBER;
+               }
        for (i=0; i<ctype_num; i++)
                s->s3->tmp.ctype[i]= p[i];
-       p+=ctype_num;
+       p+=p[-1];
        if (TLS1_get_version(s) >= TLS1_2_VERSION)
                {
                n2s(p, llen);
index 2c6e1addb46b166e59977ec0869d5029a3ad9adc..457a5c7b5c5a3ffdd1b7100d96adb674fe5562a4 100644 (file)
@@ -3089,6 +3089,8 @@ static char * MS_CALLBACK srp_password_from_info_cb(SSL *s, void *arg)
        }
 #endif
 
+static int ssl3_set_req_cert_type(CERT *c, const unsigned char *p, size_t len);
+
 long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
        {
        int ret=0;
@@ -3426,6 +3428,27 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
        case SSL_CTRL_SET_CLIENT_SIGALGS_LIST:
                return tls1_set_sigalgs_list(s->cert, parg, 1);
 
+       case SSL_CTRL_GET_CLIENT_CERT_TYPES:
+               {
+               const unsigned char **pctype = parg;
+               if (s->server || !s->s3->tmp.cert_req)
+                       return 0;
+               if (s->cert->ctypes)
+                       {
+                       if (pctype)
+                               *pctype = s->cert->ctypes;
+                       return (int)s->cert->ctype_num;
+                       }
+               if (pctype)
+                       *pctype = (unsigned char *)s->s3->tmp.ctype;
+               return s->s3->tmp.ctype_num;
+               }
+
+       case SSL_CTRL_SET_CLIENT_CERT_TYPES:
+               if (!s->server)
+                       return 0;
+               return ssl3_set_req_cert_type(s->cert, parg, larg);
+
        default:
                break;
                }
@@ -3720,6 +3743,9 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
        case SSL_CTRL_SET_CLIENT_SIGALGS_LIST:
                return tls1_set_sigalgs_list(ctx->cert, parg, 1);
 
+       case SSL_CTRL_SET_CLIENT_CERT_TYPES:
+               return ssl3_set_req_cert_type(ctx->cert, parg, larg);
+
        case SSL_CTRL_SET_TLSEXT_AUTHZ_SERVER_AUDIT_PROOF_CB_ARG:
                ctx->tlsext_authz_server_audit_proof_cb_arg = parg;
                break;
@@ -4014,8 +4040,61 @@ SSL_CIPHER *ssl3_choose_cipher(SSL *s, STACK_OF(SSL_CIPHER) *clnt,
 int ssl3_get_req_cert_type(SSL *s, unsigned char *p)
        {
        int ret=0;
+       const unsigned char *sig;
+       size_t siglen;
+       int have_rsa_sign = 0, have_dsa_sign = 0, have_ecdsa_sign = 0;
+       int nostrict = 1;
        unsigned long alg_k;
 
+       /* If we have custom certificate types set, use them */
+       if (s->cert->ctypes)
+               {
+               memcpy(p, s->cert->ctypes, s->cert->ctype_num);
+               return (int)s->cert->ctype_num;
+               }
+       /* Else see if we have any signature algorithms configured */
+       if (s->cert->client_sigalgs)
+               {
+               sig = s->cert->client_sigalgs;
+               siglen = s->cert->client_sigalgslen;
+               }
+       else
+               {
+               sig = s->cert->conf_sigalgs;
+               siglen = s->cert->conf_sigalgslen;
+               }
+       /* If we have sigalgs work out if we can sign with RSA, DSA, ECDSA */
+       if (sig)
+               {
+               size_t i;
+               if (s->cert->cert_flags & SSL_CERT_FLAG_TLS_STRICT)
+                       nostrict = 0;
+               for (i = 0; i < siglen; i+=2, sig+=2)
+                       {
+                       switch(sig[1])
+                               {
+                       case TLSEXT_signature_rsa:
+                               have_rsa_sign = 1;
+                               break;
+
+                       case TLSEXT_signature_dsa:
+                               have_dsa_sign = 1;
+                               break;
+
+                       case TLSEXT_signature_ecdsa:
+                               have_ecdsa_sign = 1;
+                               break;
+                               }
+                       }
+               }
+       /* Otherwise allow anything */
+       else
+               {
+               have_rsa_sign = 1;
+               have_dsa_sign = 1;
+               have_ecdsa_sign = 1;
+               }
+
        alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
 
 #ifndef OPENSSL_NO_GOST
@@ -4034,10 +4113,15 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p)
        if (alg_k & (SSL_kDHr|SSL_kEDH))
                {
 #  ifndef OPENSSL_NO_RSA
-               p[ret++]=SSL3_CT_RSA_FIXED_DH;
+               /* Since this refers to a certificate signed with an RSA
+                * algorithm, only check for rsa signing in strict mode.
+                */
+               if (nostrict || have_rsa_sign)
+                       p[ret++]=SSL3_CT_RSA_FIXED_DH;
 #  endif
 #  ifndef OPENSSL_NO_DSA
-               p[ret++]=SSL3_CT_DSS_FIXED_DH;
+               if (nostrict || have_dsa_sign)
+                       p[ret++]=SSL3_CT_DSS_FIXED_DH;
 #  endif
                }
        if ((s->version == SSL3_VERSION) &&
@@ -4052,16 +4136,20 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p)
                }
 #endif /* !OPENSSL_NO_DH */
 #ifndef OPENSSL_NO_RSA
-       p[ret++]=SSL3_CT_RSA_SIGN;
+       if (have_rsa_sign)
+               p[ret++]=SSL3_CT_RSA_SIGN;
 #endif
 #ifndef OPENSSL_NO_DSA
-       p[ret++]=SSL3_CT_DSS_SIGN;
+       if (have_dsa_sign)
+               p[ret++]=SSL3_CT_DSS_SIGN;
 #endif
 #ifndef OPENSSL_NO_ECDH
        if ((alg_k & (SSL_kECDHr|SSL_kECDHe)) && (s->version >= TLS1_VERSION))
                {
-               p[ret++]=TLS_CT_RSA_FIXED_ECDH;
-               p[ret++]=TLS_CT_ECDSA_FIXED_ECDH;
+               if (nostrict || have_rsa_sign)
+                       p[ret++]=TLS_CT_RSA_FIXED_ECDH;
+               if (nostrict || have_ecdsa_sign)
+                       p[ret++]=TLS_CT_ECDSA_FIXED_ECDH;
                }
 #endif
 
@@ -4071,12 +4159,32 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p)
         */
        if (s->version >= TLS1_VERSION)
                {
-               p[ret++]=TLS_CT_ECDSA_SIGN;
+               if (have_ecdsa_sign)
+                       p[ret++]=TLS_CT_ECDSA_SIGN;
                }
 #endif 
        return(ret);
        }
 
+static int ssl3_set_req_cert_type(CERT *c, const unsigned char *p, size_t len)
+       {
+       if (c->ctypes)
+               {
+               OPENSSL_free(c->ctypes);
+               c->ctypes = NULL;
+               }
+       if (!p || !len)
+               return 1;
+       if (len > 0xff)
+               return 0;
+       c->ctypes = OPENSSL_malloc(len);
+       if (!c->ctypes)
+               return 0;
+       memcpy(c->ctypes, p, len);
+       c->ctype_num = len;
+       return 1;
+       }
+
 int ssl3_shutdown(SSL *s)
        {
        int ret;
index a3da0ccf055fbb31b7a02657f8dc2fb3691833f2..0e78fb7d6629f535d64857e680e37c97892df270 100644 (file)
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -1664,6 +1664,8 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_CTRL_CLEAR_CERT_FLAGS              100
 #define SSL_CTRL_SET_CLIENT_SIGALGS            101
 #define SSL_CTRL_SET_CLIENT_SIGALGS_LIST       102
+#define SSL_CTRL_GET_CLIENT_CERT_TYPES         103
+#define SSL_CTRL_SET_CLIENT_CERT_TYPES         104
 
 #define DTLSv1_get_timeout(ssl, arg) \
        SSL_ctrl(ssl,DTLS_CTRL_GET_TIMEOUT,0, (void *)arg)
@@ -1758,6 +1760,14 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 #define SSL_set1_client_sigalgs_list(ctx, s) \
        SSL_ctrl(ctx,SSL_CTRL_SET_CLIENT_SIGALGS_LIST,0,(char *)s)
 
+#define SSL_get0_certificate_types(s, clist) \
+       SSL_ctrl(s, SSL_CTRL_GET_CLIENT_CERT_TYPES, 0, (char *)clist)
+
+#define SSL_CTX_set1_client_certificate_types(ctx, clist, clistlen) \
+       SSL_CTX_ctrl(ctx,SSL_CTRL_SET_CLIENT_CERT_TYPES,clistlen,(char *)clist)
+#define SSL_set1_client_certificate_types(s, clist, clistlen) \
+       SSL_ctrl(s,SSL_CTRL_SET_CLIENT_CERT_TYPES,clistlen,(char *)clist)
+
 #ifndef OPENSSL_NO_BIO
 BIO_METHOD *BIO_f_ssl(void);
 BIO *BIO_new_ssl(SSL_CTX *ctx,int client);
index 1edbf18f3b7ece36e59a2d3f067b7c26b0ff8b27..59a8544431e15ae0f809e2c100e3ad7ce4b3f118 100644 (file)
@@ -388,6 +388,15 @@ CERT *ssl_cert_dup(CERT *cert)
                ret->client_sigalgs = NULL;
        /* Shared sigalgs also NULL */
        ret->shared_sigalgs = NULL;
+       /* Copy any custom client certificate types */
+       if (cert->ctypes)
+               {
+               ret->ctypes = OPENSSL_malloc(cert->ctype_num);
+               if (!ret->ctypes)
+                       goto err;
+               memcpy(ret->ctypes, cert->ctypes, cert->ctype_num);
+               ret->ctype_num = cert->ctype_num;
+               }
 
        ret->cert_flags = cert->cert_flags;
 
@@ -489,6 +498,8 @@ void ssl_cert_free(CERT *c)
                OPENSSL_free(c->client_sigalgs);
        if (c->shared_sigalgs)
                OPENSSL_free(c->shared_sigalgs);
+       if (c->ctypes)
+               OPENSSL_free(c->ctypes);
        OPENSSL_free(c);
        }
 
index fd23a9c1894f5141da4771c8a9da90efb8fcfc58..75128751c9b97bcfa7621840ccc18838050e3277 100644 (file)
@@ -531,6 +531,13 @@ typedef struct cert_st
        unsigned int cert_flags;
        CERT_PKEY pkeys[SSL_PKEY_NUM];
 
+       /* Certificate types (received or sent) in certificate request
+        * message. On receive this is only set if number of certificate
+        * types exceeds SSL3_CT_NUMBER.
+        */
+       unsigned char *ctypes;
+       size_t ctype_num;
+
        /* signature algorithms peer reports: e.g. supported signature
         * algorithms extension for server or as part of a certificate
         * request for client.