Hi, Thanks for the updates.
In spite of several nits below, I'm ACKing this. All remarks are typos or grammar, important only for docs and some comments. I suggest to handle these as a minor follow up patch. I'm also ignoring most typos in commit message except a few that could be corrected during merge to add clarity. On Fri, Nov 22, 2019 at 9:34 AM Arne Schwabe <a...@rfc2549.org> wrote: > For TLS 1.0 to 1.2 and OpenSSL 1.1.0 calls us and requires a PKCS1 padded response. As TLS 1.3 mandates RSA-PSS padding support and also > requires an TLS 1.3 implementation to support RSA-PSS for older TLS version, OpenSSL will query us to sign an already RSA-PSS padded string. > > This patch adds an 'unpadded' and 'pkcs1' parameter to the management-external-key option to signal that the client is > able to support pkcs1 as well as unpadded signature requests. > > Since clients that implement the management-external-key interface > are usually rather tightly integrated solutions (OpenVPN Connect in the > past, OpenVPN for Android), it is reasonable to expect that > upgrading the OpenSSL library can be done together with > management interface changes. Therefore we provide no backwards > compatbility for mangement-interface clients not supporting > OpenSSL 1.1.1. > > Using the management api client version instead might seem like the > more logical way but since we only now that version very late, > now --> know it would extra logic and complexity to deal with this asynchronous > would --> would require behaviour. Instead just give an error early if OpenSSL 1.1.1 and > management-external-key without nopadding is detected. > > The interface is prepared for signalling PCKS1 and RSA-PSS support > instead of signalling unpadded support. > > Patch v3: fix overlong lines and few other style patches. Note > two overlong lines concerning mbedtls are not fixed as they > are removed/shortend by the mbed tls patch to avoid conflicts > > Patch v4: Setting minimum TLS version proved to be not enough and > instead of implementing a whole compability layer we require mangement-clients to implement the new feature when they want > to use OpenSSL 1.1.1 > > Add a padding=ALGORITHM argument to pk-sig to indicate the > algorithm. Drop adding PKCS1 ourselves. > Patch v5: Send the right version of the patch > Patch v6: rebase on master > Patch v7: change style and reword documentation. Make thing more consistent > Signed-off-by: Arne Schwabe <a...@rfc2549.org> > --- > doc/management-notes.txt | 21 ++++++++++++- > doc/openvpn.8 | 7 +++-- > src/openvpn/manage.c | 20 +++++++++--- > src/openvpn/manage.h | 19 +++++++----- > src/openvpn/options.c | 36 +++++++++++++++++++++- > src/openvpn/ssl_mbedtls.c | 8 +++-- > src/openvpn/ssl_openssl.c | 64 ++++++++++++++++++++++++++++++--------- > 7 files changed, 143 insertions(+), 32 deletions(-) > > diff --git a/doc/management-notes.txt b/doc/management-notes.txt > index 17645c1d..c64e594d 100644 > --- a/doc/management-notes.txt > +++ b/doc/management-notes.txt > @@ -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,12 @@ 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 interfaces expects PKCS1 padded signatures for RSA keys > "interface expects" or" interfaces expect" +(RSA_PKCS1_PADDING). EC signatures are always unpadded. > + > +The padding field is only present when pk-sig is used and > +currently the following values can be requested PCKS1 and NOPADDING for > RSA > +certificates and NOPADDING for EC certificates. > Remove this statement. Its no longer correct after the latest changes. Anyway this info is repeated below with correct keywords. > This capability is intended to allow the use of arbitrary cryptographic > service providers with OpenVPN via the management interface. > @@ -840,6 +847,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 > I think that should be "is > 2", not "is >= 2" +TLS1.2 using OpenSSL 1.1.1, not padded signature support is required and > this > "not padded" --> "unpadded" or "nonpadded" (former is more consistent with the rest) > +can be indicated in the signing request only if the client version is > 2" > + > +The currently defined padding algorithm are: > algorithm --> algorithms + > + - 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 > diff --git a/doc/openvpn.8 b/doc/openvpn.8 > index 457c2667..28d4516a 100644 > --- a/doc/openvpn.8 > +++ b/doc/openvpn.8 > @@ -2708,10 +2708,13 @@ 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 nopadding and > +pkcs1 signal support for different padding algorithms. See > format that as The optional parameters .B nopadding and .B pkcs1 signal support for .... +doc/mangement-notes.txt for a complete description of this > +feature. > .\"********************************************************* > .TP > .B \-\-management\-external\-cert certificate\-hint > diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c > index 1d97c2b6..49864c0a 100644 > --- a/src/openvpn/manage.c > +++ b/src/openvpn/manage.c > @@ -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 * > diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h > index d24abe09..6f5f34c1 100644 > --- a/src/openvpn/manage.h > +++ b/src/openvpn/manage.h > @@ -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); > > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index c282b582..ebe553af 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -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]) > diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c > index a4197cba..6fdef406 100644 > --- a/src/openvpn/ssl_mbedtls.c > +++ b/src/openvpn/ssl_mbedtls.c > @@ -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; > } > diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c > index a080338e..b7cfc77a 100644 > --- a/src/openvpn/ssl_openssl.c > +++ b/src/openvpn/ssl_openssl.c > @@ -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 need to this check for OpenSSL versions that can be > need need to this check -> need to check this + * 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/hasing algorithm for the signature > hasing -> hashing + * > + * @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) > { > > Tested for RSA and EC signatures using (i) an OpenSSL 1.1.1 build (ii) an OpenSSL 1.1.0 build upgraded to 1.1.1 at run time. Acked-by: selva.n...@gmail.com
_______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel