src/openvpn/ssl_verify_mbedtls.c
53f97e1e
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
  *  Copyright (C) 2010 Fox Crypto B.V. <openvpn@fox-it.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program (see the file COPYING included with this
  *  distribution); if not, write to the Free Software Foundation, Inc.,
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 /**
86d8cd68
  * @file Control Channel Verification Module mbed TLS backend
53f97e1e
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
31ea2ee4
 #include "syshead.h"
 
86d8cd68
 #if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS)
31ea2ee4
 
74586c65
 #include "crypto_mbedtls.h"
53f97e1e
 #include "ssl_verify.h"
86d8cd68
 #include <mbedtls/asn1.h>
 #include <mbedtls/error.h>
 #include <mbedtls/bignum.h>
 #include <mbedtls/oid.h>
 #include <mbedtls/sha1.h>
53f97e1e
 
 #define MAX_SUBJECT_LENGTH 256
 
 int
86d8cd68
 verify_callback (void *session_obj, mbedtls_x509_crt *cert, int cert_depth,
     uint32_t *flags)
53f97e1e
 {
   struct tls_session *session = (struct tls_session *) session_obj;
00b973f8
   struct gc_arena gc = gc_new();
53f97e1e
 
   ASSERT (cert);
   ASSERT (session);
 
   session->verified = false;
 
   /* Remember certificate hash */
8e5613c2
   cert_hash_remember (session, cert_depth, x509_get_sha1_hash(cert, &gc));
53f97e1e
 
   /* did peer present cert which was signed by our root cert? */
4a56d19f
   if (*flags != 0)
53f97e1e
     {
00b973f8
       char *subject = x509_get_subject(cert, &gc);
53f97e1e
 
00b973f8
       if (subject)
d572959d
 	msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, flags=%x, %s", cert_depth, *flags, subject);
00b973f8
       else
d572959d
 	msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, flags=%x, could not extract X509 "
 	      "subject string from certificate", *flags, cert_depth);
53f97e1e
 
4a56d19f
       /* Leave flags set to non-zero to indicate that the cert is not ok */
     }
   else if (SUCCESS != verify_cert(session, cert, cert_depth))
     {
86d8cd68
       *flags |= MBEDTLS_X509_BADCERT_OTHER;
53f97e1e
     }
 
75b49e40
   gc_free(&gc);
 
53f97e1e
   /*
86d8cd68
    * PolarSSL/mbed TLS-1.2.0+ expects 0 on anything except fatal errors.
53f97e1e
    */
4a56d19f
   return 0;
53f97e1e
 }
 
 #ifdef ENABLE_X509ALTUSERNAME
86d8cd68
 # warning "X509 alt user name not yet supported for mbed TLS"
53f97e1e
 #endif
 
8a840d83
 result_t
03df3a99
 backend_x509_get_username (char *cn, int cn_len,
86d8cd68
     char *x509_username_field, mbedtls_x509_crt *cert)
53f97e1e
 {
86d8cd68
   mbedtls_x509_name *name;
53f97e1e
 
   ASSERT( cn != NULL );
 
   name = &cert->subject;
 
   /* Find common name */
   while( name != NULL )
   {
86d8cd68
       if (0 == memcmp (name->oid.p, MBEDTLS_OID_AT_CN,
 	  MBEDTLS_OID_SIZE (MBEDTLS_OID_AT_CN)))
53f97e1e
 	break;
 
       name = name->next;
   }
 
   /* Not found, return an error if this is the peer's certificate */
   if( name == NULL )
8a840d83
       return FAILURE;
53f97e1e
 
   /* Found, extract CN */
   if (cn_len > name->val.len)
63559e14
     {
       memcpy( cn, name->val.p, name->val.len );
       cn[name->val.len] = '\0';
     }
53f97e1e
   else
     {
       memcpy( cn, name->val.p, cn_len);
       cn[cn_len-1] = '\0';
     }
 
8a840d83
   return SUCCESS;
53f97e1e
 }
 
 char *
86d8cd68
 backend_x509_get_serial (mbedtls_x509_crt *cert, struct gc_arena *gc)
