From patchwork Wed Sep 22 11:12:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 1969 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director8.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id eFJZGKucS2FZCQAAIUCqbw (envelope-from ) for ; Wed, 22 Sep 2021 17:14:19 -0400 Received: from proxy1.mail.ord1d.rsapps.net ([172.30.191.6]) by director8.mail.ord1d.rsapps.net with LMTP id yChCGKucS2HSVAAAfY0hYg (envelope-from ) for ; Wed, 22 Sep 2021 17:14:19 -0400 Received: from smtp30.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy1.mail.ord1d.rsapps.net with LMTPS id qJD6F6ucS2GnIAAAasrz9Q (envelope-from ) for ; Wed, 22 Sep 2021 17:14:19 -0400 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.105.38.7] Authentication-Results: smtp30.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; 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-Suspicious-Flag: YES X-Classification-ID: 0c5e8314-1bea-11ec-9dbf-5254001e8e38-1-1 Received: from [216.105.38.7] ([216.105.38.7:49742] helo=lists.sourceforge.net) by smtp30.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id F2/B9-02332-AAC9B416; Wed, 22 Sep 2021 17:14:19 -0400 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.92.3) (envelope-from ) id 1mT9YN-0006sj-US; Wed, 22 Sep 2021 21:13:19 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.3) (envelope-from ) id 1mT9YJ-0006sC-Ce for openvpn-devel@lists.sourceforge.net; Wed, 22 Sep 2021 21:13:15 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: 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=56h2EyqyXAinZ7Dl1oGWpIRi9KP2DGlmL2x9wcqbgJc=; b=gv7DvG4tNHAeWGxs8IaFSF9kN/ aoV+fz1lrFJJn6nTW1HpNt7yz9i8oJGqLSLEvF4EO28+FmVj7Q47sxA2eabK75WI7lQPdg7E3SQob HAcxImadZ8vQZZuEnqzxjm9xn8W1og23xlXg3jqRxac7iXZELPMPVa2qgQPKo+BFAPOw=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: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=56h2EyqyXAinZ7Dl1oGWpIRi9KP2DGlmL2x9wcqbgJc=; b=B0ygTS8V0vYDrNRIrz1AqIZtsA l2apZqhOLn0pAQDVXTvjzweAXJmHFsP6En/n/xwmG7da5cAH95iErI1bq3RQgWmxsnCHDGJEJz9NN /3lmKQm5YO1c6gcW9AlMQtU5qHVtskVpaqfpco45hqQu3anSV3NyHuQ7Ruhwen9/L0yI=; Received: from mail-qt1-f170.google.com ([209.85.160.170]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.92.3) id 1mT9YG-0022ye-M1 for openvpn-devel@lists.sourceforge.net; Wed, 22 Sep 2021 21:13:15 +0000 Received: by mail-qt1-f170.google.com with SMTP id u21so4147490qtw.8 for ; Wed, 22 Sep 2021 14:13:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=56h2EyqyXAinZ7Dl1oGWpIRi9KP2DGlmL2x9wcqbgJc=; b=IkVA/0SsDpbEbY8S2h6y5eXBlaRiA3/TH2ufjUukTVvLxsRcBsf7iND3smP0+n7/je LrpS3I7+C2u83cH1vbKR5v/pUryfdDKk8WkCI4b0yzogQIUm/S85WhPi31OwhsENid+J Z2LlmF5UBIVl3vR4/6ctXzRoSUxcRSO18UfQLageZBcwx10cRMhFsSfhkhn/3O1T7Ynq cxPItQOTqbwaMQdcKiZHS7tgH0AmWFE7+u16h1n7UffAEDGAPksNWvh0Dd097Ii+L21b zSlDLIynIVLw9uzd2uSoh0ROGgm9UAWhBZa3TSUYJuZz8aBEi9k9kErYUy4S46+F3EtJ S7Hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=56h2EyqyXAinZ7Dl1oGWpIRi9KP2DGlmL2x9wcqbgJc=; b=dzh9Ty/kSWwAs3ls8kbo6owyBYDj8lurZyHfCxMRBFU++JSRsilAjnLSUW6p+BMxVf obj0HYf/NqsAh2DrGL0rRZSQ2MIJMEaGM4F0IHJTG4GdazW/4D4+VqLuHYgquaCc4vPb aVMIQu1Ye21zj+AHQTa5HJIIxXlp3KD+MVllAVvpc4/aYAj77YhADYj8KUs52AkA3Ef5 /i36+QnAWmuyxlXNAIzvX5CoUzjD7MtlKGTLmuUMEpxYdBcyA5m9BlqXuO7RP7mOEacz WdqHlm252p7klTiVlZ53jX9iM7vbwB+QYUFWdG3ywkJqC0lQoDUNVDno1LfhxGn3CWop cjhQ== X-Gm-Message-State: AOAM53291yPA1eI09hugBxhskaZNiAV3q+9GZgiR643NWofuDs4bonGV oUBtvRIKMzpM3bi9J0O1eP/kxBSZ6r4= X-Google-Smtp-Source: ABdhPJziIq6998a+3q/w7nwrpK5FfHYkA1guPJnXSmmtk/WMStKAUzCII7Ahl94TGv4nVLuVa1g7vw== X-Received: by 2002:ac8:4042:: with SMTP id j2mr1445993qtl.229.1632345186543; Wed, 22 Sep 2021 14:13:06 -0700 (PDT) Received: from uranus.home.sansel.ca (bras-vprn-tnhlon4053w-lp130-02-70-51-223-227.dsl.bell.ca. [70.51.223.227]) by smtp.gmail.com with ESMTPSA id l7sm2185243qth.19.2021.09.22.14.13.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Sep 2021 14:13:05 -0700 (PDT) From: selva.nair@gmail.com To: openvpn-devel@lists.sourceforge.net Date: Wed, 22 Sep 2021 17:12:50 -0400 Message-Id: <20210922211254.7570-6-selva.nair@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210922211254.7570-1-selva.nair@gmail.com> References: <20210922211254.7570-1-selva.nair@gmail.com> MIME-Version: 1.0 X-Spam-Report: Spam detection software, running on the system "util-spamd-1.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Selva Nair Our key object retains info about the external key as an opaque handle to the backend. We also need the public key as an EVP_PKEY *. For native keys we use OpenSSL API to import data into the key. In fact the 'handle' in that case is the OpenSSL EVP_PKEY object itself. Content analysis details: (-0.2 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [selva.nair[at]gmail.com] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 RCVD_IN_MSPIKE_H3 RBL: Good reputation (+3) [209.85.160.170 listed in wl.mailspike.net] -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [209.85.160.170 listed in list.dnswl.org] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid 0.0 RCVD_IN_MSPIKE_WL Mailspike good senders X-Headers-End: 1mT9YG-0022ye-M1 Subject: [Openvpn-devel] [PATCH 5/9] Implement import of custom external keys 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: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox From: Selva Nair Our key object retains info about the external key as an opaque handle to the backend. We also need the public key as an EVP_PKEY *. For native keys we use OpenSSL API to import data into the key. In fact the 'handle' in that case is the OpenSSL EVP_PKEY object itself. For importing custom keys, we need to define custom parameters describing the key using OSSL_PARAM structure. We define 4 required and 1 optional parameters for loading the key: Required params: {.key="origin", .data_type = OSSL_PARAM_UTF8_STRING .data = "foobar", .data_size = 0 } Note: data_size = 0 refer to NUL terminated string in OpenSSL. This parameter is only used to identify that the key as non-native with an opaque handle. We really do not check the content of the string. Should not be NULL. {.key="handle", .data_type = OSSL_PARAM_OCTET_PTR, .data = &handle, .data_size = sizeof(handle)} {.key="pubkey", .data_type = OSSL_PARAM_OCTET_STRING, .data = &pubkey, .data_size = sizeof(pubkey)} {.key="sign_op", .data_type = OSSL_PARAM_OCTET_PTR, .data = &sign_op_ptr, .data_size = sizeof(sign_op_ptr)} Optional params: {.key="free_op", .data_type = OSSL_PARAM_OCTET_PTR, .data = &free_op_ptr, .data_size = sizeof(free_op_ptr)} The 'handle' is opaque to us and is retained. The caller should not free it. We will free it when no longer required by calling 'free_op()', if provided. The 'handle' should not be null as that indicates missing private key. The 'pubkey' must be an 'EVP_PKEY *' variable, and is duplicated by us. The caller may free it when not required. The 'sign_op' and 'free_op' function pointers should be of type 'XKEY_EXTERNAL_SIGN_fn' and 'XKEY_PRIVKEY_FREE_fn' defined in xkey_common.h For example, for management-external-key, we really do not need any 'handle'. Pass anything that will live long and won't dereference to NULL. We do not use it for any other purpose. Pointer to a const string would be a good choice. In this case, the 'free_op' must be NULL or some harmless function. NULL is the safest choice. The way of passing pointers via 'OSSL_PARAM' is somewhat fragile, as everything is cast into 'void *' and relies on abiding to the contract. For 'pubkey' we could pass it as a der encoded octet string, for example, and instead of function pointers we can just hard code the callbacks into our implementation. After all this is a private built-in provider. However, no external data is involved, so the only possible bad actor is the developer herself. A helper function to load the management key is in the next commit. Signed-off-by: Selva Nair --- src/openvpn/xkey_common.h | 7 ++ src/openvpn/xkey_provider.c | 166 +++++++++++++++++++++++++++++++++--- 2 files changed, 162 insertions(+), 11 deletions(-) diff --git a/src/openvpn/xkey_common.h b/src/openvpn/xkey_common.h index 0cb76db1..466b2b8d 100644 --- a/src/openvpn/xkey_common.h +++ b/src/openvpn/xkey_common.h @@ -75,4 +75,11 @@ typedef int (XKEY_EXTERNAL_SIGN_fn)(void *handle, unsigned char *sig, size_t *si const unsigned char *tbs, size_t tbslen, XKEY_SIGALG sigalg); +/** + * Signature of private key free function callback used + * to free the opaque private key handle obtained from the + * backend. Not required for management-external-key. + */ +typedef void (XKEY_PRIVKEY_FREE_fn)(void *handle); + #endif /* XKEY_PUBLIC_H_ */ diff --git a/src/openvpn/xkey_provider.c b/src/openvpn/xkey_provider.c index 88906ef4..3ff01634 100644 --- a/src/openvpn/xkey_provider.c +++ b/src/openvpn/xkey_provider.c @@ -72,6 +72,13 @@ typedef enum * * We also keep the public key in the form of a native OpenSSL EVP_PKEY. * This allows us to do all public ops by calling ops in the default provider. + * Both these are references retained by us and freed when the key is + * destroyed. As the pubkey is native, we free it using EVP_PKEY_free(). + * To free the handle we call the backend if a free function + * has been set for that key origin. It could be set when the key is + * created/imported. + * For native keys no need to free handle as its same as the pubkey + * which we always free. */ typedef struct { @@ -81,6 +88,10 @@ typedef struct EVP_PKEY *pubkey; /* origin of key -- native or external */ XKEY_ORIGIN origin; + /* sign function in backend to call */ + XKEY_EXTERNAL_SIGN_fn *sign; + /* keydata handle free function of backend */ + XKEY_PRIVKEY_FREE_fn *free; int refcount; /* reference count */ } XKEY_KEYDATA; @@ -111,6 +122,9 @@ static OSSL_FUNC_keymgmt_gettable_params_fn keymgmt_gettable; static OSSL_FUNC_keymgmt_query_operation_name_fn rsa_keymgmt_name; static OSSL_FUNC_keymgmt_query_operation_name_fn ec_keymgmt_name; +static int +keymgmt_import_helper(XKEY_KEYDATA *key, const OSSL_PARAM params[]); + static XKEY_KEYDATA * keydata_new() { @@ -134,6 +148,11 @@ keydata_free(XKEY_KEYDATA *key) { return; } + if (key->free && key->handle) + { + key->free(key->handle); + key->handle = NULL; + } if (key->pubkey) { EVP_PKEY_free(key->pubkey); @@ -167,7 +186,27 @@ keymgmt_load(const void *reference, size_t reference_sz) * appropriate for the key. We just use it to create a native * EVP_PKEY from params and assign to keydata->handle. * - * Import of external keys -- to be implemented + * For non-native keys the params[] array should include a custom + * value with name "origin". + * + * Other required parameters in the params array are: + * + * pubkey - pointer to native public key as a OCTET_STRING + * the public key is duplicated on receipt + * handle - reference to opaque handle to private key -- if not required + * pass a dummy value that is not zero. type = OCTET_PTR + * The reference is retained -- caller must _not_ free it. + * sign_op - function pointer for sign operation. type = OCTET_PTR + * Must be a reference to XKEY_EXTERNAL_SIGN_fn + * origin - A custom string to indicate the external key origin. UTF8_STRING + * The value doesn't really matter, but must be present. + * + * Optional params + * free_op - Called as free(handle) when the key is deleted. If the + * handle should not be freed, do not include. type = OCTET_PTR + * Must be a reference to XKEY_PRIVKEY_FREE_fn + * + * See xkey_load_management_key for an example use. */ static int keymgmt_import(void *keydata, int selection, const OSSL_PARAM params[], const char *name) @@ -185,6 +224,15 @@ keymgmt_import(void *keydata, int selection, const OSSL_PARAM params[], const ch return 0; } + /* if params contain a custom origin, call our import helper */ + + const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, "origin"); + if (p && p->data_type == OSSL_PARAM_UTF8_STRING) + { + key->origin = EXTERNAL_KEY; + return keymgmt_import_helper(key, params); + } + /* create a native public key and assign it to key->pubkey */ EVP_PKEY *pkey = NULL; @@ -203,6 +251,7 @@ keymgmt_import(void *keydata, int selection, const OSSL_PARAM params[], const ch key->pubkey = pkey; key->origin = OPENSSL_NATIVE; + key->sign = xkey_native_sign; if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { /* just use the same key as handle -- do not up-ref */ @@ -335,10 +384,94 @@ keymgmt_get_params(void *keydata, OSSL_PARAM *params) return EVP_PKEY_get_params(key->pubkey, params); } +/* Helper used by keymgmt_import and keymgmt_set_params + * for our keys. Not to be used for OpenSSL native keys. + */ +static int +keymgmt_import_helper(XKEY_KEYDATA *key, const OSSL_PARAM *params) +{ + dmsg(D_LOW, "In keymgmt_import_helper"); + + const OSSL_PARAM *p; + EVP_PKEY *pkey = NULL; + + ASSERT(key); + /* calling this with native keys is a coding error */ + ASSERT(key->origin != OPENSSL_NATIVE); + + if (params == NULL) + { + return 1; /* not an error */ + } + + /* our keys are immutable, we do not allow resetting parameters */ + if (key->pubkey) + { + return 0; + } + + /* only check params we understand and ignore the rest */ + + p = OSSL_PARAM_locate_const(params, "pubkey"); /*setting pubkey on our keydata */ + if (p && p->data_type == OSSL_PARAM_OCTET_STRING + && p->data_size == sizeof(pkey)) + { + pkey = *(EVP_PKEY **)p->data; + ASSERT(pkey); + + int id = EVP_PKEY_get_id(pkey); + if (id != EVP_PKEY_RSA && id != EVP_PKEY_EC) + { + msg(M_WARN, "Error: xkey keymgmt_import: unknown key type (%d)", id); + return 0; + } + + key->pubkey = EVP_PKEY_dup(pkey); + if (key->pubkey == NULL) + { + msg(M_NONFATAL, "Error: xkey keymgmt_import: duplicating pubkey failed."); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, "handle"); /*setting privkey */ + if (p && p->data_type == OSSL_PARAM_OCTET_PTR + && p->data_size == sizeof(key->handle)) + { + key->handle = *(void **)p->data; + /* caller should keep the reference alive until we call free */ + ASSERT(key->handle); /* fix your params array */ + } + + p = OSSL_PARAM_locate_const(params, "sign_op"); /*setting sign_op */ + if (p && p->data_type == OSSL_PARAM_OCTET_PTR + && p->data_size == sizeof(key->sign)) + { + key->sign = *(void **)p->data; + ASSERT(key->sign); /* fix your params array */ + } + + /* optional parameters */ + p = OSSL_PARAM_locate_const(params, "free_op"); /*setting free_op */ + if (p && p->data_type == OSSL_PARAM_OCTET_PTR + && p->data_size == sizeof(key->free)) + { + key->free = *(void **)p->data; + } + + return 1; +} + /** + * Set params on a key. + * * If the key is an encapsulated native key, we just call * EVP_PKEY_set_params in the default context. Only those params * supported by the default provider would work in that case. + * + * We treat our key object as immutable, so this works only with an + * empty key. Supported params for external keys are the + * same as those listed in the description of keymgmt_import. */ static int keymgmt_set_params(void *keydata, const OSSL_PARAM *params) @@ -348,7 +481,7 @@ keymgmt_set_params(void *keydata, const OSSL_PARAM *params) if (key->origin != OPENSSL_NATIVE) { - return 0; /* to be implemented */ + return keymgmt_import_helper(key, params); } else if (key->handle == NULL) /* once handle is set our key is immutable */ { @@ -652,6 +785,22 @@ signature_sign_init(void *ctx, void *provkey, const OSSL_PARAM params[]) return 1; } +/* Sign digest using backend sign function */ +static int +xkey_sign_dispatch(XKEY_SIGNATURE_CTX *sctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, + size_t tbslen) +{ + XKEY_EXTERNAL_SIGN_fn *sign = sctx->keydata->sign; + + if (!sign) + { + /* should not happen */ + msg(M_FATAL, "Signing function for this key is not defined."); + return 0; + } + return sign(sctx->keydata->handle, sig, siglen, tbs, tbslen, sctx->sigalg); +} + static int signature_sign(void *ctx, unsigned char *sig, size_t *siglen, size_t sigsize, const unsigned char *tbs, size_t tbslen) @@ -668,15 +817,10 @@ signature_sign(void *ctx, unsigned char *sig, size_t *siglen, size_t sigsize, return 1; } - if (sctx->keydata->origin == OPENSSL_NATIVE) - { - return xkey_native_sign(sctx->keydata->handle, sig, siglen, tbs, tbslen, sctx->sigalg); - } - else - { - /* external key handling not yet implemented */ - return 0; - } + /* we have sign_op set on natve keys as well + * so no need for the if/else here any more. + */ + return xkey_sign_dispatch(sctx, sig, siglen, tbs, tbslen); } /* Digest verify ops are simply delegated to the default provider using pubkey */