summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--buffer.c14
-rw-r--r--buffer.h2
-rw-r--r--init.c8
-rw-r--r--options.c134
-rw-r--r--options.h12
-rw-r--r--plugin.c1
-rw-r--r--ssl.c264
-rw-r--r--syshead.h6
8 files changed, 418 insertions, 23 deletions
diff --git a/buffer.c b/buffer.c
index cfff06e..3b856bc 100644
--- a/buffer.c
+++ b/buffer.c
@@ -461,6 +461,20 @@ string_alloc (const char *str, struct gc_arena *gc)
}
/*
+ * Erase all characters in a string
+ */
+void
+string_clear (char *str)
+{
+ if (str)
+ {
+ const int len = strlen (str);
+ if (len > 0)
+ memset (str, 0, len);
+ }
+}
+
+/*
* Allocate a string inside a buffer
*/
struct buffer
diff --git a/buffer.h b/buffer.h
index 1e5266b..17bc37e 100644
--- a/buffer.h
+++ b/buffer.h
@@ -83,7 +83,7 @@ void free_buf (struct buffer *buf);
bool buf_assign (struct buffer *dest, const struct buffer *src);
-
+void string_clear (char *str);
/* for dmalloc debugging */
diff --git a/init.c b/init.c
index ac40dc5..4e3b6e7 100644
--- a/init.c
+++ b/init.c
@@ -1365,6 +1365,14 @@ do_init_crypto_tls_c1 (struct context *c)
&c->c1.ks.tls_auth_key,
options->tls_auth_file,
options->key_direction);
+
+#if ENABLE_INLINE_FILES
+ if (options->priv_key_file_inline)
+ {
+ string_clear (c->options.priv_key_file_inline);
+ c->options.priv_key_file_inline = NULL;
+ }
+#endif
}
else
{
diff --git a/options.c b/options.c
index 4b66a45..2df07f4 100644
--- a/options.c
+++ b/options.c
@@ -2622,6 +2622,102 @@ bypass_doubledash (char **p)
*p += 2;
}
+#if ENABLE_INLINE_FILES
+
+struct in_src {
+# define IS_TYPE_FP 1
+# define IS_TYPE_BUF 2
+ int type;
+ union {
+ FILE *fp;
+ struct buffer *multiline;
+ } u;
+};
+
+static bool
+in_src_get (const struct in_src *is, char *line, const int size)
+{
+ if (is->type == IS_TYPE_FP)
+ {
+ return BOOL_CAST (fgets (line, size, is->u.fp));
+ }
+ else if (is->type == IS_TYPE_BUF)
+ {
+ bool status = buf_parse (is->u.multiline, '\n', line, size);
+ if ((int) strlen (line) + 1 < size)
+ strcat (line, "\n");
+ return status;
+ }
+ else
+ {
+ ASSERT (0);
+ return false;
+ }
+}
+
+static char *
+read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc)
+{
+ char line[OPTION_LINE_SIZE];
+ struct buffer buf = alloc_buf (10000);
+ char *ret;
+ while (in_src_get (is, line, sizeof (line)))
+ {
+ if (!strncmp (line, close_tag, strlen (close_tag)))
+ break;
+ buf_printf (&buf, line);
+ }
+ ret = string_alloc (BSTR (&buf), gc);
+ buf_clear (&buf);
+ free_buf (&buf);
+ CLEAR (line);
+ return ret;
+}
+
+static bool
+check_inline_file (struct in_src *is, char *p[], struct gc_arena *gc)
+{
+ bool ret = false;
+ if (p[0] && !p[1])
+ {
+ char *arg = p[0];
+ if (arg[0] == '<' && arg[strlen(arg)-1] == '>')
+ {
+ struct buffer close_tag;
+ arg[strlen(arg)-1] = '\0';
+ p[0] = string_alloc (arg+1, gc);
+ p[1] = string_alloc (INLINE_FILE_TAG, gc);
+ close_tag = alloc_buf (strlen(p[0]) + 4);
+ buf_printf (&close_tag, "</%s>", p[0]);
+ p[2] = read_inline_file (is, BSTR (&close_tag), gc);
+ p[3] = NULL;
+ free_buf (&close_tag);
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+static bool
+check_inline_file_via_fp (FILE *fp, char *p[], struct gc_arena *gc)
+{
+ struct in_src is;
+ is.type = IS_TYPE_FP;
+ is.u.fp = fp;
+ return check_inline_file (&is, p, gc);
+}
+
+static bool
+check_inline_file_via_buf (struct buffer *multiline, char *p[], struct gc_arena *gc)
+{
+ struct in_src is;
+ is.type = IS_TYPE_BUF;
+ is.u.multiline = multiline;
+ return check_inline_file (&is, p, gc);
+}
+
+#endif
+
static int
add_option (struct options *options,
int i,
@@ -2665,6 +2761,9 @@ read_config_file (struct options *options,
if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc))
{
bypass_doubledash (&p[0]);
+#if ENABLE_INLINE_FILES
+ check_inline_file_via_fp (fp, p, &options->gc);
+#endif
add_option (options, 0, p, file, line_num, level, msglevel, permission_mask, option_types_found, es);
}
}
@@ -2679,6 +2778,8 @@ read_config_file (struct options *options,
{
msg (msglevel, "In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself.", top_file, top_line, file);
}
+ CLEAR (line);
+ CLEAR (p);
}
static void
@@ -2704,9 +2805,14 @@ read_config_string (struct options *options,
if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc))
{
bypass_doubledash (&p[0]);
+#if ENABLE_INLINE_FILES
+ check_inline_file_via_buf (&multiline, p, &options->gc);
+#endif
add_option (options, 0, p, NULL, line_num, 0, msglevel, permission_mask, option_types_found, es);
}
+ CLEAR (p);
}
+ CLEAR (line);
}
void
@@ -4724,6 +4830,13 @@ add_option (struct options *options,
++i;
VERIFY_PERMISSION (OPT_P_GENERAL);
options->ca_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ ++i;
+ options->ca_file_inline = p[2];
+ }
+#endif
}
else if (streq (p[0], "capath") && p[1])
{
@@ -4736,12 +4849,26 @@ add_option (struct options *options,
++i;
VERIFY_PERMISSION (OPT_P_GENERAL);
options->dh_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ ++i;
+ options->dh_file_inline = p[2];
+ }
+#endif
}
else if (streq (p[0], "cert") && p[1])
{
++i;
VERIFY_PERMISSION (OPT_P_GENERAL);
options->cert_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ ++i;
+ options->cert_file_inline = p[2];
+ }
+#endif
}
#ifdef WIN32
else if (streq (p[0], "cryptoapicert") && p[1])
@@ -4756,6 +4883,13 @@ add_option (struct options *options,
++i;
VERIFY_PERMISSION (OPT_P_GENERAL);
options->priv_key_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ ++i;
+ options->priv_key_file_inline = p[2];
+ }
+#endif
}
else if (streq (p[0], "pkcs12") && p[1])
{
diff --git a/options.h b/options.h
index 85f989d..aa2b1c1 100644
--- a/options.h
+++ b/options.h
@@ -82,6 +82,10 @@ struct options_pre_pull
#endif
+#if ENABLE_INLINE_FILES
+#define INLINE_FILE_TAG "[[INLINE]]"
+#endif
+
/* Command line options */
struct options
{
@@ -378,6 +382,14 @@ struct options
const char *tls_verify;
const char *tls_remote;
const char *crl_file;
+
+#if ENABLE_INLINE_FILES
+ const char *ca_file_inline;
+ const char *cert_file_inline;
+ char *priv_key_file_inline;
+ const char *dh_file_inline;
+#endif
+
int ns_cert_type; /* set to 0, NS_SSL_SERVER, or NS_SSL_CLIENT */
const char *pkcs11_providers[MAX_PARMS];
const char *pkcs11_sign_mode[MAX_PARMS];
diff --git a/plugin.c b/plugin.c
index 0449293..26d1d1b 100644
--- a/plugin.c
+++ b/plugin.c
@@ -622,6 +622,7 @@ openvpn_plugin_string_list_item_free (struct openvpn_plugin_string_list *l)
if (l)
{
free (l->name);
+ string_clear (l->value);
free (l->value);
free (l);
}
diff --git a/ssl.c b/ssl.c
index 945edd0..2b78bd0 100644
--- a/ssl.c
+++ b/ssl.c
@@ -734,6 +734,170 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
}
}
+#if ENABLE_INLINE_FILES
+
+static int
+use_inline_load_verify_locations (SSL_CTX *ctx, const char *ca_string)
+{
+ X509_STORE *store = NULL;
+ X509* cert = NULL;
+ BIO *in = NULL;
+ int ret = 0;
+
+ in = BIO_new_mem_buf ((char *)ca_string, -1);
+ if (!in)
+ goto err;
+
+ for (;;)
+ {
+ if (!PEM_read_bio_X509 (in, &cert, 0, NULL))
+ {
+ ret = 1;
+ break;
+ }
+ if (!cert)
+ break;
+
+ store = SSL_CTX_get_cert_store (ctx);
+ if (!store)
+ break;
+
+ if (!X509_STORE_add_cert (store, cert))
+ break;
+
+ if (cert)
+ {
+ X509_free (cert);
+ cert = NULL;
+ }
+ }
+
+ err:
+ if (cert)
+ X509_free (cert);
+ if (in)
+ BIO_free (in);
+ return ret;
+}
+
+static int
+xname_cmp(const X509_NAME * const *a, const X509_NAME * const *b)
+{
+ return(X509_NAME_cmp(*a,*b));
+}
+
+static STACK_OF(X509_NAME) *
+use_inline_load_client_CA_file (SSL_CTX *ctx, const char *ca_string)
+{
+ BIO *in = NULL;
+ X509 *x = NULL;
+ X509_NAME *xn = NULL;
+ STACK_OF(X509_NAME) *ret = NULL, *sk;
+
+ sk=sk_X509_NAME_new(xname_cmp);
+
+ in = BIO_new_mem_buf ((char *)ca_string, -1);
+ if (!in)
+ goto err;
+
+ if ((sk == NULL) || (in == NULL))
+ goto err;
+
+ for (;;)
+ {
+ if (PEM_read_bio_X509(in,&x,NULL,NULL) == NULL)
+ break;
+ if (ret == NULL)
+ {
+ ret = sk_X509_NAME_new_null();
+ if (ret == NULL)
+ goto err;
+ }
+ if ((xn=X509_get_subject_name(x)) == NULL) goto err;
+ /* check for duplicates */
+ xn=X509_NAME_dup(xn);
+ if (xn == NULL) goto err;
+ if (sk_X509_NAME_find(sk,xn) >= 0)
+ X509_NAME_free(xn);
+ else
+ {
+ sk_X509_NAME_push(sk,xn);
+ sk_X509_NAME_push(ret,xn);
+ }
+ }
+
+ if (0)
+ {
+ err:
+ if (ret != NULL) sk_X509_NAME_pop_free(ret,X509_NAME_free);
+ ret=NULL;
+ }
+ if (sk != NULL) sk_X509_NAME_free(sk);
+ if (in != NULL) BIO_free(in);
+ if (x != NULL) X509_free(x);
+ if (ret != NULL)
+ ERR_clear_error();
+ return(ret);
+}
+
+static int
+use_inline_certificate_file (SSL_CTX *ctx, const char *cert_string)
+{
+ BIO *in = NULL;
+ X509 *x = NULL;
+ int ret = 0;
+
+ in = BIO_new_mem_buf ((char *)cert_string, -1);
+ if (!in)
+ goto end;
+
+ x = PEM_read_bio_X509 (in,
+ NULL,
+ ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata);
+ if (!x)
+ goto end;
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ end:
+ if (x)
+ X509_free (x);
+ if (in)
+ BIO_free (in);
+ return ret;
+}
+
+static int
+use_inline_PrivateKey_file (SSL_CTX *ctx, const char *key_string)
+{
+ BIO *in = NULL;
+ EVP_PKEY *pkey = NULL;
+ int ret = 0;
+
+ in = BIO_new_mem_buf ((char *)key_string, -1);
+ if (!in)
+ goto end;
+
+ pkey = PEM_read_bio_PrivateKey (in,
+ NULL,
+ ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata);
+ if (!pkey)
+ goto end;
+
+ ret = SSL_CTX_use_PrivateKey (ctx, pkey);
+
+ end:
+ if (pkey)
+ EVP_PKEY_free (pkey);
+ if (in)
+ BIO_free (in);
+ return ret;
+}
+
+#endif
+
/*
* Initialize SSL context.
* All files are in PEM format.
@@ -756,9 +920,20 @@ init_ssl (const struct options *options)
SSL_CTX_set_tmp_rsa_callback (ctx, tmp_rsa_cb);
- /* Get Diffie Hellman Parameters */
- if (!(bio = BIO_new_file (options->dh_file, "r")))
- msg (M_SSLERR, "Cannot open %s for DH parameters", options->dh_file);
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->dh_file, INLINE_FILE_TAG) && options->dh_file_inline)
+ {
+ if (!(bio = BIO_new_mem_buf ((char *)options->dh_file_inline, -1)))
+ msg (M_SSLERR, "Cannot open memory BIO for inline DH parameters");
+ }
+ else
+#endif
+ {
+ /* Get Diffie Hellman Parameters */
+ if (!(bio = BIO_new_file (options->dh_file, "r")))
+ msg (M_SSLERR, "Cannot open %s for DH parameters", options->dh_file);
+ }
+
dh = PEM_read_bio_DHparams (bio, NULL, NULL, NULL);
BIO_free (bio);
if (!dh)
@@ -874,15 +1049,37 @@ init_ssl (const struct options *options)
/* Load Certificate */
if (options->cert_file)
{
- using_cert_file = true;
- if (!SSL_CTX_use_certificate_file (ctx, options->cert_file, SSL_FILETYPE_PEM))
- msg (M_SSLERR, "Cannot load certificate file %s", options->cert_file);
+#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))
+ msg (M_SSLERR, "Cannot load inline certificate file");
+ }
+ else
+#endif
+ {
+ if (!SSL_CTX_use_certificate_file (ctx, options->cert_file, SSL_FILETYPE_PEM))
+ msg (M_SSLERR, "Cannot load certificate file %s", options->cert_file);
+ using_cert_file = true;
+ }
}
/* Load Private Key */
if (options->priv_key_file)
{
- if (!SSL_CTX_use_PrivateKey_file (ctx, options->priv_key_file, SSL_FILETYPE_PEM))
+ int status;
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->priv_key_file, INLINE_FILE_TAG) && options->priv_key_file_inline)
+ {
+ status = use_inline_PrivateKey_file (ctx, options->priv_key_file_inline);
+ }
+ else
+#endif
+ {
+ status = SSL_CTX_use_PrivateKey_file (ctx, options->priv_key_file, SSL_FILETYPE_PEM);
+ }
+ if (!status)
{
#ifdef ENABLE_MANAGEMENT
if (management && (ERR_GET_REASON (ERR_peek_error()) == EVP_R_BAD_DECRYPT))
@@ -902,34 +1099,57 @@ init_ssl (const struct options *options)
if (options->ca_file || options->ca_path)
{
- /* Load CA file for verifying peer supplied certificate */
- ASSERT (options->ca_file || options->ca_path);
- if (!SSL_CTX_load_verify_locations (ctx, options->ca_file, options->ca_path))
- msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", options->ca_file, options->ca_path);
+ int status;
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline)
+ {
+ status = use_inline_load_verify_locations (ctx, options->ca_file_inline);
+ }
+ else
+#endif
+ {
+ /* Load CA file for verifying peer supplied certificate */
+ status = SSL_CTX_load_verify_locations (ctx, options->ca_file, options->ca_path);
+ }
+
+ if (!status)
+ msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", options->ca_file, options->ca_path);
/* Set a store for certs (CA & CRL) with a lookup on the "capath" hash directory */
if (options->ca_path) {
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
- if (store) {
- X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
- if (!X509_LOOKUP_add_dir(lookup, options->ca_path, X509_FILETYPE_PEM))
- X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
- else
- msg(M_WARN, "WARNING: experimental option --capath %s", options->ca_path);
+ if (store)
+ {
+ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+ if (!X509_LOOKUP_add_dir(lookup, options->ca_path, X509_FILETYPE_PEM))
+ X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
+ else
+ msg(M_WARN, "WARNING: experimental option --capath %s", options->ca_path);
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
- X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+ X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
#else
- msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath");
+ msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath");
#endif
- } else
+ }
+ else
msg(M_SSLERR, "Cannot get certificate store (SSL_CTX_get_cert_store)");
}
/* Load names of CAs from file and use it as a client CA list */
if (options->ca_file) {
- STACK_OF(X509_NAME) *cert_names;
- cert_names = SSL_load_client_CA_file (options->ca_file);
+ STACK_OF(X509_NAME) *cert_names = NULL;
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline)
+ {
+ cert_names = use_inline_load_client_CA_file (ctx, options->ca_file_inline);
+ }
+ else
+#endif
+ {
+ cert_names = SSL_load_client_CA_file (options->ca_file);
+ }
if (!cert_names)
msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_load_client_CA_file)", options->ca_file);
SSL_CTX_set_client_CA_list (ctx, cert_names);
diff --git a/syshead.h b/syshead.h
index c5ef47b..4c79286 100644
--- a/syshead.h
+++ b/syshead.h
@@ -466,4 +466,10 @@ socket_defined (const socket_descriptor_t sd)
#define EPOLL 0
#endif
+/*
+ * Should we allow ca/cert/key files to be
+ * included inline, in the configuration file?
+ */
+#define ENABLE_INLINE_FILES 1
+
#endif