summaryrefslogtreecommitdiffstats
path: root/ssl.c
diff options
context:
space:
mode:
authorJames Yonan <james@openvpn.net>2010-12-09 11:21:04 +0000
committerJames Yonan <james@openvpn.net>2010-12-09 11:21:04 +0000
commit2a3d17ed182608cf60d731a237f9f926c28db522 (patch)
tree6ffd2d41156ecafe4715447b4b4c006112d6f514 /ssl.c
parent2d12eb12cf2a49adbc2e89e20990415947519900 (diff)
downloadopenvpn-2a3d17ed182608cf60d731a237f9f926c28db522.tar.gz
openvpn-2a3d17ed182608cf60d731a237f9f926c28db522.tar.xz
openvpn-2a3d17ed182608cf60d731a237f9f926c28db522.zip
Added "management-external-key" option. This option can be used
instead of "key" in client mode, and allows the client to run without the need to load the actual private key. When the SSL protocol needs to perform an RSA sign operation, the data to be signed will be sent to the management interface via a notification as follows: >RSA_SIGN:[BASE64_DATA] The management interface client should then sign BASE64_DATA using the private key and return the signature as follows: rsa-sig [BASE64_SIG_LINE] . . . END This capability is intended to allow the use of arbitrary cryptographic service providers with OpenVPN via the management interface. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@6708 e7ae566f-a301-0410-adde-c780ea21d3b5
Diffstat (limited to 'ssl.c')
-rw-r--r--ssl.c214
1 files changed, 211 insertions, 3 deletions
diff --git a/ssl.c b/ssl.c
index 0f93cef..9ab907c 100644
--- a/ssl.c
+++ b/ssl.c
@@ -48,6 +48,7 @@
#include "gremlin.h"
#include "pkcs11.h"
#include "list.h"
+#include "base64.h"
#ifdef WIN32
#include "cryptoapi.h"
@@ -1315,6 +1316,195 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
}
}
+#ifdef MANAGMENT_EXTERNAL_KEY
+
+/* encrypt */
+static int
+rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ ASSERT(0);
+ return -1;
+}
+
+/* verify arbitrary data */
+static int
+rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ ASSERT(0);
+ return -1;
+}
+
+/* decrypt */
+static int
+rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ ASSERT(0);
+ return -1;
+}
+
+/* called at RSA_free */
+static int
+rsa_finish(RSA *rsa)
+{
+ free ((void*)rsa->meth);
+ rsa->meth = NULL;
+ return 1;
+}
+
+/* sign arbitrary data */
+static int
+rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ /* optional app data in rsa->meth->app_data; */
+ char *in_b64 = NULL;
+ char *out_b64 = NULL;
+ int ret = -1;
+ int len;
+
+ if (padding != RSA_PKCS1_PADDING)
+ {
+ RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+ goto done;
+ }
+
+ /* convert 'from' to base64 */
+ if (base64_encode (from, flen, &in_b64) <= 0)
+ goto done;
+
+ /* call MI for signature */
+ if (management)
+ out_b64 = management_query_rsa_sig (management, in_b64);
+ if (!out_b64)
+ goto done;
+
+ /* decode base64 signature to binary */
+ len = RSA_size(rsa);
+ ret = base64_decode (out_b64, to, len);
+
+ /* verify length */
+ if (ret != len)
+ ret = -1;
+
+ done:
+ if (in_b64)
+ free (in_b64);
+ if (out_b64)
+ free (out_b64);
+ return ret;
+}
+
+static int
+use_external_private_key (SSL_CTX *ssl_ctx, X509 *cert)
+{
+ RSA *rsa = NULL;
+ RSA *pub_rsa;
+ RSA_METHOD *rsa_meth;
+
+ /* allocate custom RSA method object */
+ ALLOC_OBJ_CLEAR (rsa_meth, RSA_METHOD);
+ rsa_meth->name = "OpenVPN external private key RSA Method";
+ rsa_meth->rsa_pub_enc = rsa_pub_enc;
+ rsa_meth->rsa_pub_dec = rsa_pub_dec;
+ rsa_meth->rsa_priv_enc = rsa_priv_enc;
+ rsa_meth->rsa_priv_dec = rsa_priv_dec;
+ rsa_meth->init = NULL;
+ rsa_meth->finish = rsa_finish;
+ rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
+ rsa_meth->app_data = NULL;
+
+ /* allocate RSA object */
+ rsa = RSA_new();
+ if (rsa == NULL)
+ {
+ SSLerr(SSL_F_SSL_USE_PRIVATEKEY, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ /* get the public key */
+ ASSERT(cert->cert_info->key->pkey); /* NULL before SSL_CTX_use_certificate() is called */
+ pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
+
+ /* initialize RSA object */
+ rsa->n = BN_dup(pub_rsa->n);
+ rsa->flags |= RSA_FLAG_EXT_PKEY;
+ if (!RSA_set_method(rsa, rsa_meth))
+ goto err;
+
+ /* bind our custom RSA object to ssl_ctx */
+ if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
+ goto err;
+
+ RSA_free(rsa); /* doesn't necessarily free, just decrements refcount */
+ return 1;
+
+ err:
+ if (rsa)
+ RSA_free(rsa);
+ else
+ {
+ if (rsa_meth)
+ free(rsa_meth);
+ }
+ return 0;
+}
+
+/*
+ * Basically a clone of SSL_CTX_use_certificate_file, but also return
+ * the x509 object.
+ */
+static int
+use_certificate_file(SSL_CTX *ctx, const char *file, int type, X509 **x509)
+{
+ int j;
+ BIO *in;
+ int ret=0;
+ X509 *x=NULL;
+
+ in=BIO_new(BIO_s_file_internal());
+ if (in == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,ERR_R_BUF_LIB);
+ goto end;
+ }
+
+ if (BIO_read_filename(in,file) <= 0)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB);
+ goto end;
+ }
+ if (type == SSL_FILETYPE_ASN1)
+ {
+ j=ERR_R_ASN1_LIB;
+ x=d2i_X509_bio(in,NULL);
+ }
+ else if (type == SSL_FILETYPE_PEM)
+ {
+ j=ERR_R_PEM_LIB;
+ x=PEM_read_bio_X509(in,NULL,ctx->default_passwd_callback,ctx->default_passwd_callback_userdata);
+ }
+ else
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE);
+ goto end;
+ }
+
+ if (x == NULL)
+ {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,j);
+ goto end;
+ }
+
+ ret=SSL_CTX_use_certificate(ctx,x);
+ end:
+ if (in != NULL)
+ BIO_free(in);
+ if (x509)
+ *x509 = x;
+ return(ret);
+}
+
+#endif
+
#if ENABLE_INLINE_FILES
static int
@@ -1422,7 +1612,7 @@ use_inline_load_client_CA_file (SSL_CTX *ctx, const char *ca_string)
}
static int
-use_inline_certificate_file (SSL_CTX *ctx, const char *cert_string)
+use_inline_certificate_file (SSL_CTX *ctx, const char *cert_string, X509 **x509)
{
BIO *in = NULL;
X509 *x = NULL;
@@ -1446,6 +1636,8 @@ use_inline_certificate_file (SSL_CTX *ctx, const char *cert_string)
X509_free (x);
if (in)
BIO_free (in);
+ if (x509)
+ *x509 = x;
return ret;
}
@@ -1490,6 +1682,7 @@ init_ssl (const struct options *options)
DH *dh;
BIO *bio;
bool using_cert_file = false;
+ X509 *my_cert = NULL;
ERR_clear_error ();
@@ -1589,7 +1782,6 @@ init_ssl (const struct options *options)
management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL);
#endif
PKCS12_free(p12);
- msg (M_INFO, "OpenSSL ERROR code: %d", (ERR_GET_REASON (ERR_peek_error()))); // fixme
goto err;
}
}
@@ -1657,18 +1849,32 @@ init_ssl (const struct options *options)
#if ENABLE_INLINE_FILES
if (!strcmp (options->cert_file, INLINE_FILE_TAG) && options->cert_file_inline)
{
- if (!use_inline_certificate_file (ctx, options->cert_file_inline))
+ if (!use_inline_certificate_file (ctx, options->cert_file_inline, &my_cert))
msg (M_SSLERR, "Cannot load inline certificate file");
}
else
#endif
{
+#ifdef MANAGMENT_EXTERNAL_KEY
+ if (!use_certificate_file (ctx, options->cert_file, SSL_FILETYPE_PEM, &my_cert))
+#else
if (!SSL_CTX_use_certificate_file (ctx, options->cert_file, SSL_FILETYPE_PEM))
+#endif
msg (M_SSLERR, "Cannot load certificate file %s", options->cert_file);
using_cert_file = true;
}
}
+#ifdef MANAGMENT_EXTERNAL_KEY
+ if (options->management_flags & MF_EXTERNAL_KEY)
+ {
+ ASSERT (my_cert);
+ if (!use_external_private_key(ctx, my_cert))
+ msg (M_SSLERR, "Cannot enable SSL external private key capability");
+ }
+ else
+#endif
+
/* Load Private Key */
if (options->priv_key_file)
{
@@ -1795,6 +2001,8 @@ init_ssl (const struct options *options)
err:
ERR_clear_error ();
+ if (my_cert)
+ X509_free(my_cert);
if (ctx)
SSL_CTX_free (ctx);
return NULL;