From patchwork Thu Jan 18 07:54:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 197 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director6.mail.ord1d.rsapps.net ([172.27.255.54]) by backend31.mail.ord1d.rsapps.net (Dovecot) with LMTP id nJ4ZAAnuYFr5EgAAgoeIoA for ; Thu, 18 Jan 2018 13:57:13 -0500 Received: from proxy9.mail.iad3a.rsapps.net ([172.27.255.54]) by director6.mail.ord1d.rsapps.net (Dovecot) with LMTP id M5dBEAnuYFooHQAAhgvE6Q ; Thu, 18 Jan 2018 13:57:13 -0500 Received: from smtp12.gate.iad3a ([172.27.255.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy9.mail.iad3a.rsapps.net (Dovecot) with LMTP id nMupBgnuYFreEQAAGuSQww ; Thu, 18 Jan 2018 13:57:13 -0500 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.34.181.88] Authentication-Results: smtp12.gate.iad3a.rsapps.net; iprev=pass policy.iprev="216.34.181.88"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=gmail.com; dmarc=fail (p=none; dis=none) header.from=gmail.com X-Classification-ID: 647ae1fa-fc81-11e7-8616-bc305bf51114-1-1 Received: from [216.34.181.88] ([216.34.181.88:53259] helo=lists.sourceforge.net) by smtp12.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.1.56364 r(Core:4.2.1.14)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 14/89-10162-80EE06A5; Thu, 18 Jan 2018 13:57:12 -0500 Received: from localhost ([127.0.0.1] helo=sfs-ml-4.v29.ch3.sourceforge.com) by sfs-ml-4.v29.ch3.sourceforge.com with esmtp (Exim 4.89) (envelope-from ) id 1ecFLA-0000RA-Kf; Thu, 18 Jan 2018 18:55:08 +0000 Received: from sfi-mx-2.v28.ch3.sourceforge.com ([172.29.28.192] helo=mx.sourceforge.net) by sfs-ml-4.v29.ch3.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.89) (envelope-from ) id 1ecFL9-0000R3-Ha for openvpn-devel@lists.sourceforge.net; Thu, 18 Jan 2018 18:55:07 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=References:In-Reply-To:Message-Id:Date:Subject:Cc: To:From:Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=3um1KzgmF1EsCV71RDVd2dikkXPE1L6NQkDJhrhEgBE=; b=dUrpAlIT/2WTisi4gOoCNuroUl srkiMAo8F6mYR628z5ZWsop03XY4FZb/0g2mXUJxJaohrffDKyfulwyTOEt5MuZHtZ+4oqFv7c+hd Jrs9T5ka6eV1d+IP8MgpkHKhEmKR1dT7yF0heDtMOFZHcsKr28mblZxjEQ/e3MDPTqkQ=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To :MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=3um1KzgmF1EsCV71RDVd2dikkXPE1L6NQkDJhrhEgBE=; b=f93Q/Gxxn1wN59T1P1KT6iHBj+ l3df2vWwmikfWlysrqOPZ2rhj2QM2T9pk+isI+vaW0Yau1gUyLiP+n3RiYCeG1l8jBZIGfHKsOaSB xkq3dzMKpU+hNENGfcZhFUCeWZ253CNhp48fN7rv0yM8Q8OqAsAoz9gCtNt2CbDzk4fw=; Received: from mail-it0-f67.google.com ([209.85.214.67]) by sfi-mx-2.v28.ch3.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.89) id 1ecFL8-0000P2-5p for openvpn-devel@lists.sourceforge.net; Thu, 18 Jan 2018 18:55:07 +0000 Received: by mail-it0-f67.google.com with SMTP id c102so2772451itd.0 for ; Thu, 18 Jan 2018 10:55:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=3um1KzgmF1EsCV71RDVd2dikkXPE1L6NQkDJhrhEgBE=; b=i/iS5VCZECEmT/KdwbZ4W3k/jOlLYU0Y3UHB63kcxOOl8ppk8m5zXXzICppjjyF2tv XGQs8D2GgUvK1rOrMQfDAzfMgioUyU1pWQxa6qVAnjL9NzeWLrY8qw94g3WDsDCINz85 0r5RagPRg1nG5ZMx0GrRaigce439EElXLPL51Z7uVL7fomhVIJK2vfnZBdTluIHguwU5 0lDKdiXHFuwWZLS/bftBvEtN7JmNY1idwA1J42DZgxOtxjRHHPwo7zHAwgkObl8OjdDu I5FewueydqCrDrJBMvs32SOn67ZjgFl7dIqZKF1MXmJIeEe5+yKZgSKa56PfyhEH6BFz Wxkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=3um1KzgmF1EsCV71RDVd2dikkXPE1L6NQkDJhrhEgBE=; b=MOaL5lCxkoRFKdRenBjMZ9IDO4daTdhmr/AkUlym/mPXhLTuyOBX6OEZIAkPImr8IH HqVpCFArQalTNwWQvHudVdQ6+souBL78vy4G4BwKY4p9mGh+lpEQglkB6WvtP5A5Cipy YRKd/OZNX0+0NUTV0suS485BvJS29f2qv28m0lJpkWR5g4iRJAW8RrImJeZDRThAZXXv a/0HEVvNuTBLXIPIJUXvoq0Gk5Ge7/yZ1AtqehH0Eq+3ghloexqUUInaQCYptFBV+ecu HSd+oRc19sT4wiyH2CJrdfZ870rqRjNzn1PTIx6o0IG1S4gPpQ3lYI01n5Pf68bNoL3x 9HcQ== X-Gm-Message-State: AKwxytdb1Zv5tuHTRTy53LePj2ylZpvG/w27Ij0ZsJNTQ2SBj/FktaDl gi0bqxJd2W/e6V1MNbea/ayPPzUT X-Google-Smtp-Source: ACJfBotDVodWIowsYdgvzhI5/ZpIeb7TFI7ZiaF209ghJtdYJg96xm2WuoHNrw6z3uJPWkRALKeC9A== X-Received: by 10.36.124.197 with SMTP id a188mr15272514itd.5.1516301700450; Thu, 18 Jan 2018 10:55:00 -0800 (PST) Received: from saturn.home.sansel.ca (CPE40167ea0e1c2-CM788df74daaa0.cpe.net.cable.rogers.com. [99.228.215.92]) by smtp.gmail.com with ESMTPSA id m63sm4071241itm.12.2018.01.18.10.54.59 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 18 Jan 2018 10:54:59 -0800 (PST) From: selva.nair@gmail.com To: openvpn-devel@lists.sourceforge.net Date: Thu, 18 Jan 2018 13:54:56 -0500 Message-Id: <1516301696-11308-1-git-send-email-selva.nair@gmail.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: References: X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (selva.nair[at]gmail.com) -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [209.85.214.67 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Headers-End: 1ecFL8-0000P2-5p Subject: [Openvpn-devel] [PATCH v2 2/3] Allow external EC key through --management-external-key X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox From: Selva Nair - This automatically supports EC certificates through --management-external-cert - EC signature request from management has the same format as for rsa with '>RSA_SIGN' replaced by '>PK_SIGN'. Response should be of the form 'pk-sig' followed by DER encoded signature as base64 followed by 'END' To allow future deprecation of rsa-sig, response to '>RSA_SIGN' challenge could be presented as 'pk-sig' or 'rsa-sig'. v2: changes Based on feedback from Jan 17 meeting - Name the command as PK_SIGN and pk-sig and have it ready for replacing RSA_SIGN and rsa-sig in future Base on review by Arne Schwabe (arne@rfc2549.org) - Split rsa_priv_enc to allow code reuse for ecdsa_sign - Rename man_rsa_sig to man_external_sig with an extra arg to use it for multiple key-types - Bug fix: change len to unsigned int in ecdsa_sign_sig Signed-off-by: Selva Nair --- Note: Adding an option to determine whether the client can handle the new feature is left for a separate patch if deemed necessary. src/openvpn/manage.c | 25 +++++- src/openvpn/manage.h | 3 + src/openvpn/ssl_openssl.c | 215 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 201 insertions(+), 42 deletions(-) diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index 650f9e0..fac5eec 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -113,6 +113,8 @@ man_help(void) #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"); + msg(M_CLIENT, "pk-sig : Enter a signature in response to >PK_SIGN challenge"); + msg(M_CLIENT, " Enter signature in base64 on subsequent lines followed by END"); msg(M_CLIENT, "certificate : Enter a client certificate in response to >NEED-CERT challenge"); msg(M_CLIENT, " Enter certificate base64 on subsequent lines followed by END"); #endif @@ -936,6 +938,7 @@ in_extra_dispatch(struct management *man) #endif /* ifdef MANAGEMENT_PF */ #ifdef MANAGMENT_EXTERNAL_KEY case IEC_RSA_SIGN: + case IEC_PK_SIGN: man->connection.ext_key_state = EKS_READY; buffer_list_free(man->connection.ext_key_input); man->connection.ext_key_input = man->connection.in_extra; @@ -1103,18 +1106,21 @@ man_client_pf(struct management *man, const char *cid_str) #ifdef MANAGMENT_EXTERNAL_KEY static void -man_rsa_sig(struct management *man) +man_external_sig(struct management *man, int cmd) { + ASSERT(cmd == IEC_RSA_SIGN || cmd == IEC_PK_SIGN); struct man_connection *mc = &man->connection; + const char *cmd_name = (cmd == IEC_RSA_SIGN) ? "rsa-sig" : "pk-sig"; if (mc->ext_key_state == EKS_SOLICIT) { mc->ext_key_state = EKS_INPUT; - mc->in_extra_cmd = IEC_RSA_SIGN; + mc->in_extra_cmd = cmd; in_extra_reset(mc, IER_NEW); } else { - msg(M_CLIENT, "ERROR: The rsa-sig command is not currently available"); + msg(M_CLIENT, "ERROR: The %s command is not currently available", + cmd_name); } } @@ -1514,7 +1520,11 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha #ifdef MANAGMENT_EXTERNAL_KEY else if (streq(p[0], "rsa-sig")) { - man_rsa_sig(man); + man_external_sig(man, IEC_RSA_SIGN); + } + else if (streq(p[0], "pk-sig")) + { + man_external_sig(man, IEC_PK_SIGN); } else if (streq(p[0], "certificate")) { @@ -3655,6 +3665,13 @@ management_query_rsa_sig(struct management *man, &man->connection.ext_key_state, &man->connection.ext_key_input); } +char * +management_query_pk_sig(struct management *man, + const char *b64_data) +{ + return management_query_multiline_flatten(man, b64_data, "PK_SIGN", "pk-sign", + &man->connection.ext_key_state, &man->connection.ext_key_input); +} char * management_query_cert(struct management *man, const char *cert_name) diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h index 364488f..c468f20 100644 --- a/src/openvpn/manage.h +++ b/src/openvpn/manage.h @@ -281,6 +281,7 @@ struct man_connection { #define IEC_CLIENT_PF 2 #define IEC_RSA_SIGN 3 #define IEC_CERTIFICATE 4 +#define IEC_PK_SIGN 5 int in_extra_cmd; struct buffer_list *in_extra; #ifdef MANAGEMENT_DEF_AUTH @@ -441,6 +442,8 @@ void management_learn_addr(struct management *management, char *management_query_rsa_sig(struct management *man, const char *b64_data); +char *management_query_pk_sig(struct management *man, const char *b64_data); + char *management_query_cert(struct management *man, const char *cert_name); #endif diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 8e002c6..4869067 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -1009,58 +1009,58 @@ openvpn_extkey_rsa_finish(RSA *rsa) return 1; } -/* sign arbitrary data */ +/* 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. + */ static int -rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) +get_sig_from_man(int type, const unsigned char *dgst, unsigned int dgstlen, + unsigned char *sig, unsigned int siglen) { - /* optional app data in rsa->meth->app_data; */ char *in_b64 = NULL; char *out_b64 = NULL; - int ret = -1; - int len; + int len = -1; - if (padding != RSA_PKCS1_PADDING) + /* convert 'dgst' to base64 */ + if (management + && openvpn_base64_encode(dgst, dgstlen, &in_b64) > 0) { - RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); - goto done; + if (type == EVP_PKEY_RSA) + { + out_b64 = management_query_rsa_sig(management, in_b64); + } + else + { + out_b64 = management_query_pk_sig(management, in_b64); + } } - - /* convert 'from' to base64 */ - if (openvpn_base64_encode(from, flen, &in_b64) <= 0) + if (out_b64) { - goto done; + len = openvpn_base64_decode(out_b64, sig, siglen); } - /* call MI for signature */ - if (management) - { - out_b64 = management_query_rsa_sig(management, in_b64); - } - if (!out_b64) - { - goto done; - } + free(in_b64); + free(out_b64); + return len; +} - /* decode base64 signature to binary */ - len = RSA_size(rsa); - ret = openvpn_base64_decode(out_b64, to, len); +/* sign arbitrary data */ +static int +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; - /* verify length */ - if (ret != len) + if (padding != RSA_PKCS1_PADDING) { - ret = -1; + RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); + return -1; } -done: - if (in_b64) - { - free(in_b64); - } - if (out_b64) - { - free(out_b64); - } - return ret; + ret = get_sig_from_man(EVP_PKEY_RSA, from, flen, to, len); + + return (ret == len)? ret : -1; } static int @@ -1132,6 +1132,130 @@ err: return 0; } +#if OPENSSL_VERSION_NUMBER >= 0x10100001L && !defined(OPENSSL_NO_EC) + +/* called when EC_KEY is destroyed */ +static void +openvpn_extkey_ec_finish(EC_KEY *ec) +{ + /* release the method structure */ + const EC_KEY_METHOD *ec_meth = EC_KEY_get_method(ec); + EC_KEY_METHOD_free((EC_KEY_METHOD *) ec_meth); +} + +/* EC_KEY_METHOD callback: sign(). + * Sign the hash using EC key and return DER encoded signature in sig, + * its length in siglen. Return value is 1 on success, 0 on error. + */ +static int +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(EVP_PKEY_EC, dgst, dgstlen, sig, capacity); + + if (len > 0) + { + *siglen = len; + return 1; + } + return 0; +} + +/* EC_KEY_METHOD callback: sign_setup(). We do no precomputations */ +static int +ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp) +{ + return 1; +} + +/* EC_KEY_METHOD callback: sign_sig(). + * Sign the hash and return the result as a newly allocated ECDSA_SIG + * struct or NULL on error. + */ +static ECDSA_SIG * +ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, const BIGNUM *in_kinv, + const BIGNUM *in_r, EC_KEY *ec) +{ + ECDSA_SIG *ecsig = NULL; + unsigned int len = ECDSA_size(ec); + struct gc_arena gc = gc_new(); + + unsigned char *buf = gc_malloc(len, false, &gc); + if (ecdsa_sign(0, dgst, dgstlen, buf, &len, NULL, NULL, ec) != 1) + { + goto out; + } + /* const char ** should be avoided: not up to us, so we cast our way through */ + ecsig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&buf, len); + +out: + gc_free(&gc); + return ecsig; +} + +static int +tls_ctx_use_external_ec_key(struct tls_root_ctx *ctx, EVP_PKEY *pkey) +{ + EC_KEY *ec = NULL; + EVP_PKEY *privkey = NULL; + EC_KEY_METHOD *ec_method; + + ASSERT(ctx); + + ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); + if (!ec_method) + { + goto err; + } + + /* Among init methods, we only need the finish method */ + EC_KEY_METHOD_set_init(ec_method, NULL, openvpn_extkey_ec_finish, NULL, NULL, NULL, NULL); + EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig); + + ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey)); + if (!ec) + { + EC_KEY_METHOD_free(ec_method); + goto err; + } + if (!EC_KEY_set_method(ec, ec_method)) + { + EC_KEY_METHOD_free(ec_method); + goto err; + } + /* from this point ec_method will get freed when ec is freed */ + + privkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_EC_KEY(privkey, ec)) + { + goto err; + } + /* from this point ec will get freed when privkey is freed */ + + if (!SSL_CTX_use_PrivateKey(ctx->ctx, privkey)) + { + ec = NULL; /* avoid double freeing it below */ + goto err; + } + + EVP_PKEY_free(privkey); /* this will down ref privkey and ec */ + return 1; + +err: + /* Reach here only when ec and privkey can be independenly freed */ + if (privkey) + { + EVP_PKEY_free(privkey); + } + if(ec) + { + EC_KEY_free(ec); + } + return 0; +} +#endif // OPENSSL_VERSION_NUMBER > 1.1.0 dev + int tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, const char *cert_file, const char *cert_file_inline) @@ -1156,11 +1280,26 @@ tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, goto err; } } +#if OPENSSL_VERSION_NUMBER >= 0x10100001L && !defined(OPENSSL_NO_EC) + else if (EVP_PKEY_get0_EC_KEY(pkey)) + { + if (!tls_ctx_use_external_ec_key(ctx, pkey)) + { + goto err; + } + } else { - crypto_msg(M_WARN, "management-external-key requires a RSA certificate"); + crypto_msg(M_WARN, "management-external-key requires an RSA or EC certificate"); goto err; } +#else + else + { + crypto_msg(M_WARN, "management-external-key requires an RSA certificate"); + goto err; + } +#endif return 1; err: