@@ -816,6 +816,7 @@ actual private key. When the SSL protocol needs to perform a sign
operation, the data to be signed will be sent to the management interface
via a notification as follows:
+>PK_SIGN:[BASE64_DATA],[ALG] (if client announces support for management version > 2)
>PK_SIGN:[BASE64_DATA] (if client announces support for management version > 1)
>RSA_SIGN:[BASE64_DATA] (only older clients will be prompted like this)
@@ -823,7 +824,7 @@ The management interface client should then create an appropriate signature of
the (decoded) BASE64_DATA using the private key and return the SSL signature as
follows:
-pk-sig (or rsa-sig)
+pk-sig (or rsa-sig)
[BASE64_SIG_LINE]
.
.
@@ -833,6 +834,8 @@ END
Base 64 encoded output of RSA_private_encrypt for RSA or ECDSA_sign()
for EC using OpenSSL or mbedtls_pk_sign() using mbed TLS will provide a
correct signature.
+The rsa-sig interface expects PKCS1 padded signatures for RSA keys
+(RSA_PKCS1_PADDING). EC signatures are always unpadded.
This capability is intended to allow the use of arbitrary cryptographic
service providers with OpenVPN via the management interface.
@@ -840,6 +843,18 @@ service providers with OpenVPN via the management interface.
New and updated clients are expected to use the version command to announce
a version > 1 and handle '>PK_SIGN' prompt and respond with 'pk-sig'.
+The signature algorithm is indicated in the PK_SIGN request only if the
+management client-version is > 2. In particular, to support TLS1.3 and
+TLS1.2 using OpenSSL 1.1.1, unpadded signature support is required and this
+can be indicated in the signing request only if the client version is > 2"
+
+The currently defined padding algorithms are:
+
+ - RSA_PKCS1_PADDING - PKCS1 padding and RSA signature
+ - RSA_NO_PADDING - No padding may be added for the signature
+ - ECDSA - EC signature.
+
+
COMMAND -- certificate (OpenVPN 2.4 or higher)
----------------------------------------------
Provides support for external storage of the certificate. Requires the
@@ -2708,10 +2708,16 @@ Allow management interface to override
directives (client\-only).
.\"*********************************************************
.TP
-.B \-\-management\-external\-key
+.B \-\-management\-external\-key [nopadding] [pkcs1]
Allows usage for external private key file instead of
.B \-\-key
-option (client\-only).
+option (client\-only). The optional parameters
+.B nopadding
+and
+.B pkcs1
+signal support for different padding algorithms. See
+doc/mangement-notes.txt for a complete description of this
+feature.
.\"*********************************************************
.TP
.B \-\-management\-external\-cert certificate\-hint
@@ -3641,18 +3641,30 @@ management_query_multiline_flatten(struct management *man,
char *
/* returns allocated base64 signature */
-management_query_pk_sig(struct management *man,
- const char *b64_data)
+management_query_pk_sig(struct management *man, const char *b64_data,
+ const char *algorithm)
{
const char *prompt = "PK_SIGN";
const char *desc = "pk-sign";
+ struct buffer buf_data = alloc_buf(strlen(b64_data) + strlen(algorithm) + 20);
+
if (man->connection.client_version <= 1)
{
prompt = "RSA_SIGN";
desc = "rsa-sign";
}
- return management_query_multiline_flatten(man, b64_data, prompt, desc,
- &man->connection.ext_key_state, &man->connection.ext_key_input);
+
+ buf_write(&buf_data, b64_data, (int) strlen(b64_data));
+ if (man->connection.client_version > 2)
+ {
+ buf_write(&buf_data, ",", (int) strlen(","));
+ buf_write(&buf_data, algorithm, (int) strlen(algorithm));
+ }
+ char* ret = management_query_multiline_flatten(man,
+ (char *)buf_bptr(&buf_data), prompt, desc,
+ &man->connection.ext_key_state, &man->connection.ext_key_input);
+ free_buf(&buf_data);
+ return ret;
}
char *
@@ -31,7 +31,7 @@
#include "socket.h"
#include "mroute.h"
-#define MANAGEMENT_VERSION 2
+#define MANAGEMENT_VERSION 3
#define MANAGEMENT_N_PASSWORD_RETRIES 3
#define MANAGEMENT_LOG_HISTORY_INITIAL_SIZE 100
#define MANAGEMENT_ECHO_BUFFER_SIZE 100
@@ -341,12 +341,14 @@ struct management *management_init(void);
#ifdef MANAGEMENT_PF
#define MF_CLIENT_PF (1<<7)
#endif
-#define MF_UNIX_SOCK (1<<8)
-#define MF_EXTERNAL_KEY (1<<9)
-#define MF_UP_DOWN (1<<10)
-#define MF_QUERY_REMOTE (1<<11)
-#define MF_QUERY_PROXY (1<<12)
-#define MF_EXTERNAL_CERT (1<<13)
+#define MF_UNIX_SOCK (1<<8)
+#define MF_EXTERNAL_KEY (1<<9)
+#define MF_EXTERNAL_KEY_NOPADDING (1<<10)
+#define MF_EXTERNAL_KEY_PKCS1PAD (1<<11)
+#define MF_UP_DOWN (1<<12)
+#define MF_QUERY_REMOTE (1<<13)
+#define MF_QUERY_PROXY (1<<14)
+#define MF_EXTERNAL_CERT (1<<15)
bool management_open(struct management *man,
const char *addr,
@@ -430,7 +432,8 @@ void management_learn_addr(struct management *management,
#endif
-char *management_query_pk_sig(struct management *man, const char *b64_data);
+char *management_query_pk_sig(struct management *man, const char *b64_data,
+ const char *algorithm);
char *management_query_cert(struct management *man, const char *cert_name);
@@ -2171,6 +2171,16 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
#endif /* ifdef ENABLE_MANAGEMENT */
+#if defined(ENABLE_MANAGEMENT)
+ if ((tls_version_max() >= TLS_VER_1_3)
+ && (options->management_flags & MF_EXTERNAL_KEY)
+ && !(options->management_flags & (MF_EXTERNAL_KEY_NOPADDING))
+ )
+ {
+ msg(M_ERR, "management-external-key with OpenSSL 1.1.1 requires "
+ "the nopadding argument/support");
+ }
+#endif
/*
* Windows-specific options.
*/
@@ -5241,9 +5251,33 @@ add_option(struct options *options,
options->management_write_peer_info_file = p[1];
}
#ifdef ENABLE_MANAGEMENT
- else if (streq(p[0], "management-external-key") && !p[1])
+ else if (streq(p[0], "management-external-key"))
{
VERIFY_PERMISSION(OPT_P_GENERAL);
+ for (int j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ {
+ if (streq(p[j], "nopadding"))
+ {
+ options->management_flags |= MF_EXTERNAL_KEY_NOPADDING;
+ }
+ else if (streq(p[j], "pkcs1"))
+ {
+ options->management_flags |= MF_EXTERNAL_KEY_PKCS1PAD;
+ }
+ else
+ {
+ msg(msglevel, "Unknown management-external-key flag: %s", p[j]);
+ }
+ }
+ /*
+ * When no option is present, assume that only PKCS1
+ * padding is supported
+ */
+ if (!(options->management_flags
+ &(MF_EXTERNAL_KEY_NOPADDING | MF_EXTERNAL_KEY_PKCS1PAD)))
+ {
+ options->management_flags |= MF_EXTERNAL_KEY_PKCS1PAD;
+ }
options->management_flags |= MF_EXTERNAL_KEY;
}
else if (streq(p[0], "management-external-cert") && p[1] && !p[2])
@@ -626,7 +626,6 @@ tls_ctx_use_external_signing_func(struct tls_root_ctx *ctx,
}
#ifdef ENABLE_MANAGEMENT
-
/** Query the management interface for a signature, see external_sign_func. */
static bool
management_sign_func(void *sign_ctx, const void *src, size_t src_len,
@@ -641,7 +640,12 @@ management_sign_func(void *sign_ctx, const void *src, size_t src_len,
goto cleanup;
}
- if (!(dst_b64 = management_query_pk_sig(management, src_b64)))
+ /*
+ * We only support RSA external keys and PKCS1 signatures at the moment
+ * in mbed TLS, so the signature parameter is hardcoded to this encoding
+ */
+ if (!(dst_b64 = management_query_pk_sig(management, src_b64,
+ "RSA_PKCS1_PADDING")))
{
goto cleanup;
}
@@ -225,7 +225,9 @@ tls_version_max(void)
* However, the library we are *linked* against might be OpenSSL 1.1.1
* and therefore supports TLS 1.3. This needs to be checked at runtime
* since we can be compiled against 1.1.0 and then the library can be
- * upgraded to 1.1.1
+ * upgraded to 1.1.1.
+ * We only need to check this for OpenSSL versions that can be
+ * upgraded to 1.1.1 without recompile (>= 1.1.0)
*/
if (OpenSSL_version_num() >= 0x1010100fL)
{
@@ -1133,24 +1135,52 @@ openvpn_extkey_rsa_finish(RSA *rsa)
return 1;
}
-/* Pass the input hash in 'dgst' to management and get the signature back.
- * On input siglen contains the capacity of the buffer 'sig'.
- * On return signature is in sig.
- * Return value is signature length or -1 on error.
+/*
+ * Convert OpenSSL's constant to the strings used in the management
+ * interface query
+ */
+const char *
+get_rsa_padding_name (const int padding)
+{
+ switch (padding)
+ {
+ case RSA_PKCS1_PADDING:
+ return "RSA_PKCS1_PADDING";
+
+ case RSA_NO_PADDING:
+ return "RSA_NO_PADDING";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/**
+ * Pass the input hash in 'dgst' to management and get the signature back.
+ *
+ * @param dgst hash to be signed
+ * @param dgstlen len of data in dgst
+ * @param sig On successful return signature is in sig.
+ * @param siglen length of buffer sig
+ * @param algorithm padding/hashing algorithm for the signature
+ *
+ * @return signature length or -1 on error.
*/
static int
get_sig_from_man(const unsigned char *dgst, unsigned int dgstlen,
- unsigned char *sig, unsigned int siglen)
+ unsigned char *sig, unsigned int siglen,
+ const char *algorithm)
{
char *in_b64 = NULL;
char *out_b64 = NULL;
int len = -1;
- /* convert 'dgst' to base64 */
- if (management
- && openvpn_base64_encode(dgst, dgstlen, &in_b64) > 0)
+ int bencret = openvpn_base64_encode(dgst, dgstlen, &in_b64);
+
+ if (management && bencret > 0)
{
- out_b64 = management_query_pk_sig(management, in_b64);
+ out_b64 = management_query_pk_sig(management, in_b64, algorithm);
+
}
if (out_b64)
{
@@ -1164,18 +1194,19 @@ get_sig_from_man(const unsigned char *dgst, unsigned int dgstlen,
/* sign arbitrary data */
static int
-rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
+ int padding)
{
unsigned int len = RSA_size(rsa);
int ret = -1;
- if (padding != RSA_PKCS1_PADDING)
+ if (padding != RSA_PKCS1_PADDING && padding != RSA_NO_PADDING)
{
RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
return -1;
}
- ret = get_sig_from_man(from, flen, to, len);
+ ret = get_sig_from_man(from, flen, to, len, get_rsa_padding_name (padding));
return (ret == len) ? ret : -1;
}
@@ -1271,7 +1302,12 @@ ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig,
unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec)
{
int capacity = ECDSA_size(ec);
- int len = get_sig_from_man(dgst, dgstlen, sig, capacity);
+ /*
+ * ECDSA does not seem to have proper constants for paddings since
+ * there are only signatures without padding at the moment, use
+ * a generic ECDSA for the moment
+ */
+ int len = get_sig_from_man(dgst, dgstlen, sig, capacity, "ECDSA");
if (len > 0)
{