New functions to check a hostname email or IP address against a

certificate. Add options to s_client, s_server and x509 utilities
to print results of checks.
This commit is contained in:
Dr. Stephen Henson 2012-10-08 15:10:07 +00:00
parent c5f6da54fc
commit a70da5b3ec
10 changed files with 256 additions and 0 deletions

View File

@ -4,6 +4,11 @@
Changes between 1.0.x and 1.1.0 [xx XXX xxxx] Changes between 1.0.x and 1.1.0 [xx XXX xxxx]
*) New functions to check a hostname email or IP address against a
certificate. Add options to s_client, s_server and x509 utilities
to print results of checks against a certificate.
[Steve Henson]
*) Add -rev test option to s_server to just reverse order of characters *) Add -rev test option to s_server to just reverse order of characters
received by client and send back to server. Also prints an abbreviated received by client and send back to server. Also prints an abbreviated
summary of the connection parameters. summary of the connection parameters.

View File

@ -2791,6 +2791,35 @@ unsigned char *next_protos_parse(unsigned short *outlen, const char *in)
} }
#endif /* !OPENSSL_NO_TLSEXT && !OPENSSL_NO_NEXTPROTONEG */ #endif /* !OPENSSL_NO_TLSEXT && !OPENSSL_NO_NEXTPROTONEG */
void print_cert_checks(BIO *bio, X509 *x,
const unsigned char *checkhost,
const unsigned char *checkemail,
const char *checkip)
{
if (x == NULL)
return;
if (checkhost)
{
BIO_printf(bio, "Hostname %s does%s match certificate\n",
checkhost, X509_check_host(x, checkhost, 0, 0)
? "" : " NOT");
}
if (checkemail)
{
BIO_printf(bio, "Email %s does%s match certificate\n",
checkemail, X509_check_email(x, checkemail, 0,
0) ? "" : " NOT");
}
if (checkip)
{
BIO_printf(bio, "IP %s does%s match certificate\n",
checkip, X509_check_ip_asc(x, checkip,
0) ? "" : " NOT");
}
}
/* /*
* Platform-specific sections * Platform-specific sections
*/ */

View File

@ -335,6 +335,11 @@ void jpake_server_auth(BIO *out, BIO *conn, const char *secret);
unsigned char *next_protos_parse(unsigned short *outlen, const char *in); unsigned char *next_protos_parse(unsigned short *outlen, const char *in);
#endif /* !OPENSSL_NO_TLSEXT && !OPENSSL_NO_NEXTPROTONEG */ #endif /* !OPENSSL_NO_TLSEXT && !OPENSSL_NO_NEXTPROTONEG */
void print_cert_checks(BIO *bio, X509 *x,
const unsigned char *checkhost,
const unsigned char *checkemail,
const char *checkip);
#define FORMAT_UNDEF 0 #define FORMAT_UNDEF 0
#define FORMAT_ASN1 1 #define FORMAT_ASN1 1
#define FORMAT_TEXT 2 #define FORMAT_TEXT 2

View File

@ -191,3 +191,7 @@ int args_excert(char ***pargs, int *pargc,
int *badarg, BIO *err, SSL_EXCERT **pexc); int *badarg, BIO *err, SSL_EXCERT **pexc);
int load_excert(SSL_EXCERT **pexc, BIO *err); int load_excert(SSL_EXCERT **pexc, BIO *err);
void print_ssl_summary(BIO *bio, SSL *s); void print_ssl_summary(BIO *bio, SSL *s);
void print_ssl_cert_checks(BIO *bio, SSL *s,
const unsigned char *checkhost,
const unsigned char *checkemail,
const char *checkip);

View File

@ -1533,3 +1533,16 @@ void print_ssl_summary(BIO *bio, SSL *s)
ssl_print_tmp_key(bio, s); ssl_print_tmp_key(bio, s);
} }
void print_ssl_cert_checks(BIO *bio, SSL *s,
const unsigned char *checkhost,
const unsigned char *checkemail,
const char *checkip)
{
X509 *peer;
peer = SSL_get_peer_certificate(s);
if (peer)
{
print_cert_checks(bio, peer, checkhost, checkemail, checkip);
X509_free(peer);
}
}

