summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--init.c3
-rw-r--r--openvpn.827
-rw-r--r--options.c52
-rw-r--r--options.h3
-rw-r--r--ssl.c41
-rw-r--r--ssl.h1
-rw-r--r--version.m42
7 files changed, 128 insertions, 1 deletions
diff --git a/init.c b/init.c
index ef09e8e..b1f65a9 100644
--- a/init.c
+++ b/init.c
@@ -1706,9 +1706,11 @@ socket_restart_pause (struct context *c)
if (auth_retry_get () == AR_NOINTERACT)
sec = 10;
+#if 0 /* not really needed because of c->persist.restart_sleep_seconds */
if (c->options.server_poll_timeout && sec > 1)
sec = 1;
#endif
+#endif
if (c->persist.restart_sleep_seconds > 0 && c->persist.restart_sleep_seconds > sec)
sec = c->persist.restart_sleep_seconds;
@@ -2057,6 +2059,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.ns_cert_type = options->ns_cert_type;
memmove (to.remote_cert_ku, options->remote_cert_ku, sizeof (to.remote_cert_ku));
to.remote_cert_eku = options->remote_cert_eku;
+ to.verify_hash = options->verify_hash;
to.es = c->c2.es;
#ifdef ENABLE_DEBUG
diff --git a/openvpn.8 b/openvpn.8
index 1953b16..85889de 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -3887,6 +3887,22 @@ that for certificate authority functions, you must set up the files
).
.\"*********************************************************
.TP
+.B --extra-certs file
+Specify a
+.B file
+containing one or more PEM certs (concatenated together)
+that complete the
+local certificate chain.
+
+This option is useful for "split" CAs, where the CA for server
+certs is different than the CA for client certs. Putting certs
+in this file allows them to be used to complete the local
+certificate chain without trusting them to verify the peer-submitted
+certificate, as would be the case if the certs were placed in the
+.B ca
+file.
+.\"*********************************************************
+.TP
.B --key file
Local peer's private key in .pem format. Use the private key which was generated
when you built your peer's certificate (see
@@ -3903,6 +3919,17 @@ and
.B --key.
.\"*********************************************************
.TP
+.B --verify-hash hash
+Specify SHA1 fingerprint for level-1 cert. The level-1 cert is the
+CA (or intermediate cert) that signs the leaf certificate, and is
+one removed from the leaf certificate in the direction of the root.
+When accepting a connection from a peer, the level-1 cert
+fingerprint must match
+.B hash
+or certificate verification will fail. Hash is specified
+as XX:XX:... For example: AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
+.\"*********************************************************
+.TP
.B --pkcs11-cert-private [0|1]...
Set if access to certificate object should be performed after login.
Every provider has its own setting.
diff --git a/options.c b/options.c
index fd3fec6..1cfccd3 100644
--- a/options.c
+++ b/options.c
@@ -507,9 +507,11 @@ static const char usage_message[] =
" Use \"openssl dhparam -out dh1024.pem 1024\" to generate.\n"
"--cert file : Local certificate in .pem format -- must be signed\n"
" by a Certificate Authority in --ca file.\n"
+ "--extra-certs file : one or more PEM certs that complete the cert chain.\n"
"--key file : Local private key in .pem format.\n"
"--pkcs12 file : PKCS#12 file containing local private key, local certificate\n"
" and optionally the root CA certificate.\n"
+ "--verify-hash : Specify SHA1 fingerprint for level-1 cert.\n"
#ifdef WIN32
"--cryptoapicert select-string : Load the certificate and private key from the\n"
" Windows Certificate System Store.\n"
@@ -894,6 +896,40 @@ is_stateful_restart (const struct options *o)
return is_persist_option (o) || connection_list_defined (o);
}
+#ifdef USE_SSL
+static uint8_t *
+parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_arena *gc)
+{
+ int i;
+ const char *cp = str;
+ uint8_t *ret = (uint8_t *) gc_malloc (nbytes, true, gc);
+ char term = 1;
+ int byte;
+ char bs[3];
+
+ for (i = 0; i < nbytes; ++i)
+ {
+ if (strlen(cp) < 2)
+ msg (msglevel, "format error in hash fingerprint: %s", str);
+ bs[0] = *cp++;
+ bs[1] = *cp++;
+ bs[2] = 0;
+ byte = 0;
+ if (sscanf(bs, "%x", &byte) != 1)
+ msg (msglevel, "format error in hash fingerprint hex byte: %s", str);
+ ret[i] = (uint8_t)byte;
+ term = *cp++;
+ if (term != ':' && term != 0)
+ msg (msglevel, "format error in hash fingerprint delimiter: %s", str);
+ if (term == 0)
+ break;
+ }
+ if (term != 0 || i != nbytes-1)
+ msg (msglevel, "hash fingerprint is different length than expected (%d bytes): %s", nbytes, str);
+ return ret;
+}
+#endif
+
#ifdef WIN32
#ifdef ENABLE_DEBUG
@@ -5758,6 +5794,22 @@ add_option (struct options *options,
}
#endif
}
+ else if (streq (p[0], "extra-certs") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->extra_certs_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->extra_certs_file_inline = p[2];
+ }
+#endif
+ }
+ else if (streq (p[0], "verify-hash") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->verify_hash = parse_hash_fingerprint(p[1], SHA_DIGEST_LENGTH, msglevel, &options->gc);
+ }
#ifdef WIN32
else if (streq (p[0], "cryptoapicert") && p[1])
{
diff --git a/options.h b/options.h
index 74ba9d4..f0baabe 100644
--- a/options.h
+++ b/options.h
@@ -477,6 +477,7 @@ struct options
const char *ca_path;
const char *dh_file;
const char *cert_file;
+ const char *extra_certs_file;
const char *priv_key_file;
const char *pkcs12_file;
const char *cipher_list;
@@ -487,6 +488,7 @@ struct options
#if ENABLE_INLINE_FILES
const char *ca_file_inline;
const char *cert_file_inline;
+ const char *extra_certs_file_inline;
char *priv_key_file_inline;
const char *dh_file_inline;
const char *pkcs12_file_inline; /* contains the base64 encoding of pkcs12 file */
@@ -495,6 +497,7 @@ struct options
int ns_cert_type; /* set to 0, NS_SSL_SERVER, or NS_SSL_CLIENT */
unsigned remote_cert_ku[MAX_PARMS];
const char *remote_cert_eku;
+ uint8_t *verify_hash;
#ifdef ENABLE_PKCS11
const char *pkcs11_providers[MAX_PARMS];
diff --git a/ssl.c b/ssl.c
index 095b6a6..df237cc 100644
--- a/ssl.c
+++ b/ssl.c
@@ -910,6 +910,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
goto err; /* Reject connection */
}
+ /* verify level 1 cert, i.e. the CA that signed our leaf cert */
+ if (ctx->error_depth == 1 && opt->verify_hash)
+ {
+ if (memcmp (ctx->current_cert->sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed");
+ goto err;
+ }
+ }
+
/* save common name in session object */
if (ctx->error_depth == 0)
set_common_name (session, common_name);
@@ -2140,6 +2150,37 @@ init_ssl (const struct options *options)
msg (M_SSLERR, "Cannot load certificate chain file %s (SSL_use_certificate_chain_file)", options->cert_file);
}
+ /* Load extra certificates that are part of our own certificate
+ chain but shouldn't be included in the verify chain */
+ if (options->extra_certs_file || options->extra_certs_file_inline)
+ {
+ BIO *bio;
+ X509 *cert;
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->extra_certs_file, INLINE_FILE_TAG) && options->extra_certs_file_inline)
+ {
+ bio = BIO_new_mem_buf ((char *)options->extra_certs_file_inline, -1);
+ }
+ else
+#endif
+ {
+ bio = BIO_new(BIO_s_file());
+ if (BIO_read_filename(bio, options->extra_certs_file) <= 0)
+ msg (M_SSLERR, "Cannot load extra-certs file: %s", options->extra_certs_file);
+ }
+ for (;;)
+ {
+ cert = NULL;
+ if (!PEM_read_bio_X509 (bio, &cert, 0, NULL)) /* takes ownership of cert */
+ break;
+ if (!cert)
+ msg (M_SSLERR, "Error reading extra-certs certificate");
+ if (SSL_CTX_add_extra_chain_cert(ctx, cert) != 1)
+ msg (M_SSLERR, "Error adding extra-certs certificate");
+ }
+ BIO_free (bio);
+ }
+
/* Require peer certificate verification */
#if P2MP_SERVER
if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED)
diff --git a/ssl.h b/ssl.h
index fe494b1..1b23d7d 100644
--- a/ssl.h
+++ b/ssl.h
@@ -466,6 +466,7 @@ struct tls_options
int ns_cert_type;
unsigned remote_cert_ku[MAX_PARMS];
const char *remote_cert_eku;
+ uint8_t *verify_hash;
/* allow openvpn config info to be
passed over control channel */
diff --git a/version.m4 b/version.m4
index 85cd043..3642b71 100644
--- a/version.m4
+++ b/version.m4
@@ -1,5 +1,5 @@
dnl define the OpenVPN version
-define(PRODUCT_VERSION,[2.1.3u])
+define(PRODUCT_VERSION,[2.1.3v])
dnl define the TAP version
define(PRODUCT_TAP_ID,[tap0901])
define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])