#include "syshead.h"
-#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR)
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR) || defined(MANAGMENT_EXTERNAL_KEY)
#include "base64.h"
}
int
-base64_decode(const char *str, void *data)
+base64_decode(const char *str, void *data, int size)
{
const char *p;
unsigned char *q;
+ unsigned char *e = NULL;
q = data;
+ if (size >= 0)
+ e = q + size;
for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
unsigned int val = token_decode(p);
unsigned int marker = (val >> 24) & 0xff;
if (val == DECODE_ERROR)
return -1;
+ if (e && q >= e)
+ return -1;
*q++ = (val >> 16) & 0xff;
if (marker < 2)
+ {
+ if (e && q >= e)
+ return -1;
*q++ = (val >> 8) & 0xff;
+ }
if (marker < 1)
+ {
+ if (e && q >= e)
+ return -1;
*q++ = val & 0xff;
+ }
}
return q - (unsigned char *) data;
}
#ifndef _BASE64_H_
#define _BASE64_H_
-#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR)
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR) || defined(MANAGMENT_EXTERNAL_KEY)
int base64_encode(const void *data, int size, char **str);
-int base64_decode(const char *str, void *data);
+int base64_decode(const char *str, void *data, int size);
#endif
bool
buffer_list_defined (const struct buffer_list *ol)
{
- return ol->head != NULL;
+ return ol && ol->head != NULL;
}
void
#ifdef MANAGEMENT_PF
msg (M_CLIENT, "client-pf CID : Define packet filter for client CID (MULTILINE)");
#endif
+#endif
+#ifdef MANAGMENT_EXTERNAL_KEY
+ msg (M_CLIENT, "rsa-sig : Enter an RSA signature in response to >RSA_SIGN challenge");
+ msg (M_CLIENT, " Enter signature base64 on subsequent lines followed by END");
#endif
msg (M_CLIENT, "signal s : Send signal s to daemon,");
msg (M_CLIENT, " s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2.");
msg (M_CLIENT, "SUCCESS: hold=%d", BOOL_CAST(man->settings.flags & MF_HOLD));
}
-#ifdef MANAGEMENT_DEF_AUTH
-
-static bool
-parse_cid (const char *str, unsigned long *cid)
-{
- if (sscanf (str, "%lu", cid) == 1)
- return true;
- else
- {
- msg (M_CLIENT, "ERROR: cannot parse CID");
- return false;
- }
-}
+#ifdef MANAGEMENT_IN_EXTRA
-static bool
-parse_kid (const char *str, unsigned int *kid)
-{
- if (sscanf (str, "%u", kid) == 1)
- return true;
- else
- {
- msg (M_CLIENT, "ERROR: cannot parse KID");
- return false;
- }
-}
+#define IER_RESET 0
+#define IER_NEW 1
+#define IER_CONDRESET 2
static void
-in_extra_reset (struct man_connection *mc, const bool new)
+in_extra_reset (struct man_connection *mc, const int mode)
{
- if (mc)
+ if (mc && (mc->in_extra_cmd < IEC_STATEFUL_BASE || mode != IER_CONDRESET))
{
- if (!new)
+ if (mode != IER_NEW)
{
mc->in_extra_cmd = IEC_UNDEF;
+#ifdef MANAGEMENT_DEF_AUTH
mc->in_extra_cid = 0;
mc->in_extra_kid = 0;
+#endif
}
if (mc->in_extra)
{
buffer_list_free (mc->in_extra);
mc->in_extra = NULL;
}
- if (new)
+ if (mode == IER_NEW)
mc->in_extra = buffer_list_new (0);
}
}
{
switch (man->connection.in_extra_cmd)
{
+#ifdef MANAGEMENT_DEF_AUTH
case IEC_CLIENT_AUTH:
if (man->persist.callback.client_auth)
{
msg (M_CLIENT, "ERROR: The client-auth command is not supported by the current daemon mode");
}
break;
+#endif
#ifdef MANAGEMENT_PF
case IEC_CLIENT_PF:
if (man->persist.callback.client_pf)
}
break;
#endif
+#ifdef MANAGMENT_EXTERNAL_KEY
+ case IEC_RSA_SIGN:
+ man->connection.in_extra_cmd = IEC_RSA_SIGN_FINAL;
+ return;
+#endif
+ }
+ in_extra_reset (&man->connection, IER_RESET);
+}
+
+#endif /* MANAGEMENT_IN_EXTRA */
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+static bool
+parse_cid (const char *str, unsigned long *cid)
+{
+ if (sscanf (str, "%lu", cid) == 1)
+ return true;
+ else
+ {
+ msg (M_CLIENT, "ERROR: cannot parse CID");
+ return false;
+ }
+}
+
+static bool
+parse_kid (const char *str, unsigned int *kid)
+{
+ if (sscanf (str, "%u", kid) == 1)
+ return true;
+ else
+ {
+ msg (M_CLIENT, "ERROR: cannot parse KID");
+ return false;
}
- in_extra_reset (&man->connection, false);
}
static void
&& parse_kid (kid_str, &mc->in_extra_kid))
{
mc->in_extra_cmd = IEC_CLIENT_AUTH;
- in_extra_reset (mc, true);
+ in_extra_reset (mc, IER_NEW);
if (!extra)
in_extra_dispatch (man);
}
if (parse_cid (cid_str, &mc->in_extra_cid))
{
mc->in_extra_cmd = IEC_CLIENT_PF;
- in_extra_reset (mc, true);
+ in_extra_reset (mc, IER_NEW);
}
}
-#endif
+#endif /* MANAGEMENT_PF */
+#endif /* MANAGEMENT_DEF_AUTH */
+
+#ifdef MANAGMENT_EXTERNAL_KEY
+
+static void
+man_rsa_sig (struct management *man)
+{
+ struct man_connection *mc = &man->connection;
+ if (mc->in_extra_cmd == IEC_RSA_SIGN_PRE)
+ {
+ in_extra_reset (&man->connection, IER_NEW);
+ mc->in_extra_cmd = IEC_RSA_SIGN;
+ }
+ else
+ msg (M_CLIENT, "ERROR: The rsa-sig command is not currently available");
+}
+
#endif
static void
}
#endif
#endif
+#ifdef MANAGMENT_EXTERNAL_KEY
+ else if (streq (p[0], "rsa-sig"))
+ {
+ man_rsa_sig (man);
+ }
+#endif
#ifdef ENABLE_PKCS11
else if (streq (p[0], "pkcs11-id-count"))
{
man->connection.state = MS_INITIAL;
command_line_reset (man->connection.in);
buffer_list_reset (man->connection.out);
-#ifdef MANAGEMENT_DEF_AUTH
- in_extra_reset (&man->connection, false);
+#ifdef MANAGEMENT_IN_EXTRA
+ in_extra_reset (&man->connection, IER_RESET);
#endif
msg (D_MANAGEMENT, "MANAGEMENT: Client disconnected");
}
CLEAR (parms);
so = status_open (NULL, 0, -1, &man->persist.vout, 0);
-#ifdef MANAGEMENT_DEF_AUTH
- in_extra_reset (&man->connection, false);
+#ifdef MANAGEMENT_IN_EXTRA
+ in_extra_reset (&man->connection, IER_CONDRESET);
#endif
if (man_password_needed (man))
const unsigned char *line;
while ((line = command_line_get (man->connection.in)))
{
-#ifdef MANAGEMENT_DEF_AUTH
+#ifdef MANAGEMENT_IN_EXTRA
if (man->connection.in_extra)
{
if (!strcmp ((char *)line, "END"))
- {
- in_extra_dispatch (man);
- in_extra_reset (&man->connection, false);
- }
+ in_extra_dispatch (man);
else
- {
- buffer_list_push (man->connection.in_extra, line);
- }
+ buffer_list_push (man->connection.in_extra, line);
}
else
#endif
command_line_free (mc->in);
if (mc->out)
buffer_list_free (mc->out);
-#ifdef MANAGEMENT_DEF_AUTH
- in_extra_reset (&man->connection, false);
+#ifdef MANAGEMENT_IN_EXTRA
+ in_extra_reset (&man->connection, IER_RESET);
#endif
man_connection_clear (mc);
}
gc_free (&gc);
}
-#endif
+#endif /* MANAGEMENT_DEF_AUTH */
void
management_echo (struct management *man, const char *string, const bool pull)
#define MWCC_PASSWORD_WAIT (1<<0)
#define MWCC_HOLD_WAIT (1<<1)
+#define MWCC_OTHER_WAIT (1<<2)
/*
* Block until client connects
msg (D_MANAGEMENT, "Need password(s) from management interface, waiting...");
if (flags & MWCC_HOLD_WAIT)
msg (D_MANAGEMENT, "Need hold release from management interface, waiting...");
+ if (flags & MWCC_OTHER_WAIT)
+ msg (D_MANAGEMENT, "Need information from management interface, waiting...");
do {
man_standalone_event_loop (man, signal_received, expire);
if (signal_received && *signal_received)
return ret;
}
+#ifdef MANAGMENT_EXTERNAL_KEY
+
+char * /* returns allocated base64 signature */
+management_query_rsa_sig (struct management *man,
+ const char *b64_data)
+{
+ struct gc_arena gc = gc_new ();
+ char *ret = NULL;
+ volatile int signal_received = 0;
+ struct buffer alert_msg = clear_buf();
+ struct buffer *buf;
+ const bool standalone_disabled_save = man->persist.standalone_disabled;
+
+ if (man_standalone_ok (man))
+ {
+ man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
+ man->persist.special_state_msg = NULL;
+
+ in_extra_reset (&man->connection, IER_RESET);
+ man->connection.in_extra_cmd = IEC_RSA_SIGN_PRE;
+
+ alert_msg = alloc_buf_gc (strlen(b64_data)+64, &gc);
+ buf_printf (&alert_msg, ">RSA_SIGN:%s", b64_data);
+
+ man_wait_for_client_connection (man, &signal_received, 0, MWCC_OTHER_WAIT);
+
+ if (signal_received)
+ goto done;
+
+ man->persist.special_state_msg = BSTR (&alert_msg);
+ msg (M_CLIENT, "%s", man->persist.special_state_msg);
+
+ /* run command processing event loop until we get our signature */
+ do
+ {
+ man_standalone_event_loop (man, &signal_received, 0);
+ if (!signal_received)
+ man_check_for_signals (&signal_received);
+ if (signal_received)
+ goto done;
+ } while (man->connection.in_extra_cmd != IEC_RSA_SIGN_FINAL);
+
+ if (buffer_list_defined(man->connection.in_extra))
+ {
+ buffer_list_aggregate (man->connection.in_extra, 2000);
+ buf = buffer_list_peek (man->connection.in_extra);
+ if (buf && BLEN(buf) > 0)
+ {
+ ret = (char *) malloc(BLEN(buf)+1);
+ check_malloc_return(ret);
+ memcpy(ret, buf->data, BLEN(buf));
+ ret[BLEN(buf)] = '\0';
+ }
+ }
+ }
+
+ done:
+ /* revert state */
+ man->persist.standalone_disabled = standalone_disabled_save;
+ man->persist.special_state_msg = NULL;
+ in_extra_reset (&man->connection, IER_RESET);
+
+ gc_free (&gc);
+ return ret;
+}
+
+#endif
+
/*
* Return true if management_hold() would block
*/
struct command_line *in;
struct buffer_list *out;
-#ifdef MANAGEMENT_DEF_AUTH
+#ifdef MANAGEMENT_IN_EXTRA
# define IEC_UNDEF 0
# define IEC_CLIENT_AUTH 1
# define IEC_CLIENT_PF 2
+
+# define IEC_STATEFUL_BASE 16
+# define IEC_RSA_SIGN_PRE 16
+# define IEC_RSA_SIGN 17
+# define IEC_RSA_SIGN_FINAL 18
int in_extra_cmd;
+ struct buffer_list *in_extra;
+#ifdef MANAGEMENT_DEF_AUTH
unsigned long in_extra_cid;
unsigned int in_extra_kid;
- struct buffer_list *in_extra;
int env_filter_level;
+#endif
#endif
struct event_set *es;
const char *up_query_type;
int up_query_mode;
struct user_pass up_query;
+
+#ifdef MANAGMENT_EXTERNAL_KEY
+ struct buffer_list *rsa_sig;
+#endif
};
struct management
# define MF_CLIENT_PF (1<<7)
#endif
# define MF_UNIX_SOCK (1<<8)
+#ifdef MANAGMENT_EXTERNAL_KEY
+# define MF_EXTERNAL_KEY (1<<9)
+#endif
bool management_open (struct management *man,
const char *addr,
const bool primary);
#endif
+#ifdef MANAGMENT_EXTERNAL_KEY
+
+char *management_query_rsa_sig (struct management *man, const char *b64_data);
+
+#endif
+
static inline bool
management_connected (const struct management *man)
{
if (!buf_parse(&b, ':', work, len))
return NULL;
ac->user = (char *) gc_malloc (strlen(work)+1, true, gc);
- base64_decode(work, (void*)ac->user);
+ base64_decode(work, (void*)ac->user, -1);
/* parse challenge text */
ac->challenge_text = string_alloc(BSTR(&b), gc);
/* pad to 21 bytes */
memset (md4_hash + 16, 0, 5);
- ret_val = base64_decode( phase_2, (void *)buf2);
+ ret_val = base64_decode( phase_2, (void *)buf2, -1);
if (ret_val < 0)
return NULL;
options->management_flags |= MF_CONNECT_AS_CLIENT;
options->management_write_peer_info_file = p[1];
}
+#ifdef MANAGMENT_EXTERNAL_KEY
+ else if (streq (p[0], "management-external-key"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_EXTERNAL_KEY;
+ options->priv_key_file = "EXTERNAL_PRIVATE_KEY";
+ }
+#endif
#ifdef MANAGEMENT_DEF_AUTH
else if (streq (p[0], "management-client-auth"))
{
#include "gremlin.h"
#include "pkcs11.h"
#include "list.h"
+#include "base64.h"
#ifdef WIN32
#include "cryptoapi.h"
}
}
+#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
}
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;
X509_free (x);
if (in)
BIO_free (in);
+ if (x509)
+ *x509 = x;
return ret;
}
DH *dh;
BIO *bio;
bool using_cert_file = false;
+ X509 *my_cert = NULL;
ERR_clear_error ();
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;
}
}
#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)
{
err:
ERR_clear_error ();
+ if (my_cert)
+ X509_free(my_cert);
if (ctx)
SSL_CTX_free (ctx);
return NULL;
#define ENABLE_DEF_AUTH
#endif
+/*
+ * Enable external private key
+ */
+#if defined(ENABLE_MANAGEMENT) && defined(USE_SSL)
+#define MANAGMENT_EXTERNAL_KEY
+#endif
+
+/*
+ * MANAGEMENT_IN_EXTRA allows the management interface to
+ * read multi-line inputs from clients.
+ */
+#if defined(MANAGEMENT_DEF_AUTH) || defined(MANAGMENT_EXTERNAL_KEY)
+#define MANAGEMENT_IN_EXTRA
+#endif
+
/*
* Enable packet filter?
*/