53f97e1e
 {
   char *buf = NULL;
f80a52b0
   size_t buflen = 0;
86d8cd68
   mbedtls_mpi serial_mpi = { 0 };
f80a52b0
 
86d8cd68
   /* Transform asn1 integer serial into mbed TLS MPI */
   mbedtls_mpi_init(&serial_mpi);
   if (!mbed_ok(mbedtls_mpi_read_binary(&serial_mpi, cert->serial.p,
       cert->serial.len)))
f80a52b0
     {
d17d362d
       msg(M_WARN, "Failed to retrieve serial from certificate.");
f80a52b0
       return NULL;
     }
 
   /* Determine decimal representation length, allocate buffer */
86d8cd68
   mbedtls_mpi_write_string(&serial_mpi, 10, NULL, 0, &buflen);
f80a52b0
   buf = gc_malloc(buflen, true, gc);
 
   /* Write MPI serial as decimal string into buffer */
86d8cd68
   if (!mbed_ok(mbedtls_mpi_write_string(&serial_mpi, 10, buf, buflen, &buflen)))
f80a52b0
     {
d17d362d
       msg(M_WARN, "Failed to write serial to string.");
f80a52b0
       return NULL;
     }
 
   return buf;
 }
 
 char *
86d8cd68
 backend_x509_get_serial_hex (mbedtls_x509_crt *cert, struct gc_arena *gc)
f80a52b0
 {
   char *buf = NULL;
2e2939e8
   size_t len = cert->serial.len * 3 + 1;
53f97e1e
 
025f30d7
   buf = gc_malloc(len, true, gc);
53f97e1e
 
86d8cd68
   if(mbedtls_x509_serial_gets(buf, len-1, &cert->serial) < 0)
025f30d7
     buf = NULL;
53f97e1e
 
   return buf;
 }
 
 unsigned char *
86d8cd68
 x509_get_sha1_hash (mbedtls_x509_crt *cert, struct gc_arena *gc)
53f97e1e
 {
8e5613c2
   unsigned char *sha1_hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc);
86d8cd68
   mbedtls_sha1(cert->raw.p, cert->tbs.len, sha1_hash);
53f97e1e
   return sha1_hash;
 }
 
 char *
86d8cd68
 x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc)
53f97e1e
 {
   char tmp_subject[MAX_SUBJECT_LENGTH] = {0};
   char *subject = NULL;
 
   int ret = 0;
 
86d8cd68
   ret = mbedtls_x509_dn_gets( tmp_subject, MAX_SUBJECT_LENGTH-1, &cert->subject );
53f97e1e
   if (ret > 0)
     {
       /* Allocate the required space for the subject */
00b973f8
       subject = string_alloc(tmp_subject, gc);
53f97e1e
     }
 
   return subject;
 }
 
fab49d17
 static void
 do_setenv_x509 (struct env_set *es, const char *name, char *value, int depth)
 {
   char *name_expand;
   size_t name_expand_size;
 
   string_mod (value, CC_ANY, CC_CRLF, '?');
   msg (D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth);
   name_expand_size = 64 + strlen (name);
   name_expand = (char *) malloc (name_expand_size);
   check_malloc_return (name_expand);
   openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", depth, name);
   setenv_str (es, name_expand, value);
   free (name_expand);
 }
 
 static char *
86d8cd68
 asn1_buf_to_c_string(const mbedtls_asn1_buf *orig, struct gc_arena *gc)
fab49d17
 {
   size_t i;
   char *val;
 
   for (i = 0; i < orig->len; ++i)
     if (orig->p[i] == '\0')
       return "ERROR: embedded null value";
   val = gc_malloc(orig->len+1, false, gc);
   memcpy(val, orig->p, orig->len);
   val[orig->len] = '\0';
   return val;
 }
 
 static void
 do_setenv_name(struct env_set *es, const struct x509_track *xt,
86d8cd68
 	       const mbedtls_x509_crt *cert, int depth, struct gc_arena *gc)
fab49d17
 {
86d8cd68
   const mbedtls_x509_name *xn;
fab49d17
   for (xn = &cert->subject; xn != NULL; xn = xn->next)
     {
       const char *xn_short_name = NULL;
86d8cd68
       if (0 == mbedtls_oid_get_attr_short_name (&xn->oid, &xn_short_name) &&
fab49d17
 	  0 == strcmp (xt->name, xn_short_name))
 	{
 	  char *val_str = asn1_buf_to_c_string (&xn->val, gc);
 	  do_setenv_x509 (es, xt->name, val_str, depth);
 	}
     }
 }
 
 void
 x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc)
 {
   struct x509_track *xt;
   ALLOC_OBJ_CLEAR_GC (xt, struct x509_track, gc);
   if (*name == '+')
     {
       xt->flags |= XT_FULL_CHAIN;
       ++name;
     }
   xt->name = name;
   xt->next = *ll_head;
   *ll_head = xt;
 }
 
 void
