aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xeasy-rsa/2.0/openssl.cnf2
-rw-r--r--init.c2
-rw-r--r--openvpn.855
-rw-r--r--options.c58
-rw-r--r--options.h2
-rw-r--r--ssl.c117
-rw-r--r--ssl.h2
7 files changed, 238 insertions, 0 deletions
diff --git a/easy-rsa/2.0/openssl.cnf b/easy-rsa/2.0/openssl.cnf
index 7fedebe..b430b83 100755
--- a/easy-rsa/2.0/openssl.cnf
+++ b/easy-rsa/2.0/openssl.cnf
@@ -207,6 +207,8 @@ nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
[ v3_req ]
diff --git a/init.c b/init.c
index bd90b80..0c9a509 100644
--- a/init.c
+++ b/init.c
@@ -1445,6 +1445,8 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.verify_x509name = options->tls_remote;
to.crl_file = options->crl_file;
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.es = c->c2.es;
#ifdef ENABLE_DEBUG
diff --git a/openvpn.8 b/openvpn.8
index 0c634a9..7d14524 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -225,6 +225,9 @@ openvpn \- secure IP tunnel daemon.
[\ \fB\-\-remap\-usr1\fR\ \fIsignal\fR\ ]
[\ \fB\-\-remote\-random\fR\ ]
[\ \fB\-\-remote\fR\ \fIhost\ [port]\fR\ ]
+[\ \fB\-\-remote\-cert\-ku\ \fIv...\fR\ ]
+[\ \fB\-\-remote\-cert\-eku\ \fIoid\fR\ ]
+[\ \fB\-\-remote\-cert\-tls\ \fIt\fR\ ]
[\ \fB\-\-reneg\-bytes\fR\ \fIn\fR\ ]
[\ \fB\-\-reneg\-pkts\fR\ \fIn\fR\ ]
[\ \fB\-\-reneg\-sec\fR\ \fIn\fR\ ]
@@ -4044,6 +4047,58 @@ or
.B --tls-verify.
.\"*********************************************************
.TP
+.B --remote-cert-ku v...
+Require that peer certificate was signed with an explicit
+.B key usage.
+
+This is useful security option for clients, to ensure that
+the host they connect with is a designated server.
+
+The key usage should be encoded in hex, more than one key
+usage can be specified.
+.\"*********************************************************
+.TP
+.B --remote-cert-eku oid
+Require that peer certificate was signed with an explicit
+.B extended key usage.
+
+This is useful security option for clients, to ensure that
+the host they connect with is a designated server.
+
+The extended key usage should be encoded in oid notation, or
+OpenSSL symbolic representation.
+.\"*********************************************************
+.TP
+.B --remote-cert-tls client|server
+Require that peer certificate was signed with an explicit
+.B key usage
+and
+.B extended key usage
+based on TLS rules.
+
+This is a useful security option for clients, to ensure that
+the host they connect with is a designated server.
+
+The
+.B --remote-cert-tls client
+option is equivalent to
+.B --remote-cert-ku 80 08 88 --remote-cert-eku \fB"TLS Web Client Authentication"
+
+The
+.B --remote-cert-tls server
+option is equivalent to
+.B --remote-cert-ku a0 08 --remote-cert-eku \fB"TLS Web Server Authentication"
+
+This is an important security precaution to protect against
+a man-in-the-middle attack where an authorized client
+attempts to connect to another client by impersonating the server.
+The attack is easily prevented by having clients verify
+the server certificate using any one of
+.B --remote-cert-tls, --tls-remote,
+or
+.B --tls-verify.
+.\"*********************************************************
+.TP
.B --crl-verify crl
Check peer certificate against the file
.B crl
diff --git a/options.c b/options.c
index 466ba13..97fb130 100644
--- a/options.c
+++ b/options.c
@@ -463,6 +463,17 @@ static const char usage_message[] =
" of verification.\n"
"--ns-cert-type t: Require that peer certificate was signed with an explicit\n"
" nsCertType designation t = 'client' | 'server'.\n"
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ "--remote-cert-ku v ... : Require that the peer certificate was signed with\n"
+ " explicit key usage, you can specify more than one value.\n"
+ " value should be given in hex format.\n"
+ "--remote-cert-eku oid : Require that the peer certificate was signed with\n"
+ " explicit extended key usage. Extended key usage can be encoded\n"
+ " as on object identifier or OpenSSL string representation.\n"
+ "--remote-cert-tls t: Require that peer certificate was signed with explicit\n"
+ " key usage and extended key usage based on TLS rules.\n"
+ " t = 'client | 'server'.\n"
+#endif /* OPENSSL_VERSION_NUMBER */
#endif /* USE_SSL */
#ifdef ENABLE_PKCS11
"\n"
@@ -1197,6 +1208,12 @@ show_settings (const struct options *o)
SHOW_STR (tls_remote);
SHOW_STR (crl_file);
SHOW_INT (ns_cert_type);
+ {
+ int i;
+ for (i=0;i<MAX_PARMS;i++)
+ SHOW_INT (remote_cert_ku[i]);
+ }
+ SHOW_STR (remote_cert_eku);
SHOW_INT (tls_timeout);
@@ -1813,6 +1830,8 @@ options_postprocess (struct options *options, bool first_time)
MUST_BE_UNDEF (crl_file);
MUST_BE_UNDEF (key_method);
MUST_BE_UNDEF (ns_cert_type);
+ MUST_BE_UNDEF (remote_cert_ku[0]);
+ MUST_BE_UNDEF (remote_cert_eku);
#ifdef ENABLE_PKCS11
MUST_BE_UNDEF (pkcs11_providers[0]);
MUST_BE_UNDEF (pkcs11_sign_mode[0]);
@@ -4786,6 +4805,45 @@ add_option (struct options *options,
goto err;
}
}
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ else if (streq (p[0], "remote-cert-ku"))
+ {
+ int j;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ sscanf (p[j], "%x", &(options->remote_cert_ku[j-1]));
+ }
+ else if (streq (p[0], "remote-cert-eku") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->remote_cert_eku = p[1];
+ }
+ else if (streq (p[0], "remote-cert-tls") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ if (streq (p[1], "server"))
+ {
+ options->remote_cert_ku[0] = 0xa0;
+ options->remote_cert_ku[1] = 0x08;
+ options->remote_cert_eku = "TLS Web Server Authentication";
+ }
+ else if (streq (p[1], "client"))
+ {
+ options->remote_cert_ku[0] = 0x80;
+ options->remote_cert_ku[1] = 0x08;
+ options->remote_cert_ku[2] = 0x88;
+ options->remote_cert_eku = "TLS Web Client Authentication";
+ }
+ else
+ {
+ msg (msglevel, "--remote-cert-tls must be 'client' or 'server'");
+ goto err;
+ }
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
else if (streq (p[0], "tls-timeout") && p[1])
{
VERIFY_PERMISSION (OPT_P_TLS_PARMS);
diff --git a/options.h b/options.h
index 6ae6aee..6d7b785 100644
--- a/options.h
+++ b/options.h
@@ -392,6 +392,8 @@ struct options
#endif
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;
const char *pkcs11_providers[MAX_PARMS];
const char *pkcs11_sign_mode[MAX_PARMS];
const char *pkcs11_slot_type;
diff --git a/ssl.c b/ssl.c
index 2b78bd0..d76cda6 100644
--- a/ssl.c
+++ b/ssl.c
@@ -389,6 +389,91 @@ set_common_name (struct tls_session *session, const char *common_name)
}
}
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+
+bool verify_cert_eku (X509 *x509, const char * const expected_oid) {
+
+ EXTENDED_KEY_USAGE *eku = NULL;
+ bool fFound = false;
+
+ if ((eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i (x509, NID_ext_key_usage, NULL, NULL)) == NULL) {
+ msg (D_HANDSHAKE, "Certificate does not have extended key usage extension");
+ }
+ else {
+ int i;
+
+ msg (D_HANDSHAKE, "Validating certificate extended key usage");
+ for(i = 0; !fFound && i < sk_ASN1_OBJECT_num (eku); i++) {
+ ASN1_OBJECT *oid = sk_ASN1_OBJECT_value (eku, i);
+ char szOid[1024];
+
+ if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 0) != -1) {
+ msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", szOid, expected_oid);
+ if (!strcmp (expected_oid, szOid)) {
+ fFound = true;
+ }
+ }
+ if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 1) != -1) {
+ msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", szOid, expected_oid);
+ if (!strcmp (expected_oid, szOid)) {
+ fFound = true;
+ }
+ }
+ }
+ }
+
+ if (eku != NULL) {
+ sk_ASN1_OBJECT_pop_free (eku, ASN1_OBJECT_free);
+ }
+
+ return fFound;
+}
+
+bool verify_cert_ku (X509 *x509, const unsigned * const expected_ku, int expected_len) {
+
+ ASN1_BIT_STRING *ku = NULL;
+ bool fFound = false;
+
+ if ((ku = (ASN1_BIT_STRING *)X509_get_ext_d2i (x509, NID_key_usage, NULL, NULL)) == NULL) {
+ msg (D_HANDSHAKE, "Certificate does not have key usage extension");
+ }
+ else {
+ unsigned nku = 0;
+ int i;
+ for (i=0;i<8;i++) {
+ if (ASN1_BIT_STRING_get_bit (ku, i)) {
+ nku |= 1<<(7-i);
+ }
+ }
+
+ /*
+ * Fixup if no LSB bits
+ */
+ if ((nku & 0xff) == 0) {
+ nku >>= 8;
+ }
+
+ msg (D_HANDSHAKE, "Validating certificate key usage");
+ for (i=0;!fFound && i<expected_len;i++) {
+ if (expected_ku[i] != 0) {
+ msg (D_HANDSHAKE, "++ Certificate has key usage %04x, expects %04x", nku, expected_ku[i]);
+
+ if (nku == expected_ku[i]) {
+ fFound = true;
+ }
+ }
+ }
+ }
+
+ if (ku != NULL) {
+ ASN1_BIT_STRING_free (ku);
+ }
+
+ return fFound;
+}
+
+#endif /* OPENSSL_VERSION_NUMBER */
+
/*
* nsCertType checking
*/
@@ -506,6 +591,38 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
}
}
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+
+ /* verify certificate ku */
+ if (opt->remote_cert_ku[0] != 0 && ctx->error_depth == 0)
+ {
+ if (verify_cert_ku (ctx->current_cert, opt->remote_cert_ku, MAX_PARMS))
+ {
+ msg (D_HANDSHAKE, "VERIFY KU OK");
+ }
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY KU ERROR");
+ goto err; /* Reject connection */
+ }
+ }
+
+ /* verify certificate eku */
+ if (opt->remote_cert_eku != NULL && ctx->error_depth == 0)
+ {
+ if (verify_cert_eku (ctx->current_cert, opt->remote_cert_eku))
+ {
+ msg (D_HANDSHAKE, "VERIFY EKU OK");
+ }
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY EKU ERROR");
+ goto err; /* Reject connection */
+ }
+ }
+
+#endif /* OPENSSL_VERSION_NUMBER */
+
/* verify X509 name or common name against --tls-remote */
if (opt->verify_x509name && strlen (opt->verify_x509name) > 0 && ctx->error_depth == 0)
{
diff --git a/ssl.h b/ssl.h
index 0e41690..4acb259 100644
--- a/ssl.h
+++ b/ssl.h
@@ -410,6 +410,8 @@ struct tls_options
const char *verify_x509name;
const char *crl_file;
int ns_cert_type;
+ unsigned remote_cert_ku[MAX_PARMS];
+ const char *remote_cert_eku;
/* allow openvpn config info to be
passed over control channel */