View File

@ -634,6 +634,9 @@ int MAIN(int argc, char **argv)
#endif #endif
SSL_EXCERT *exc = NULL; SSL_EXCERT *exc = NULL;
unsigned char *checkhost = NULL, *checkemail = NULL;
char *checkip = NULL;
meth=SSLv23_client_method(); meth=SSLv23_client_method();
apps_startup(); apps_startup();
@ -993,6 +996,21 @@ int MAIN(int argc, char **argv)
client_sigalgs= *(++argv); client_sigalgs= *(++argv);
} }
#endif #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 #ifndef OPENSSL_NO_JPAKE
else if (strcmp(*argv,"-jpake") == 0) else if (strcmp(*argv,"-jpake") == 0)
{ {
@ -1628,6 +1646,8 @@ SSL_set_tlsext_status_ids(con, ids);
"CONNECTION ESTABLISHED\n"); "CONNECTION ESTABLISHED\n");
print_ssl_summary(bio_err, con); print_ssl_summary(bio_err, con);
} }
print_ssl_cert_checks(bio_err, con, checkhost,
checkemail, checkip);
print_stuff(bio_c_out,con,full_log); print_stuff(bio_c_out,con,full_log);
if (full_log > 0) full_log--; if (full_log > 0) full_log--;

View File

@ -1003,6 +1003,10 @@ int MAIN(int argc, char *argv[])
char *srp_verifier_file = NULL; char *srp_verifier_file = NULL;
#endif #endif
SSL_EXCERT *exc = NULL; SSL_EXCERT *exc = NULL;
unsigned char *checkhost = NULL, *checkemail = NULL;
char *checkip = NULL;
meth=SSLv23_server_method(); meth=SSLv23_server_method();
local_argc=argc; local_argc=argc;
@ -1260,6 +1264,21 @@ int MAIN(int argc, char *argv[])
client_sigalgs= *(++argv); client_sigalgs= *(++argv);
} }
#endif #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) else if (strcmp(*argv,"-msg") == 0)
{ s_msg=1; } { s_msg=1; }
else if (strcmp(*argv,"-msgfile") == 0) else if (strcmp(*argv,"-msgfile") == 0)
@ -2661,6 +2680,8 @@ static int init_ssl_connection(SSL *con)
if (s_brief) if (s_brief)
print_ssl_summary(bio_err, con); 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)); PEM_write_bio_SSL_SESSION(bio_s_out,SSL_get_session(con));
peer=SSL_get_peer_certificate(con); peer=SSL_get_peer_certificate(con);

View File

