diff --git a/CHANGES b/CHANGES index 5f84cfd403..c4a7cb4fd1 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ Changes between 1.0.x and 1.1.0 [xx XXX xxxx] + *) Integrate hostname, email address and IP address checking with certificate + verification. New verify options supporting checking in opensl utility. + [Steve Henson] + *) New function X509_CRL_diff to generate a delta CRL from the difference of two full CRLs. Add support to "crl" utility. [Steve Henson] diff --git a/apps/apps.c b/apps/apps.c index 34dc70b354..adf78665b0 100644 --- a/apps/apps.c +++ b/apps/apps.c @@ -2382,6 +2382,8 @@ int args_verify(char ***pargs, int *pargc, char *arg = **pargs, *argn = (*pargs)[1]; const X509_VERIFY_PARAM *vpm = NULL; time_t at_time = 0; + const unsigned char *hostname = NULL, *email = NULL; + char *ipasc = NULL; if (!strcmp(arg, "-policy")) { if (!argn) @@ -2470,6 +2472,27 @@ int args_verify(char ***pargs, int *pargc, } (*pargs)++; } + else if (strcmp(arg,"-verify_hostname") == 0) + { + if (!argn) + *badarg = 1; + hostname = (unsigned char *)argn; + (*pargs)++; + } + else if (strcmp(arg,"-verify_email") == 0) + { + if (!argn) + *badarg = 1; + email = (unsigned char *)argn; + (*pargs)++; + } + else if (strcmp(arg,"-verify_ip") == 0) + { + if (!argn) + *badarg = 1; + ipasc = argn; + (*pargs)++; + } else if (!strcmp(arg, "-ignore_critical")) flags |= X509_V_FLAG_IGNORE_CRITICAL; else if (!strcmp(arg, "-issuer_checks")) @@ -2538,6 +2561,15 @@ int args_verify(char ***pargs, int *pargc, if (at_time) X509_VERIFY_PARAM_set_time(*pm, at_time); + if (hostname && !X509_VERIFY_PARAM_set1_host(*pm, hostname, 0)) + *badarg = 1; + + if (email && !X509_VERIFY_PARAM_set1_email(*pm, email, 0)) + *badarg = 1; + + if (ipasc && !X509_VERIFY_PARAM_set1_ip_asc(*pm, ipasc)) + *badarg = 1; + end: (*pargs)++; diff --git a/apps/s_client.c b/apps/s_client.c index 7041fb49fe..27c1696bf3 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -293,10 +293,6 @@ static void sc_usage(void) BIO_printf(bio_err," -host host - use -connect instead\n"); BIO_printf(bio_err," -port port - use -connect instead\n"); BIO_printf(bio_err," -connect host:port - who to connect to (default is %s:%s)\n",SSL_HOST_NAME,PORT_STR); - BIO_printf(bio_err," -checkhost host - check peer certificate matches \"host\"\n"); - BIO_printf(bio_err," -checkemail email - check peer certificate matches \"email\"\n"); - BIO_printf(bio_err," -checkip ipaddr - check peer certificate matches \"ipaddr\"\n"); - BIO_printf(bio_err," -verify arg - turn on peer certificate verification\n"); BIO_printf(bio_err," -cert arg - certificate file to use, PEM format assumed\n"); BIO_printf(bio_err," -certform arg - certificate format (PEM or DER) PEM default\n"); @@ -634,8 +630,6 @@ int MAIN(int argc, char **argv) #endif SSL_EXCERT *exc = NULL; - unsigned char *checkhost = NULL, *checkemail = NULL; - char *checkip = NULL; SSL_CONF_CTX *cctx = NULL; STACK_OF(OPENSSL_STRING) *ssl_args = NULL; @@ -999,21 +993,6 @@ int MAIN(int argc, char **argv) /* meth=TLSv1_client_method(); */ } #endif - else if (strcmp(*argv,"-checkhost") == 0) - { - if (--argc < 1) goto bad; - checkhost=(unsigned char *)*(++argv); - } - else if (strcmp(*argv,"-checkemail") == 0) - { - if (--argc < 1) goto bad; - checkemail=(unsigned char *)*(++argv); - } - else if (strcmp(*argv,"-checkip") == 0) - { - if (--argc < 1) goto bad; - checkip=*(++argv); - } #ifndef OPENSSL_NO_JPAKE else if (strcmp(*argv,"-jpake") == 0) { @@ -1648,8 +1627,6 @@ SSL_set_tlsext_status_ids(con, ids); "CONNECTION ESTABLISHED\n"); print_ssl_summary(bio_err, con); } - print_ssl_cert_checks(bio_err, con, checkhost, - checkemail, checkip); print_stuff(bio_c_out,con,full_log); if (full_log > 0) full_log--; diff --git a/apps/s_server.c b/apps/s_server.c index b9f6f30b0a..2de8565367 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -473,9 +473,6 @@ static void sv_usage(void) BIO_printf(bio_err,"usage: s_server [args ...]\n"); BIO_printf(bio_err,"\n"); BIO_printf(bio_err," -accept arg - port to accept on (default is %d)\n",PORT); - BIO_printf(bio_err," -checkhost host - check peer certificate matches \"host\"\n"); - BIO_printf(bio_err," -checkemail email - check peer certificate matches \"email\"\n"); - BIO_printf(bio_err," -checkip ipaddr - check peer certificate matches \"ipaddr\"\n"); BIO_printf(bio_err," -context arg - set session ID context\n"); BIO_printf(bio_err," -verify arg - turn on peer certificate verification\n"); BIO_printf(bio_err," -Verify arg - turn on peer certificate verification, must have a cert.\n"); @@ -946,9 +943,6 @@ static char *jpake_secret = NULL; static srpsrvparm srp_callback_parm; #endif static char *srtp_profiles = NULL; -static unsigned char *checkhost = NULL, *checkemail = NULL; -static char *checkip = NULL; - int MAIN(int argc, char *argv[]) { @@ -1268,21 +1262,6 @@ int MAIN(int argc, char *argv[]) } } #endif - else if (strcmp(*argv,"-checkhost") == 0) - { - if (--argc < 1) goto bad; - checkhost=(unsigned char *)*(++argv); - } - else if (strcmp(*argv,"-checkemail") == 0) - { - if (--argc < 1) goto bad; - checkemail=(unsigned char *)*(++argv); - } - else if (strcmp(*argv,"-checkip") == 0) - { - if (--argc < 1) goto bad; - checkip=*(++argv); - } else if (strcmp(*argv,"-msg") == 0) { s_msg=1; } else if (strcmp(*argv,"-msgfile") == 0) @@ -2578,8 +2557,6 @@ static int init_ssl_connection(SSL *con) if (s_brief) print_ssl_summary(bio_err, con); - print_ssl_cert_checks(bio_err, con, checkhost, checkemail, checkip); - PEM_write_bio_SSL_SESSION(bio_s_out,SSL_get_session(con)); peer=SSL_get_peer_certificate(con); diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c index 595efcead3..361cd21987 100644 --- a/crypto/x509/x509_txt.c +++ b/crypto/x509/x509_txt.c @@ -197,6 +197,12 @@ const char *X509_verify_cert_error_string(long n) return("Suite B: curve not allowed for this LOS"); case X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256: return("Suite B: cannot sign P-384 with P-256"); + case X509_V_ERR_HOSTNAME_MISMATCH: + return("Hostname mismatch"); + case X509_V_ERR_EMAIL_MISMATCH: + return("Email address mismatch"); + case X509_V_ERR_IP_ADDRESS_MISMATCH: + return("IP address mismatch"); default: BIO_snprintf(buf,sizeof buf,"error number %ld",n); diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index a21fa39c32..8ba03d5d43 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -113,6 +113,7 @@ static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); static int check_chain_extensions(X509_STORE_CTX *ctx); static int check_name_constraints(X509_STORE_CTX *ctx); +static int check_id(X509_STORE_CTX *ctx); static int check_trust(X509_STORE_CTX *ctx); static int check_revocation(X509_STORE_CTX *ctx); static int check_cert(X509_STORE_CTX *ctx); @@ -377,6 +378,10 @@ int X509_verify_cert(X509_STORE_CTX *ctx) if (!ok) goto end; + ok = check_id(ctx); + + if (!ok) goto end; + /* We may as well copy down any DSA parameters that are required */ X509_get_pubkey_parameters(NULL,ctx->chain); @@ -694,6 +699,36 @@ static int check_name_constraints(X509_STORE_CTX *ctx) return 1; } +static int check_id_error(X509_STORE_CTX *ctx, int errcode) + { + ctx->error = errcode; + ctx->current_cert = ctx->cert; + ctx->error_depth = 0; + return ctx->verify_cb(0, ctx); + } + +static int check_id(X509_STORE_CTX *ctx) + { + X509_VERIFY_PARAM *vpm = ctx->param; + X509 *x = ctx->cert; + if (vpm->host && !X509_check_host(x, vpm->host, vpm->hostlen, 0)) + { + if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH)) + return 0; + } + if (vpm->email && !X509_check_email(x, vpm->email, vpm->emaillen, 0)) + { + if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH)) + return 0; + } + if (vpm->ip && !X509_check_ip(x, vpm->ip, vpm->iplen, 0)) + { + if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH)) + return 0; + } + return 1; + } + static int check_trust(X509_STORE_CTX *ctx) { int i, ok; diff --git a/crypto/x509/x509_vfy.h b/crypto/x509/x509_vfy.h index a6f3943025..58eff53f72 100644 --- a/crypto/x509/x509_vfy.h +++ b/crypto/x509/x509_vfy.h @@ -173,6 +173,12 @@ typedef struct X509_VERIFY_PARAM_st int trust; /* trust setting to check */ int depth; /* Verify depth */ STACK_OF(ASN1_OBJECT) *policies; /* Permissible policies */ + unsigned char *host; /* If not NULL hostname to match */ + size_t hostlen; + unsigned char *email; /* If not NULL email address to match */ + size_t emaillen; + unsigned char *ip; /* If not NULL IP address to match */ + size_t iplen; /* Length of IP address */ } X509_VERIFY_PARAM; DECLARE_STACK_OF(X509_VERIFY_PARAM) @@ -362,6 +368,10 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); #define X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM 59 #define X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED 60 #define X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 61 +/* Host, email and IP check errors */ +#define X509_V_ERR_HOSTNAME_MISMATCH 62 +#define X509_V_ERR_EMAIL_MISMATCH 63 +#define X509_V_ERR_IP_ADDRESS_MISMATCH 64 /* The application is not happy */ #define X509_V_ERR_APPLICATION_VERIFICATION 50 @@ -548,6 +558,15 @@ int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, ASN1_OBJECT *policy); int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, STACK_OF(ASN1_OBJECT) *policies); + +int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, + const unsigned char *name, size_t namelen); +int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param, + const unsigned char *email, size_t emaillen); +int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, + const unsigned char *ip, size_t iplen); +int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc); + int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param); const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param); diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c index 9e7d996661..68f158435e 100644 --- a/crypto/x509/x509_vpm.c +++ b/crypto/x509/x509_vpm.c @@ -83,6 +83,24 @@ static void x509_verify_param_zero(X509_VERIFY_PARAM *param) sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free); param->policies = NULL; } + if (param->host) + { + OPENSSL_free(param->host); + param->host = NULL; + param->hostlen = 0; + } + if (param->email) + { + OPENSSL_free(param->email); + param->email = NULL; + param->emaillen = 0; + } + if (param->ip) + { + OPENSSL_free(param->ip); + param->ip = NULL; + param->iplen = 0; + } } X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void) @@ -193,6 +211,24 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, return 0; } + if (test_x509_verify_param_copy(host, NULL)) + { + if (!X509_VERIFY_PARAM_set1_host(dest, src->host, src->hostlen)) + return 0; + } + + if (test_x509_verify_param_copy(email, NULL)) + { + if (!X509_VERIFY_PARAM_set1_email(dest, src->email, src->emaillen)) + return 0; + } + + if (test_x509_verify_param_copy(ip, NULL)) + { + if (!X509_VERIFY_PARAM_set1_ip(dest, src->ip, src->iplen)) + return 0; + } + return 1; } @@ -207,6 +243,35 @@ int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, return ret; } +static int int_x509_param_set1(unsigned char **pdest, size_t *pdestlen, + const unsigned char *src, size_t srclen) + { + void *tmp; + if (src) + { + if (srclen == 0) + { + tmp = BUF_strdup((char *)src); + srclen = strlen((char *)src); + } + else + tmp = BUF_memdup(src, srclen); + if (!tmp) + return 0; + } + else + { + tmp = NULL; + srclen = 0; + } + if (*pdest) + OPENSSL_free(*pdest); + *pdest = tmp; + if (pdestlen) + *pdestlen = srclen; + return 1; + } + int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name) { if (param->name) @@ -306,6 +371,38 @@ int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, return 1; } +int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, + const unsigned char *name, size_t namelen) + { + return int_x509_param_set1(¶m->host, ¶m->hostlen, + name, namelen); + } + +int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param, + const unsigned char *email, size_t emaillen) + { + return int_x509_param_set1(¶m->email, ¶m->emaillen, + email, emaillen); + } + +int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, + const unsigned char *ip, size_t iplen) + { + if (iplen != 0 && iplen != 4 && iplen != 16) + return 0; + return int_x509_param_set1(¶m->ip, ¶m->iplen, ip, iplen); + } + +int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc) + { + unsigned char ipout[16]; + int iplen; + iplen = a2i_ipadd(ipout, ipasc); + if (iplen == 0) + return 0; + return X509_VERIFY_PARAM_set1_ip(param, ipout, (size_t)iplen); + } + int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param) { return param->depth;