86d8cd68
 x509_setenv_track (const struct x509_track *xt, struct env_set *es,
     const int depth, mbedtls_x509_crt *cert)
fab49d17
 {
   struct gc_arena gc = gc_new();
   while (xt)
     {
       if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
 	{
 	  if (0 == strcmp(xt->name, "SHA1"))
 	    {
 	      /* SHA1 fingerprint is not part of X509 structure */
 	      unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc);
 	      char *sha1_fingerprint = format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc);
 	      do_setenv_x509(es, xt->name, sha1_fingerprint, depth);
 	    }
 	  else
 	    {
 	      do_setenv_name(es, xt, cert, depth, &gc);
 	    }
 	}
       xt = xt->next;
     }
   gc_free(&gc);
 }
 
53f97e1e
 /*
  * Save X509 fields to environment, using the naming convention:
  *
  * X509_{cert_depth}_{name}={value}
  */
 void
86d8cd68
 x509_setenv (struct env_set *es, int cert_depth, mbedtls_x509_crt *cert)
53f97e1e
 {
be960aad
   int i;
53f97e1e
   unsigned char c;
86d8cd68
   const mbedtls_x509_name *name;
53f97e1e
   char s[128];
 
   name = &cert->subject;
 
   memset( s, 0, sizeof( s ) );
 
   while( name != NULL )
     {
       char name_expand[64+8];
03df3a99
       const char *shortname;
53f97e1e
 
86d8cd68
       if( 0 == mbedtls_oid_get_attr_short_name(&name->oid, &shortname) )
53f97e1e
 	{
03df3a99
 	  openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_%s",
 	      cert_depth, shortname);
53f97e1e
 	}
03df3a99
       else
 	{
 	  openvpn_snprintf (name_expand, sizeof(name_expand), "X509_%d_\?\?",
 	      cert_depth);
 	}
 
       for( i = 0; i < name->val.len; i++ )
53f97e1e
 	{
03df3a99
 	  if( i >= (int) sizeof( s ) - 1 )
 	      break;
53f97e1e
 
03df3a99
 	  c = name->val.p[i];
 	  if( c < 32 || c == 127 || ( c > 128 && c < 160 ) )
 	       s[i] = '?';
 	  else s[i] = c;
53f97e1e
 	}
 	s[i] = '\0';
 
 	/* Check both strings, set environment variable */
 	string_mod (name_expand, CC_PRINT, CC_CRLF, '_');
 	string_mod ((char*)s, CC_PRINT, CC_CRLF, '_');
13b585e8
 	setenv_str_incr (es, name_expand, (char*)s);
53f97e1e
 
 	name = name->next;
     }
 }
 
8a840d83
 result_t
86d8cd68
 x509_verify_ns_cert_type(const mbedtls_x509_crt *cert, const int usage)
53f97e1e
 {
   if (usage == NS_CERT_CHECK_NONE)
8a840d83
     return SUCCESS;
53f97e1e
   if (usage == NS_CERT_CHECK_CLIENT)
86d8cd68
     return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE)
 	&& (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT)) ?
 	    SUCCESS : FAILURE;
53f97e1e
   if (usage == NS_CERT_CHECK_SERVER)
86d8cd68
     return ((cert->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE)
 	&& (cert->ns_cert_type & MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER)) ?
 	    SUCCESS : FAILURE;
53f97e1e
 
8a840d83
   return FAILURE;
53f97e1e
 }
 
8a840d83
 result_t
86d8cd68
 x509_verify_cert_ku (mbedtls_x509_crt *cert, const unsigned * const expected_ku,
53f97e1e
     int expected_len)
 {
8a840d83
   result_t fFound = FAILURE;
53f97e1e
 
86d8cd68
   if(!(cert->ext_types & MBEDTLS_X509_EXT_KEY_USAGE))
53f97e1e
     {
       msg (D_HANDSHAKE, "Certificate does not have key usage extension");
     }
   else
     {
       int i;
       unsigned nku = cert->key_usage;
 
       msg (D_HANDSHAKE, "Validating certificate key usage");
8a840d83
       for (i=0; SUCCESS != fFound && i<expected_len; i++)
53f97e1e
 	{
 	  if (expected_ku[i] != 0)
 	    {
 	      msg (D_HANDSHAKE, "++ Certificate has key usage  %04x, expects "
 		  "%04x", nku, expected_ku[i]);
 
 	      if (nku == expected_ku[i])
 		{
8a840d83
 		  fFound = SUCCESS;
53f97e1e
 		}
 	    }
 	}
     }
   return fFound;
 }
 
