From nobody Fri Nov 21 10:02:43 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=1763657997; cv=none; d=zohomail.com; s=zohoarc; b=YJW1qH1MgEn7vnE5dEWMJ/+chFP+avcqOVmWr2EYF8uPrqlCZTU3HU0W8ImNwOayA/3OJ11l8jl1DnQ32VrVY987VCRmKHe+0TQKuCMicKrGNzZ7pt0Ij5T0tgQU46vaTS+hJeO/Hx79PxXTVvEDIvUqMXVJeK6uZjOfBWtn9Vc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1763657997; 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=J80UWdiWpVPOd9ikjKg6QVTpkk3ymUbrzbxgP34aO7M=; b=Y502/ZSwoNDrw4flCcnanWWybMF0D9EeyM7q783xhbP2KWZJ7nZjJQjzOMejkSCOF6Bm5gQcMvlNdXEbmx1e2rwZhbx2bUA9Z1UZZ/LCi0TK629Rc0Z6V3aZQqjOGt0as+IoFNps7MkN6jhsfzHdjWfgsHN0fOqpx9oIuF9zua4= 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 1763657997506619.7126018838964; Thu, 20 Nov 2025 08:59:57 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id B8778440B0; Thu, 20 Nov 2025 11:59:56 -0500 (EST) Received: from [172.19.199.53] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id EC6024420D; Thu, 20 Nov 2025 11:57:16 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 485F641BD5; Thu, 20 Nov 2025 11:53:58 -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 A379E41ADD for ; Thu, 20 Nov 2025 11:53:56 -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-264-Be_YQc2NMc2TAjagZtnK3w-1; Thu, 20 Nov 2025 11:53:55 -0500 Received: by mail-pl1-f200.google.com with SMTP id d9443c01a7336-29806c42760so47603345ad.2 for ; Thu, 20 Nov 2025 08:53:54 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.104.36]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-34727bcaf5asm2887551a91.4.2025.11.20.08.53.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Nov 2025 08:53:52 -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=1763657636; 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=J80UWdiWpVPOd9ikjKg6QVTpkk3ymUbrzbxgP34aO7M=; b=UpfbG/hvgpk8EeYiQhRgt1MSRvaX3pMp0GvttmZA32UbCHJp6QvwQW0S8uc09dljdGiFia GU76rchW3DDhQwvA63yeZYM9oesPJH/POB554dPpbxS8PET6mwdptJatdu62+DttQ5u3Wu 3vt7e4OSZ+oBp2G1CyIutANTlLWTQf4= X-MC-Unique: Be_YQc2NMc2TAjagZtnK3w-1 X-Mimecast-MFC-AGG-ID: Be_YQc2NMc2TAjagZtnK3w_1763657634 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763657634; x=1764262434; 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=J80UWdiWpVPOd9ikjKg6QVTpkk3ymUbrzbxgP34aO7M=; b=PMXy9NzwjLCsqtDPDCJCvdlrAcFgUyX7rUbtVt/g4eq2MuqVhii+bQKp0efWZbQpkd edYMlZ0ECpKs5DyrOm3a+RLLT7s57IkF31ZXNN5ra0LwF0WNnUKeZAm9JsNdIqucO9pd n3Puip+y0FuBv32zFEaH+jEEzRMn2mw1VyxBIdRNNriZUhgEKnFaRPdA+ts6CB5GcvUM YhwwBslGoLQ+66RosWXs0y8Z1vUnDb231ICtUPk2HF2QMNCaoOcukmNWnJcIvMuukuPN eld43qEwuDjKJXYtRe7B7jap/wnqQrwD6a+OhuUANdWlaZSrsH1NM8pDlLBTJYuLhC4m A1CQ== X-Gm-Message-State: AOJu0YxSnGNWYLGyp6OqGLr3A3Q2zQB56CTjcC9kdKrKqqE57T/AA3W0 3IpV43I6n/kr9zTdTkNzLSYuWTcblvj/ZFxTQKyvzbhG2K9MHy8I76nlx4ZySCMTz2BrrgCwWwR UFpSRKf+45guKLhHBZ/73ZeLgMGPbky5I5meP2ABYe4rjuGm9KCPcUIV8bdN+ZSl5CrkzZxyZ6S AzcMFeI6hZmk0CwZENA6f8hRClO6IJb/XIueslHolCVQ== X-Gm-Gg: ASbGnctZUVWxm/Xo5XUHJIBrcI/YLbJ5XG3OiNT+Xj8/kgEAg/zI8xBlSjenZ/822Bb tKPGVfuQj7dEP5YU3fmhQg4iTjg9dB0n/losrDa0PzYfNUY9jVc+Khj6c21PUnLaqCoZzM2/TpM w805hsY+h6vDLDnb3KJjDyX40AKGFMIl6qvEmlWZa2MbypQ8mKEGTxKwc5cUyO33cc9abMOfF0K 6koBVl+jXoS5yNf9lBLOd7Kx7BRWO+hWdcfAq13ieAmafWtat/etZViS06O6n240WJZ4IyT8R7J 1saOdemlrW57ye4stjt/odV5fXx2+kjKQyjBF9lv5RlWeezK44DGn6RxKGhyBfmuiwSAaXS2REJ SsYoxnd7GsTC7ozLh4AIURjA6bivYE/lAg8kL0R5mPll7gmLFaN0u3EXz X-Received: by 2002:a17:903:1248:b0:298:1156:acd5 with SMTP id d9443c01a7336-29b5b0f6739mr54266065ad.39.1763657633750; Thu, 20 Nov 2025 08:53:53 -0800 (PST) X-Google-Smtp-Source: AGHT+IEcGiIn6E+NdPLSywku425y+FQMzalMz8cNtAqaYf/pBctuvNTkUvLSrSCGIFQ6o3c8BMj1zQ== X-Received: by 2002:a17:903:1248:b0:298:1156:acd5 with SMTP id d9443c01a7336-29b5b0f6739mr54265745ad.39.1763657633237; Thu, 20 Nov 2025 08:53:53 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v2 1/5] util: Add support for GnuTLS decryption Date: Thu, 20 Nov 2025 22:23:42 +0530 Message-ID: <20251120165346.161124-2-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251120165346.161124-1-armenon@redhat.com> References: <20251120165346.161124-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: vH5uA01mb6xFbm5MD6Meq64w4UW3NC-9vofz_6DRN64_1763657634 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: UU4XBOPM3AQUOQBKWNOBXNANY4BUA2MG X-Message-ID-Hash: UU4XBOPM3AQUOQBKWNOBXNANY4BUA2MG 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: 1763658009405018900 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 --- src/libvirt_private.syms | 1 + src/util/vircrypto.c | 128 ++++++++++++++++++++++++++++++++++++++- src/util/vircrypto.h | 8 +++ tests/vircryptotest.c | 65 ++++++++++++++++++++ 4 files changed, 201 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fb482fff40..fc5fdb00f4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2252,6 +2252,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..fedb39b167 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,129 @@ 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; + size_t i; + 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; + + 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; + } + if (plaintextlen =3D=3D 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("decrypted data has zero length")); + goto error; + } + i =3D plaintext[plaintextlen - 1]; + if (i > plaintextlen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("decrypted data has invalid padding")); + goto error; + } + *plaintextlenret =3D plaintextlen - i; + *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; + } + /* + * Decrypt the data buffer using a decryption key and + * initialization vector via the gnutls_cipher_decrypt API + * for GNUTLS_CIPHER_AES_256_CBC. + */ + 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 Nov 21 10:02:43 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=1763658080; cv=none; d=zohomail.com; s=zohoarc; b=UAm0PONikmKav7WmnlE28QaWxubvSwzJlJ6DofMXPQd8m/syljboI2M44wA40E4CBT21ElEzUS5IY8RbV1LKrsmeWMDjFccRk/34dLAuTV98bkKb0RI/rC1Gs9yboCwwHonPwnNxUJsJPDWiNahlJSeX8VDUhtJkBeHCBUEqYlE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1763658080; 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=2dDQewInVeWRq4aMUN6K3LU5il+wfkjI3csuS3DNvMY=; b=VxMqYG6FkdZ6SWY3IzpWr5APTR6fTdLGJo1bF4D9nUEHBAkBu3BGccNXjm3I7SFa5kAH9TTIKis3nvzLNY4+IvCbHG5AaTrYpdHcr/F+hsMxUYjh8MtBBthkvdP9IvetTDS7lIvM/Po1n7z2lVrItrVxA+Azra2hw9GbpzCYQ+Y= 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 1763658080438231.94704533640618; Thu, 20 Nov 2025 09:01:20 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id E513F44059; Thu, 20 Nov 2025 12:01:17 -0500 (EST) Received: from [172.19.199.53] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id 0DEF9442F9; Thu, 20 Nov 2025 11:57:25 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 334C241ADD; Thu, 20 Nov 2025 11:54:01 -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 024A43F87C for ; Thu, 20 Nov 2025 11:53:58 -0500 (EST) Received: from mail-pj1-f72.google.com (mail-pj1-f72.google.com [209.85.216.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-665-Xy7EUwndNk-4QjwF4yG2vQ-1; Thu, 20 Nov 2025 11:53:57 -0500 Received: by mail-pj1-f72.google.com with SMTP id 98e67ed59e1d1-343823be748so1146217a91.0 for ; Thu, 20 Nov 2025 08:53:56 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.104.36]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-34727bcaf5asm2887551a91.4.2025.11.20.08.53.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Nov 2025 08:53:54 -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=1763657638; 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=2dDQewInVeWRq4aMUN6K3LU5il+wfkjI3csuS3DNvMY=; b=dbBecIfch5ZqQOiJmjmgSgNRpfDgipONPATc+obSBvdpNybdUMMfB9NoE9FVziVedyCfgp mpWIsvMvPXAFvS40qVpczyU2XTva+6KLhg02yukz6hrtg7YZgfsNecxBte47oSNq3UzEBG zIWgPYku0UwRrGgytLt9HGQi3SK9VQk= X-MC-Unique: Xy7EUwndNk-4QjwF4yG2vQ-1 X-Mimecast-MFC-AGG-ID: Xy7EUwndNk-4QjwF4yG2vQ_1763657636 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763657636; x=1764262436; 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=2dDQewInVeWRq4aMUN6K3LU5il+wfkjI3csuS3DNvMY=; b=f61dy8yEybBmEOD6G2KRrMPW5I0RQoxGs4zFYw8ifk9Zcyk8XWMAtdF19iacWsB4il 94CYc/Rua7dlAI61yh+KmpdJgAMBsLrPFwnh/wh+LVW7h1hHgU0w1usHMQ/DpVrTMaz1 8K0N7Rdv8mjGUeTuEqyWPUtvAg0F1xTrV1sx3zLcg6Y3ABbbLqXeKI1gmpjOmhkYljKj RKI25atz8hiI3w+lmioSqmzPyl9chScjbl5g6pm/T/MIW6RZN8VuN6mk2M9wMzKgOYO0 AnaAJUMiruN8vao2/LrdHiKSVGaiXKe/v4QQCm1DryhCmuxw0oEwNx0O+9miTyMOk6es dkLw== X-Gm-Message-State: AOJu0YzbaX71oSLYlnPooZX9GYyqQdI/H0t3aC5dnY1LfVZAysHRWVLz LeM1CqF5xo0R4/yzM9Ic1YNQHTofA5hklt9USdfpDuCzkbHsKQQgVlwOlKx59L5Z6H8u1QOsFnV EniQgZJ5e3JfE8PoHQlZklSdZZZAHsPLH9Z1pc0qzvrPoLAnEbdqeKpyeooxyf4rmiSNXkyVYdN 1HAK34EysGYECJTkz0Xxrkr3P9WWmztp0NgLk0u+vuIQ== X-Gm-Gg: ASbGncv479G8y7Tx8b50ZkLmArRAFm7heK8H2LICn4zN+PzxNfdju646FPcZ6HII8Ke pd1/EtrTwC9qIPGrk2waeRMBObht7FT6YsFxs2U1ixuFkMPWQHZl0wnhEFozOR2Kh8dT7pSLCIS /edUrNw3Vw2iV4srcd5RMQ/m/bTYQKyxxyQGXPIWSbLCuDd0ezTFKH+r1wb8NkrLB8DCayamN/4 QZ45F0nojbCQaZPOo2vcVKk2LaWzuvaZfRRU7peAGDUvubclvXx4RWoH/w46r7cgVkEY96Y/Xap g72pzfcBTvE41pCSfixoQKlrCYgkqH+6/zkbsKftc35VAxsovCywLtXFTlsE/W16vQl9sAq/Nz9 A8ppyR1wZQtf9e3TxwQZ3cyz1Wdvkz+j7eVZi87ZpWKOE07EFDgo6Sece X-Received: by 2002:a17:90b:3ec8:b0:33f:ebdd:9961 with SMTP id 98e67ed59e1d1-34727d702b9mr3831373a91.28.1763657635575; Thu, 20 Nov 2025 08:53:55 -0800 (PST) X-Google-Smtp-Source: AGHT+IHIeBNBb8nhftEHIpS+Fkf0hVDDI7/HLgrk075FzI+WQOcuWsytnavyRRdKDxS8yIyrAl2zmw== X-Received: by 2002:a17:90b:3ec8:b0:33f:ebdd:9961 with SMTP id 98e67ed59e1d1-34727d702b9mr3831351a91.28.1763657634971; Thu, 20 Nov 2025 08:53:54 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v2 2/5] secret: Set up default encrypted secret key for the virtsecretd service Date: Thu, 20 Nov 2025 22:23:43 +0530 Message-ID: <20251120165346.161124-3-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251120165346.161124-1-armenon@redhat.com> References: <20251120165346.161124-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: UYo8yQMpY5BN8WNAdAntHKSrFadkuofqHDR3dQLqRSY_1763657636 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: FWUKBPXVSC6CMNW4QMX5ZG7P7BZWCVWG X-Message-ID-Hash: FWUKBPXVSC6CMNW4QMX5ZG7P7BZWCVWG 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: 1763658095082018900 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 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 | 7 +++++++ src/secret/meson.build | 8 ++++++++ src/secret/secret-init-encryption.in | 11 +++++++++++ src/secret/virtsecretd.service.extra.in | 8 ++++++++ 4 files changed, 34 insertions(+) create mode 100644 src/secret/secret-init-encryption.in diff --git a/libvirt.spec.in b/libvirt.spec.in index 79738bd7bb..fa477db031 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1889,13 +1889,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 @@ -2247,9 +2250,13 @@ 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 +%{_unitdir}/virt-secret-init-encryption.socket +%{_unitdir}/virt-secret-init-encryption-ro.socket +%{_unitdir}/virt-secret-init-encryption-admin.socket %attr(0755, root, root) %{_sbindir}/virtsecretd %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/secrets/ %ghost %dir %attr(0700, root, root) %{_rundir}/libvirt/secrets/ diff --git a/src/secret/meson.build b/src/secret/meson.build index 3b859ea7b4..d8861fcbcd 100644 --- a/src/secret/meson.build +++ b/src/secret/meson.build @@ -42,6 +42,14 @@ if conf.has('WITH_SECRETS') ], } =20 + virt_daemon_units +=3D { + 'service': 'virt-secret-init-encryption', + 'name': 'secret-init-encryption', + 'service_in': files('secret-init-encryption.in'), + 'service_extra_in': [], + 'socket_extra_in': [], + } + openrc_init_files +=3D { 'name': 'virtsecretd', 'in_file': files('virtsecretd.init.in'), diff --git a/src/secret/secret-init-encryption.in b/src/secret/secret-init-= encryption.in new file mode 100644 index 0000000000..4dc00e5bbb --- /dev/null +++ b/src/secret/secret-init-encryption.in @@ -0,0 +1,11 @@ +[Unit] +Before=3Dvirtsecretd.service +ConditionPathExists=3D!/var/lib/libvirt/secrets/secrets-encryption-key + +[Service] +Type=3Doneshot +ExecStart=3D/usr/bin/sh -c 'test -f /var/lib/libvirt/secrets/secrets-encry= ption-key || (dd if=3D/dev/urandom status=3Dnone bs=3D32 count=3D1 | system= d-creds encrypt --name=3Dsecrets-encryption-key - /var/lib/libvirt/secrets/= secrets-encryption-key)' +ExecStart=3D-/usr/bin/chmod 0400 /var/lib/libvirt/secrets/secrets-encrypti= on-key + +[Install] +WantedBy=3Dmulti-user.target diff --git a/src/secret/virtsecretd.service.extra.in b/src/secret/virtsecre= td.service.extra.in index 1fc8c672f7..1df9163814 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:/var/lib/libvirt/secrets/= secrets-encryption-key +Environment=3DSECRETS_ENCRYPTION_KEY=3D%d/secrets-encryption-key --=20 2.51.1 From nobody Fri Nov 21 10:02:43 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=1763658246; cv=none; d=zohomail.com; s=zohoarc; b=MG0IXOHJH+4sSGEEtypEn0b7iBjvY4swF7sNm9Hexe6omW+j0a3b/JCZ4r8ohBMHXzq5oQCreJt/1bwfRTSD9lo/NwyoWfqj6I5YCgojOgE8R1z8Yhz+ResPUlE4L/1QkgMr0cr3GpJw9Bp4VdduY3jN8ObPzexvF0TQAjfRrVM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1763658246; 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=VOzY1QvoNB0HFCV0tjWh5vsEBBB2Pjcc1MnlbMZVh2w=; b=PSgW6rmWj+4BLqZPzKCrsDCTzwS237xOtQhIZDcdlVD3eGtHRr7DTbgvoXvQj+OgrpmpRUAqisnB77pL8fnG2zIsJTb/DlMdL+XuwiEKiiI+ocnK3JLqG9g6UxVoalIbIWR4J4RYAEsCyuqS7+ho96RCbS+fY1TDTebQY6AVXSY= 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 1763658245880780.3180624589407; Thu, 20 Nov 2025 09:04:05 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id 6C4564184C; Thu, 20 Nov 2025 12:03:56 -0500 (EST) Received: from [172.19.199.53] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id E726E44381; Thu, 20 Nov 2025 11:57:33 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id B38813F84A; Thu, 20 Nov 2025 11:54:03 -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 E37293F87C for ; Thu, 20 Nov 2025 11:54:01 -0500 (EST) Received: from mail-pg1-f198.google.com (mail-pg1-f198.google.com [209.85.215.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-563-FUSjpKydM86qgfNYtoNHpQ-1; Thu, 20 Nov 2025 11:53:59 -0500 Received: by mail-pg1-f198.google.com with SMTP id 41be03b00d2f7-bcecfea0e8aso1750000a12.0 for ; Thu, 20 Nov 2025 08:53:59 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.104.36]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-34727bcaf5asm2887551a91.4.2025.11.20.08.53.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Nov 2025 08:53:57 -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=1763657641; 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=VOzY1QvoNB0HFCV0tjWh5vsEBBB2Pjcc1MnlbMZVh2w=; b=XPOnHHIPScvr24Tmq2cx67SzGlmTAkEpg7NUyH5H90GiA2SVxe6Wj//7FZ/Uo8s9GCYMtx sFPv2X0a3Q31+CaLWDemGdAEseNVdC+xfW2QoNTjNS1LhOkS1j6y9SJUoZ09oVIlsXMdep qyROA3xJXflNKhcmuDdDCoyIWjuXdGM= X-MC-Unique: FUSjpKydM86qgfNYtoNHpQ-1 X-Mimecast-MFC-AGG-ID: FUSjpKydM86qgfNYtoNHpQ_1763657639 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763657638; x=1764262438; 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=VOzY1QvoNB0HFCV0tjWh5vsEBBB2Pjcc1MnlbMZVh2w=; b=QxonUCo3bRYNxuSFwYZ7zU5pzbuSjQGcaE0/4HTBSHOf+5nU/8UHyRLQD7N3bCZJWL DlwUszQJj2/J6+sVDxkO0kIOGXZ0Ze7tizs768lOrXhLdedfVsU/gBk1IbumHdvgOO06 94odozAdnTPt9r65AhXAa/GvypyuaDMIC75ZPVCpVYIpUmbHIm7+2R5lSTJOV5DeSh6p jNskMjKbKi6YbYcxEpx2mVraCNRXBi1DL7u7/eCbQw+PLkj3pnqjhQGi8fTNfSBM6s8r C7sphuP8/C8U5N7B7hRq7xUrVuGwoihaEi8cBKKXWmx9U558EKKB9QUICl90uGKAJhcU rMUg== X-Gm-Message-State: AOJu0YzYOvkg2hp+1Pbrjiv0VbkmPbyVhswHWREtLfBpePjk3YxzT6Z2 v4t7x5N40oHG6YMNVeKKUanzwAlcaM2eODUGui2ewuByYaJMFIxqnW/8+aZOzQoeJfjBJLq86DP p6XsHfHoEGIV4DQMoFH7ci2Kry98p4Cg3C7OEfjrikDs9UHuU750D4SmiSuR/EBex8QGfwWyY4X ZxZK2+/PfAwj7aN9SxHkk0wB4q1feH0qpr5e/NTKT/KQ== X-Gm-Gg: ASbGncskiZSkL/iSzQBymShx7k9ReU833wD956OYgeJ0bf+F0lrDKfMAnMSfVKbeevW 1V0U4c8eYgiv8B3SZUNQKGRr+CBxBvJ0vojj77Qz0g6XF8sqjShp0n02O8BKNrakdCp+bjYl5bw 5sm35j48g3zt4qzDhjSBdJehoZXoh8UzoWyKK6te/M4A/OiN2eUiHLxmSjR/ynUoBVt10m4zUJa 2vAIYzjivNlRpz8Q0KWzM44YOHLQoKOqioiQGrTyr1JO2iA98rVj9eFfm0xfDJxfc1XyZ1I+LVF Gqo69toYQ2RXtPymXTsUVXVL4b1XsR2sHYLnCd5gIZp3HajEJ0gxb/2hz6NCJPCPUaRAkQ19IRD p4QTTk7R2pja7FwafBUCyFxerV/thDy79dl5sokMiE0MoJ9XXVwV5e+ZF X-Received: by 2002:a17:90b:5826:b0:340:8d99:49d4 with SMTP id 98e67ed59e1d1-3472983a03dmr3790520a91.1.1763657638405; Thu, 20 Nov 2025 08:53:58 -0800 (PST) X-Google-Smtp-Source: AGHT+IF6dEYsdf5wm7r8c07r3tfILv6+W2cGYwyayY6ZJpaDmFaB/PCvJpWZ4w/Rxq6Te9iGLi+SgA== X-Received: by 2002:a17:90b:5826:b0:340:8d99:49d4 with SMTP id 98e67ed59e1d1-3472983a03dmr3790482a91.1.1763657637637; Thu, 20 Nov 2025 08:53:57 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v2 3/5] secret: Add secrets.conf configuration file and parse it Date: Thu, 20 Nov 2025 22:23:44 +0530 Message-ID: <20251120165346.161124-4-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251120165346.161124-1-armenon@redhat.com> References: <20251120165346.161124-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: _pSwuZ8W5Ih8Fh2i9YPH3XqW1reTYxtca3IgFYL8w9c_1763657639 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 6FXZEXVJMKD4H3J6YII322WFKLRSDJPL X-Message-ID-Hash: 6FXZEXVJMKD4H3J6YII322WFKLRSDJPL 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: 1763658257183018900 Content-Type: text/plain; charset="utf-8"; x-default="true" A new configuration file called secrets.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. By default, 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 --- libvirt.spec.in | 3 + po/POTFILES | 1 + src/conf/meson.build | 1 + src/conf/secret_config.c | 207 +++++++++++++++++++++++++ src/conf/secret_config.h | 48 ++++++ src/libvirt_private.syms | 2 + src/secret/libvirt_secrets.aug | 40 +++++ src/secret/meson.build | 18 +++ src/secret/secrets.conf.in | 12 ++ src/secret/test_libvirt_secrets.aug.in | 6 + 10 files changed, 338 insertions(+) create mode 100644 src/conf/secret_config.c create mode 100644 src/conf/secret_config.h create mode 100644 src/secret/libvirt_secrets.aug create mode 100644 src/secret/secrets.conf.in create mode 100644 src/secret/test_libvirt_secrets.aug.in diff --git a/libvirt.spec.in b/libvirt.spec.in index fa477db031..8462d08c61 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -2249,6 +2249,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/secrets.conf %{_unitdir}/virtsecretd.service %{_unitdir}/virt-secret-init-encryption.service %{_unitdir}/virtsecretd.socket diff --git a/po/POTFILES b/po/POTFILES index 23da794f84..1a76e0505a 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -53,6 +53,7 @@ src/conf/nwfilter_conf.c src/conf/nwfilter_params.c src/conf/object_event.c src/conf/secret_conf.c +src/conf/secret_config.c src/conf/snapshot_conf.c src/conf/storage_adapter_conf.c src/conf/storage_conf.c diff --git a/src/conf/meson.build b/src/conf/meson.build index 5116c23fe3..9c51e99107 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -68,6 +68,7 @@ interface_conf_sources =3D [ =20 secret_conf_sources =3D [ 'secret_conf.c', + 'secret_config.c', 'virsecretobj.c', ] =20 diff --git a/src/conf/secret_config.c b/src/conf/secret_config.c new file mode 100644 index 0000000000..a1c9b6bc2f --- /dev/null +++ b/src/conf/secret_config.c @@ -0,0 +1,207 @@ +/* + * secret_config.c: secrets.conf config file handling + * + * Copyright (C) 2025 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include +#include +#include "configmake.h" +#include "datatypes.h" +#include "virlog.h" +#include "virerror.h" +#include "virfile.h" +#include "virutil.h" +#include "secret_config.h" + + +#define VIR_FROM_THIS VIR_FROM_CONF + +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/secrets.conf"); + } else { + g_autofree char *configdir =3D NULL; + + configdir =3D virGetUserConfigDirectory(); + + *configfile =3D g_strdup_printf("%s/secrets.conf", configdir); + } + + return 0; +} + +static int +virSecretLoadDaemonConfig(virSecretDaemonConfig *cfg, + const char *filename) +{ + g_autoptr(virConf) conf =3D NULL; + + if (access(filename, R_OK) =3D=3D 0) { + conf =3D virConfReadFile(filename, 0); + if (!conf) + return -1; + if (virConfGetValueInt(conf, "encrypt_data", &cfg->encrypt_data) <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to get encrypt_data from %1$s"), + filename); + return -1; + } + + if (virConfGetValueString(conf, "secrets_encryption_key", + &cfg->secretsEncryptionKeyPath) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to get secrets_encryption_key from %1= $s"), + filename); + return -1; + } + } + return 0; +} + +static bool getSecretsEncryptionKey(virSecretDaemonConfig *cfg, + uint8_t **secrets_encryption_key, size= _t *secrets_encryption_keylen) +{ + int fd =3D -1; + struct stat st; + + if ((fd =3D open(cfg->secretsEncryptionKeyPath, O_RDONLY)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot open secrets key = file '%1$s'"), + cfg->secretsEncryptionKeyPath); + return false; + } + if (fstat(fd, &st) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot stat secrets key = file '%1$s'"), + cfg->secretsEncryptionKeyPath); + VIR_FORCE_CLOSE(fd); + return false; + } + *secrets_encryption_keylen =3D st.st_size; + if (*secrets_encryption_keylen =3D=3D 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Secrets encryption key f= ile %1$s is empty"), + cfg->secretsEncryptionKeyPath); + VIR_FORCE_CLOSE(fd); + return false; + } + *secrets_encryption_key =3D g_new0(uint8_t, *secrets_encryption_keylen= ); + if (saferead(fd, &secrets_encryption_key, *secrets_encryption_keylen) = !=3D *secrets_encryption_keylen) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot read secrets key = file '%1$s'"), + cfg->secretsEncryptionKeyPath); + VIR_FORCE_CLOSE(fd); + return false; + } + VIR_FORCE_CLOSE(fd); + if (*secrets_encryption_keylen !=3D 32) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Secrets encryption key f= ile %1$s must be 32 bytes"), + cfg->secretsEncryptionKeyPath); + return false; + } + return true; +} + +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) + goto error; + + if (!(cfg =3D virObjectNew(virSecretDaemonConfigClass))) + goto error; + + cfg->secretsEncryptionKeyPath =3D NULL; + + if (privileged) { + configdir =3D g_strdup(SYSCONFDIR "/libvirt"); + } else { + g_autofree char *rundir =3D virGetUserRuntimeDirectory(); + configdir =3D virGetUserConfigDirectory(); + } + configfile =3D g_strconcat(configdir, "/secrets.conf", NULL); + + if (virSecretLoadDaemonConfig(cfg, configfile) < 0) + goto error; + + if (!(credentials_directory =3D getenv("CREDENTIALS_DIRECTORY"))) { + credentials_directory =3D NULL; + } + + if (cfg->secretsEncryptionKeyPath) { + VIR_DEBUG("Secrets encryption key path: %s", cfg->secretsEncryptio= nKeyPath); + } else if (credentials_directory) { + VIR_DEBUG("Using credentials directory from environment: %s", + credentials_directory); + cfg->secretsEncryptionKeyPath =3D g_strdup_printf("%s/secrets-encr= yption-key", + credentials_direct= ory); + } else { + VIR_DEBUG("No secrets encryption key found in credentials dire= ctory"); + cfg->secretsEncryptionKeyPath =3D NULL; + } + if (cfg->secretsEncryptionKeyPath && access(cfg->secretsEncryptionKeyP= ath, R_OK) =3D=3D 0) { + if (!getSecretsEncryptionKey(cfg, &cfg->secrets_encryption_key, &c= fg->secretsKeyLen)) { + VIR_DEBUG("Failed to get secrets encryption key from path: %s", + cfg->secretsEncryptionKeyPath); + goto error; + } + } + + if (cfg->encrypt_data !=3D 1) { + cfg->encrypt_data =3D (cfg->secretsKeyLen =3D=3D 32) ? 1 : 0; + } else if (cfg->encrypt_data =3D=3D 1) { + if (!cfg->secretsEncryptionKeyPath) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("secretsEncryptionKeyPath must be set if encr= ypt_data is 1 in %1$s"), + configfile); + goto error; + } + } + return g_steal_pointer(&cfg); + error: + virSecretDaemonConfigDispose(cfg); + return NULL; +} + +static void +virSecretDaemonConfigDispose(void *obj) +{ + virSecretDaemonConfig *cfg =3D obj; + + g_free(cfg->secrets_encryption_key); + g_free(cfg->secretsEncryptionKeyPath); +} diff --git a/src/conf/secret_config.h b/src/conf/secret_config.h new file mode 100644 index 0000000000..638b7c49a4 --- /dev/null +++ b/src/conf/secret_config.h @@ -0,0 +1,48 @@ +/* + * secret_config.h: secrets.conf config file handling + * + * Copyright (C) 2025 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "internal.h" +#include "virinhibitor.h" +#include "secret_event.h" + +typedef struct _virSecretDaemonConfig virSecretDaemonConfig; +struct _virSecretDaemonConfig { + virObject parent; + /* secrets encryption key path from secrets.conf file */ + char *secretsEncryptionKeyPath; + + unsigned char* secrets_encryption_key; + size_t secretsKeyLen; + + /* Indicates if the newly written secrets are encrypted or not. + * 0 if not encrypted and 1 if encrypted. + */ + int encrypt_data; +}; + +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/libvirt_private.syms b/src/libvirt_private.syms index fc5fdb00f4..7ecb573851 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1064,6 +1064,8 @@ virSecretDefParse; virSecretUsageTypeFromString; virSecretUsageTypeToString; =20 +# conf/secret_config.h +virSecretDaemonConfigNew; =20 # conf/secret_event.h virSecretEventLifecycleNew; diff --git a/src/secret/libvirt_secrets.aug b/src/secret/libvirt_secrets.aug new file mode 100644 index 0000000000..092cdef41f --- /dev/null +++ b/src/secret/libvirt_secrets.aug @@ -0,0 +1,40 @@ +(* /etc/libvirt/secrets.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/secrets.conf" + . Util.stdexcl + + let xfm =3D transform lns filter diff --git a/src/secret/meson.build b/src/secret/meson.build index d8861fcbcd..e453e71464 100644 --- a/src/secret/meson.build +++ b/src/secret/meson.build @@ -27,6 +27,24 @@ if conf.has('WITH_SECRETS') ], } =20 + secrets_conf =3D configure_file( + input: 'secrets.conf.in', + output: 'secrets.conf', + copy: true + ) + virt_conf_files +=3D secrets_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('secrets.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/secrets.conf.in b/src/secret/secrets.conf.in new file mode 100644 index 0000000000..d998940140 --- /dev/null +++ b/src/secret/secrets.conf.in @@ -0,0 +1,12 @@ +# +# 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 set to on. +#encrypt_data =3D 1 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" } --=20 2.51.1 From nobody Fri Nov 21 10:02:43 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=1763658397; cv=none; d=zohomail.com; s=zohoarc; b=XKm/DnX+3S9CDZn7NhWUXCpvuFCiK+Qe3bWQ6q1geQ+StnQI+DuP5aO3YtbxRj2/94nN6KQ/T+7ZxMYLgw/xXRryhyJFr6LqtLsFPzlZjZXVAqYCduNy08Tu3f2bgh/2Fqi9PCwOFSkiIVPuUDd4fZQWh1KltZq2ssfmiXCw7kA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1763658397; 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=5rDQ3k2W6K7YPKsOQW/R3AgIjjVtv5tQ536B8VBn5Kg=; b=h3SvF5u/IiHRAudRIyDe9x8ja/+wol2tlRNcdnGuaNG6UfYlyL9H41WtfMl/R8oJuqCneouSRAKNFVxOxJuw7ciy4oAH9Zrq7ae4QTN2XsNBq/2RhagB8fo1M5W2THhQpi+LDXgWaa3u5GGZv63Hr8mWlIcLpdX86BpttqM1BCs= 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 1763658397621844.2552763877574; Thu, 20 Nov 2025 09:06:37 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id 732414412A; Thu, 20 Nov 2025 12:06:36 -0500 (EST) Received: from [172.19.199.53] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id C617D44130; Thu, 20 Nov 2025 11:57:42 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 3E7F93F84A; Thu, 20 Nov 2025 11:54:05 -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 026FD3F87C for ; Thu, 20 Nov 2025 11:54:03 -0500 (EST) Received: from mail-pj1-f70.google.com (mail-pj1-f70.google.com [209.85.216.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-621-ohQuXxCWMfOJ-6UUFICbLg-1; Thu, 20 Nov 2025 11:54:01 -0500 Received: by mail-pj1-f70.google.com with SMTP id 98e67ed59e1d1-34188ba5990so3183143a91.0 for ; Thu, 20 Nov 2025 08:54:01 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.104.36]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-34727bcaf5asm2887551a91.4.2025.11.20.08.53.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Nov 2025 08:53:59 -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=1763657643; 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=5rDQ3k2W6K7YPKsOQW/R3AgIjjVtv5tQ536B8VBn5Kg=; b=deMEULM03H+N7qAEsr4PdjkR5qPBKAVXB3a3tcKhI6AXt0YeQJhqnDGhpeJF6De0WYoT6V Nr0UbmCcYi+BcS8ldT1uNwgC5/mwwREmvsBOnfh7o2n7t9VwamHNCfF6VwAvCJCqBwUIzU MeyPiUKE7ytMIYMOl3wg2I1nBh6Uxwk= X-MC-Unique: ohQuXxCWMfOJ-6UUFICbLg-1 X-Mimecast-MFC-AGG-ID: ohQuXxCWMfOJ-6UUFICbLg_1763657640 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763657640; x=1764262440; 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=5rDQ3k2W6K7YPKsOQW/R3AgIjjVtv5tQ536B8VBn5Kg=; b=t79AyPJ0VEGzWqzmzJFDClwf6yihpDtt7nO3islbLsqkunp5yGmFkWYt1PpSjkWt77 T1B1rpRDwtdCwTxEUvw3gl9cpTUiGDEYW8EElw5OmFTl6QNrxmyxEYNvUF5kslgTqh4s 2PA4cU2Afz77E3Q4wzMad0W+xYq9FC9f4LbFCg3k3a0qTaJgIe/MOUGY5umECeyA/TlU RPao7or7HSknxFSHmo51ecIdQjR3/hamoP8Un2r9gnK76ttD0mID8qDD8YIN5r6Q1U3R yCsssZA0NBEDVFOm2q0wLQiH4DJO4Zsa8avK4v9BqR99dsYLLh3hKKhPjnVAtXPMWHac 9ZLw== X-Gm-Message-State: AOJu0YxqeSG+76kE2BhqHjeTKnltrTW8KH5U4zDEQ01XRSdnL0r7FC7z VJNEulin6SSQ8D6fIblFA221cuxK/UABbZO1dVxQOOs4ptzD47kx1/+Ztnxk2HZSlC0EsW3Z0Kn bhEyBKVj9pXqeBoFlhEuP/XAP8jh09un7PeGZ0L4zB1jS2673IkKjot4kiqxU3dRRjrgbS4aEIX UWnwZThl5CK1BK/V74SBZAejfnP3AA4yIPQJlIGwX9pA== X-Gm-Gg: ASbGnctgS22wXdYJsKzBD5JYQIvzF214AjOtOsfjE79o/IYiv3YagsM1+HRfo0kIcBl tbVI2tXk1IDtA3YPRgM7lZQxFyI5WJHZYFQaLwkzyynQw1VnXo5ONPUJkj2suT5E6E8f0cH310y cLGZz5jl2c+0rFj7cWJHNAnYfBmmIJ+uhrP0to7G+mOSvu/EsIHrbxI87gCl6WOLcl/Mg66IdnK fsG0Ptpn0uIy9XPGjUyq2xFb6eKEJLOHUYmPMkCS26rt1NHqgU0SoHG6SYeTOkuSmwEQNb+w0e2 D6T1T2XWGU/5Iy59APBkChAM2ngF/QEm4TtGUJj//2YZ1HpByJrwcQTWmkWRY6UUp3hDsdWyVvS nTkSyHsVzFYbmwOO6FLbVtJZsuGQnMV8UlLw006O/xhW8siscIlJVaZ/2 X-Received: by 2002:a17:90b:288d:b0:33f:ebc2:645 with SMTP id 98e67ed59e1d1-34727c4bba8mr4758357a91.20.1763657640219; Thu, 20 Nov 2025 08:54:00 -0800 (PST) X-Google-Smtp-Source: AGHT+IHIE56tSz2LjeTVEIMn8zLAsrdRXioBjSTYqmBIpwL5VEhlGjnKxOK4VMXkkInwysdfSi35KA== X-Received: by 2002:a17:90b:288d:b0:33f:ebc2:645 with SMTP id 98e67ed59e1d1-34727c4bba8mr4758332a91.20.1763657639809; Thu, 20 Nov 2025 08:53:59 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v2 4/5] secret: Add encryptionScheme attribute to the secrets xml configuration Date: Thu, 20 Nov 2025 22:23:45 +0530 Message-ID: <20251120165346.161124-5-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251120165346.161124-1-armenon@redhat.com> References: <20251120165346.161124-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: LX5yuWog2mZlDoWdDXcWhordVlCxWiioeme8IwlYFZU_1763657640 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 6BCMK4XTCNN5PJBHAKVCFS3RHQP3ZUYT X-Message-ID-Hash: 6BCMK4XTCNN5PJBHAKVCFS3RHQP3ZUYT 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: 1763658407885018900 Content-Type: text/plain; charset="utf-8"; x-default="true" A new attribute is required, to store the encryption scheme used while encrypting the secret. This value will be "none" if the secret is stored in base64 format. For backwards compatibility, the secret will not be encrypted when the attribute itself is absent in the configuration file. In other words, the secret will be stored on the disk in base64 encoded format. This new attribute is essential to be stored on the disk in the xml file, so that we can effectively decrypt the secrets while loading them. It also allows us to add more encryption schemes in the future. Signed-off-by: Arun Menon --- include/libvirt/libvirt-secret.h | 20 ++++++++++++++++++++ src/conf/schemas/secret.rng | 5 +++++ src/conf/secret_conf.c | 21 +++++++++++++++++++++ src/conf/secret_conf.h | 1 + src/util/virsecret.c | 4 ++++ src/util/virsecret.h | 1 + tests/secretxml2xmlin/usage-ceph-space.xml | 1 + tests/secretxml2xmlin/usage-ceph.xml | 1 + tests/secretxml2xmlin/usage-iscsi.xml | 1 + tests/secretxml2xmlin/usage-tls.xml | 1 + tests/secretxml2xmlin/usage-volume.xml | 1 + tests/secretxml2xmlin/usage-vtpm.xml | 1 + 12 files changed, 58 insertions(+) diff --git a/include/libvirt/libvirt-secret.h b/include/libvirt/libvirt-sec= ret.h index 761437d4ad..96a4359107 100644 --- a/include/libvirt/libvirt-secret.h +++ b/include/libvirt/libvirt-secret.h @@ -70,6 +70,26 @@ typedef enum { # endif } virSecretUsageType; =20 +/** + * virSecretEncryptionSchemeType: + * + * Since: 11.10.0 + */ +typedef enum { + VIR_SECRET_ENCRYPTION_SCHEME_NONE =3D 0, /* (Since: 11.10.0) */ + VIR_SECRET_ENCRYPTION_SCHEME_AES256CBS =3D 1, /* (Since: 11.10.0) */ +# ifdef VIR_ENUM_SENTINELS + VIR_SECRET_ENCRYPTION_SCHEME_LAST + /* + * NB: this enum value will increase over time as new encryption schem= es are + * added to the libvirt API. It reflects the last enncryption scheme s= upported + * by this version of the libvirt API. + * + * Since: 11.10.0 + */ +# endif +} virSecretEncryptionSchemeType; + virConnectPtr virSecretGetConnect (virSecretPtr secret); int virConnectNumOfSecrets (virConnectPtr conn); int virConnectListSecrets (virConnectPtr conn, diff --git a/src/conf/schemas/secret.rng b/src/conf/schemas/secret.rng index c90e2eb81f..ae6e62b438 100644 --- a/src/conf/schemas/secret.rng +++ b/src/conf/schemas/secret.rng @@ -42,6 +42,11 @@ + + + + + diff --git a/src/conf/secret_conf.c b/src/conf/secret_conf.c index 966536599e..2fdf3f7f2c 100644 --- a/src/conf/secret_conf.c +++ b/src/conf/secret_conf.c @@ -131,6 +131,12 @@ virSecretParseXML(xmlXPathContext *ctxt) g_autofree char *ephemeralstr =3D NULL; g_autofree char *privatestr =3D NULL; g_autofree char *uuidstr =3D NULL; + g_autofree char *encryptionScheme =3D NULL; + + /* Encryption scheme is set to -1 to support existing xml secret confi= guration + * files. This indicates that no encryption scheme is specified in th= e XML + */ + int type =3D -1; =20 def =3D g_new0(virSecretDef, 1); =20 @@ -170,6 +176,15 @@ virSecretParseXML(xmlXPathContext *ctxt) if (virSecretDefParseUsage(ctxt, def) < 0) return NULL; =20 + encryptionScheme =3D virXPathString("string(./encryptionScheme)", ctxt= ); + if (encryptionScheme) { + if ((type =3D virSecretEncryptionSchemeTypeFromString(encryptionSc= heme)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown secret encryption scheme %1$d"), def= ->encryption_scheme); + return NULL; + } + } + def->encryption_scheme =3D type; return g_steal_pointer(&def); } =20 @@ -242,6 +257,7 @@ virSecretDefFormat(const virSecretDef *def) g_auto(virBuffer) buf =3D VIR_BUFFER_INITIALIZER; g_auto(virBuffer) attrBuf =3D VIR_BUFFER_INITIALIZER; g_auto(virBuffer) childBuf =3D VIR_BUFFER_INIT_CHILD(&buf); + const char *type =3D NULL; char uuidstr[VIR_UUID_STRING_BUFLEN]; =20 virBufferAsprintf(&attrBuf, " ephemeral=3D'%s' private=3D'%s'", @@ -257,6 +273,11 @@ virSecretDefFormat(const virSecretDef *def) virSecretDefFormatUsage(&childBuf, def) < 0) return NULL; =20 + type =3D virSecretEncryptionSchemeTypeToString(def->encryption_scheme); + if (type !=3D NULL) { + virBufferEscapeString(&childBuf, "%s\n", + type); + } virXMLFormatElement(&buf, "secret", &attrBuf, &childBuf); return virBufferContentAndReset(&buf); } diff --git a/src/conf/secret_conf.h b/src/conf/secret_conf.h index 8f8f47933a..a12bc8e095 100644 --- a/src/conf/secret_conf.h +++ b/src/conf/secret_conf.h @@ -30,6 +30,7 @@ struct _virSecretDef { char *description; /* May be NULL */ virSecretUsageType usage_type; char *usage_id; /* May be NULL */ + virSecretEncryptionSchemeType encryption_scheme; /* virSecretEncryptio= nSchemeType */ }; =20 void virSecretDefFree(virSecretDef *def); diff --git a/src/util/virsecret.c b/src/util/virsecret.c index 8e74df3b93..c9d9cf2c8a 100644 --- a/src/util/virsecret.c +++ b/src/util/virsecret.c @@ -36,6 +36,10 @@ VIR_ENUM_IMPL(virSecretUsage, VIR_SECRET_USAGE_TYPE_LAST, "none", "volume", "ceph", "iscsi", "tls", "vtpm", ); +VIR_ENUM_IMPL(virSecretEncryptionScheme, + VIR_SECRET_ENCRYPTION_SCHEME_LAST, + "none", "aes256cbc", +); =20 void virSecretLookupDefClear(virSecretLookupTypeDef *def) diff --git a/src/util/virsecret.h b/src/util/virsecret.h index c803f0fe33..01998e307d 100644 --- a/src/util/virsecret.h +++ b/src/util/virsecret.h @@ -27,6 +27,7 @@ #include "virenum.h" =20 VIR_ENUM_DECL(virSecretUsage); +VIR_ENUM_DECL(virSecretEncryptionScheme); =20 typedef enum { VIR_SECRET_LOOKUP_TYPE_NONE, diff --git a/tests/secretxml2xmlin/usage-ceph-space.xml b/tests/secretxml2x= mlin/usage-ceph-space.xml index 557b12474d..2a7a177931 100644 --- a/tests/secretxml2xmlin/usage-ceph-space.xml +++ b/tests/secretxml2xmlin/usage-ceph-space.xml @@ -4,4 +4,5 @@ client.admin secret + none diff --git a/tests/secretxml2xmlin/usage-ceph.xml b/tests/secretxml2xmlin/u= sage-ceph.xml index e880293a63..8a2501c21f 100644 --- a/tests/secretxml2xmlin/usage-ceph.xml +++ b/tests/secretxml2xmlin/usage-ceph.xml @@ -4,4 +4,5 @@ CephCephCephCeph + none diff --git a/tests/secretxml2xmlin/usage-iscsi.xml b/tests/secretxml2xmlin/= usage-iscsi.xml index bfc94722e0..c36a0f8661 100644 --- a/tests/secretxml2xmlin/usage-iscsi.xml +++ b/tests/secretxml2xmlin/usage-iscsi.xml @@ -4,4 +4,5 @@ iscsitarget + none diff --git a/tests/secretxml2xmlin/usage-tls.xml b/tests/secretxml2xmlin/us= age-tls.xml index 88068b56e0..a021e96279 100644 --- a/tests/secretxml2xmlin/usage-tls.xml +++ b/tests/secretxml2xmlin/usage-tls.xml @@ -4,4 +4,5 @@ mumblyfratz + none diff --git a/tests/secretxml2xmlin/usage-volume.xml b/tests/secretxml2xmlin= /usage-volume.xml index e273c57686..7f9a4e13b8 100644 --- a/tests/secretxml2xmlin/usage-volume.xml +++ b/tests/secretxml2xmlin/usage-volume.xml @@ -4,4 +4,5 @@ /var/lib/libvirt/images/image.img + none diff --git a/tests/secretxml2xmlin/usage-vtpm.xml b/tests/secretxml2xmlin/u= sage-vtpm.xml index 5baff3034d..f9b801f765 100644 --- a/tests/secretxml2xmlin/usage-vtpm.xml +++ b/tests/secretxml2xmlin/usage-vtpm.xml @@ -4,4 +4,5 @@ vTPMvTPMvTPM + aes256cbc --=20 2.51.1 From nobody Fri Nov 21 10:02:43 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=1763658537; cv=none; d=zohomail.com; s=zohoarc; b=bO9wIBNontMmOhH7YGco7Hpo4+ky2x0oS8h1eEupLEolcS+E+rRuhYZEZgIlRrxvKYWU11LHbsLzOKmOD39w3Pr+OSU0bjhA5L5G6wupf08qox1Un+USNaPFTd/yRWn4k+zxz8rox56jcDPqGEdzBiPgjLjoRLaZ+XTYuv48phQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1763658537; 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=eTeJFcacBhZvP0iR8KUIW/73vqrEKCxKYt8FapwTv9g=; b=OkmAcpQKUUP7m2UYtq61NYdT4fylzj7Szzn6VWVGt6uZALqk69B/bwHt7lqOq+Y810ZTxnu32D6g9d5qbHVxZHVMLhQr5C13D64JVFowaRQWOXvp4ijWmadfj6x7HAWJvBETgtftaZzpz73UmIYqh07xnp8BP/Iw/DI9lO8VgpY= 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 1763658537038941.1345491639419; Thu, 20 Nov 2025 09:08:57 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id CF28E44033; Thu, 20 Nov 2025 12:08:55 -0500 (EST) Received: from [172.19.199.53] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id 6A732445F0; Thu, 20 Nov 2025 11:57:48 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 6BEB43F87C; Thu, 20 Nov 2025 11:54:07 -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 B8FBF3F87C for ; Thu, 20 Nov 2025 11:54:05 -0500 (EST) Received: from mail-pj1-f69.google.com (mail-pj1-f69.google.com [209.85.216.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-426-Jo3d9Rz7MPqvZ7zRCr7OaA-1; Thu, 20 Nov 2025 11:54:04 -0500 Received: by mail-pj1-f69.google.com with SMTP id 98e67ed59e1d1-343bf6ded5cso2434032a91.0 for ; Thu, 20 Nov 2025 08:54:03 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.36.104.36]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-34727bcaf5asm2887551a91.4.2025.11.20.08.54.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Nov 2025 08:54:01 -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=1763657645; 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=eTeJFcacBhZvP0iR8KUIW/73vqrEKCxKYt8FapwTv9g=; b=ddmN1WdrxdvFqB7ULsKKSZ1dyWqWfq5p1FrO4dai+Ko51cMKSuHhyZf/SFXSy5rorvbGeC i9OVckxOnLF5ZSXczpxBuJ+GSDhXx4xG86C3uj6bFdgGSTM6wn5nSvQYyKSot5vOI+A98i buKePHN1DBzUXyQMVgQcIRB5jfYiNPE= X-MC-Unique: Jo3d9Rz7MPqvZ7zRCr7OaA-1 X-Mimecast-MFC-AGG-ID: Jo3d9Rz7MPqvZ7zRCr7OaA_1763657643 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763657643; x=1764262443; 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=eTeJFcacBhZvP0iR8KUIW/73vqrEKCxKYt8FapwTv9g=; b=P5fTAkq0xaw8Qvigi6lMbBfbwTdVewEHTqEIdvP0BEHzp2j7HWyXCQOj5Xb/rY/e4h g0Gt/RYoixDTTQpe39y0gqVWFayTQauKsY13TQC0+XfXeRXXts5Au1O/FWW3zWHxBhxJ JQ5Yp6Z/QhbwPaHenG6rF1vfedExbMY/1/2ymBXxr6Ml/nwp/nmZt07u9baHGXGIyz0A mv/xjxeY+wnRpHobscT/DTB+sAsQmExQYt5wj/JZ/0pe1+r67ToDEbTFBSQGEVa2nzMP B3e6EIdf8SHA2WCBrynr0S4o7K7cJZ/Qvb131sAEgzIagihMfoeUDsnaqMJI9ITu0yox rReQ== X-Gm-Message-State: AOJu0YyGDGXkA85WY7ARz/WUaD2PzvVZv4XsYkX4Id0/WQBobJ7ePV/Z CsiSPeDZsZuzERMTILF2HoSB+egi1Q8XW9ZWvQCSefezuq7eQt4ODORRrDz0n0Tqhgu8b/JmIc4 he4CY5r90O1iXfqju9OxpFhlzRy3xXD3IcGEVt39RXvZ3Adw+OolD59bvjvWaZnroyekH/1AU8b b85pDGRfhrw/Ojacq2Ubdh089QH9al3ZQ6C2RuWw/ugQ== X-Gm-Gg: ASbGncu9E7FBv8R6uR6kP7iYdaTSOwsjg3E7/28YSWN3Z0K6Sk2D5shpu9vhlK7BsnY F8cY+7cZEk+wor3ZMr+dO94k60IV4ItIbARACUBMQNm8Jsa0mB+CAfvNqBsNjqAudyRcOF7Vwmx /rlbGDzLNWb7hit5o6NWJ/LxfdKc8TL7aF9IFwwJn5sEBH6deJwxqmPi0+4EiGCLIzBRIqWm2TK ik1ulRw6QTKdRpv4ooAMgAf4OaKrPt10bQ/ovH3wvKdtHHy3ZHNoGxbVH/erG/fxJSTL3l1aiQw B2ZByI1SCg8XIoskXOYw7EanCZ8fLIo75lxjWDY8BwTrOt9aGI/nXBvSHO+y4hZQTFman7OEm4p N/jgCarDml9HFtL0X6P0ZgcpuAj1KhOw4CxKzGtJkTJHMo6HdNr6UMwAn X-Received: by 2002:a17:90b:17c5:b0:341:88c9:6eb2 with SMTP id 98e67ed59e1d1-34727bd6352mr4000992a91.1.1763657642778; Thu, 20 Nov 2025 08:54:02 -0800 (PST) X-Google-Smtp-Source: AGHT+IHVvqattU2OxhxcJh/bA6+8HWgP85yISUCTX7wCI5ZWQMHMWsi4PUnZVMbf4SNeEjLVpzMK9g== X-Received: by 2002:a17:90b:17c5:b0:341:88c9:6eb2 with SMTP id 98e67ed59e1d1-34727bd6352mr4000948a91.1.1763657641821; Thu, 20 Nov 2025 08:54:01 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v2 5/5] secret: Add functionality to load and save secrets in encrypted format Date: Thu, 20 Nov 2025 22:23:46 +0530 Message-ID: <20251120165346.161124-6-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251120165346.161124-1-armenon@redhat.com> References: <20251120165346.161124-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: nQrM4GSxMaI44im6XTZFkQizYf0DPOaOF0f82XrnH_Q_1763657643 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: BJU64BDCAY2X7R2FTBRAUF4XMN3DPHTN X-Message-ID-Hash: BJU64BDCAY2X7R2FTBRAUF4XMN3DPHTN 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: 1763658548866018900 Content-Type: text/plain; charset="utf-8"; x-default="true" Since we now have the functionality to provide the secrets driver with an encryption key, and the newly introduced attribute to store the encryption scheme across driver restarts, we can use the key to encrypt secrets. While loading the secrets, we check whether the secret is encrypted or not and accordingly get the value. While the stored encryption scheme ensures the driver can successfully load secrets after a restart, If the user changes the encryption key between driver restarts, any secrets encrypted with the previous key will become permanently inaccessible upon the next restart. Users must ensure key consistency to maintain access to existing encrypted secrets. Signed-off-by: Arun Menon --- src/conf/virsecretobj.c | 165 ++++++++++++++++++++++++++++++------- src/conf/virsecretobj.h | 10 ++- src/secret/secret_driver.c | 23 ++++-- 3 files changed, 157 insertions(+), 41 deletions(-) diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index 66270e2751..37b2db960f 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 @@ -328,6 +332,8 @@ virSecretObjListAdd(virSecretObjList *secrets, virSecretObj *obj; virSecretDef *objdef; virSecretObj *ret =3D NULL; + const char *encryptionScheme =3D NULL; + const char *encryptionSchemeExt =3D NULL; char uuidstr[VIR_UUID_STRING_BUFLEN]; =20 virObjectRWLockWrite(secrets); @@ -379,10 +385,26 @@ virSecretObjListAdd(virSecretObjList *secrets, goto cleanup; =20 /* Generate the possible configFile and base64File strings - * using the configDir, uuidstr, and appropriate suffix + * using the configDir, uuidstr, and appropriate encryption scheme */ - if (!(obj->configFile =3D virFileBuildPath(configDir, uuidstr, ".x= ml")) || - !(obj->base64File =3D virFileBuildPath(configDir, uuidstr, ".b= ase64"))) + if ((*newdef)->encryption_scheme !=3D VIR_SECRET_ENCRYPTION_SCHEME= _NONE + && (*newdef)->encryption_scheme !=3D -1) { + encryptionScheme =3D virSecretEncryptionSchemeTypeToString((*n= ewdef)->encryption_scheme); + if (!encryptionScheme) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown secret encryption scheme %1$d"),= (*newdef)->encryption_scheme); + goto cleanup; + } + encryptionSchemeExt =3D g_strconcat(".", encryptionScheme, NUL= L); + if (!(obj->base64File =3D virFileBuildPath(configDir, uuidstr,= encryptionSchemeExt))) { + goto cleanup; + } + } else { + if (!(obj->base64File =3D virFileBuildPath(configDir, uuidstr,= ".base64"))) { + goto cleanup; + } + } + if (!(obj->configFile =3D virFileBuildPath(configDir, uuidstr, ".x= ml"))) goto cleanup; =20 if (virHashAddEntry(secrets->objs, uuidstr, obj) < 0) @@ -397,6 +419,7 @@ virSecretObjListAdd(virSecretObjList *secrets, cleanup: virSecretObjEndAPI(&obj); virObjectRWUnlock(secrets); + g_clear_pointer((gpointer *)&encryptionSchemeExt, g_free); return ret; } =20 @@ -680,17 +703,49 @@ virSecretObjSaveConfig(virSecretObj *obj) return 0; } =20 - int -virSecretObjSaveData(virSecretObj *obj) +virSecretObjSaveData(virSecretObj *obj, + virSecretDaemonConfig *driverConfig) { g_autofree char *base64 =3D NULL; + g_autofree uint8_t *encryptedValue =3D NULL; + size_t encryptedValueLen =3D 0; + size_t base64Len =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 (obj->def->encryption_scheme =3D=3D VIR_SECRET_ENCRYPTION_SCHEME_NO= NE + || obj->def->encryption_scheme =3D=3D -1) { + base64 =3D g_base64_encode(obj->value, obj->value_size); + } else { + if (driverConfig =3D=3D NULL || driverConfig->secrets_encryption_k= ey =3D=3D NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot encrypt secret value without encrypti= on key")); + return -1; + } + if (virRandomBytes(iv, sizeof(iv)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to gene= rate random IV")); + return -1; + } + if (virCryptoEncryptData(VIR_CRYPTO_CIPHER_AES256CBC, + driverConfig->secrets_encryption_key, dri= verConfig->secretsKeyLen, + iv, sizeof(iv), + (uint8_t *)obj->value, obj->value_size, + &encryptedValue, &encryptedValueLen) < 0)= { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to encr= ypt secret value")); + return -1; + } + base64Len =3D sizeof(iv) + encryptedValueLen; + base64 =3D g_new0(char, base64Len); + memcpy(base64, iv, sizeof(iv)); + memcpy(base64 + sizeof(iv), encryptedValue, encryptedValueLen); + /* Now the secret is encrypted and stored on disk. However, + * we did not change anything in the obj->value. This is done on + * purpose, as SecretObjGetValue should be able to read it as is. + * This will indeed be a base64 encoded secret*/ + } if (virFileRewriteStr(obj->base64File, S_IRUSR | S_IWUSR, base64) < 0) return -1; =20 @@ -733,27 +788,25 @@ virSecretObjGetValue(virSecretObj *obj) return ret; } =20 - int virSecretObjSetValue(virSecretObj *obj, const unsigned char *value, - size_t value_size) + size_t value_size, + virSecretDaemonConfig *driverConfig) { virSecretDef *def =3D obj->def; g_autofree unsigned char *old_value =3D NULL; g_autofree unsigned char *new_value =3D NULL; size_t old_value_size; - new_value =3D g_new0(unsigned char, value_size); =20 old_value =3D obj->value; old_value_size =3D obj->value_size; - memcpy(new_value, value, value_size); 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, driverConfig) < 0) goto error; =20 /* Saved successfully - drop old value */ @@ -786,7 +839,6 @@ virSecretObjSetValueSize(virSecretObj *obj, obj->value_size =3D value_size; } =20 - static int virSecretLoadValidateUUID(virSecretDef *def, const char *file) @@ -807,11 +859,18 @@ virSecretLoadValidateUUID(virSecretDef *def, =20 =20 static int -virSecretLoadValue(virSecretObj *obj) +virSecretLoadValue(virSecretObj *obj, + virSecretDaemonConfig *driverConfig) { int ret =3D -1, fd =3D -1; struct stat st; g_autofree char *contents =3D NULL; + g_autofree uint8_t *contents_encrypted =3D NULL; + g_autofree uint8_t *decryptedValue =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->base64File, O_RDONLY)) =3D=3D -1) { if (errno =3D=3D ENOENT) { @@ -841,25 +900,65 @@ 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->base64File); - goto cleanup; + if (obj->def->encryption_scheme =3D=3D VIR_SECRET_ENCRYPTION_SCHEME_NO= NE || + obj->def->encryption_scheme =3D=3D -1) { + 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->base64File); + goto cleanup; + } + contents[st.st_size] =3D '\0'; + obj->value =3D g_base64_decode(contents, &obj->value_size); + if (obj->value =3D=3D NULL) { + virReportError(VIR_ERR_INVALID_SECRET, "%s", + _("cannot decode base64 secret value")); + goto cleanup; + } + } else { + if (driverConfig->secrets_encryption_key =3D=3D NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot decrypt secret value without encrypti= on key")); + goto cleanup; + } + contents_encrypted =3D g_new0(uint8_t, st.st_size); + if (saferead(fd, contents_encrypted, st.st_size) !=3D st.st_size) { + virReportSystemError(errno, _("cannot read '%1$s'"), + obj->base64File); + goto cleanup; + } + if ((st.st_size) < sizeof(iv)) { + virReportError(VIR_ERR_INVALID_SECRET, "%s", + _("Encrypted secret size is invalid")); + goto cleanup; + } + memcpy(iv, contents_encrypted, sizeof(iv)); + ciphertext =3D contents_encrypted + sizeof(iv); + ciphertextLen =3D st.st_size - sizeof(iv); + if (virCryptoDecryptData(VIR_CRYPTO_CIPHER_AES256CBC, + driverConfig->secrets_encryption_key, dri= verConfig->secretsKeyLen, + iv, sizeof(iv), + ciphertext, ciphertextLen, + &decryptedValue, &decryptedValueLen) < 0)= { + virReportError(VIR_ERR_INVALID_SECRET, "%s", + _("Decryption of secret value failed")); + goto cleanup; + } + g_free(obj->value); + obj->value =3D g_steal_pointer(&decryptedValue); + obj->value_size =3D decryptedValueLen; } - contents[st.st_size] =3D '\0'; - - VIR_FORCE_CLOSE(fd); - - obj->value =3D g_base64_decode(contents, &obj->value_size); - ret =3D 0; =20 cleanup: - if (contents !=3D NULL) - memset(contents, 0, st.st_size); + if (contents !=3D NULL) { + memset(contents, 0, st.st_size+1); + } + if (contents_encrypted !=3D NULL) { + memset(contents_encrypted, 0, st.st_size); + } VIR_FORCE_CLOSE(fd); + virSecureErase(iv, sizeof(iv)); return ret; } =20 @@ -868,7 +967,8 @@ static virSecretObj * virSecretLoad(virSecretObjList *secrets, const char *file, const char *path, - const char *configDir) + const char *configDir, + virSecretDaemonConfig *driverConfig) { g_autoptr(virSecretDef) def =3D NULL; virSecretObj *obj =3D NULL; @@ -882,7 +982,7 @@ virSecretLoad(virSecretObjList *secrets, if (!(obj =3D virSecretObjListAdd(secrets, &def, configDir, NULL))) return NULL; =20 - if (virSecretLoadValue(obj) < 0) { + if (virSecretLoadValue(obj, driverConfig) < 0) { virSecretObjListRemove(secrets, obj); g_clear_pointer(&obj, virObjectUnref); return NULL; @@ -894,7 +994,8 @@ virSecretLoad(virSecretObjList *secrets, =20 int virSecretLoadAllConfigs(virSecretObjList *secrets, - const char *configDir) + const char *configDir, + virSecretDaemonConfig *driverConfig) { g_autoptr(DIR) dir =3D NULL; struct dirent *de; @@ -915,7 +1016,7 @@ 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, = driverConfig))) { VIR_ERROR(_("Error reading secret: %1$s"), virGetLastErrorMessage()); continue; diff --git a/src/conf/virsecretobj.h b/src/conf/virsecretobj.h index 17897c5513..f49600a75c 100644 --- a/src/conf/virsecretobj.h +++ b/src/conf/virsecretobj.h @@ -23,6 +23,7 @@ #include "internal.h" =20 #include "secret_conf.h" +#include "secret_config.h" =20 typedef struct _virSecretObj virSecretObj; =20 @@ -86,7 +87,8 @@ int virSecretObjSaveConfig(virSecretObj *obj); =20 int -virSecretObjSaveData(virSecretObj *obj); +virSecretObjSaveData(virSecretObj *obj, + virSecretDaemonConfig *driverConfig); =20 virSecretDef * virSecretObjGetDef(virSecretObj *obj); @@ -101,7 +103,8 @@ virSecretObjGetValue(virSecretObj *obj); int virSecretObjSetValue(virSecretObj *obj, const unsigned char *value, - size_t value_size); + size_t value_size, + virSecretDaemonConfig *driverConfig); =20 size_t virSecretObjGetValueSize(virSecretObj *obj); @@ -112,4 +115,5 @@ virSecretObjSetValueSize(virSecretObj *obj, =20 int virSecretLoadAllConfigs(virSecretObjList *secrets, - const char *configDir); + const char *configDir, + virSecretDaemonConfig *cfg); diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 04c3ca49f1..c0cac39a28 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,10 @@ #include "secret_event.h" #include "virutil.h" #include "virinhibitor.h" +#include "virfile.h" +#include "virrandom.h" +#include "vircrypto.h" +#include "virsecureerase.h" =20 #define VIR_FROM_THIS VIR_FROM_SECRET =20 @@ -70,6 +75,8 @@ struct _virSecretDriverState { =20 /* Immutable pointer, self-locking APIs */ virInhibitor *inhibitor; + + virSecretDaemonConfig *config; }; =20 static virSecretDriverState *driver; @@ -224,7 +231,7 @@ secretDefineXML(virConnectPtr conn, =20 if (!objDef->isephemeral) { if (backup && backup->isephemeral) { - if (virSecretObjSaveData(obj) < 0) + if (virSecretObjSaveData(obj, driver->config) < 0) goto restore_backup; } =20 @@ -307,7 +314,6 @@ secretGetXMLDesc(virSecretPtr secret, return ret; } =20 - static int secretSetValue(virSecretPtr secret, const unsigned char *value, @@ -327,8 +333,7 @@ secretSetValue(virSecretPtr secret, def =3D virSecretObjGetDef(obj); if (virSecretSetValueEnsureACL(secret->conn, def) < 0) goto cleanup; - - if (virSecretObjSetValue(obj, value, value_size) < 0) + if (virSecretObjSetValue(obj, value, value_size, driver->config) < 0) goto cleanup; =20 event =3D virSecretEventValueChangedNew(def->uuid, @@ -454,6 +459,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 +524,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, @@ -534,7 +542,7 @@ 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) < 0) goto error; =20 return VIR_DRV_STATE_INIT_COMPLETE; @@ -553,7 +561,10 @@ secretStateReload(void) if (!driver) return -1; =20 - ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDi= r)); + if (!(driver->config =3D virSecretDaemonConfigNew(driver->privileged))) + return -1; + + ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDi= r, driver->config)); =20 return 0; } --=20 2.51.1