New functions to check a hostname email or IP address against a
authorDr. Stephen Henson <steve@openssl.org>
Mon, 8 Oct 2012 15:10:07 +0000 (15:10 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Mon, 8 Oct 2012 15:10:07 +0000 (15:10 +0000)
certificate. Add options to s_client, s_server and x509 utilities
to print results of checks.

CHANGES
apps/apps.c
apps/apps.h
apps/s_apps.h
apps/s_cb.c
apps/s_client.c
apps/s_server.c
apps/x509.c
crypto/x509v3/v3_utl.c
crypto/x509v3/x509v3.h

diff --git a/CHANGES b/CHANGES
index be586a2f8a0d1ca1f55080e4261fe2d16d2f50b0..79d31f2d007c17065a23f191b8c387bb9283a84f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,11 @@
 
  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
      received by client and send back to server. Also prints an abbreviated
      summary of the connection parameters.
index 490ae3b61bdc810a238de7ec6649e46dfc4b9b40..0ce0af5505b9ac75134bcde1663d5c7892d68933 100644 (file)
@@ -2791,6 +2791,35 @@ unsigned char *next_protos_parse(unsigned short *outlen, const char *in)
        }
 #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
  */
index c1ca99da12e8bfa75bdd71c2ceaee5311c4e5e2e..4c9f95a1ce2228c1ca85e0c523e66934d9cab134 100644 (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);
 #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_ASN1     1
 #define FORMAT_TEXT     2
index b1ae531367da3f5a423f3215fe0f8eb2096e8ad4..31b89234f19a5f5686e14fe29fec438c1ce23d34 100644 (file)
@@ -191,3 +191,7 @@ int args_excert(char ***pargs, int *pargc,
                        int *badarg, BIO *err, SSL_EXCERT **pexc);
 int load_excert(SSL_EXCERT **pexc, BIO *err);
 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);
index e339a6ce5d5ea26c7c17049d2f2acba17bc116e0..5a2222ea6bd9ea5e7491b12911176c4287a2e5d1 100644 (file)
@@ -1533,3 +1533,16 @@ void print_ssl_summary(BIO *bio, SSL *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);
+               }
+       }
index a7b150b02a6412964e7dcdef36966237155c404f..5ecc957302376120f2e0fe16a91dcad649748f39 100644 (file)
@@ -634,6 +634,9 @@ int MAIN(int argc, char **argv)
 #endif
        SSL_EXCERT *exc = NULL;
 
+       unsigned char *checkhost = NULL, *checkemail = NULL;
+       char *checkip = NULL;
+
        meth=SSLv23_client_method();
 
        apps_startup();
@@ -993,6 +996,21 @@ int MAIN(int argc, char **argv)
                        client_sigalgs= *(++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);
+                       }
 #ifndef OPENSSL_NO_JPAKE
                else if (strcmp(*argv,"-jpake") == 0)
                        {
@@ -1628,6 +1646,8 @@ 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--;
 
index e89f057ccafdc019efba9f3eed777bb8ba75c899..00dc219eb7694bf58a0613800f5f4f0f12ac0dc1 100644 (file)
@@ -1003,6 +1003,10 @@ int MAIN(int argc, char *argv[])
        char *srp_verifier_file = NULL;
 #endif
        SSL_EXCERT *exc = NULL;
+
+       unsigned char *checkhost = NULL, *checkemail = NULL;
+       char *checkip = NULL;
+
        meth=SSLv23_server_method();
 
        local_argc=argc;
@@ -1260,6 +1264,21 @@ int MAIN(int argc, char *argv[])
                        client_sigalgs= *(++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)
@@ -2661,6 +2680,8 @@ 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);
index e9f1163088cfc17346aa2d1036a9e9f1a6ee58a0..788eb7b3d0a94ae13b8624f478107874ced073c4 100644 (file)
@@ -208,6 +208,8 @@ int MAIN(int argc, char **argv)
        int need_rand = 0;
        int checkend=0,checkoffset=0;
        unsigned long nmflag = 0, certflag = 0;
+       unsigned char *checkhost = NULL, *checkemail = NULL;
+       char *checkip = NULL;
 #ifndef OPENSSL_NO_ENGINE
        char *engine=NULL;
 #endif
@@ -456,6 +458,21 @@ int MAIN(int argc, char **argv)
                        checkoffset=atoi(*(++argv));
                        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)
                        noout= ++num;
                else if (strcmp(*argv,"-trustout") == 0)
@@ -1061,6 +1078,8 @@ bad:
                goto end;
                }
 
+       print_cert_checks(STDout, x, checkhost, checkemail, checkip);
+
        if (noout)
                {
                ret=0;
index e0302345400af1dec202ac6674e4ba9a98271f9f..db9f05ebb95d028deed78cbc6f28ac34f776b5b2 100644 (file)
@@ -568,6 +568,134 @@ void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
        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 
  * OCTET STRING compatible with RFC3280.
  */
index bf409997e759e5b7fefc99615e74b88c8e2595e7..23f7091db0b760770f65d17b11976c3359bebf57 100644 (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);
 void X509_email_free(STACK_OF(OPENSSL_STRING) *sk);
 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_NC(const char *ipasc);