From nobody Fri Dec 12 12:54:59 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1765224201; cv=none; d=zohomail.com; s=zohoarc; b=ZmN0AuJEBFRyKUEXE4Vc2WYIDnn2kxPpOxqZn0vlcc2xTIamNbpF/yDcVS1Zd6ltY1T5OzAgZ4eBZsyyfkExxAgSw/ztwINc+7FRCeZ685DGz0vsYjMCi9VAZIi0srszN/wymQyvsagxk97hPjdqjPwlHmcauiTs/FvEDnt+Lpo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1765224201; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=M9+8i5xfhA7DnB0DcE7PEN4oPUUzX9+rJEKOGgu00Zo=; b=G+SkzV9zEni0i+40PVhP3kdIRsxsoo8Aevf+PCTbJx0vmU4vAzGB74iVVF1yfA5ZOZXNlNCTMWB4YOjoEeY6hoSs4Xm5m6tasy0F6fkriwWtuE9zbKYJ3bMkC6r+O741r7SM6QHoNKP0HUMXiXE4PiHm1gt3CMHQ7wyzqcftY3E= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1765224201104437.5644356002597; Mon, 8 Dec 2025 12:03:21 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id 2BF2443DD4; Mon, 8 Dec 2025 15:03:20 -0500 (EST) Received: from [172.19.199.80] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id C5C83440F0; Mon, 8 Dec 2025 14:54:04 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 802C943E02; Mon, 8 Dec 2025 14:53:50 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id BF89F43ECB for ; Mon, 8 Dec 2025 14:52:52 -0500 (EST) Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-298-EB86w7W6NHOqqPjoKPuYAg-1; Mon, 08 Dec 2025 14:52:41 -0500 Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-299ddb0269eso54543525ad.0 for ; Mon, 08 Dec 2025 11:52:40 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.110.66]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29daeaab9c0sm134095235ad.68.2025.12.08.11.52.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Dec 2025 11:52:37 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1765223572; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=M9+8i5xfhA7DnB0DcE7PEN4oPUUzX9+rJEKOGgu00Zo=; b=ZHrT5N6+Yyj9iNFX7wa36154dOYD/yDBoEfZES43T6S2ggeVzMdmzai60lxvDfuWA+Uvva mvIoaWlL38/MX7+ksbAENQ679VAWpDWD1j0RVIBoYrjWax2EWlOXuXZEIHTMd8dIORwD6x v88fdGfKbKHYP2AGn3ufyamerp0njBc= X-MC-Unique: EB86w7W6NHOqqPjoKPuYAg-1 X-Mimecast-MFC-AGG-ID: EB86w7W6NHOqqPjoKPuYAg_1765223560 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765223559; x=1765828359; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=M9+8i5xfhA7DnB0DcE7PEN4oPUUzX9+rJEKOGgu00Zo=; b=CzUGeOayOB8L9/Tck/pM7H2mh9tNZt4jB2qaryreRehiF3/2mJ1acjN7OQn9L1FEHO NPcMhp2PmL+urxHIrui8jLkz35Y3GVByEcieqSb0Sk5qHD9HIdeER+fL+okvVROPwNSR MdL7fKgQPIjDH2m6m7B4JCnkHus47HXdiMdMvVLgg4it7rjkesoppawdjNoDilhZ3KKR NDBSPXo+siMxuOc01RSn9iLfIQTSDopkcX3Bw1tf+uwDIdTYQxqy9mXXDzHJA8diR2bU aY6u30GFn4aPePA9Yxzrr0ElXIg311t3+g2YspZGSZDZO7wHaDRNjb3zTHP2JeQn0g2S G6wA== X-Gm-Message-State: AOJu0YwAetuXC7kKhtsCh9PRFUgmiyqjZQZcOPZCjCw6jkSa8zjVdolf vkLUlXObB/ZJoG8r3xw6R9ga5K7uDLqAGLY1JlmjxjeCUQDLgbPXkYdgUlw/rmoAAdSp3oXn3h3 UuEX/L6DvAPp4qm5wQ22juGhLh5BOZrTyFBuiZLZdtEUDLSSDlFQM6elTrmiNXtyNSMRuf3Km19 IbYdiRHgAokKnp4sMz2RjE7Xp1HcmEWPG2ksnMXPsVFw== X-Gm-Gg: ASbGnctQQbXHj8D4o5m+jO1tvzBY1mF+GkOSbVEWu1H400V8H3lwjVBMw0K10Dz2mVn zSEbn2pebZgLR55T7ZLlHTMWI8HTkd2HwLcX/z12+26N7mHfo0purSbPG/M+S6bxl+aXtxprMJm evdGm4EPWPYfbYk9W6AWTN0dB94aHE2aA1bp7vDncXKL9gLUFF2kDf9devV3SJnwrFMNq/SdLLY lg8kOuScFzhtOwtrv8qi2mjdVJ1LYoLkmH4ap8I3GLAbyKaRclVyCXsi5SjKrGyO3YpalEuEPDE uPgV6Bv9l0Pf6emX2a3LcZtQ7I9bDaUnCo6XCifRtFZxl7yFITd3Fr6KJ2BZJZzgOjxHsRdTcvM rxgeYBza3hhHk/gJC6bnkbCSxzvFil1DglVzbhqw5A3LFpwEcQ/7Qxq1w X-Received: by 2002:a17:903:2c06:b0:29e:38de:6140 with SMTP id d9443c01a7336-29e38de61a3mr43836995ad.13.1765223559411; Mon, 08 Dec 2025 11:52:39 -0800 (PST) X-Google-Smtp-Source: AGHT+IECaTdntMo3GGYNGM+/H9HcMttcFjufdgu4cvzTSRvV4Bjl/As6smKtP3glExBNDSTKlVXGcQ== X-Received: by 2002:a17:903:2c06:b0:29e:38de:6140 with SMTP id d9443c01a7336-29e38de61a3mr43836805ad.13.1765223558809; Mon, 08 Dec 2025 11:52:38 -0800 (PST) To: devel@lists.libvirt.org Subject: [PATCH 1/5] util: Add support for GnuTLS decryption Date: Tue, 9 Dec 2025 01:22:27 +0530 Message-ID: <20251208195231.98170-2-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251208195231.98170-1-armenon@redhat.com> References: <20251208195231.98170-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: ofk1_m8vT2X2ui4PgiDQ20bLPHL3WSGErpUQcLU-zgI_1765223560 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 3UFUP7EMZM5LQWZ5MROC7WDS6YHJ3FJ4 X-Message-ID-Hash: 3UFUP7EMZM5LQWZ5MROC7WDS6YHJ3FJ4 X-MailFrom: armenon@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Arun Menon X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Arun Menon via Devel Reply-To: Arun Menon X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1765224206629158500 Content-Type: text/plain; charset="utf-8"; x-default="true" Adds `virCryptoDecryptDataAESgnutls` and `virCryptoDecryptData` as wrapper functions for GnuTLS decryption. These functions are the inverse of the existing GnuTLS encryption wrappers. This commit also includes a corresponding test case to validate data decryp= tion. Signed-off-by: Arun Menon Reviewed-by: Peter Krempa --- src/libvirt_private.syms | 1 + src/util/vircrypto.c | 126 ++++++++++++++++++++++++++++++++++++++- src/util/vircrypto.h | 8 +++ tests/vircryptotest.c | 65 ++++++++++++++++++++ 4 files changed, 199 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4e57e4a8f6..63a1ae4c70 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2254,6 +2254,7 @@ virConfWriteMem; =20 =20 # util/vircrypto.h +virCryptoDecryptData; virCryptoEncryptData; virCryptoHashBuf; virCryptoHashString; diff --git a/src/util/vircrypto.c b/src/util/vircrypto.c index 3ce23264ca..00f723bb75 100644 --- a/src/util/vircrypto.c +++ b/src/util/vircrypto.c @@ -98,7 +98,7 @@ virCryptoHashString(virCryptoHash hash, } =20 =20 -/* virCryptoEncryptDataAESgntuls: +/* virCryptoEncryptDataAESgnutls: * * Performs the AES gnutls encryption * @@ -233,3 +233,127 @@ virCryptoEncryptData(virCryptoCipher algorithm, _("algorithm=3D%1$d is not supported"), algorithm); return -1; } + +/* virCryptoDecryptDataAESgnutls: + * + * Performs the AES gnutls decryption + * + * Same input as virCryptoDecryptData, except the algorithm is replaced + * by the specific gnutls algorithm. + * + * Decrypts the @data buffer using the @deckey and if available the @iv + * + * Returns 0 on success with the plaintext being filled. It is the + * caller's responsibility to clear and free it. Returns -1 on failure + * w/ error set. + */ +static int +virCryptoDecryptDataAESgnutls(gnutls_cipher_algorithm_t gnutls_dec_alg, + uint8_t *deckey, + size_t deckeylen, + uint8_t *iv, + size_t ivlen, + uint8_t *data, + size_t datalen, + uint8_t **plaintextret, + size_t *plaintextlenret) +{ + int rc; + uint8_t padding_length; + gnutls_cipher_hd_t handle =3D NULL; + gnutls_datum_t dec_key =3D { .data =3D deckey, .size =3D deckeylen }; + gnutls_datum_t iv_buf =3D { .data =3D iv, .size =3D ivlen }; + g_autofree uint8_t *plaintext =3D NULL; + size_t plaintextlen; + + if ((rc =3D gnutls_cipher_init(&handle, gnutls_dec_alg, + &dec_key, &iv_buf)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to initialize cipher: '%1$s'"), + gnutls_strerror(rc)); + return -1; + } + + plaintext =3D g_memdup2(data, datalen); + plaintextlen =3D datalen; + if (plaintextlen =3D=3D 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("decrypted data has zero length")); + goto error; + } + rc =3D gnutls_cipher_decrypt(handle, plaintext, plaintextlen); + gnutls_cipher_deinit(handle); + if (rc < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to decrypt the data: '%1$s'"), + gnutls_strerror(rc)); + goto error; + } + /* Before encryption, padding is added to the data. + * The last byte indicates the padding length, because in PKCS#7, all + * padding bytes are set to the padding length value. + */ + padding_length =3D plaintext[plaintextlen - 1]; + if (padding_length > plaintextlen) { + virReportError(VIR_ERR_INVALID_SECRET, "%s", + _("decrypted data has invalid padding")); + goto error; + } + *plaintextlenret =3D plaintextlen - padding_length; + *plaintextret =3D g_steal_pointer(&plaintext); + return 0; + error: + virSecureErase(plaintext, plaintextlen); + return -1; +} + +/* virCryptoDecryptData: + * @algorithm: algorithm desired for decryption + * @deckey: decryption key + * @deckeylen: decryption key length + * @iv: initialization vector + * @ivlen: length of initialization vector + * @data: data to decrypt + * @datalen: length of data + * @plaintext: stream of bytes allocated to store plaintext + * @plaintextlen: size of the stream of bytes + * Returns 0 on success, -1 on failure with error set + */ +int +virCryptoDecryptData(virCryptoCipher algorithm, + uint8_t *deckey, + size_t deckeylen, + uint8_t *iv, + size_t ivlen, + uint8_t *data, + size_t datalen, + uint8_t **plaintext, + size_t *plaintextlen) +{ + switch (algorithm) { + case VIR_CRYPTO_CIPHER_AES256CBC: + if (deckeylen !=3D 32) { + virReportError(VIR_ERR_INVALID_ARG, + _("AES256CBC decryption invalid keylen=3D%1$zu= "), + deckeylen); + return -1; + } + if (ivlen !=3D 16) { + virReportError(VIR_ERR_INVALID_ARG, + _("AES256CBC initialization vector invalid len= =3D%1$zu"), + ivlen); + return -1; + } + return virCryptoDecryptDataAESgnutls(GNUTLS_CIPHER_AES_256_CBC, + deckey, deckeylen, iv, ivlen, + data, datalen, + plaintext, plaintextlen); + case VIR_CRYPTO_CIPHER_NONE: + case VIR_CRYPTO_CIPHER_LAST: + break; + } + + virReportError(VIR_ERR_INVALID_ARG, + _("algorithm=3D%1$d is not supported"), algorithm); + return -1; +} diff --git a/src/util/vircrypto.h b/src/util/vircrypto.h index 5f079ac335..2e8557839d 100644 --- a/src/util/vircrypto.h +++ b/src/util/vircrypto.h @@ -61,3 +61,11 @@ int virCryptoEncryptData(virCryptoCipher algorithm, uint8_t **ciphertext, size_t *ciphertextlen) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6) ATTRIBUTE_NONNULL(8) ATTRIBUTE_NONNULL(9) G_GNUC_WARN_UNUSED_RESULT; + +int virCryptoDecryptData(virCryptoCipher algorithm, + uint8_t *deckey, size_t deckeylen, + uint8_t *iv, size_t ivlen, + uint8_t *data, size_t datalen, + uint8_t **plaintext, size_t *plaintextlen) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6) + ATTRIBUTE_NONNULL(8) ATTRIBUTE_NONNULL(9) G_GNUC_WARN_UNUSED_RESULT; diff --git a/tests/vircryptotest.c b/tests/vircryptotest.c index 9ffe70756e..864fa8838d 100644 --- a/tests/vircryptotest.c +++ b/tests/vircryptotest.c @@ -62,6 +62,14 @@ struct testCryptoEncryptData { size_t ciphertextlen; }; =20 +struct testCryptoDecryptData { + virCryptoCipher algorithm; + uint8_t *input; + size_t inputlen; + uint8_t *plaintext; + size_t plaintextlen; +}; + static int testCryptoEncrypt(const void *opaque) { @@ -101,6 +109,44 @@ testCryptoEncrypt(const void *opaque) return 0; } =20 +static int +testCryptoDecrypt(const void *opaque) +{ + const struct testCryptoDecryptData *data =3D opaque; + g_autofree uint8_t *deckey =3D NULL; + size_t deckeylen =3D 32; + g_autofree uint8_t *iv =3D NULL; + size_t ivlen =3D 16; + g_autofree uint8_t *plaintext =3D NULL; + size_t plaintextlen =3D 0; + + deckey =3D g_new0(uint8_t, deckeylen); + iv =3D g_new0(uint8_t, ivlen); + + if (virRandomBytes(deckey, deckeylen) < 0 || + virRandomBytes(iv, ivlen) < 0) { + fprintf(stderr, "Failed to generate random bytes\n"); + return -1; + } + + if (virCryptoDecryptData(data->algorithm, deckey, deckeylen, iv, ivlen, + data->input, data->inputlen, + &plaintext, &plaintextlen) < 0) + return -1; + + if (data->plaintextlen !=3D plaintextlen) { + fprintf(stderr, "Expected plaintexlen(%zu) doesn't match (%zu)\n", + data->plaintextlen, plaintextlen); + return -1; + } + + if (memcmp(data->plaintext, plaintext, plaintextlen)) { + fprintf(stderr, "Expected plaintext doesn't match\n"); + return -1; + } + + return 0; +} =20 static int mymain(void) @@ -155,7 +201,26 @@ mymain(void) =20 #undef VIR_CRYPTO_ENCRYPT =20 +#define VIR_CRYPTO_DECRYPT(a, n, i, il, c, cl) \ + do { \ + struct testCryptoDecryptData data =3D { \ + .algorithm =3D a, \ + .input =3D i, \ + .inputlen =3D il, \ + .plaintext =3D c, \ + .plaintextlen =3D cl, \ + }; \ + if (virTestRun("Decrypt " n, testCryptoDecrypt, &data) < 0) \ + ret =3D -1; \ + } while (0) + + VIR_CRYPTO_DECRYPT(VIR_CRYPTO_CIPHER_AES256CBC, "aes256cbc", + expected_ciphertext, 16, secretdata, 7); + +#undef VIR_CRYPTO_DECRYPT + return ret =3D=3D 0 ? EXIT_SUCCESS : EXIT_FAILURE; + } =20 /* Forces usage of not so random virRandomBytes */ --=20 2.51.1 From nobody Fri Dec 12 12:54:59 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1765223989; cv=none; d=zohomail.com; s=zohoarc; b=gt9yut3CSEXkVzHJaWu0B+vmCVf/f6dTek+xEg+CPb1XqBVh/nrPXNeHcMREZ6UZlU1UXyN5hp9Q35szkdDcVNjtPeoo6U6uvlTUjNzRg8FJLM/h+pVTUdv3OXmupQvF4HsQ07CE8KQ18tkWrVP8NgGX7mpWPgQOAqeafbYaQms= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1765223989; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=GH2uqIsdJBJ4/hAdR1cPgM5LESiNOjysz14YsnbrO24=; b=a3k6ysn28h4u+EyiL2xneBkUEogqlJrcPHRWCbswGF8+k18wk5busLDOC4tNVlTbECU5PckHa2sURQINyzikxvNx/yNbln+yOxc5FcrlmMtjMtOenwU9DldBJZr+nJnDOnT1MiL2vlsUjQs6t1GITmTFnC0ZDJF5kM31nmN+98g= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1765223989809364.5032320969482; Mon, 8 Dec 2025 11:59:49 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id B14D543DE0; Mon, 8 Dec 2025 14:59:48 -0500 (EST) Received: from [172.19.199.80] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id A083D43DD1; Mon, 8 Dec 2025 14:53:31 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 0E34543E77; Mon, 8 Dec 2025 14:53:15 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 2D82043E72 for ; Mon, 8 Dec 2025 14:52:50 -0500 (EST) Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-318-0mg4_05ROniC_NJCq-BTRQ-1; Mon, 08 Dec 2025 14:52:43 -0500 Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-29848363458so99423735ad.2 for ; Mon, 08 Dec 2025 11:52:43 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.110.66]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29daeaab9c0sm134095235ad.68.2025.12.08.11.52.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Dec 2025 11:52:40 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1765223569; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GH2uqIsdJBJ4/hAdR1cPgM5LESiNOjysz14YsnbrO24=; b=cbK5EQ+kdcTxaBzJzzD7XVCEMFOkC2lGN18aJWvoNQxB0eyXOifq1yyHxuzeWfC/K4APcs fsS7b/BCIiIIuebmOUaSkbNed+KJW29pBfIuJ40Mw07bl2Wl59EVAygfIrKA7WAOZBzheU FzUdN9MlPN73a+nkX6Q4Yu/OTyYVILE= X-MC-Unique: 0mg4_05ROniC_NJCq-BTRQ-1 X-Mimecast-MFC-AGG-ID: 0mg4_05ROniC_NJCq-BTRQ_1765223562 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765223561; x=1765828361; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=GH2uqIsdJBJ4/hAdR1cPgM5LESiNOjysz14YsnbrO24=; b=LL+yaYYgTlCz0MmcSQxnMdGFGgugyLDWaqtNTxxbqvWZwLirIwCtZBJlvDnZDn/DJ4 noWWm2q4CTmCjLgC4D7cB6+KfKyZ1ghUdkmqKI3Q8P6306y/Tow4a8vzkQKCNiKYv7PY hW56B9/aNXrtTOuDodagMjI0agWIjbACQIUYsZErDnu/suonqCzI/mgezVii738qEtQQ Oyi/orn4oiF/j+qIcQGtBAqbnmh55w32gXOeRukK7Ode9pYxyHU+8JvwkIqdv+PEQvAS N3dBAANPEV1IdhiCxSf/LHPhMXV/BSnxjg+Wb4QLeCvd3WDs+7W6DaBAdo5vY9Xyvy9k fxtw== X-Gm-Message-State: AOJu0YyKZvb8JgJm0SiMXRhWWOmaNIY/nrMF1DsCjnHlietKOjMpA2gm 9NuSHR7KaVi+d39/955ObOINaCsnp9+MhPhqaEdI0oN5a5uwk5oA8tx61tfgLtPb95fy7cl+dUf Io2RRkSWh7Qa/t04y4+rzM/hCQokRT0cqvL4IUlYmSrh/PjcobNBgdGlRFKBJT7FLmrD8nv2eBl lIiG38R0Tr/4N16EkVL5MvaGOfdxlQ6rf5EgnU55LRPA== X-Gm-Gg: ASbGncs5ktAo1SeZ3me4eRZabLiJO1fZWp8H27MXHaaM1BcY02thRkxfBCokj+NJWzt 9jM3OB7Ld7JGZSBOoCcN8FxwBGVh4qD/Fz+T5SRa0W12cOjfLxrRdP7YruA/JWWv/DdhI+WvhRW hHeELzV2qBQAO6uwYiYztWYd0LlC8YLiJViHrpirsk90Ixip5/XioLCZ4KND1dqRAx5NkihglIB P8ya+G/Unw06VikM+Pyz8e+c7u6Z8708pqJMIf2G49fMkbVaavKI+uNj94GO566neRjKudfw8K2 jCR+muYt2b/4+an2ogkNzH1WsdJJ0j1nshDgXDSdQ587Vl8SQ4/8uFD+4LHw45Z/3oYre8aUbIe 7qCNALNrWaDRZtEVdMtFdPaM1yt3WXFKHS+2wafX5Kyjlb0E5GY+6IkZO X-Received: by 2002:a17:902:e80a:b0:295:f47:75cb with SMTP id d9443c01a7336-29df5793251mr90407165ad.23.1765223561508; Mon, 08 Dec 2025 11:52:41 -0800 (PST) X-Google-Smtp-Source: AGHT+IG+LIZIaBnj1BY6Q4BcQw288iKRRTI65376PCqBKCHCATEUDIBaFODONAo2cAzYPoP82DFQTg== X-Received: by 2002:a17:902:e80a:b0:295:f47:75cb with SMTP id d9443c01a7336-29df5793251mr90406975ad.23.1765223561030; Mon, 08 Dec 2025 11:52:41 -0800 (PST) To: devel@lists.libvirt.org Subject: [PATCH 2/5] secret: Set up default encryption secret key for the virtsecretd service Date: Tue, 9 Dec 2025 01:22:28 +0530 Message-ID: <20251208195231.98170-3-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251208195231.98170-1-armenon@redhat.com> References: <20251208195231.98170-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: hZ6KXUSyq7_OE8g7p92CeWQEYLhECwMMHIS5cE6oEhE_1765223562 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: RZ2XN35SK7CWG3V6V7IIKYFEMMUZABB7 X-Message-ID-Hash: RZ2XN35SK7CWG3V6V7IIKYFEMMUZABB7 X-MailFrom: armenon@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Arun Menon X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Arun Menon via Devel Reply-To: Arun Menon X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1765223995579158500 Content-Type: text/plain; charset="utf-8"; x-default="true" This commit sets the foundation for encrypting the libvirt secrets by provi= ding a secure way to pass a secret encryption key to the virtsecretd service. A random secret key is generated using the new virt-secret-init-encryption service. This key can be consumed by the virtsecretd service. By using the "Before=3D" directive in the new virt-secret-init-encryption service and using "Requires=3D" directive in the virtsecretd service, we make sure that the daemon is run only after we have an encrypted secret key file generated and placed in /var/lib/libvirt/secrets. The virtsecretd service can then read the key from CREDENTIALS_DIRECTORY. [= 1] This setup therefore provides a default key out-of-the-box for initial use. A subsequent commit will introduce the logic for virtsecretd to access and use this key via the $CREDENTIALS_DIRECTORY environment varia= ble. [2] [1] https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.h= tml [2] https://systemd.io/CREDENTIALS/ Signed-off-by: Arun Menon --- libvirt.spec.in | 4 ++++ src/meson.build | 1 + src/remote/libvirtd.service.in | 2 ++ src/secret/meson.build | 15 +++++++++++++++ src/secret/virt-secret-init-encryption.service.in | 7 +++++++ src/secret/virtsecretd.service.extra.in | 8 ++++++++ 6 files changed, 37 insertions(+) create mode 100644 src/secret/virt-secret-init-encryption.service.in diff --git a/libvirt.spec.in b/libvirt.spec.in index ccfe75135b..8505a220c7 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1890,13 +1890,16 @@ exit 0 %pre daemon-driver-secret %libvirt_sysconfig_pre virtsecretd %libvirt_systemd_unix_pre virtsecretd +%libvirt_systemd_oneshot_pre virt-secret-init-encryption =20 %posttrans daemon-driver-secret %libvirt_sysconfig_posttrans virtsecretd %libvirt_systemd_unix_posttrans virtsecretd +%libvirt_systemd_unix_posttrans virt-secret-init-encryption =20 %preun daemon-driver-secret %libvirt_systemd_unix_preun virtsecretd +%libvirt_systemd_unix_preun virt-secret-init-encryption =20 %pre daemon-driver-storage-core %libvirt_sysconfig_pre virtstoraged @@ -2248,6 +2251,7 @@ exit 0 %{_datadir}/augeas/lenses/virtsecretd.aug %{_datadir}/augeas/lenses/tests/test_virtsecretd.aug %{_unitdir}/virtsecretd.service +%{_unitdir}/virt-secret-init-encryption.service %{_unitdir}/virtsecretd.socket %{_unitdir}/virtsecretd-ro.socket %{_unitdir}/virtsecretd-admin.socket diff --git a/src/meson.build b/src/meson.build index 47c978cc1f..f18f562fd9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -837,6 +837,7 @@ if conf.has('WITH_LIBVIRTD') 'sbindir': sbindir, 'sysconfdir': sysconfdir, 'initconfdir': initconfdir, + 'localstatedir': localstatedir, 'name': unit['name'], 'service': unit['service'], 'SERVICE': unit['service'].to_upper(), diff --git a/src/remote/libvirtd.service.in b/src/remote/libvirtd.service.in index b0a062e885..84447e0bcf 100644 --- a/src/remote/libvirtd.service.in +++ b/src/remote/libvirtd.service.in @@ -29,6 +29,8 @@ Conflicts=3Dxendomains.service Type=3Dnotify-reload Environment=3DLIBVIRTD_ARGS=3D"--timeout 120" EnvironmentFile=3D-@initconfdir@/libvirtd +Environment=3DSECRETS_ENCRYPTION_KEY=3D%d/secrets-encryption-key +LoadCredentialEncrypted=3Dsecrets-encryption-key:@localstatedir@/lib/libvi= rt/secrets/secrets-encryption-key ExecStart=3D@sbindir@/libvirtd $LIBVIRTD_ARGS ExecReload=3D/bin/kill -HUP $MAINPID KillMode=3Dprocess diff --git a/src/secret/meson.build b/src/secret/meson.build index 3b859ea7b4..b0977eadc8 100644 --- a/src/secret/meson.build +++ b/src/secret/meson.build @@ -31,6 +31,21 @@ if conf.has('WITH_SECRETS') 'name': 'virtsecretd', } =20 + virt_secret_init_encryption_conf =3D configuration_data() + + virt_secret_init_encryption_conf.set('localstatedir', localstatedir) + virt_secret_init_encryption_conf.set('name', 'Libvirt Secret Encryption = Init') + virt_secret_init_encryption_conf.set('service', 'virt-secret-init-encryp= tion') + virt_secret_init_encryption_conf.set('SERVICE', 'virt-secret-init-encryp= tion'.to_upper()) + + configure_file( + input: 'virt-secret-init-encryption.service.in', + output: '@0@.service'.format('virt-secret-init-encryption'), + configuration: virt_secret_init_encryption_conf, + install: true, + install_dir: unitdir, + ) + virt_daemon_units +=3D { 'service': 'virtsecretd', 'name': 'secret', diff --git a/src/secret/virt-secret-init-encryption.service.in b/src/secret= /virt-secret-init-encryption.service.in new file mode 100644 index 0000000000..0a23d452e8 --- /dev/null +++ b/src/secret/virt-secret-init-encryption.service.in @@ -0,0 +1,7 @@ +[Unit] +Before=3Dvirtsecretd.service +ConditionPathExists=3D!@localstatedir@/lib/libvirt/secrets/secrets-encrypt= ion-key + +[Service] +Type=3Doneshot +ExecStart=3D/usr/bin/sh -c 'umask 0066 && (dd if=3D/dev/urandom status=3Dn= one bs=3D32 count=3D1 | systemd-creds encrypt --name=3Dsecrets-encryption-k= ey - @localstatedir@/lib/libvirt/secrets/secrets-encryption-key)' diff --git a/src/secret/virtsecretd.service.extra.in b/src/secret/virtsecre= td.service.extra.in index 1fc8c672f7..116458b22a 100644 --- a/src/secret/virtsecretd.service.extra.in +++ b/src/secret/virtsecretd.service.extra.in @@ -1,2 +1,10 @@ # The contents of this unit will be merged into a base template. # Additional units might be merged as well. See meson.build for details. +# +[Unit] +Requires=3Dvirt-secret-init-encryption.service +After=3Dvirt-secret-init-encryption.service + +[Service] +LoadCredentialEncrypted=3Dsecrets-encryption-key:@localstatedir@/lib/libvi= rt/secrets/secrets-encryption-key +Environment=3DSECRETS_ENCRYPTION_KEY=3D%d/secrets-encryption-key --=20 2.51.1 From nobody Fri Dec 12 12:54:59 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1765223820; cv=none; d=zohomail.com; s=zohoarc; b=oCpasBHUOlNTWHcPb3LJAUCLM+zab32cPwd2Lv2LrzShSgwhcfypVg57tHkb6SEr/aVRHu+/3v9sVsOJEe3CR4aYO6ORqqukTmZmWSc9MMqWtjxgdLhv6wAOhcxQontTXjxMj5uu5o+AVUg+31oumfn6gvmKjD/qOeh56Co7ITw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1765223820; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=/8nK2mB8YAQ3Q6PGndojvxYtgjX3ZieEmSTO3aMWWVg=; b=hf/aey4yiC2VNh2dWNovoeQBuYAR9KFDtvbnWhQBjWxOOh2c7gLkF+l4thTmEWIYawopXRwvAA2VSgFEMMh2ltw+6Rtjqg//OJwzVkgzge41iRRASVqZ/SKQJW44s7S+FZrL70Ee4fTqCtHQVO5X/ncEHUE9GWBBa/ojTpHdr+c= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1765223820126161.47299233496653; Mon, 8 Dec 2025 11:57:00 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id 1F68043E67; Mon, 8 Dec 2025 14:56:59 -0500 (EST) Received: from [172.19.199.80] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id 6348643F7C; Mon, 8 Dec 2025 14:53:12 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id B955843E26; Mon, 8 Dec 2025 14:53:01 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 0010143E2A for ; Mon, 8 Dec 2025 14:52:47 -0500 (EST) Received: from mail-pl1-f199.google.com (mail-pl1-f199.google.com [209.85.214.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-536-JRLMYZOuO0GTjSlhInWB8Q-1; Mon, 08 Dec 2025 14:52:45 -0500 Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-299ddb0269eso54543845ad.0 for ; Mon, 08 Dec 2025 11:52:45 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.110.66]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29daeaab9c0sm134095235ad.68.2025.12.08.11.52.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Dec 2025 11:52:42 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1765223567; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/8nK2mB8YAQ3Q6PGndojvxYtgjX3ZieEmSTO3aMWWVg=; b=b999WpClsD25Mo6g1eHLaFlGYFAyQ1JeOyE3VpJRDS8CxdSyBsgWLj5siCVIrQlgh/lQFk aaDH8SlgMHP1OKnmsd6tBZBoxiIqp0055XRxbp7hP4GOfMNTFECqg3yR86B1rmg6Rs/hzo cYJQbdHAqaJ5MuAzdkuBm3L7QUT4+Cw= X-MC-Unique: JRLMYZOuO0GTjSlhInWB8Q-1 X-Mimecast-MFC-AGG-ID: JRLMYZOuO0GTjSlhInWB8Q_1765223565 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765223564; x=1765828364; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=/8nK2mB8YAQ3Q6PGndojvxYtgjX3ZieEmSTO3aMWWVg=; b=NGnCPwRVxesF4TqZEiu7nS3hCWqeSmAKpxsDmC+kO9AwNyyh1Vayw503+yxMmBOBBT /eEiJZu8kH2YYoAJ3HPpS2nCneE8d+6sVzyakzcYZ4nZHkPJT2S3asddkPtKWyPzjCpG DixJWh4iBlM4Vv475jlWp5TDuIZ0SE1eVayhp8KhIpcUTj3Sl26NjPSgJhooSKqJWBc9 ChnGVXQc/ue7wFZL0dYNON09O+P/+TcTj108DQkT2Fs5kCLSbL3YLTTpExtB+EIKNZNt cUjx/x1+yE6AWlulI4ZIkqzK9o4qDiVb1ujO4abh2aM5ib8spqmcTRfx5y4iDGGLMf68 LGDQ== X-Gm-Message-State: AOJu0YwwJuzcshrywhIibBzbZZLjRp4pBDXoBJXhnwsK/wEj6j5pAsHS AqEOGJ25rBbLGYAUWmPQyxkFc2fWaTnXOs0ASD8fDWgCxPuD7Nbk4k1hex67SKYuuz5KWaEW1kV dBFDYJFSF9UXpJ53P0UEh3Gl6rFXKf0ddXJycn1d3rmc2m7Yew5b5scD7/ANfUUksXhM25Jghat fGC8TQo/YBA3jFOMNYPAK1fqrvnERsQNcDvWyShfVkLQ== X-Gm-Gg: ASbGncvQPSoJdtPhX31xGzV/agT6gtUXMebQRJy/dw7pDCSWXADhhZzo4FghJvPz/O1 fQdbiMNaGu5dTS4dTQc7bbI5EFu6YukKEexHofSAcvYJ5cXd3bHzTkBBrlr6qm8r/8Z1x6+CTVa QbmLKwKJiGK5fPJFpqSkWaH4IDiYCtkypzpC3zPokcuKvKcn1hMh3bS+sCkAAbEcRgA3d1dFUES yqJExQk6ELrIMvs0/pPnLlZKiiEMn7h5XTs0pdQkBCVdOkkyYaixQsJ+PsCK7Oy/z8m4NcwSQwM YSz3iPhf4DYuCcvrmwoEUWURnIO1bsQz8u27Uzu+HZjAq5kaKX1IdS3s4i/Ik0mo2L1YYirjx4y hbxpESyETpd2hkP9xnVr7EZFPCG15Az34bF7bJ7HwKRP72v4h5q19Ns68 X-Received: by 2002:a17:903:3d0d:b0:295:7453:b58b with SMTP id d9443c01a7336-29df579e8e6mr79397595ad.4.1765223563990; Mon, 08 Dec 2025 11:52:43 -0800 (PST) X-Google-Smtp-Source: AGHT+IHtanGpa+B63qmPzOXO9X+iAd5aJ/M5zVBGYpA5lNsVzeGli7vZLxN/fJAqTE+ZS49Uf42zCA== X-Received: by 2002:a17:903:3d0d:b0:295:7453:b58b with SMTP id d9443c01a7336-29df579e8e6mr79397375ad.4.1765223563360; Mon, 08 Dec 2025 11:52:43 -0800 (PST) To: devel@lists.libvirt.org Subject: [PATCH 3/5] secret: Add secret.conf configuration file and parse it Date: Tue, 9 Dec 2025 01:22:29 +0530 Message-ID: <20251208195231.98170-4-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251208195231.98170-1-armenon@redhat.com> References: <20251208195231.98170-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: m0duGMCw7akkzJRGq2nTVHawkjMyife2WpldRAvg6Rk_1765223565 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: ZGXJIILFYESVQUJBW4W2RLK5JXVS5SNP X-Message-ID-Hash: ZGXJIILFYESVQUJBW4W2RLK5JXVS5SNP X-MailFrom: armenon@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Arun Menon X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Arun Menon via Devel Reply-To: Arun Menon X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1765223830738158500 Content-Type: text/plain; charset="utf-8"; x-default="true" A new configuration file called secret.conf is introduced to let the user configure the path to the secrets encryption key. This key will be used to encrypt/decrypt the secrets in libvirt. By default the path is set to the runtime directory /run/libvirt/secrets, and it is commented in the config file. After parsing the file, the virtsecretd driver checks if an encryption key is present in the path and is valid. If no encryption key is present in the path, then the service will by default use the encryption key stored in the CREDENTIALS_DIRECTORY. Add logic to parse the encryption key file and store the key. It also checks for the encrypt_data attribute in the config file. The encryption and decryption logic will be added in the subsequent patches. Signed-off-by: Arun Menon --- include/libvirt/virterror.h | 1 + libvirt.spec.in | 3 + po/POTFILES | 1 + src/secret/libvirt_secrets.aug | 40 ++++++ src/secret/meson.build | 19 +++ src/secret/secret.conf.in | 14 ++ src/secret/secret_config.c | 171 +++++++++++++++++++++++++ src/secret/secret_config.h | 40 ++++++ src/secret/secret_driver.c | 9 ++ src/secret/test_libvirt_secrets.aug.in | 6 + src/util/virerror.c | 3 + 11 files changed, 307 insertions(+) create mode 100644 src/secret/libvirt_secrets.aug create mode 100644 src/secret/secret.conf.in create mode 100644 src/secret/secret_config.c create mode 100644 src/secret/secret_config.h create mode 100644 src/secret/test_libvirt_secrets.aug.in diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index f02da046a3..5d9e06b4bb 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -353,6 +353,7 @@ typedef enum { command within timeout (Since: = 11.2.0) */ VIR_ERR_AGENT_COMMAND_FAILED =3D 113, /* guest agent responded with fa= ilure to a command (Since: 11.2.0) */ + VIR_ERR_INVALID_ENCR_KEY_SECRET =3D 114, /* encrypion key is invalid (= Since: 12.0.0) */ =20 # ifdef VIR_ENUM_SENTINELS VIR_ERR_NUMBER_LAST /* (Since: 5.0.0) */ diff --git a/libvirt.spec.in b/libvirt.spec.in index 8505a220c7..bb1dcc367f 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -2250,6 +2250,9 @@ exit 0 %config(noreplace) %{_sysconfdir}/libvirt/virtsecretd.conf %{_datadir}/augeas/lenses/virtsecretd.aug %{_datadir}/augeas/lenses/tests/test_virtsecretd.aug +%{_datadir}/augeas/lenses/libvirt_secrets.aug +%{_datadir}/augeas/lenses/tests/test_libvirt_secrets.aug +%config(noreplace) %{_sysconfdir}/libvirt/secret.conf %{_unitdir}/virtsecretd.service %{_unitdir}/virt-secret-init-encryption.service %{_unitdir}/virtsecretd.socket diff --git a/po/POTFILES b/po/POTFILES index f0aad35c8c..045981a9ac 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -233,6 +233,7 @@ src/rpc/virnettlscert.c src/rpc/virnettlsconfig.c src/rpc/virnettlscontext.c src/secret/secret_driver.c +src/secret/secret_config.c src/security/security_apparmor.c src/security/security_dac.c src/security/security_driver.c diff --git a/src/secret/libvirt_secrets.aug b/src/secret/libvirt_secrets.aug new file mode 100644 index 0000000000..8dda373e62 --- /dev/null +++ b/src/secret/libvirt_secrets.aug @@ -0,0 +1,40 @@ +(* /etc/libvirt/secret.conf *) + +module Libvirt_secrets =3D + autoload xfm + + let eol =3D del /[ \t]*\n/ "\n" + let value_sep =3D del /[ \t]*=3D[ \t]*/ " =3D " + let indent =3D del /[ \t]*/ "" + + let array_sep =3D del /,[ \t\n]*/ ", " + let array_start =3D del /\[[ \t\n]*/ "[ " + let array_end =3D del /\]/ "]" + + let str_val =3D del /\"/ "\"" . store /[^\"]*/ . del /\"/ "\"" + let bool_val =3D store /0|1/ + let int_val =3D store /[0-9]+/ + let str_array_element =3D [ seq "el" . str_val ] . del /[ \t\n]*/ "" + let str_array_val =3D counter "el" . array_start . ( str_array_element = . ( array_sep . str_array_element ) * ) ? . array_end + + let str_entry (kw:string) =3D [ key kw . value_sep . str_val ] + let bool_entry (kw:string) =3D [ key kw . value_sep . bool_val ] + let int_entry (kw:string) =3D [ key kw . value_sep . int_val ] + let str_array_entry (kw:string) =3D [ key kw . value_sep . str_array_va= l ] + + let secrets_entry =3D str_entry "secrets_encryption_key" + | bool_entry "encrypt_data" + + (* Each entry in the config is one of the following three ... *) + let entry =3D secrets_entry + let comment =3D [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \= t\n][^\n]*)?/ . del /\n/ "\n" ] + let empty =3D [ label "#empty" . eol ] + + let record =3D indent . entry . eol + + let lns =3D ( record | comment | empty ) * + + let filter =3D incl "/etc/libvirt/secret.conf" + . Util.stdexcl + + let xfm =3D transform lns filter diff --git a/src/secret/meson.build b/src/secret/meson.build index b0977eadc8..c29b992190 100644 --- a/src/secret/meson.build +++ b/src/secret/meson.build @@ -1,5 +1,6 @@ secret_driver_sources =3D [ 'secret_driver.c', + 'secret_config.c', ] =20 driver_source_files +=3D files(secret_driver_sources) @@ -27,6 +28,24 @@ if conf.has('WITH_SECRETS') ], } =20 + secret_conf =3D configure_file( + input: 'secret.conf.in', + output: 'secret.conf', + copy: true + ) + virt_conf_files +=3D secret_conf + + virt_aug_files +=3D files('libvirt_secrets.aug') + + virt_test_aug_files +=3D { + 'name': 'test_libvirt_secrets.aug', + 'aug': files('test_libvirt_secrets.aug.in'), + 'conf': files('secret.conf.in'), + 'test_name': 'libvirt_secrets', + 'test_srcdir': meson.current_source_dir(), + 'test_builddir': meson.current_build_dir(), + } + virt_daemon_confs +=3D { 'name': 'virtsecretd', } diff --git a/src/secret/secret.conf.in b/src/secret/secret.conf.in new file mode 100644 index 0000000000..a231c48f8d --- /dev/null +++ b/src/secret/secret.conf.in @@ -0,0 +1,14 @@ +# +# Configuration file for the secrets driver. +# +# The secret encryption key is used to override default encryption +# key path. The user can create an encryption key and set the secret_encry= ption_key +# to the path on which it resides. +# The key must be 32-bytes long. +#secrets_encryption_key =3D "/run/libvirt/secrets/secret-encryption-key" + +# The encrypt_data setting is used to indicate if the encryption is on or = off. +# 0 indicates off and 1 indicates on. By default it is on +# if secrets_encryption_key is set to a non-NULL +# path, or if a systemd credential named "secrets-encryption-key" exists. +#encrypt_data =3D 1 diff --git a/src/secret/secret_config.c b/src/secret/secret_config.c new file mode 100644 index 0000000000..b63567944e --- /dev/null +++ b/src/secret/secret_config.c @@ -0,0 +1,171 @@ +/* + * secret_config.c: secret.conf config file handling + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include +#include +#include "configmake.h" +#include "datatypes.h" +#include "virlog.h" +#include "virerror.h" +#include "virfile.h" +#include "virutil.h" +#include "virsecureerase.h" +#include "secret_config.h" + + +#define VIR_FROM_THIS VIR_FROM_SECRET + +VIR_LOG_INIT("secret.secret_config"); + +static virClass *virSecretDaemonConfigClass; +static void virSecretDaemonConfigDispose(void *obj); + +static int +virSecretConfigOnceInit(void) +{ + if (!VIR_CLASS_NEW(virSecretDaemonConfig, virClassForObject())) + return -1; + + return 0; +} + + +VIR_ONCE_GLOBAL_INIT(virSecretConfig); + + +int +virSecretDaemonConfigFilePath(bool privileged, char **configfile) +{ + if (privileged) { + *configfile =3D g_strdup(SYSCONFDIR "/libvirt/secret.conf"); + } else { + g_autofree char *configdir =3D NULL; + + configdir =3D virGetUserConfigDirectory(); + + *configfile =3D g_strdup_printf("%s/secret.conf", configdir); + } + + return 0; +} + + +static int +virSecretLoadDaemonConfig(virSecretDaemonConfig *cfg, + const char *filename) +{ + g_autoptr(virConf) conf =3D NULL; + int res; + + if (virFileExists(filename)) { + conf =3D virConfReadFile(filename, 0); + if (!conf) + return -1; + res =3D virConfGetValueBool(conf, "encrypt_data", &cfg->encryptDat= a); + if (res < 0) { + return -1; + } else if (res =3D=3D 1) { + cfg->encryptDataWasSet =3D true; + } else { + cfg->encryptDataWasSet =3D false; + } + + if (virConfGetValueString(conf, "secrets_encryption_key", + &cfg->secretsEncryptionKeyPath) < 0) { + return -1; + } + } + return 0; +} + + +static int virGetSecretsEncryptionKey(virSecretDaemonConfig *cfg, + uint8_t **secretsEncryptionKey, + size_t *secretsKeyLen) +{ + VIR_AUTOCLOSE fd =3D -1; + int encryption_key_length; + + if ((encryption_key_length =3D virFileReadAll(cfg->secretsEncryptionKe= yPath, VIR_SECRETS_ENCRYPTION_KEY_LEN, (char**)secretsEncryptionKey)) < 0) { + return -1; + } + if (encryption_key_length !=3D VIR_SECRETS_ENCRYPTION_KEY_LEN) { + virReportError(VIR_ERR_INVALID_ENCR_KEY_SECRET, + _("Encryption key length must be '%1$d' '%2$s'"), + VIR_SECRETS_ENCRYPTION_KEY_LEN, cfg->secretsEncrypt= ionKeyPath); + } + + *secretsKeyLen =3D (size_t)encryption_key_length; + return 0; +} + + +virSecretDaemonConfig * +virSecretDaemonConfigNew(bool privileged) +{ + g_autoptr(virSecretDaemonConfig) cfg =3D NULL; + g_autofree char *configdir =3D NULL; + g_autofree char *configfile =3D NULL; + const char *credentials_directory; + + if (virSecretConfigInitialize() < 0) + return NULL; + + if (!(cfg =3D virObjectNew(virSecretDaemonConfigClass))) + return NULL; + + cfg->secretsEncryptionKeyPath =3D NULL; + + if (virSecretDaemonConfigFilePath(privileged, &configfile) < 0) + return NULL; + + if (virSecretLoadDaemonConfig(cfg, configfile) < 0) + return NULL; + + credentials_directory =3D getenv("CREDENTIALS_DIRECTORY"); + + if (!cfg->secretsEncryptionKeyPath && credentials_directory) { + cfg->secretsEncryptionKeyPath =3D g_strdup_printf("%s/secrets-encr= yption-key", + credentials_direct= ory); + if (!virFileExists(cfg->secretsEncryptionKeyPath)) { + g_clear_pointer(&cfg->secretsEncryptionKeyPath, g_free); + } + } + + if (!cfg->encryptDataWasSet) { + if (!cfg->secretsEncryptionKeyPath) { + /* No path specified by user or environment, disable encryptio= n */ + cfg->encryptData =3D false; + } else { + cfg->encryptData =3D true; + } + } else { + if (cfg->encryptData) { + if (!cfg->secretsEncryptionKeyPath) { + /* Built-in default path must be used */ + cfg->secretsEncryptionKeyPath =3D g_strdup("/run/libvirt/s= ecrets/encryption-key"); + } + } + } + VIR_DEBUG("Secrets encryption key path: %s", NULLSTR(cfg->secretsEncry= ptionKeyPath)); + + if (cfg->encryptData) { + if (virGetSecretsEncryptionKey(cfg, &cfg->secretsEncryptionKey, &c= fg->secretsKeyLen) < 0) { + return NULL; + } + } + return g_steal_pointer(&cfg); +} + + +static void +virSecretDaemonConfigDispose(void *obj) +{ + virSecretDaemonConfig *cfg =3D obj; + + virSecureErase(cfg->secretsEncryptionKey, cfg->secretsKeyLen); + g_free(cfg->secretsEncryptionKeyPath); +} diff --git a/src/secret/secret_config.h b/src/secret/secret_config.h new file mode 100644 index 0000000000..888acf272b --- /dev/null +++ b/src/secret/secret_config.h @@ -0,0 +1,40 @@ +/* + * secret_config.h: secret.conf config file handling + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "internal.h" +#include "virinhibitor.h" +#include "secret_event.h" +#define VIR_SECRETS_ENCRYPTION_KEY_LEN 32 + +typedef struct _virSecretDaemonConfig virSecretDaemonConfig; +struct _virSecretDaemonConfig { + virObject parent; + /* secrets encryption key path from secret.conf file */ + char *secretsEncryptionKeyPath; + + /* Store the key to encrypt secrets on the disk */ + unsigned char *secretsEncryptionKey; + + size_t secretsKeyLen; + + /* Indicates if the newly written secrets are encrypted or not. + */ + bool encryptData; + + /* Indicates if the config file has encrypt_data set or not. + */ + bool encryptDataWasSet; +}; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSecretDaemonConfig, virObjectUnref); + +int virSecretDaemonConfigFilePath(bool privileged, char **configfile); +virSecretDaemonConfig *virSecretDaemonConfigNew(bool privileged); +int virSecretDaemonConfigLoadFile(virSecretDaemonConfig *data, + const char *filename, + bool allow_missing); diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 04c3ca49f1..408a629ea0 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -70,6 +70,9 @@ struct _virSecretDriverState { =20 /* Immutable pointer, self-locking APIs */ virInhibitor *inhibitor; + + /* Settings from secret.conf file */ + virSecretDaemonConfig *config; }; =20 static virSecretDriverState *driver; @@ -454,6 +457,7 @@ secretStateCleanupLocked(void) VIR_FREE(driver->configDir); =20 virObjectUnref(driver->secretEventState); + virObjectUnref(driver->config); virInhibitorFree(driver->inhibitor); =20 if (driver->lockFD !=3D -1) @@ -518,6 +522,8 @@ secretStateInitialize(bool privileged, driver->stateDir); goto error; } + if (!(driver->config =3D virSecretDaemonConfigNew(driver->privileged))) + goto error; =20 driver->inhibitor =3D virInhibitorNew( VIR_INHIBITOR_WHAT_NONE, @@ -553,6 +559,9 @@ secretStateReload(void) if (!driver) return -1; =20 + if (!(driver->config =3D virSecretDaemonConfigNew(driver->privileged))) + return -1; + ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDi= r)); =20 return 0; diff --git a/src/secret/test_libvirt_secrets.aug.in b/src/secret/test_libvi= rt_secrets.aug.in new file mode 100644 index 0000000000..1bb205e0f2 --- /dev/null +++ b/src/secret/test_libvirt_secrets.aug.in @@ -0,0 +1,6 @@ +module Test_libvirt_secrets =3D + @CONFIG@ + + test Libvirt_secrets.lns get conf =3D +{ "secrets_encryption_key" =3D "/run/libvirt/secrets/secret-encryption-key= " } +{ "encrypt_data" =3D "1" } diff --git a/src/util/virerror.c b/src/util/virerror.c index abb014b522..b7d23f81c7 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -1296,6 +1296,9 @@ static const virErrorMsgTuple virErrorMsgStrings[] = =3D { [VIR_ERR_AGENT_COMMAND_FAILED] =3D { N_("guest agent command failed"), N_("guest agent command failed: %1$s") }, + [VIR_ERR_INVALID_ENCR_KEY_SECRET] =3D { + N_("Invalid encryption key for the secret"), + N_("Invalid encryption key for the secret: %1$s") }, }; =20 G_STATIC_ASSERT(G_N_ELEMENTS(virErrorMsgStrings) =3D=3D VIR_ERR_NUMBER_LAS= T); --=20 2.51.1 From nobody Fri Dec 12 12:54:59 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1765223884; cv=none; d=zohomail.com; s=zohoarc; b=heW2BW8kNA0DFHvg5xyRCr+NBmWEjoQlbNO32+jD7uMZk1HGUFiuKhYRNNZx8rTb/ElasIeK0BRmwHC9RiLAx+I21POw8f1qIVozoDU6xBLhFFw75gN8QDVE52zjqOyJ5ypRcZHTDN61o42d3cPCgFM6gAsEnUdZ+g/6HokMOx0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1765223884; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=TfYkarqOFCsxCxX4AjmQBE/qLOH4sLm7SdhOmuVgxWk=; b=Rs93yVjC85Jm/nZ2ZeH24kbTxCp0Pc5X33r7XPkwVa5aYKwDcRPfdgljd+77uh2JJm8ozVrd7Hp6MTIBWrta/r2s8cLq7Fzja3WeBmkXmWQQiCh6zAWYfDYRHy1laIRt6U2nAFquBn7dS61obQKBrTrHuZwFQd3dY9l/3Cur7fs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1765223884615147.69892634538473; Mon, 8 Dec 2025 11:58:04 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id B4F6943F06; Mon, 8 Dec 2025 14:58:03 -0500 (EST) Received: from [172.19.199.80] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id A305043FD0; Mon, 8 Dec 2025 14:53:27 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id DC9C143F81; Mon, 8 Dec 2025 14:53:14 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 0058F43E65 for ; Mon, 8 Dec 2025 14:52:49 -0500 (EST) Received: from mail-pl1-f200.google.com (mail-pl1-f200.google.com [209.85.214.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-185-F0YTmTTTN2asyP00aDpbfQ-1; Mon, 08 Dec 2025 14:52:47 -0500 Received: by mail-pl1-f200.google.com with SMTP id d9443c01a7336-297df52c960so96049445ad.1 for ; Mon, 08 Dec 2025 11:52:47 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.110.66]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29daeaab9c0sm134095235ad.68.2025.12.08.11.52.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Dec 2025 11:52:45 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1765223569; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TfYkarqOFCsxCxX4AjmQBE/qLOH4sLm7SdhOmuVgxWk=; b=HNx7cUZSbnpWlwxjHgZUXl6SBxgZLWZ6fI8x07Sp98MwrgGvvifTKcmovBR5BDb4lThjUO uUEMa5zEfIPPjSljcjkfgSWdtbGWp70TeczsqEuknceQyQyRJlOdEZHIzLS2s7LALqO6dT AXjBc2u21DXSyccrWi0PqTgYWrXM4vY= X-MC-Unique: F0YTmTTTN2asyP00aDpbfQ-1 X-Mimecast-MFC-AGG-ID: F0YTmTTTN2asyP00aDpbfQ_1765223567 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765223566; x=1765828366; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=TfYkarqOFCsxCxX4AjmQBE/qLOH4sLm7SdhOmuVgxWk=; b=EkFATt2M+4yii9FZAZ4s8wrOw/cb8XjvMf1xcRvnNwyS7NgMWBj8VVXLLL7wZvwjru gMI3QdfAMdqJB7IKo/A3JqkvQE6Flpo1iNqfMJDIV/mjjGdvXRYcRgXRRBMO1hIRexx1 Lqzb+YyPn0HjC2731ORbxicuCVS9pVEa2Nhsjq3+c3u2J5ssXmxLq0jYWdF39Gxs4ZbL jxK9KtvZyWOxSmBWu2BFnDjv5UbbRn4VL0IGCavaT/hONQW19K0ROcDuK5WDs89z+IWa JFqCV9WzO8lGIb3ALo0GvVylp9eMhXJqEnHQmpa62USQLfa3Uo8QY1OIJMtta52X3HRK 6AKg== X-Gm-Message-State: AOJu0YwYTGJDnvllXQZzaR0RG6UBo0Dr1jwswb2RtjCXCbk29+3f+ut9 g0MyfYtqPhL0yL+jxIN4/RzDyt7WlvV/Ycd5QchIWZf7QeCGy6esM0qh+2myKN2KgpQ5RGt8Nti iHvZvz2Bn1APAS7unisATA7Nnle99NSm6jUnJLGe4rGRaH8vhTHic71e9BlIpCQ7i7I4RRdNiaV bJsyUPHQgibBGa/zVaZdSRhRMRsAf0SCq2PRYeOeXYLw== X-Gm-Gg: ASbGnct9ZkyP3gosV067HMuYBhkZIddtq+emPRwThqucjXBzlwkf9QsG5RKTzV4XAl2 TuKaQAmWXK+z4pklb+AD6F8Z9/kWO/hUQ4N+L6Fv3FTxbIHvAeVqJEiBkUYLYiGDi14SSETm1xa 4DH2TOICpLb1SRzVFY+I+sXbXW0Nidn5A7Y/s5hOmQp1lpwvcrRLkY7ZUG9PfXEGDi10LI5Bh4W 4+IuYfDz4ThVSb7W86blRyLoy605iDE6Pb7ztpKNFZLaZrX8xJ2PD2LLfFaeNY2kzhdV0sldwF6 dLBXCZYhFwm3MKvTYN5UcbAOYfEd/EUIBMlPgKjgd0q9M8wUtiln9aORrJ4oQ+ZyvCquvQaZI4d HkCMGtVx6b6vrVbMVQ1dlkRA9nklaT60f7dlBS/38cA4LLGtRzW4ml6A2 X-Received: by 2002:a17:902:fc43:b0:29d:9b3c:4fc8 with SMTP id d9443c01a7336-29df6107093mr77337875ad.61.1765223566295; Mon, 08 Dec 2025 11:52:46 -0800 (PST) X-Google-Smtp-Source: AGHT+IFL2Lh4eT4Fllq8B3a7QRQvh5u0Q5fCX9U5sV5jbmW+BUsm1lQmPlt0CqhZvjbGKi8otftWaA== X-Received: by 2002:a17:902:fc43:b0:29d:9b3c:4fc8 with SMTP id d9443c01a7336-29df6107093mr77337655ad.61.1765223565717; Mon, 08 Dec 2025 11:52:45 -0800 (PST) To: devel@lists.libvirt.org Subject: [PATCH 4/5] secret: Add encryptionSchemeType attribute to store ciphers Date: Tue, 9 Dec 2025 01:22:30 +0530 Message-ID: <20251208195231.98170-5-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251208195231.98170-1-armenon@redhat.com> References: <20251208195231.98170-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 7g2bmFfXCRl0B4qIhuuvPVsWHEzLxucIQjjfWM_rFJI_1765223567 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 2LAR2HS4GM4WLUOWQCZI46FCUPJSQXXN X-Message-ID-Hash: 2LAR2HS4GM4WLUOWQCZI46FCUPJSQXXN X-MailFrom: armenon@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Arun Menon X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Arun Menon via Devel Reply-To: Arun Menon X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1765223889129158500 Content-Type: text/plain; charset="utf-8"; x-default="true" The new attribute will store the available cipher modes with which secrets can be encrypted. At the moment only aes256cbc encryption method is used. This can be extended in future with other modes of cipher. Rename the file-name structure attribute from base64File to secretValueFile. Signed-off-by: Arun Menon --- src/conf/secret_conf.c | 6 ++++++ src/conf/secret_conf.h | 9 +++++++++ src/conf/virsecretobj.c | 22 +++++++++++----------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/conf/secret_conf.c b/src/conf/secret_conf.c index 966536599e..dd808aa21a 100644 --- a/src/conf/secret_conf.c +++ b/src/conf/secret_conf.c @@ -32,6 +32,12 @@ =20 #define VIR_FROM_THIS VIR_FROM_SECRET =20 +VIR_ENUM_IMPL(virSecretEncryptionScheme, + VIR_SECRET_ENCRYPTION_SCHEME_LAST, + "none", + "aes256cbc", +); + VIR_LOG_INIT("conf.secret_conf"); =20 void diff --git a/src/conf/secret_conf.h b/src/conf/secret_conf.h index 8f8f47933a..c11558357e 100644 --- a/src/conf/secret_conf.h +++ b/src/conf/secret_conf.h @@ -21,6 +21,7 @@ #pragma once =20 #include "internal.h" +#include "virenum.h" =20 typedef struct _virSecretDef virSecretDef; struct _virSecretDef { @@ -32,6 +33,12 @@ struct _virSecretDef { char *usage_id; /* May be NULL */ }; =20 +typedef enum { + VIR_SECRET_ENCRYPTION_SCHEME_NONE =3D 0, + VIR_SECRET_ENCRYPTION_SCHEME_AES256CBC =3D 1, + VIR_SECRET_ENCRYPTION_SCHEME_LAST +} virSecretEncryptionScheme; + void virSecretDefFree(virSecretDef *def); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSecretDef, virSecretDefFree); =20 @@ -53,3 +60,5 @@ char *virSecretDefFormat(const virSecretDef *def); #define VIR_CONNECT_LIST_SECRETS_FILTERS_ALL \ (VIR_CONNECT_LIST_SECRETS_FILTERS_EPHEMERAL | \ VIR_CONNECT_LIST_SECRETS_FILTERS_PRIVATE) + +VIR_ENUM_DECL(virSecretEncryptionScheme); diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index 66270e2751..a3dd7983bb 100644 --- a/src/conf/virsecretobj.c +++ b/src/conf/virsecretobj.c @@ -39,7 +39,7 @@ VIR_LOG_INIT("conf.virsecretobj"); struct _virSecretObj { virObjectLockable parent; char *configFile; - char *base64File; + char *secretValueFile; virSecretDef *def; unsigned char *value; /* May be NULL */ size_t value_size; @@ -139,7 +139,7 @@ virSecretObjDispose(void *opaque) g_free(obj->value); } g_free(obj->configFile); - g_free(obj->base64File); + g_free(obj->secretValueFile); } =20 =20 @@ -378,11 +378,11 @@ virSecretObjListAdd(virSecretObjList *secrets, if (!(obj =3D virSecretObjNew())) goto cleanup; =20 - /* Generate the possible configFile and base64File strings + /* Generate the possible configFile and secretValueFile strings * using the configDir, uuidstr, and appropriate suffix */ if (!(obj->configFile =3D virFileBuildPath(configDir, uuidstr, ".x= ml")) || - !(obj->base64File =3D virFileBuildPath(configDir, uuidstr, ".b= ase64"))) + !(obj->secretValueFile =3D virFileBuildPath(configDir, uuidstr= , ".base64"))) goto cleanup; =20 if (virHashAddEntry(secrets->objs, uuidstr, obj) < 0) @@ -656,7 +656,7 @@ virSecretObjDeleteData(virSecretObj *obj) { /* The configFile will already be removed, so secret won't be * loaded again if this fails */ - unlink(obj->base64File); + unlink(obj->secretValueFile); } =20 =20 @@ -691,7 +691,7 @@ virSecretObjSaveData(virSecretObj *obj) =20 base64 =3D g_base64_encode(obj->value, obj->value_size); =20 - if (virFileRewriteStr(obj->base64File, S_IRUSR | S_IWUSR, base64) < 0) + if (virFileRewriteStr(obj->secretValueFile, S_IRUSR | S_IWUSR, base64)= < 0) return -1; =20 return 0; @@ -813,26 +813,26 @@ virSecretLoadValue(virSecretObj *obj) struct stat st; g_autofree char *contents =3D NULL; =20 - if ((fd =3D open(obj->base64File, O_RDONLY)) =3D=3D -1) { + if ((fd =3D open(obj->secretValueFile, O_RDONLY)) =3D=3D -1) { if (errno =3D=3D ENOENT) { ret =3D 0; goto cleanup; } virReportSystemError(errno, _("cannot open '%1$s'"), - obj->base64File); + obj->secretValueFile); goto cleanup; } =20 if (fstat(fd, &st) < 0) { virReportSystemError(errno, _("cannot stat '%1$s'"), - obj->base64File); + obj->secretValueFile); goto cleanup; } =20 if ((size_t)st.st_size !=3D st.st_size) { virReportError(VIR_ERR_INTERNAL_ERROR, _("'%1$s' file does not fit in memory"), - obj->base64File); + obj->secretValueFile); goto cleanup; } =20 @@ -845,7 +845,7 @@ virSecretLoadValue(virSecretObj *obj) =20 if (saferead(fd, contents, st.st_size) !=3D st.st_size) { virReportSystemError(errno, _("cannot read '%1$s'"), - obj->base64File); + obj->secretValueFile); goto cleanup; } contents[st.st_size] =3D '\0'; --=20 2.51.1 From nobody Fri Dec 12 12:54:59 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1765224126; cv=none; d=zohomail.com; s=zohoarc; b=WEnZcBvGoOvRsOU40aPgVOiDjXb4IBc38gSj1nnt5NNQ781slLy3/TzGtXG8gBJa4vIcR1EGCLvrxoEcJ24VxQD98IH+eQNf90i6x7Zl6+S8u0nxFgBNdeXEELws09V8Fy4vyGiwbrL6x8A14upaMyISMix61uEgr3CPIE4Ls5w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1765224126; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=3iu19/V3oYeLJG9uXrHF36bWxX3M6VkxeeDpoPFGoHA=; b=BTQGly/LrlBTIcP0ymfZlhWVScY0/AMGtNRfk5VtvPdYC6pOGaBX13hZhgy/PwyxOSal8nheVFsJN4IrLddAZMyiDw94IvSRufu39BSra6uhHZkvJ44o3gHck4QcT6Hfo3MNlLbkwrlLmIVKEHwIEswPBf/T+0yMlFA+lBvy7pM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1765224126872221.69838697986233; Mon, 8 Dec 2025 12:02:06 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id BE4D043F39; Mon, 8 Dec 2025 15:02:05 -0500 (EST) Received: from [172.19.199.80] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id 328C544089; Mon, 8 Dec 2025 14:53:57 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id DCEE543E14; Mon, 8 Dec 2025 14:53:46 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 445A743DF6 for ; Mon, 8 Dec 2025 14:52:52 -0500 (EST) Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-211-7o295I69OFq5-ixLFZ-KZQ-1; Mon, 08 Dec 2025 14:52:50 -0500 Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-297d50cd8c4so132838985ad.0 for ; Mon, 08 Dec 2025 11:52:49 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.110.66]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29daeaab9c0sm134095235ad.68.2025.12.08.11.52.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Dec 2025 11:52:47 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1765223571; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=3iu19/V3oYeLJG9uXrHF36bWxX3M6VkxeeDpoPFGoHA=; b=Aag1FWTUsudrdecU/O3xO00tq+Q92fof0ty2yfJi93y5/auNaB4APDSguDzoCUuzT+pz1b fgMNkmcQhQi0Ziz+ZWRQwI5M3oSnzUuljIQBQSs3C1sajlybYELHbesC4Lyp8sEv3QHt/s Xc8x4SqrhSkETlgJut+q/OVCAmn1NTQ= X-MC-Unique: 7o295I69OFq5-ixLFZ-KZQ-1 X-Mimecast-MFC-AGG-ID: 7o295I69OFq5-ixLFZ-KZQ_1765223569 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765223568; x=1765828368; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=3iu19/V3oYeLJG9uXrHF36bWxX3M6VkxeeDpoPFGoHA=; b=MuFP5Oc0NhfFXpzqNlk2QSrM1SPj1b8B0FAa1GnzgeWW6QHBi/Yr5comxHa8e4mL4V EM7yKcvg/dQQa2qtJYfB3YZtzfP9C364Y2TI3L7sKI0ab9ctTSkmiJOFHq1TKm1Jdr5y pL2Sg+qSZYEcjm6KlYDGWYakcJDWbscPjE9lt8pPmYc0NpsAWkQcYAXZvB0Sa77wMbdv KuXJtbNPL2AR/Sejvt0wh5kfAnTlrXh+Jqnd3scOL/JQkvvcCxd0/YJVkPAIuHFa6E57 jrbF4F38SWYjW437r/nxYGf/2Hb2l3DU370Ul9PJSLe0BxKelcVRfz/R8obUcrcvx32I Gzfg== X-Gm-Message-State: AOJu0YzCVs6yycH84Er2rucVlHXl6RBdjSYDVF+vGgiWdItliJjusqzY 14uqh5VPqPus4llsg8SJpLtXyVgz31zkRLezRfCmvWZorjg2JN3J3mFEzNUWVsugLulYzZct+Fz MYnaqE5kc8jcRsnsct1dgcncynhO/SBjBuMlzppGVt2BCuu3fvcsTBWVPPThv24bBUdGfv7byoS coa6q15PJ4HwiBctU27lTwqlGzbBLrMlzPZQajJhlhNg== X-Gm-Gg: ASbGncuqI07x4WkkEk4QcArFDJXOBLZLAp66g62QbH73TFDeC4N0aLP5tVkL0iNeJfw Uekn0kR9OYeypMqhHsygtMvoQDGgsYh8YCPBag0Dinw0HeOihlBfQlGfzAh/cMQ+HGgENSBghdP UYddqUrqBQMnqiqX23HIZhLz+2JLdsRUKW3jfz0E/SW60SJJer6xbEI+LhtnPgqr9v7RDegnzkT Pcm+xmzUpLYIm+o3pChwmCA/FD88eUArDjtLftttLF2xXx1Vfwi9T/ZB+gohmMXBqFdLjn0II1E ZAvEvEMjih0NSzWETb8CzbAwgWT43/Dkea72Up2xfc54sPc0NHcVGhIy1MyV80184e8SlLgTp25 ZqZbF+Asu//voginXr97Hvzw/PTEsnKkZuTEzfQA1cjYXN7B7JPlGxtr8 X-Received: by 2002:a17:902:e890:b0:29b:5c65:4531 with SMTP id d9443c01a7336-29df5e1b9demr76121835ad.59.1765223568282; Mon, 08 Dec 2025 11:52:48 -0800 (PST) X-Google-Smtp-Source: AGHT+IFldxTxkTxkX77QlfPsSZXCuI/PZC7Kz79MmuTSSm4PxTyuilJqAu7PN23hvQjlti0SbXP3tw== X-Received: by 2002:a17:902:e890:b0:29b:5c65:4531 with SMTP id d9443c01a7336-29df5e1b9demr76121715ad.59.1765223567807; Mon, 08 Dec 2025 11:52:47 -0800 (PST) To: devel@lists.libvirt.org Subject: [PATCH 5/5] secret: Add functionality to load and save secrets in encrypted format Date: Tue, 9 Dec 2025 01:22:31 +0530 Message-ID: <20251208195231.98170-6-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251208195231.98170-1-armenon@redhat.com> References: <20251208195231.98170-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: IPf_xSF9V8YYfdMIbEzoTNeRUgzWWPaHnGmbyjBJT70_1765223569 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: UM7EGC5ZPPATC3672W76BII36Z6MYZVN X-Message-ID-Hash: UM7EGC5ZPPATC3672W76BII36Z6MYZVN X-MailFrom: armenon@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Arun Menon X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Arun Menon via Devel Reply-To: Arun Menon X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1765224136386158500 Content-Type: text/plain; charset="utf-8"; x-default="true" Now that we have the functionality to provide the secrets driver with an encryption key through a configuration file or using system credentials, and the newly introduced attribute to iterate over the encryption schemes, we can use the key to save and load secrets. Encrypt all secrets that are going to be saved on the disk if the 'secrets_encryption_key' path is set in the secret.conf file OR if a valid systemd generated credential exists. While loading secrets, identify the decryption method by matching the file extension of the stored secret against the known encryptionSchemeType value= s. If no matching scheme is found, the secret is skipped. If the encryption key is changed across restarts, then also the secret driver will fail to lo= ad the secrets from the disk that were encrypted with the former key. Signed-off-by: Arun Menon --- src/conf/virsecretobj.c | 166 ++++++++++++++++++++++++++++++------- src/conf/virsecretobj.h | 18 +++- src/secret/secret_driver.c | 25 ++++-- 3 files changed, 169 insertions(+), 40 deletions(-) diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index a3dd7983bb..60a1323625 100644 --- a/src/conf/virsecretobj.c +++ b/src/conf/virsecretobj.c @@ -31,6 +31,10 @@ #include "virhash.h" #include "virlog.h" #include "virstring.h" +#include "virsecret.h" +#include "virrandom.h" +#include "vircrypto.h" +#include "virsecureerase.h" =20 #define VIR_FROM_THIS VIR_FROM_SECRET =20 @@ -45,6 +49,16 @@ struct _virSecretObj { size_t value_size; }; =20 +typedef struct _virSecretSchemeInfo { + const char *suffix; + virCryptoCipher cipher; +} virSecretSchemeInfo; + +virSecretSchemeInfo schemeInfo[] =3D { + { ".base64", -1 }, + { ".aes256cbc", VIR_CRYPTO_CIPHER_AES256CBC }, +}; + static virClass *virSecretObjClass; static virClass *virSecretObjListClass; static void virSecretObjDispose(void *obj); @@ -323,7 +337,8 @@ virSecretObj * virSecretObjListAdd(virSecretObjList *secrets, virSecretDef **newdef, const char *configDir, - virSecretDef **oldDef) + virSecretDef **oldDef, + bool encryptData) { virSecretObj *obj; virSecretDef *objdef; @@ -363,6 +378,8 @@ virSecretObjListAdd(virSecretObjList *secrets, } else { /* No existing secret with same UUID, * try look for matching usage instead */ + const char *secretSuffix =3D ".base64"; + g_autofree char *encryptionSchemeSuffix =3D NULL; if ((obj =3D virSecretObjListFindByUsageLocked(secrets, (*newdef)->usage_type, (*newdef)->usage_id))= ) { @@ -379,10 +396,17 @@ virSecretObjListAdd(virSecretObjList *secrets, goto cleanup; =20 /* Generate the possible configFile and secretValueFile strings - * using the configDir, uuidstr, and appropriate suffix + * using the configDir, uuidstr, and appropriate suffix. + * By default, the latest encryption cipher will be used to encryp= t secrets. */ + + if (encryptData) { + encryptionSchemeSuffix =3D g_strdup_printf(".%s", virSecretEnc= ryptionSchemeTypeToString(VIR_SECRET_ENCRYPTION_SCHEME_LAST- 1)); + } else { + encryptionSchemeSuffix =3D g_strdup(secretSuffix); + } if (!(obj->configFile =3D virFileBuildPath(configDir, uuidstr, ".x= ml")) || - !(obj->secretValueFile =3D virFileBuildPath(configDir, uuidstr= , ".base64"))) + !(obj->secretValueFile =3D virFileBuildPath(configDir, uuidstr= , encryptionSchemeSuffix))) goto cleanup; =20 if (virHashAddEntry(secrets->objs, uuidstr, obj) < 0) @@ -682,15 +706,40 @@ virSecretObjSaveConfig(virSecretObj *obj) =20 =20 int -virSecretObjSaveData(virSecretObj *obj) +virSecretObjSaveData(virSecretObj *obj, + bool encryptData, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen) { g_autofree char *base64 =3D NULL; + g_autofree uint8_t *secret =3D NULL; + g_autofree uint8_t *encryptedValue =3D NULL; + size_t encryptedValueLen =3D 0; + size_t secretLen =3D 0; + uint8_t iv[16] =3D { 0 }; =20 if (!obj->value) return 0; =20 - base64 =3D g_base64_encode(obj->value, obj->value_size); - + if (encryptData && secretsEncryptionKey) { + if (virRandomBytes(iv, sizeof(iv)) < 0) { + return -1; + } + if (virCryptoEncryptData(VIR_CRYPTO_CIPHER_LAST-1, + secretsEncryptionKey, secretsKeyLen, + iv, sizeof(iv), + (uint8_t *)obj->value, obj->value_size, + &encryptedValue, &encryptedValueLen) < 0)= { + return -1; + } + secretLen =3D sizeof(iv) + encryptedValueLen; + secret =3D g_new0(uint8_t, secretLen); + memcpy(secret, iv, sizeof(iv)); + memcpy(secret + sizeof(iv), encryptedValue, encryptedValueLen); + base64 =3D g_base64_encode(secret, secretLen); + } else { + base64 =3D g_base64_encode(obj->value, obj->value_size); + } if (virFileRewriteStr(obj->secretValueFile, S_IRUSR | S_IWUSR, base64)= < 0) return -1; =20 @@ -737,7 +786,10 @@ virSecretObjGetValue(virSecretObj *obj) int virSecretObjSetValue(virSecretObj *obj, const unsigned char *value, - size_t value_size) + size_t value_size, + bool encryptData, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen) { virSecretDef *def =3D obj->def; g_autofree unsigned char *old_value =3D NULL; @@ -753,7 +805,10 @@ virSecretObjSetValue(virSecretObj *obj, obj->value =3D g_steal_pointer(&new_value); obj->value_size =3D value_size; =20 - if (!def->isephemeral && virSecretObjSaveData(obj) < 0) + if (!def->isephemeral && virSecretObjSaveData(obj, + encryptData, + secretsEncryptionKey, + secretsKeyLen) < 0) goto error; =20 /* Saved successfully - drop old value */ @@ -807,11 +862,24 @@ virSecretLoadValidateUUID(virSecretDef *def, =20 =20 static int -virSecretLoadValue(virSecretObj *obj) +virSecretLoadValue(virSecretObj *obj, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen) { - int ret =3D -1, fd =3D -1; + int ret =3D -1; + VIR_AUTOCLOSE fd =3D -1; struct stat st; + size_t i; + g_autofree char *contents =3D NULL; + g_autofree uint8_t *contents_encrypted =3D NULL; + g_autofree uint8_t *decryptedValue =3D NULL; + g_autofree char *encryptionScheme =3D NULL; + + size_t decryptedValueLen =3D 0; + uint8_t iv[16] =3D { 0 }; + uint8_t *ciphertext =3D NULL; + size_t ciphertextLen =3D 0; =20 if ((fd =3D open(obj->secretValueFile, O_RDONLY)) =3D=3D -1) { if (errno =3D=3D ENOENT) { @@ -841,25 +909,52 @@ virSecretLoadValue(virSecretObj *obj) goto cleanup; } =20 - contents =3D g_new0(char, st.st_size + 1); - - if (saferead(fd, contents, st.st_size) !=3D st.st_size) { - virReportSystemError(errno, _("cannot read '%1$s'"), - obj->secretValueFile); - goto cleanup; + /* Iterate over the encryption schemes and decrypt the contents + * of the file on the disk, by matching the file extension with the en= cryption + * scheme. + * If there is no scheme matching the file extension, then that secret= is not loaded. */ + + for (i =3D 0; i < G_N_ELEMENTS(schemeInfo); i++) { + if (virStringHasSuffix(obj->secretValueFile, schemeInfo[i].suffix)= ) { + contents =3D g_new0(char, st.st_size + 1); + if (saferead(fd, contents, st.st_size) !=3D st.st_size) { + virReportSystemError(errno, _("cannot read '%1$s'"), + obj->secretValueFile); + goto cleanup; + } + contents[st.st_size] =3D '\0'; + if (schemeInfo[i].cipher !=3D -1) { + contents_encrypted =3D g_base64_decode(contents, &obj->val= ue_size); + if (sizeof(iv) > obj->value_size) { + virReportError(VIR_ERR_INVALID_SECRET, + _("Encrypted secret size '%1$zu' is inv= alid"), + obj->value_size); + goto cleanup; + } + memcpy(iv, contents_encrypted, sizeof(iv)); + ciphertext =3D contents_encrypted + sizeof(iv); + ciphertextLen =3D st.st_size - sizeof(iv); + if (virCryptoDecryptData(schemeInfo[i].cipher, + secretsEncryptionKey, secretsKeyL= en, + iv, sizeof(iv), + ciphertext, ciphertextLen, + &decryptedValue, &decryptedValueL= en) < 0) { + goto cleanup; + } + g_free(obj->value); + obj->value =3D g_steal_pointer(&decryptedValue); + obj->value_size =3D decryptedValueLen; + } else { + obj->value =3D g_base64_decode(contents, &obj->value_size); + } + break; + } } - contents[st.st_size] =3D '\0'; - - VIR_FORCE_CLOSE(fd); - - obj->value =3D g_base64_decode(contents, &obj->value_size); - ret =3D 0; - cleanup: - if (contents !=3D NULL) - memset(contents, 0, st.st_size); - VIR_FORCE_CLOSE(fd); + virSecureErase(contents_encrypted, obj->value_size); + virSecureErase(contents, st.st_size); + virSecureErase(iv, sizeof(iv)); return ret; } =20 @@ -868,7 +963,10 @@ static virSecretObj * virSecretLoad(virSecretObjList *secrets, const char *file, const char *path, - const char *configDir) + const char *configDir, + bool encryptData, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen) { g_autoptr(virSecretDef) def =3D NULL; virSecretObj *obj =3D NULL; @@ -879,10 +977,10 @@ virSecretLoad(virSecretObjList *secrets, if (virSecretLoadValidateUUID(def, file) < 0) return NULL; =20 - if (!(obj =3D virSecretObjListAdd(secrets, &def, configDir, NULL))) + if (!(obj =3D virSecretObjListAdd(secrets, &def, configDir, NULL, encr= yptData))) return NULL; =20 - if (virSecretLoadValue(obj) < 0) { + if (virSecretLoadValue(obj, secretsEncryptionKey, secretsKeyLen) < 0) { virSecretObjListRemove(secrets, obj); g_clear_pointer(&obj, virObjectUnref); return NULL; @@ -894,7 +992,10 @@ virSecretLoad(virSecretObjList *secrets, =20 int virSecretLoadAllConfigs(virSecretObjList *secrets, - const char *configDir) + const char *configDir, + bool encryptData, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen) { g_autoptr(DIR) dir =3D NULL; struct dirent *de; @@ -915,7 +1016,10 @@ virSecretLoadAllConfigs(virSecretObjList *secrets, if (!(path =3D virFileBuildPath(configDir, de->d_name, NULL))) continue; =20 - if (!(obj =3D virSecretLoad(secrets, de->d_name, path, configDir))= ) { + if (!(obj =3D virSecretLoad(secrets, de->d_name, path, configDir, + encryptData, + secretsEncryptionKey, + secretsKeyLen))) { VIR_ERROR(_("Error reading secret: %1$s"), virGetLastErrorMessage()); continue; diff --git a/src/conf/virsecretobj.h b/src/conf/virsecretobj.h index 17897c5513..2e4d980988 100644 --- a/src/conf/virsecretobj.h +++ b/src/conf/virsecretobj.h @@ -51,7 +51,8 @@ virSecretObj * virSecretObjListAdd(virSecretObjList *secrets, virSecretDef **newdef, const char *configDir, - virSecretDef **oldDef); + virSecretDef **oldDef, + bool encryptData); =20 typedef bool (*virSecretObjListACLFilter)(virConnectPtr conn, @@ -86,7 +87,10 @@ int virSecretObjSaveConfig(virSecretObj *obj); =20 int -virSecretObjSaveData(virSecretObj *obj); +virSecretObjSaveData(virSecretObj *obj, + bool encryptData, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen); =20 virSecretDef * virSecretObjGetDef(virSecretObj *obj); @@ -101,7 +105,10 @@ virSecretObjGetValue(virSecretObj *obj); int virSecretObjSetValue(virSecretObj *obj, const unsigned char *value, - size_t value_size); + size_t value_size, + bool encryptData, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen); =20 size_t virSecretObjGetValueSize(virSecretObj *obj); @@ -112,4 +119,7 @@ virSecretObjSetValueSize(virSecretObj *obj, =20 int virSecretLoadAllConfigs(virSecretObjList *secrets, - const char *configDir); + const char *configDir, + bool encryptData, + uint8_t *secretsEncryptionKey, + size_t secretsKeyLen); diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 408a629ea0..8c0796c86a 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -30,6 +30,7 @@ #include "virlog.h" #include "viralloc.h" #include "secret_conf.h" +#include "secret_config.h" #include "virsecretobj.h" #include "secret_driver.h" #include "virthread.h" @@ -42,6 +43,7 @@ #include "secret_event.h" #include "virutil.h" #include "virinhibitor.h" +#include "virfile.h" =20 #define VIR_FROM_THIS VIR_FROM_SECRET =20 @@ -221,13 +223,17 @@ secretDefineXML(virConnectPtr conn, goto cleanup; =20 if (!(obj =3D virSecretObjListAdd(driver->secrets, &def, - driver->configDir, &backup))) + driver->configDir, &backup, + driver->config->encryptData))) goto cleanup; objDef =3D virSecretObjGetDef(obj); =20 if (!objDef->isephemeral) { if (backup && backup->isephemeral) { - if (virSecretObjSaveData(obj) < 0) + if (virSecretObjSaveData(obj, + driver->config->encryptData, + driver->config->secretsEncryptionKey, + driver->config->secretsKeyLen) < 0) goto restore_backup; } =20 @@ -331,7 +337,10 @@ secretSetValue(virSecretPtr secret, if (virSecretSetValueEnsureACL(secret->conn, def) < 0) goto cleanup; =20 - if (virSecretObjSetValue(obj, value, value_size) < 0) + if (virSecretObjSetValue(obj, value, value_size, + driver->config->encryptData, + driver->config->secretsEncryptionKey, + driver->config->secretsKeyLen) < 0) goto cleanup; =20 event =3D virSecretEventValueChangedNew(def->uuid, @@ -540,7 +549,10 @@ secretStateInitialize(bool privileged, if (!(driver->secrets =3D virSecretObjListNew())) goto error; =20 - if (virSecretLoadAllConfigs(driver->secrets, driver->configDir) < 0) + if (virSecretLoadAllConfigs(driver->secrets, driver->configDir, + driver->config->encryptData, + driver->config->secretsEncryptionKey, + driver->config->secretsKeyLen) < 0) goto error; =20 return VIR_DRV_STATE_INIT_COMPLETE; @@ -562,7 +574,10 @@ secretStateReload(void) if (!(driver->config =3D virSecretDaemonConfigNew(driver->privileged))) return -1; =20 - ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDi= r)); + ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDi= r, + driver->config->encryptData, + driver->config->secretsEncryption= Key, + driver->config->secretsKeyLen)); =20 return 0; } --=20 2.51.1