@ -208,6 +208,8 @@ int MAIN(int argc, char **argv)
int need_rand = 0; int need_rand = 0;
int checkend=0,checkoffset=0; int checkend=0,checkoffset=0;
unsigned long nmflag = 0, certflag = 0; unsigned long nmflag = 0, certflag = 0;
unsigned char *checkhost = NULL, *checkemail = NULL;
char *checkip = NULL;
#ifndef OPENSSL_NO_ENGINE #ifndef OPENSSL_NO_ENGINE
char *engine=NULL; char *engine=NULL;
#endif #endif
@ -456,6 +458,21 @@ int MAIN(int argc, char **argv)
checkoffset=atoi(*(++argv)); checkoffset=atoi(*(++argv));
checkend=1; checkend=1;
} }
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,"-noout") == 0) else if (strcmp(*argv,"-noout") == 0)
noout= ++num; noout= ++num;
else if (strcmp(*argv,"-trustout") == 0) else if (strcmp(*argv,"-trustout") == 0)
@ -1061,6 +1078,8 @@ bad:
goto end; goto end;
} }
print_cert_checks(STDout, x, checkhost, checkemail, checkip);
if (noout) if (noout)
{ {
ret=0; ret=0;

View File

@ -568,6 +568,134 @@ void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
sk_OPENSSL_STRING_pop_free(sk, str_free); sk_OPENSSL_STRING_pop_free(sk, str_free);
} }
/* Compare an ASN1_STRING to a supplied string. If they match
* return 1. If cmp_type > 0 only compare if string matches the
* type, otherwise convert it to UTF8.
*/
static int do_check_string(ASN1_STRING *a, int cmp_type,
const unsigned char *b, size_t blen)
{
if (!a->data || !a->length)
return 0;
if (cmp_type > 0)
{
if (cmp_type != a->type)
return 0;
if (a->length == (int)blen && !memcmp(a->data, b, blen))
return 1;
else
return 0;
}
else
{
int astrlen, rv;
unsigned char *astr;
astrlen = ASN1_STRING_to_UTF8(&astr, a);
if (astrlen < 0)
return 0;
if (astrlen == (int)blen && !memcmp(astr, b, blen))
rv = 1;
else
rv = 0;
OPENSSL_free(astr);
return rv;
}
}
static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags, int check_type)
{
GENERAL_NAMES *gens = NULL;
X509_NAME *name = NULL;
int i;
int cnid;
if (check_type == GEN_EMAIL)
cnid = NID_pkcs9_emailAddress;
else if (check_type == GEN_DNS)
cnid = NID_commonName;
else
cnid = 0;
if (chklen == 0)
chklen = strlen((const char *)chk);
gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
if (gens)
{
int rv = 0;
int alt_type;
if (cnid)
alt_type = V_ASN1_IA5STRING;
else
alt_type = V_ASN1_OCTET_STRING;
for (i = 0; i < sk_GENERAL_NAME_num(gens); i++)
{
GENERAL_NAME *gen;
ASN1_STRING *cstr;
gen = sk_GENERAL_NAME_value(gens, i);
if(gen->type != check_type)
continue;
if (check_type == GEN_EMAIL)
cstr = gen->d.rfc822Name;
else if (check_type == GEN_DNS)
cstr = gen->d.dNSName;
else
cstr = gen->d.iPAddress;
if (do_check_string(cstr, alt_type, chk, chklen))
{
rv = 1;
break;
}
}
GENERAL_NAMES_free(gens);
if (rv)
return 1;
if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid)
return 0;
}
i = -1;
name = X509_get_subject_name(x);
while((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0)
{
X509_NAME_ENTRY *ne;
ASN1_STRING *str;
ne = X509_NAME_get_entry(name, i);
str = X509_NAME_ENTRY_get_data(ne);
if (do_check_string(str, -1, chk, chklen))
return 1;
}
return 0;
}
int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags)
{
return do_x509_check(x, chk, chklen, flags, GEN_DNS);
}
int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags)
{
return do_x509_check(x, chk, chklen, flags, GEN_EMAIL);
}
int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags)
{
return do_x509_check(x, chk, chklen, flags, GEN_IPADD);
}
int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags)
{
unsigned char ipout[16];
int iplen;
iplen = a2i_ipadd(ipout, ipasc);
if (iplen == 0)
return 0;
return do_x509_check(x, ipout, (size_t)iplen, flags, GEN_IPADD);
}
/* Convert IP addresses both IPv4 and IPv6 into an /* Convert IP addresses both IPv4 and IPv6 into an
* OCTET STRING compatible with RFC3280. * OCTET STRING compatible with RFC3280.
*/ */

View File

@ -700,6 +700,18 @@ STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x);
STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x); STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x);
void X509_email_free(STACK_OF(OPENSSL_STRING) *sk); void X509_email_free(STACK_OF(OPENSSL_STRING) *sk);
STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x); STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
/* Flags for X509_check_* functions */
/* Always check subject name for host match even if subject alt names present */
#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1
int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags);
int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags);
int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags);
int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags);
ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc); ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc);
ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc); ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc);