]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Added "management-external-key" option. This option can be used
authorJames Yonan <james@openvpn.net>
Thu, 9 Dec 2010 11:21:04 +0000 (11:21 +0000)
committerDavid Sommerseth <davids@redhat.com>
Fri, 25 Mar 2011 08:38:28 +0000 (09:38 +0100)
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

base64.c
base64.h
buffer.c
manage.c
manage.h
misc.c
ntlm.c
options.c
ssl.c
syshead.h

index 3449ae58a3783ef8e08cc02c49a36498918a904b..8f0fb6c04f382552af528ef085cbd0a96a99a2d2 100644 (file)
--- a/base64.c
+++ b/base64.c
@@ -33,7 +33,7 @@
 
 #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"
 
@@ -115,22 +115,35 @@ token_decode(const char *token)
 }
 
 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;
 }
index 968d18d7cb9367bff3938f2468850d5388105480..64439a0f1cbb20025369d35b41f17ce3aba89b32 100644 (file)
--- a/base64.h
+++ b/base64.h
 #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
 
index 947df90c62de9a5ab50c63e09c6b163889a9f303..e2f8caab0a5b2a870092c6cd508a1a50c21c3ba3 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -901,7 +901,7 @@ buffer_list_free (struct buffer_list *ol)
 bool
 buffer_list_defined (const struct buffer_list *ol)
 {
-  return ol->head != NULL;
+  return ol && ol->head != NULL;
 }
 
 void
index 820621e5916eda23d4489566cbd40ccb9159e2b3..0c99d0fc5a890881a3c6bfef0cbdd9721eab3837 100644 (file)
--- a/manage.c
+++ b/manage.c
@@ -101,6 +101,10 @@ man_help ()
 #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.");
@@ -768,49 +772,31 @@ man_hold (struct management *man, const char *cmd)
     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);
     }
 }
@@ -820,6 +806,7 @@ in_extra_dispatch (struct management *man)
 {
    switch (man->connection.in_extra_cmd)
     {
+#ifdef MANAGEMENT_DEF_AUTH
     case IEC_CLIENT_AUTH:
        if (man->persist.callback.client_auth)
        {
@@ -846,6 +833,7 @@ in_extra_dispatch (struct management *man)
          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)
@@ -870,8 +858,41 @@ in_extra_dispatch (struct management *man)
        }
       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
@@ -884,7 +905,7 @@ man_client_auth (struct management *man, const char *cid_str, const char *kid_st
       && 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);
     }
@@ -980,11 +1001,28 @@ man_client_pf (struct management *man, const char *cid_str)
   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
@@ -1250,6 +1288,12 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch
     }
 #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"))
     {
@@ -1626,8 +1670,8 @@ man_reset_client_socket (struct management *man, const bool exiting)
       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");
     }
@@ -1666,8 +1710,8 @@ man_process_command (struct management *man, const char *line)
 
   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))
@@ -1751,18 +1795,13 @@ man_read (struct management *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
@@ -2063,8 +2102,8 @@ man_connection_close (struct management *man)
     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);
 }
@@ -2387,7 +2426,7 @@ management_learn_addr (struct management *management,
   gc_free (&gc);
 }
 
-#endif
+#endif /* MANAGEMENT_DEF_AUTH */
 
 void
 management_echo (struct management *man, const char *string, const bool pull)
@@ -2693,6 +2732,7 @@ man_standalone_event_loop (struct management *man, volatile int *signal_received
 
 #define MWCC_PASSWORD_WAIT (1<<0)
 #define MWCC_HOLD_WAIT     (1<<1)
+#define MWCC_OTHER_WAIT    (1<<2)
 
 /*
  * Block until client connects
@@ -2710,6 +2750,8 @@ man_wait_for_client_connection (struct management *man,
        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)
@@ -2873,6 +2915,74 @@ management_query_user_pass (struct management *man,
   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
  */
index 6a9ccd885966010ddce2e0eacd04af3459307db0..697ddf87d1f86eff24bfd3a9ef1f8a97d47d7a2e 100644 (file)
--- a/manage.h
+++ b/manage.h
@@ -264,15 +264,22 @@ struct man_connection {
   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;
 
@@ -285,6 +292,10 @@ struct man_connection {
   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
@@ -314,6 +325,9 @@ struct management *management_init (void);
 # 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,
@@ -374,6 +388,12 @@ void management_learn_addr (struct management *management,
                            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)
 {
diff --git a/misc.c b/misc.c
index 4067d85abb77802745f16edfbedc7aecf7aed003..9e70744c1c19e67d7385331abd43c8b2c8a8c4ab 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -1618,7 +1618,7 @@ get_auth_challenge (const char *auth_challenge, struct gc_arena *gc)
       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);
diff --git a/ntlm.c b/ntlm.c
index 0453358cc22a8aade5e93f50109513b68f38cb3d..4dfeed38909caa656b612dd1a553eb2ad35eaf9d 100644 (file)
--- a/ntlm.c
+++ b/ntlm.c
@@ -242,7 +242,7 @@ ntlm_phase_3 (const struct http_proxy_info *p, const char *phase_2, struct gc_ar
   /* 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;
 
index 4f6434b89997814ea5dc278fc1bf0c23c8670b81..fca4a8e3de78fb7ec3a040c56251262b52dbf415 100644 (file)
--- a/options.c
+++ b/options.c
@@ -3663,6 +3663,14 @@ add_option (struct options *options,
       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"))
     {
diff --git a/ssl.c b/ssl.c
index 7b2291c6b3bd86a62ca02830e3fdf3c6f1ac310a..66ee14d0f514079e59015a787ad40b7d4b348f68 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -51,6 +51,7 @@
 #include "gremlin.h"
 #include "pkcs11.h"
 #include "list.h"
+#include "base64.h"
 
 #ifdef WIN32
 #include "cryptoapi.h"
@@ -1482,6 +1483,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
@@ -1589,7 +1779,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;
@@ -1613,6 +1803,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;
 }
 
@@ -1657,6 +1849,7 @@ init_ssl (const struct options *options)
   DH *dh;
   BIO *bio;
   bool using_cert_file = false;
+  X509 *my_cert = NULL;
 
   ERR_clear_error ();
 
@@ -1756,7 +1949,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;
            }
         }
@@ -1824,18 +2016,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)
            {
@@ -1967,6 +2173,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;
index 63b82babc73d76dcaed42e4dd805108db58e1bc3..30ff5561edf169c9fc6e9e674a7a87c0ef34e06a 100644 (file)
--- a/syshead.h
+++ b/syshead.h
@@ -509,6 +509,21 @@ socket_defined (const socket_descriptor_t sd)
 #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?
  */