8a840d83
 result_t
86d8cd68
 x509_verify_cert_eku (mbedtls_x509_crt *cert, const char * const expected_oid)
53f97e1e
 {
8a840d83
   result_t fFound = FAILURE;
53f97e1e
 
86d8cd68
   if (!(cert->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE))
53f97e1e
     {
       msg (D_HANDSHAKE, "Certificate does not have extended key usage extension");
     }
   else
     {
86d8cd68
       mbedtls_x509_sequence *oid_seq = &(cert->ext_key_usage);
53f97e1e
 
       msg (D_HANDSHAKE, "Validating certificate extended key usage");
       while (oid_seq != NULL)
 	{
86d8cd68
 	  mbedtls_x509_buf *oid = &oid_seq->buf;
53f97e1e
 	  char oid_num_str[1024];
 	  const char *oid_str;
 
86d8cd68
 	  if (0 == mbedtls_oid_get_extended_key_usage( oid, &oid_str ))
53f97e1e
 	    {
 	      msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s",
 		  oid_str, expected_oid);
 	      if (!strcmp (expected_oid, oid_str))
 		{
8a840d83
 		  fFound = SUCCESS;
53f97e1e
 		  break;
 		}
 	    }
 
86d8cd68
 	  if (0 < mbedtls_oid_get_numeric_string( oid_num_str,
53f97e1e
 	      sizeof (oid_num_str), oid))
 	    {
 	      msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s",
 		  oid_num_str, expected_oid);
 	      if (!strcmp (expected_oid, oid_num_str))
 		{
8a840d83
 		  fFound = SUCCESS;
53f97e1e
 		  break;
 		}
 	    }
 	  oid_seq = oid_seq->next;
 	}
     }
 
     return fFound;
 }
 
8a840d83
 result_t
86d8cd68
 x509_write_pem(FILE *peercert_file, mbedtls_x509_crt *peercert)
53f97e1e
 {
86d8cd68
     msg (M_WARN, "mbed TLS does not support writing peer certificate in PEM format");
8a840d83
     return FAILURE;
53f97e1e
 }
 
 /*
  * check peer cert against CRL
  */
8a840d83
 result_t
86d8cd68
 x509_verify_crl(const char *crl_file, const char *crl_inline,
                 mbedtls_x509_crt *cert, const char *subject)
53f97e1e
 {
8a840d83
   result_t retval = FAILURE;
86d8cd68
   mbedtls_x509_crl crl = {0};
767e4c56
   struct gc_arena gc = gc_new();
   char *serial;
53f97e1e
 
7a7a79f6
   if (!strcmp (crl_file, INLINE_FILE_TAG) && crl_inline)
53f97e1e
     {
86d8cd68
       if (!mbed_ok(mbedtls_x509_crl_parse(&crl,
 	  (const unsigned char *)crl_inline, strlen(crl_inline)+1)))
7a7a79f6
         {
            msg (M_WARN, "CRL: cannot parse inline CRL");
            goto end;
         }
53f97e1e
     }
7a7a79f6
   else
     {
86d8cd68
       if (!mbed_ok(mbedtls_x509_crl_parse_file(&crl, crl_file)))
7a7a79f6
       {
           msg (M_WARN, "CRL: cannot read CRL from file %s", crl_file);
           goto end;
       }
   }
53f97e1e
 
   if(cert->issuer_raw.len != crl.issuer_raw.len ||
       memcmp(crl.issuer_raw.p, cert->issuer_raw.p, crl.issuer_raw.len) != 0)
     {
       msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of "
 	  "certificate %s", crl_file, subject);
8a840d83
       retval = SUCCESS;
53f97e1e
       goto end;
     }
 
86d8cd68
   if (!mbed_ok(mbedtls_x509_crt_is_revoked(cert, &crl)))
53f97e1e
     {
767e4c56
       serial = backend_x509_get_serial_hex(cert, &gc);
       msg (D_HANDSHAKE, "CRL CHECK FAILED: %s (serial %s) is REVOKED", subject, (serial ? serial : "NOT AVAILABLE"));
53f97e1e
       goto end;
     }
 
8a840d83
   retval = SUCCESS;
53f97e1e
   msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
 
 end:
767e4c56
   gc_free(&gc);
86d8cd68
   mbedtls_x509_crl_free(&crl);
8a840d83
   return retval;
53f97e1e
 }
31ea2ee4
 
86d8cd68
 #endif /* #if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_MBEDTLS) */