diff --git a/CHANGES b/CHANGES index ac40cce67813e66ddf2718dcce60b1d349442ea3..9aaaa063be1f9f8d8323fe8e677b6c017bc2ad34 100644 --- 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] diff --git a/apps/s_apps.h b/apps/s_apps.h index 3491b1ab6904dfb3ed6817a35b9bed23ae4bc108..c04e2d3611f0dae4085789648d055955eb51f5c0 100644 --- a/apps/s_apps.h +++ b/apps/s_apps.h @@ -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); diff --git a/apps/s_cb.c b/apps/s_cb.c index 6c4c4057921aafc2872f9708d0210e3c0cb9d951..afc30f2650955f43e7d458de6ffc270a84317fc0 100644 --- a/apps/s_cb.c +++ b/apps/s_cb.c @@ -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; } diff --git a/apps/s_client.c b/apps/s_client.c index ef798e8c08457665b8d8a3d3f8e4bcafb3cc9f29..97f7cbd9225916126c790c0ef08a45affccb0baa 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -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)), diff --git a/apps/s_server.c b/apps/s_server.c index 6be2b628a793b6fa1efa1475d8b4c2cce598b22e..5dd9e8e059b69ded3d301157222db6175146f49e 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -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, " diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 8d7bcfef39e287ffc08aea9966e96ecdee7a83a8..812af8cb5ade1689447a1e22ae3aecfd4332444d 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -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; is3->tmp.ctype[i]= p[i]; - p+=ctype_num; + p+=p[-1]; if (TLS1_get_version(s) >= TLS1_2_VERSION) { n2s(p, llen); diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 2c6e1addb46b166e59977ec0869d5029a3ad9adc..457a5c7b5c5a3ffdd1b7100d96adb674fe5562a4 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -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; diff --git a/ssl/ssl.h b/ssl/ssl.h index a3da0ccf055fbb31b7a02657f8dc2fb3691833f2..0e78fb7d6629f535d64857e680e37c97892df270 100644 --- 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); diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 1edbf18f3b7ece36e59a2d3f067b7c26b0ff8b27..59a8544431e15ae0f809e2c100e3ad7ce4b3f118 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -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); } diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index fd23a9c1894f5141da4771c8a9da90efb8fcfc58..75128751c9b97bcfa7621840ccc18838050e3277 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -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.