From nobody Fri Dec 12 12:54:57 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=1764228464; cv=none; d=zohomail.com; s=zohoarc; b=mbBHu36Dh2aBNBiudZSPr0HIVbkt8TyRXozNy8zrUEEgczQfHZIkrZqcjCB2bD/QNcUdSfx1kj9at16mqO7BD+vPkvbz0Kyt8dM12o/IR4zL0JAytCSHzXgAA1LlcOdZv+onYv5kK4NeDNJ6FIPI7NUgpoe2i9Bn6OSUYVsqeNk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764228464; 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=WEJI074ULOaJU7DOhklWsa5ndSS+aLsFa3J7Z/Fh04U=; b=kAZPZXdQCpR21rAF+nEwR0YJG6Ysf7lazeN2M14Vx3RkP1VxSoOBDM8qxekKwzcsKDj3ODyHUWMF1YXk6T3cWbb+l7H/jIpV6+mRWPHkv6GF9U68I3qo/oFNhmCM2gnLzoMy5n7yaQeyFUJ3OYEaMjgQhi14Uf+kh1pFhV5lB6o= 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 1764228464719385.46414676240977; Wed, 26 Nov 2025 23:27:44 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id D4BB1417F0; Thu, 27 Nov 2025 02:27:43 -0500 (EST) Received: from [172.19.199.74] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id D580D44B94; Thu, 27 Nov 2025 02:26:27 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 17CA1442E2; Thu, 27 Nov 2025 02:22:45 -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 B38E9442E4 for ; Thu, 27 Nov 2025 02:22:44 -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-299-9HQ6E4iiMkq3JrA34QOaBQ-1; Thu, 27 Nov 2025 02:22:42 -0500 Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-295595cd102so9753195ad.3 for ; Wed, 26 Nov 2025 23:22:42 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.47.195.90]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29bce478762sm7801695ad.45.2025.11.26.23.22.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Nov 2025 23:22: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=1764228164; 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=WEJI074ULOaJU7DOhklWsa5ndSS+aLsFa3J7Z/Fh04U=; b=GqiOpbhgpYHX5XSnVc+KIDTBmH/1kYRGFdn/LSXdjkjKJdxIWAT/ArQbD6eNgtCNR/5cw5 V894baNtoF3be/zw2x03PdCTiCx0iBYgvtoslUT8gwO63+1kJx47UpQwCsLmjIXRCOTeXJ JX22eEZsGh24WrHG/PY//eDY67x+ikU= X-MC-Unique: 9HQ6E4iiMkq3JrA34QOaBQ-1 X-Mimecast-MFC-AGG-ID: 9HQ6E4iiMkq3JrA34QOaBQ_1764228162 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764228161; x=1764832961; 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=WEJI074ULOaJU7DOhklWsa5ndSS+aLsFa3J7Z/Fh04U=; b=fhMevvfizazV6BCx4jAwZOHbPDnhF7kzqDoQozPH3dHj+9lbBvzoi34Qs/gdDe6fEc nWrBEr/UNKR27aspASs/L/uQGaw4EdlWIw/DPxuG1U/QwNzBeAAcf5g6i/CtLINL2Nfm gwbaTEuE3MALGPaiJkF43RNTy4qs8FsXr0zTMltUW74QHtDXnxjuxl+eT+PQMgA1QEIP w0L7Y9pGbAwgkFIhz3IS2nZ0TXepJ1OTP0pipwqk5RbowKb0avzLTa2MEvgTqRnCPPZz 6xokg6V+R4szoyasloX7xH9lP3ZICcpXYL2DWX2oAVwgYBllTZz0K9Amu+ilWzZ96UIw Rxwg== X-Gm-Message-State: AOJu0YxbnIsGqa81G1d7an2vl0a4KWu77CYHA53DNGiO/3HgqVVJGCVX SesAcgy59wG68ZAofVnxHM9PX+TDTT0OiUMmen0hgSP5HG50P9Qe2C6FE7XaJ4dJur/fF4JfEQ5 qauXT0MmpBEykXYK7bJHqY5nDze22nKojlDS3hxoeSWQCkhbdRD9iNANW9igbRH8E0wCEEIDmnP EqrY4Zdq0JEwTMiZNCqpfYwaksCtBMyg2VIsg9oW0hjw== X-Gm-Gg: ASbGncsLPEkSDufiWToYWplX91Cc1b3tMzzij1hcoEoVdNwGwRhlykHZ4VjzfcKlK90 0/yAkB/+Nq5CL+25RIQXorVhwazB4NDMK0J6G2jFp6+3HPJMKJ+GVFQ0HqeJE2d6PthFddJSHlx TOIVAA2Uqaez+kubfTI8C0w2IbOWXRIXfJ5XQkGXVlXOmIxqEDRe+tMu4LkcqPqbIZU4JGGXh+z nFJZGMAgkPW0r92ii6zATMC1W+hSCJMK/C8iry9MhHYqDDOPEbakccwbdFiIGIl+ayaJAOLnm8h ah9yHr/e+4qt0fcLdrDqT7nmuT9BOQrQSUwzQ+O79c3lS5DEDudp1rN2okz1nljrDZ2v4jMm1Gm YPhij1FYsvpqttng8ztKNVi6D9tYOP0O/6o24yhFuUqwIEVkevZ0otkRe X-Received: by 2002:a17:903:17cd:b0:271:45c0:9ec8 with SMTP id d9443c01a7336-29b6bf35db0mr263248985ad.37.1764228161431; Wed, 26 Nov 2025 23:22:41 -0800 (PST) X-Google-Smtp-Source: AGHT+IHyMhwEA1GFNpEoJrho/fIvFxdnItdh2GgnGtqpDypgNi70l6jhMe5repmiNmCgRSft7EoO9g== X-Received: by 2002:a17:903:17cd:b0:271:45c0:9ec8 with SMTP id d9443c01a7336-29b6bf35db0mr263248775ad.37.1764228160931; Wed, 26 Nov 2025 23:22:40 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v3 1/5] util: Add support for GnuTLS decryption Date: Thu, 27 Nov 2025 12:52:28 +0530 Message-ID: <20251127072232.38426-2-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251127072232.38426-1-armenon@redhat.com> References: <20251127072232.38426-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: -DZPidBauQao8Cb1U59Zcjsoi1R8OlQ5w2i-5zhvy08_1764228162 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: FTJI34PB7QQ4OK2M25MWVZAOVWSQ3GRE X-Message-ID-Hash: FTJI34PB7QQ4OK2M25MWVZAOVWSQ3GRE 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: 1764228470886019200 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:57 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=1764228590; cv=none; d=zohomail.com; s=zohoarc; b=RGa2cC32Anpg8OZnppvvWnQnI74tbnUNzEHDM6J656kxse1XH/TkeTTb6wHedFzOMQg3Qlah/7pSFGvlM4ngO34equr1J0ToWZd1qJ/fgL1fPmIztEmDK0kXmmWXimj24Aoj3UvmlwGy4tobjAkF1fFeyW+XyjEAtb2Xw9f3rRA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764228590; 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=qyiQygNl4dbtYf/9vDVmTXZVM5C3pL8uRhDWVvmRuVw=; b=Xq7SG7/hVDBqcrFxGFs+Kwb+NRhSg1j4ktkoRj11XwifPT7RlhsEams02I4WGnRf9Xbhlq5ePall4qyE6Fzjiz8VEx0NRBGsoVGm/LFnlX1EiGzbz2RLeMvxITQ+IACG3pZYGbvRlMCuigr4I3XfuSjf4mXXYeCRbn1lNNJ6VLs= 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 1764228590364359.02891531035914; Wed, 26 Nov 2025 23:29:50 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id BD16F44B87; Thu, 27 Nov 2025 02:29:49 -0500 (EST) Received: from [172.19.199.74] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id B7493419F5; Thu, 27 Nov 2025 02:28:39 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 01AE4442E2; Thu, 27 Nov 2025 02:22:48 -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 C6ED0442E2 for ; Thu, 27 Nov 2025 02:22:46 -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-345-Cg3j4kTtN0mizTlslP_kkw-1; Thu, 27 Nov 2025 02:22:44 -0500 Received: by mail-pl1-f200.google.com with SMTP id d9443c01a7336-29845b18d1aso11026625ad.1 for ; Wed, 26 Nov 2025 23:22:44 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.47.195.90]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29bce478762sm7801695ad.45.2025.11.26.23.22.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Nov 2025 23:22: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=1764228166; 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=qyiQygNl4dbtYf/9vDVmTXZVM5C3pL8uRhDWVvmRuVw=; b=cE78Yg3YmqcRyZoqWXwxKhiyCYrz+ZclEWCR25ndBi+NNYy1jHhdsuMuBTJrTCYkOZLHIe 7DTmv7vf0B3KqjhIOZPaLxPHY0WcvsWME2tytpuKpKCTBdkAyoo6UToEhuiRIJVXIneoqf TovxYZaJbnc62MsirPpdW+Q32nL2svU= X-MC-Unique: Cg3j4kTtN0mizTlslP_kkw-1 X-Mimecast-MFC-AGG-ID: Cg3j4kTtN0mizTlslP_kkw_1764228164 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764228163; x=1764832963; 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=qyiQygNl4dbtYf/9vDVmTXZVM5C3pL8uRhDWVvmRuVw=; b=hv5D30GmjXMzyL3h5ybXLwuWdWeTRaG/2nVD3x/vi1svf5yapKj+8zyYGzQhZ1MMuz ClCV6auTkvfLP6nSm8ODw0HQS6l0SUCDfLkqzEoySbUNQdH8ZSvUYX7hexsoxUxNbFta tWV0zFK98gjjw7/JMDuFL2LFDWS4j09O9LNlIahayV8sywLHr4b2DedCnt+53JV/yg7K 6i7g1iRGl2qwAQdUdhOJ/njTELeQevT8zfg0cjURos0mcAtVNFytyw2x1P45hQsJARa8 AQ+Rg14DrHYF6ATT6xAwhJ9bZipUhIFsDXWI1nu9cZtffExxD1+fOAXyTmnzLO+2S+HD KfrQ== X-Gm-Message-State: AOJu0YxzaLybsLkm0gLQG36tYhs8Dl4AZ591R363ECxG0IiT3/7tfnKI CBkCQ+iOzgK/tk2DovJ3ApmvYDMeb+pZIjp2fMpfI1S9D6Io9SyN9bUdbBthQ9ngwCIGB2CG62X o1KdeGkdcf1ZYjhgW1W04+kMtmGxny9A0jrKyd6x3ARYYVN8PdmyuJno118mGf09Ub1hddsuhSt EqnbPn9NcuM120VIksL+zoBX463J+ehxf3xcxJPCxjXw== X-Gm-Gg: ASbGncuASei59VQnJNpx0jK/nZzTjI6VpWujK8F2BQC8n29Hv3vkx+b6Dl1AVdeSjVl 9lCzp0b5CJD8f74Pz4iDtJYAxRg+kK7gZgINuWDLkMNXz8a0yBb/vp4fWwBRAMXREkXH8hL+Oj+ lXdb7Ir9VK2zw2+Auj2SZXIZZIa9DSp841QPUHepaEn9lxKLMwSyxZwtNfxfXuuKuQ89hDZtVH0 UTUhADkQhOBNx0jPzB7dkC/xv8vsVCBlqR2vK4qbK4+X3z2iZwXd4TAV7fpKbBMe4/tD5CAZQRC XMvRLMr2U83e6me9aPcKBUlmsPWFd7aEdgd5CATI9dr63QXQPgvO8QdNobEYnmEEjwkrooC4tiM bfR7MVMSxFQhibm9+V00WJlOvAnf3mEreGWPi2dHDh2C4cDcQUACnt0mq X-Received: by 2002:a17:902:c950:b0:295:2645:9f5 with SMTP id d9443c01a7336-29b6c575196mr288828625ad.37.1764228163427; Wed, 26 Nov 2025 23:22:43 -0800 (PST) X-Google-Smtp-Source: AGHT+IF/RTnwVkH1tjZ1hn9Nhv2DZYdCB9Rwq5dVJqVFrzbjizNnpSSSsD/yYyloJ/HqfJ3ob6cGgw== X-Received: by 2002:a17:902:c950:b0:295:2645:9f5 with SMTP id d9443c01a7336-29b6c575196mr288828325ad.37.1764228162910; Wed, 26 Nov 2025 23:22:42 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service Date: Thu, 27 Nov 2025 12:52:29 +0530 Message-ID: <20251127072232.38426-3-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251127072232.38426-1-armenon@redhat.com> References: <20251127072232.38426-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: kaLtM_AcPYrSIP6Icwv_zujoQmph8p5IrmH0ugIT5wk_1764228164 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 7GZPTL6M3ECOTHV7HXFTQIK3EI2M6DXT X-Message-ID-Hash: 7GZPTL6M3ECOTHV7HXFTQIK3EI2M6DXT 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: 1764228597685019200 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 | 4 ++++ src/meson.build | 1 + src/secret/meson.build | 24 ++++++++++++++++++++++++ src/secret/secret-init-encryption.in | 10 ++++++++++ src/secret/virtsecretd.service.extra.in | 8 ++++++++ 5 files changed, 47 insertions(+) create mode 100644 src/secret/secret-init-encryption.in diff --git a/libvirt.spec.in b/libvirt.spec.in index 62af7fb517..dba8a71311 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1880,13 +1880,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 @@ -2238,6 +2241,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/secret/meson.build b/src/secret/meson.build index 3b859ea7b4..c02d1064a9 100644 --- a/src/secret/meson.build +++ b/src/secret/meson.build @@ -31,6 +31,30 @@ if conf.has('WITH_SECRETS') 'name': 'virtsecretd', } =20 + secret_init_encryption_unit =3D { + 'service': 'virt-secret-init-encryption', + 'name': 'Libvirt Secret Encryption Init', + 'input': 'secret-init-encryption.in', + } + + unit_conf =3D configuration_data() + unit_conf.set('runstatedir', runstatedir) + unit_conf.set('sbindir', sbindir) + unit_conf.set('localstatedir', localstatedir) + unit_conf.set('sysconfdir', sysconfdir) + unit_conf.set('initconfdir', initconfdir) + unit_conf.set('name', secret_init_encryption_unit['name']) + unit_conf.set('service', secret_init_encryption_unit['service']) + unit_conf.set('SERVICE', secret_init_encryption_unit['service'].to_upper= ()) + + configure_file( + input: secret_init_encryption_unit['input'], + output: '@0@.service'.format(secret_init_encryption_unit['service']), + configuration: unit_conf, + install: true, + install_dir: unitdir, + ) + virt_daemon_units +=3D { 'service': 'virtsecretd', 'name': 'secret', diff --git a/src/secret/secret-init-encryption.in b/src/secret/secret-init-= encryption.in new file mode 100644 index 0000000000..29da6dbcce --- /dev/null +++ b/src/secret/secret-init-encryption.in @@ -0,0 +1,10 @@ +[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)' + +[Install] +WantedBy=3Dmulti-user.target 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:57 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=1764228678; cv=none; d=zohomail.com; s=zohoarc; b=WjLYpHzpnEkug97FoOXhPKizD+rOqDJyJh0nQVJuiHMZeunTh42FEBvdh54JzIhciYvmHlLU9GGNcaqc8czTUDWydxsSTf1CWHO3o+vu4oiLEyFyM7NeiUp719C1EOMQby6i4Y9NHkdQbBs2BwE6ue4Wh4iQcxz4wPQBeY5gprw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764228678; 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=L3qIyEJrYHhFNFweIt1Sq00UzMVZ2Ij7Ox7MlQnyAoM=; b=BcLj43L6YFPSnCx4pSYz0oXYym52n6hswvAN5nysiwADYxIg7z8gA5oVhcb7hFuwmPVAwxfl1o+xevl8U3KfOJSDIKKEddwv+FD8a+fgyCG35Foibqi8aXnj7zGxnqanpK0LuGJ5INaQKop29j0uNbu0qwfZ7tYBP/JQnucuqPc= 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 1764228678633357.8482973129413; Wed, 26 Nov 2025 23:31:18 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id F3DF14194C; Thu, 27 Nov 2025 02:31:17 -0500 (EST) Received: from [172.19.199.74] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id 579EF44B0F; Thu, 27 Nov 2025 02:28:53 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id B709E442E7; Thu, 27 Nov 2025 02:22:50 -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 680C3442E4 for ; Thu, 27 Nov 2025 02:22:48 -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-407-qHameyRoOq6TnXeiUMQiEg-1; Thu, 27 Nov 2025 02:22:46 -0500 Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-295952a4dd6so18557065ad.1 for ; Wed, 26 Nov 2025 23:22:46 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.47.195.90]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29bce478762sm7801695ad.45.2025.11.26.23.22.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Nov 2025 23:22:44 -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=1764228168; 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=L3qIyEJrYHhFNFweIt1Sq00UzMVZ2Ij7Ox7MlQnyAoM=; b=d60ilw5t9ZVzCt2e7MzOe2e8yF5EtiIerOZp8RTvOJR/XMLyJO5IORioU6PoqWmviyyoFw rU/5kSvcLFzWz40vNI5T4bIw5JlpqJMr/XQrxST1q6p6XC53BIRE9L3EOKEtM+pNOX3Ldc NuknfvzdN5P+lmvRofdQNr5bpZXmOyo= X-MC-Unique: qHameyRoOq6TnXeiUMQiEg-1 X-Mimecast-MFC-AGG-ID: qHameyRoOq6TnXeiUMQiEg_1764228165 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764228165; x=1764832965; 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=L3qIyEJrYHhFNFweIt1Sq00UzMVZ2Ij7Ox7MlQnyAoM=; b=cUugg1PfY4376GelBlrdjUWs53VrOGf2/DlyNB0nodjpsZ2Y4ChqCSePsvTncBTKPS EsAL8fX658Dgw2gjlz3/0Iq/xb5O7iGDpbnW/u9I670ervraMu49uTadvTJC7Ecam/ey McfR09YtL6G8KPrjqLSaVBgZ++Q361acHiAc7/jLkVrl1q5qDkdMuOa4E5pFYUqhLK/I YIQMdqfNkFkhpRR1Odd2mYahcCeif1b0W6GMbMQuSxurNvJps1JkPRgXzaRcUOrwl2cW XvQnv9YYiZ1b2AWCjeR/zdWUID6P1GdtD1bu44I1yRSYz5QD+kSrJVq3EVNvkHExWERh 5Viw== X-Gm-Message-State: AOJu0YwDyvQjsiQHCMh0dxx2H1FnxrT/8X/7r02HmBeMohH8kIocm+1L K5OfqAXZiqSXpMSMSjFGI6VdIvvbT4tMuFvkryCuuO4nSC99bwPDpZDak3BMpRyhWn5CH+7U9H4 Q6xp8F5q7yYRPh5PDRYOpGUvMvY0A6QX/M9H5EQeOK1qmGkrEYxUyc4jrs3y3KZ8imfu2W0+Lak gZkpJfIEHlbIyi8BQozuagdJBTO1K8+oxw7YU52swJuA== X-Gm-Gg: ASbGncvsCNz9EbdwD8RJNHBQYOJ61KaAxsMnSHvQEmFayvS5aoqHv16iE+5/E5b/5fK N2gDscPJZxRVw7z/mO3/6vquzEdi8rIi7YhCHCsbbFadobA9Lv/VGkhL7S1TN1LIt6zKEPnK7SZ BjBggdA2wQWbAfxbO2jrJ0jK74dS1Db5awGlyIvnmVf6jxG7cdGARBzlsAUwFKQOUZZLd80xCxW Jc6gR1xBI5x04CiHVQcsVJRoeOE+1Y/yPFGeoXuQti5jT4ZHza/RYKJENRUd5xN6QadibZQJHUC BG/NDYERwDhgxsHukBnRVnKAi7WYX8vTDdqT6GnB3Fr4lBNcBhpgJJtY+AkF/AzOcw9ih7YI9a1 98TBirS82FtrykdWJfLqeZY8idE9+kivuxJrlrnFmP4gt6PB4NnCjk2d5 X-Received: by 2002:a17:902:ef4f:b0:295:82d0:9baa with SMTP id d9443c01a7336-29b6bfa8cd3mr271461805ad.17.1764228165106; Wed, 26 Nov 2025 23:22:45 -0800 (PST) X-Google-Smtp-Source: AGHT+IHTr8p6R9O2UV9etOPS9lGth2E+8r/UO/mLx4TgV2Hrr1ajzrtBVDwrFZNZhPbDUBiunHhV5g== X-Received: by 2002:a17:902:ef4f:b0:295:82d0:9baa with SMTP id d9443c01a7336-29b6bfa8cd3mr271461445ad.17.1764228164404; Wed, 26 Nov 2025 23:22:44 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v3 3/5] secret: Add secrets.conf configuration file and parse it Date: Thu, 27 Nov 2025 12:52:30 +0530 Message-ID: <20251127072232.38426-4-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251127072232.38426-1-armenon@redhat.com> References: <20251127072232.38426-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: NTRNBdiPn0YnNMaDysOYqCjlgim1Uhe2fOdETtiQdOQ_1764228165 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: M536UW5NUYE5JFP37B4VWNZJB3A2ALZN X-Message-ID-Hash: M536UW5NUYE5JFP37B4VWNZJB3A2ALZN 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: 1764228684517019200 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 | 177 +++++++++++++++++++++++++ src/conf/secret_config.h | 38 ++++++ 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, 298 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 dba8a71311..01ecf7e7ca 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -2240,6 +2240,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 f0aad35c8c..a64e4b2d87 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..5bc0b24380 --- /dev/null +++ b/src/conf/secret_config.c @@ -0,0 +1,177 @@ +/* + * secret_config.c: secrets.conf config file handling + * + * Copyright (C) 2025 Red Hat, Inc. + * 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 "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; + /* Encrypt secrets by default unless the configuration sets it otherwi= se */ + cfg->encrypt_data =3D 1; + + if (virFileExists(filename)) { + conf =3D virConfReadFile(filename, 0); + if (!conf) + return -1; + if (virConfGetValueBool(conf, "encrypt_data", &cfg->encrypt_data) = < 0) { + virReportError(VIR_ERR_CONF_SYNTAX, + _("Failed to get encrypt_data from %1$s"), + filename); + return -1; + } + + if (virConfGetValueString(conf, "secrets_encryption_key", + &cfg->secretsEncryptionKeyPath) < 0) { + virReportError(VIR_ERR_CONF_SYNTAX, + _("Failed to get secrets_encryption_key from %1= $s"), + filename); + return -1; + } + } + return 0; +} + + +static int virGetSecretsEncryptionKey(virSecretDaemonConfig *cfg, + uint8_t **secrets_encryption_key, size= _t *secrets_encryption_keylen) +{ + VIR_AUTOCLOSE fd =3D -1; + ssize_t encryption_key_length; + + if (!virFileExists(cfg->secretsEncryptionKeyPath)) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Secrets key file '%1$s' = does not exist"), + cfg->secretsEncryptionKeyPath); + return -1; + } + + if ((fd =3D open(cfg->secretsEncryptionKeyPath, O_RDONLY)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot open secrets key = file '%1$s'"), + cfg->secretsEncryptionKeyPath); + return -1; + } + + *secrets_encryption_key =3D g_new0(uint8_t, VIR_SECRETS_ENCRYPTION_KEY= _LEN); + + if ((encryption_key_length =3D saferead(fd, *secrets_encryption_key, V= IR_SECRETS_ENCRYPTION_KEY_LEN)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot read secrets key = file '%1$s'"), + cfg->secretsEncryptionKeyPath); + return -1; + } + if (encryption_key_length !=3D VIR_SECRETS_ENCRYPTION_KEY_LEN) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Secrets encryption key f= ile %1$s must be 32 bytes"), + cfg->secretsEncryptionKeyPath); + return -1; + } + + *secrets_encryption_keylen =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); + } + VIR_DEBUG("Secrets encryption key path: %s", NULLSTR(cfg->secretsEncry= ptionKeyPath)); + + if (cfg->secretsEncryptionKeyPath && virFileExists(cfg->secretsEncrypt= ionKeyPath)) { + if (virGetSecretsEncryptionKey(cfg, &cfg->secrets_encryption_key, = &cfg->secretsKeyLen) < 0) { + return NULL; + } + } + if (cfg->encrypt_data =3D=3D 1) { + if (!cfg->secretsEncryptionKeyPath) { + virReportError(VIR_ERR_CONF_SYNTAX, + _("secretsEncryptionKeyPath must be set if encr= ypt_data is 1 in %1$s"), + configfile); + return NULL; + } + } + return g_steal_pointer(&cfg); +} + + +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..4cc6589814 --- /dev/null +++ b/src/conf/secret_config.h @@ -0,0 +1,38 @@ +/* + * secret_config.h: secrets.conf config file handling + * + * Copyright (C) 2025 Red Hat, Inc. + * 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 secrets.conf file */ + char *secretsEncryptionKeyPath; + + /* Store the key to encrypt secrets on the disk */ + 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. + */ + bool 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 63a1ae4c70..cdf5426af6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1066,6 +1066,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 c02d1064a9..cff0f0678d 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 Dec 12 12:54:57 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=1764228757; cv=none; d=zohomail.com; s=zohoarc; b=QcETRr9LEYBmuI4H4BPEVg3g3tFFzAsZtZvsGoMIArM231BLxv1yNmgzKdkiMuwngGy5H41pZgcJpSSfRy70SgZFpiv8oMp3beHfUDZnSBaWIacdMYsCwijR3tx9pZfEUYZFO14ntVdGcpmbLgDRz0Vq2gIlc14bCEu9+VsmphQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764228757; 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=lA0uZitrNVtHHY1KGKd3uXjNLwxTN92r80ylLtcDuSw=; b=hcJuU74h+kdWR2i/O4c4tQozt8w622/lGV6+/xCg0EsI/VOB2pQo0UojhBIi8nqLS2OcNe0jDiPH+Sec0Pz9yV+TzpxhlmhwTUEEacAzNj1u7i+OdCC1PZfzw1ftTTppZNpt5kePmvTuYqhUlx582ayBj2PHBo1W9DeICY2f5CI= 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 17642287575741005.3279417814666; Wed, 26 Nov 2025 23:32:37 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id 08DA8418CE; Thu, 27 Nov 2025 02:32:36 -0500 (EST) Received: from [172.19.199.74] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id B4EFB44C5A; Thu, 27 Nov 2025 02:28:57 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id C93E6442E2; Thu, 27 Nov 2025 02:22:53 -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 C1A5843E80 for ; Thu, 27 Nov 2025 02:22:51 -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-433-1M3s--XaMHybaIG-IBLfbg-1; Thu, 27 Nov 2025 02:22:48 -0500 Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-297f3710070so12104355ad.2 for ; Wed, 26 Nov 2025 23:22:47 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.47.195.90]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29bce478762sm7801695ad.45.2025.11.26.23.22.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Nov 2025 23:22: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=1764228171; 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=lA0uZitrNVtHHY1KGKd3uXjNLwxTN92r80ylLtcDuSw=; b=O9/mEPNzPAik6kwrC1sHlvNKvz9p97Kyt9Hz3XbEW9XufFaFai0geE2yXy4nVOUDp1CJnu LN33AzDigJLHsm1qjXkkZ6pFemqcFC3jtMqBopbnbr40zU6maDaU579tY3pSOVEo+zKhh4 bD/kHfhG+qKZsK/2evVB86kOge43AHA= X-MC-Unique: 1M3s--XaMHybaIG-IBLfbg-1 X-Mimecast-MFC-AGG-ID: 1M3s--XaMHybaIG-IBLfbg_1764228167 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764228167; x=1764832967; 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=lA0uZitrNVtHHY1KGKd3uXjNLwxTN92r80ylLtcDuSw=; b=BO26ZOixaOsbYM55aTqKdgEF7AZfpI526oaA3eZuZyxGpA0anc3pl+ryDua2t/4fcU c61ztIrcqGB0ui6BGYEl1TYc1pFG8QpK/6LZHKG1PLehbikGJj7oRWrfxfCBoctgqwqy NsLeH9/A8LHqVKLyk8XsPqfMFBfYXP0FGU1kjxLgS7pZwrPDS30s7IfDej0oLVCpepJs KgCr9OjbLQhTL4IaEU7ihV9nRUOETLwD4Led+viMObTsCXqFqdetJeqq6lA7Ye9ySekU IBU80VwWJn5073QSlXtiBw8QBiXXJab5qfIoUO+Rs3HSBSMkhyH/Wyf3Y8OE6Bx8re1c kJMA== X-Gm-Message-State: AOJu0Yzm4UHj/W3pOCOz/H1ACBHTpSdZdD9zxOowMq/52HZn2Bb99KTU SxculGKkP8dCkx3sLzM42HFMq+DzPITyTN3iNQ+rBfNS2rFOmuJ/xKFl2xArQOdm2/siKSG7iLJ Y2qulch0Az4MFbN3/OXwDuErA5Tm26k0m4zH8KG+6SHHsZhcn9Dagk6rnaNe8GiAPlwxDFOrjez 3qhP/rUXaoH0H4U04IICvzYtrlpR6Nk9/HBRYrzZDzZQ== X-Gm-Gg: ASbGncvso0vFBGU4zPypc3ZQXPymbixGCgoiDMfTwT3RW2mBxyWOMnS+sA7ZCMED/DJ XnQ+GOX68LmkwU1yJZ066exjLGmVOSefE3Aikh6Bau+7pGUaI4moQSAkw6P3ovi+63nbLkbBd0f ZVs/ZbuwRYMdNIrXNH6qtSTBT9tYuYdVTlDnMYUktpLfFOAOxMpK8+MBPiLVnGkpyIxzQ09Jnxz 7EERR0Pubik69w3bAfbV+UsZqO49klgoX8HcucmjDU8NArnvBo8EsxQitYKgdrSrT1G4F1h1tTI ZHTlPWCRikdFU1smbdOOPjWUK9ikSO+xH1sT7uyhCFylOR1N/cQoXQB1WiApFsMuSGyuusLLhAY fAnTsnjwmytZ97hGxyxBHVpDvOBYFvsdmjMuohFamBNOhcwniuyjZO/Mi X-Received: by 2002:a17:903:11cd:b0:295:1e50:e7cb with SMTP id d9443c01a7336-29b6c514f0amr242844335ad.23.1764228166782; Wed, 26 Nov 2025 23:22:46 -0800 (PST) X-Google-Smtp-Source: AGHT+IFxF/ZHJUnXZsKW5H68ux1xasg03b0eRJy9x27u4uTLcGTo03F8wi/3wNvNVKkGyhe6jpvspA== X-Received: by 2002:a17:903:11cd:b0:295:1e50:e7cb with SMTP id d9443c01a7336-29b6c514f0amr242844085ad.23.1764228166212; Wed, 26 Nov 2025 23:22:46 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v3 4/5] secret: Add encryptionSchemeType attribute to store ciphers Date: Thu, 27 Nov 2025 12:52:31 +0530 Message-ID: <20251127072232.38426-5-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251127072232.38426-1-armenon@redhat.com> References: <20251127072232.38426-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: W1_2prx8soURbFq7WoJn09Y0KpDcXjkhy7oPAacwRr0_1764228167 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 6LISX7VO4NH6IQBRQNLSTSQUMN7M55XI X-Message-ID-Hash: 6LISX7VO4NH6IQBRQNLSTSQUMN7M55XI 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: 1764228762575019200 Content-Type: text/plain; charset="utf-8"; x-default="true" The new attribute will store the available ciphers with which secrets can be encrypted. At the moment only aes256cbc encryption method is used. This can be extended in future. Rename the file name structure attribute from base64File to secretValueFile. Signed-off-by: Arun Menon --- include/libvirt/libvirt-secret.h | 20 ++++++++++++++++++++ src/conf/secret_conf.h | 1 + src/conf/virsecretobj.c | 22 +++++++++++----------- src/util/virsecret.c | 4 ++++ src/util/virsecret.h | 1 + 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/include/libvirt/libvirt-secret.h b/include/libvirt/libvirt-sec= ret.h index 761437d4ad..768c92c10c 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_AES256CBC =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/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/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'; 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, --=20 2.51.1 From nobody Fri Dec 12 12:54:57 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=1764228912; cv=none; d=zohomail.com; s=zohoarc; b=AhSVuG0IiIFarXGioSjA7hGrD5fW4aMVxiKeviHbIV0b3h0oiOuMtDeot4Zgk7PQcL1mN4vTLBkvNVfVozVK2N5OlM42cssDLF93vw7UgB0Oqc5mIPd3se84/Wi/vu133reJZCstJ+FYpFzdvrXANica4MRtrV0NzoNWqayqu8w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1764228912; 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=EWjFsTZMlsAoG37+IDhzGPY7G8XtjPQEwVkpJgv0EXM=; b=QabCafUFR4jNCr/t+7E1gWO6HIky31GKIIpT3qOuP4zq23BAKsl59qspYOxbe4jYWTSKcOak3X5pUiHGKNMaDzNmNmQZ4URFmaaDy8m3914F6W23bcEHClOgL5YDw9i07yV9NRLwXJPYylxO/LpzjdBVKdbrXjKGb6YtF58u7Zw= 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 1764228912168195.2786273399946; Wed, 26 Nov 2025 23:35:12 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 993) id 983B244C09; Thu, 27 Nov 2025 02:35:11 -0500 (EST) Received: from [172.19.199.74] (lists.libvirt.org [8.43.85.245]) by lists.libvirt.org (Postfix) with ESMTP id 92E6844BDC; Thu, 27 Nov 2025 02:29:12 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 993) id 4964F43E80; Thu, 27 Nov 2025 02:22:57 -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 2B864442E2 for ; Thu, 27 Nov 2025 02:22:56 -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-447-iT8LU3SSPm6RogPQyHGT9A-1; Thu, 27 Nov 2025 02:22:50 -0500 Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2955555f73dso7646745ad.0 for ; Wed, 26 Nov 2025 23:22:50 -0800 (PST) Received: from armenon-kvm.armenon-thinkpadp16vgen1.bengluru.csb ([49.47.195.90]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-29bce478762sm7801695ad.45.2025.11.26.23.22.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 26 Nov 2025 23:22:48 -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=1764228175; 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=EWjFsTZMlsAoG37+IDhzGPY7G8XtjPQEwVkpJgv0EXM=; b=aNDKt8/eZwLcDKuyRDQHopUY1n/G9xsoYQzyCp0QBTZXLErUB2OoRGleix5YzfJwklhUrf BxyW7c1HLskn30yiYYwCOsf5nOuIMZqeZg7r+vjoYRo7t5+Djts0awULLIsMGGHUGW6NVl /V4HC6mg/F7PSwjaouSGzZDGH8jQOZc= X-MC-Unique: iT8LU3SSPm6RogPQyHGT9A-1 X-Mimecast-MFC-AGG-ID: iT8LU3SSPm6RogPQyHGT9A_1764228169 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764228169; x=1764832969; 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=EWjFsTZMlsAoG37+IDhzGPY7G8XtjPQEwVkpJgv0EXM=; b=A/9IQksF8KaC92AocsCmLD31h3uUDfVAXd4Vwej8Z8Sso9F8cm21N2cJL9E9Zau6uj DOEvYTLjlD3e3+b7QwoJ6ogA7FYpThuV1WksGaezYm6p2KFQZkrjugg0yaB0DXYjIWjH FRkFQFsxkD+JAyfNNHnvpsAsLL0LM2jPVIekbyRUJRxNZayECO6igsQsXToMGdEKSyBe Gyoc+X2bacKS5s64tx6zAVY1xMUhjO9Ejp14tOnia/usUD/dhXKOBwUpvFFzHJgub8W6 nAJuFgWvPwu8eTfqv8eA3MKFbQLrRbO/DoUhQrfAV5ZlkskZexjCuNaHxQ54s759CcED 9ovw== X-Gm-Message-State: AOJu0YzmGRR+lovMnuxT1LBGgrOJhL1cHCGtE48VLPYQdOdUcH1Bohec DuVRZ13uwwapYCcApB1m7Aw1uhYV8UOL60CdsNtQcrGL3bbNQsiZatPUOHX0iOHKAvu3GTI4Gs0 JJsV2CLR/Tpy9aWyf72CBYf1dP+DDVxQisyIoKjzY72Znhim3A+rxAj5zqKGBO4Mlx0HcmsegUP wsLOyhjPj680Nivbj69+7UDUHAgq70roGvDjDDjvC4mQ== X-Gm-Gg: ASbGncuea6gdcrqZnVhrFxAp9JeY2TMKYKuY+ufjb1PqzoJ1g4zWPerqAjH04MgcqAV OP5S/mpuJucSYtqfJ7ME1YfJA9Ib7eClmEERRlJrYnmCcPXLiYWKoV76IrXVT6k7se1pF/6SWxs vyyGY8Ts5syshCeTvf345+v4imo8jumsKNWg1xPAWb884NmG6U9DKoYjF96Xrc7FQlJQW8y6/y/ QuAXPG2i0E8fAXhxnXlHidcFpMg2Uz6AZdQeDp8OuVFO9nJFseq8H8OYqT/jwQcN8vkcfQFX2GJ gP6M59XUjKxj5kp1Kc0f23d/6SnuOznixvlodP00y1D4cHOwobyxitm9AMe9ko2Inpt+XVZf4SR H8q6F/jSHuePqdcKc8tUHiRdoDzNWCDPZnQ4taZ8Sij14k+6fG2HDeQT3 X-Received: by 2002:a17:902:e788:b0:294:def6:5961 with SMTP id d9443c01a7336-29b6bf6578dmr234123105ad.45.1764228169082; Wed, 26 Nov 2025 23:22:49 -0800 (PST) X-Google-Smtp-Source: AGHT+IEA8yNmwwKd9nX6b1XIW0W4ihEDDVDHEkwrH64J4XmxgZMc+utaCPBSLVCwwRNyYs3he+ubKQ== X-Received: by 2002:a17:902:e788:b0:294:def6:5961 with SMTP id d9443c01a7336-29b6bf6578dmr234122795ad.45.1764228168482; Wed, 26 Nov 2025 23:22:48 -0800 (PST) To: devel@lists.libvirt.org Subject: [RFC v3 5/5] secret: Add functionality to load and save secrets in encrypted format Date: Thu, 27 Nov 2025 12:52:32 +0530 Message-ID: <20251127072232.38426-6-armenon@redhat.com> X-Mailer: git-send-email 2.51.1 In-Reply-To: <20251127072232.38426-1-armenon@redhat.com> References: <20251127072232.38426-1-armenon@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: y5B4PAIkyuVlBW-2gMllNyH-OPPetjQueeJioRkq-o0_1764228169 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: 6XB5O7HNT6DBU26SQV5NTMPDTYGR5WFB X-Message-ID-Hash: 6XB5O7HNT6DBU26SQV5NTMPDTYGR5WFB 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: 1764228920101019200 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 cipher, we can use the key to save and load secrets. Encrypt all secrets stored on disk by default. When loading secrets, identify the decryption method by matching the file extension against known encryptionSchemeType values, iterating from the mos= t recent. 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 on the disk that were encrypted with the former key. Signed-off-by: Arun Menon --- src/conf/virsecretobj.c | 159 +++++++++++++++++++++++++++++-------- src/conf/virsecretobj.h | 13 ++- src/secret/secret_driver.c | 27 +++++-- 3 files changed, 156 insertions(+), 43 deletions(-) diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index a3dd7983bb..8b658a6f4c 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 @@ -323,12 +327,16 @@ virSecretObj * virSecretObjListAdd(virSecretObjList *secrets, virSecretDef **newdef, const char *configDir, - virSecretDef **oldDef) + virSecretDef **oldDef, + virSecretDaemonConfig *driverConfig) { virSecretObj *obj; virSecretDef *objdef; virSecretObj *ret =3D NULL; + g_autofree char *encryptionScheme =3D NULL; + g_autofree char *encryptionSchemeExt =3D NULL; char uuidstr[VIR_UUID_STRING_BUFLEN]; + virSecretEncryptionSchemeType latestEncryptionScheme; =20 virObjectRWLockWrite(secrets); =20 @@ -379,10 +387,24 @@ 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 scheme will be used to encryp= t secrets. */ - if (!(obj->configFile =3D virFileBuildPath(configDir, uuidstr, ".x= ml")) || - !(obj->secretValueFile =3D virFileBuildPath(configDir, uuidstr= , ".base64"))) + + latestEncryptionScheme =3D VIR_SECRET_ENCRYPTION_SCHEME_LAST - 1; + encryptionScheme =3D g_strdup(virSecretEncryptionSchemeTypeToStrin= g(latestEncryptionScheme)); + encryptionSchemeExt =3D g_strconcat(".", encryptionScheme, NULL); + + if (driverConfig->encrypt_data) { + if (!(obj->secretValueFile =3D virFileBuildPath(configDir, uui= dstr, encryptionSchemeExt))) { + goto cleanup; + } + } else { + if (!(obj->secretValueFile =3D virFileBuildPath(configDir, uui= dstr, ".base64"))) { + goto cleanup; + } + } + if (!(obj->configFile =3D virFileBuildPath(configDir, uuidstr, ".x= ml"))) goto cleanup; =20 if (virHashAddEntry(secrets->objs, uuidstr, obj) < 0) @@ -407,6 +429,7 @@ struct virSecretCountData { int count; }; =20 + static int virSecretObjListNumOfSecretsCallback(void *payload, const char *name G_GNUC_UNUSED, @@ -530,6 +553,7 @@ struct _virSecretObjListExportData { bool error; }; =20 + static int virSecretObjListExportCallback(void *payload, const char *name G_GNUC_UNUSED, @@ -682,15 +706,38 @@ virSecretObjSaveConfig(virSecretObj *obj) =20 =20 int -virSecretObjSaveData(virSecretObj *obj) +virSecretObjSaveData(virSecretObj *obj, + virSecretDaemonConfig *driverConfig) { 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 (driverConfig->encrypt_data && driverConfig->secrets_encryption_key= ) { + if (virRandomBytes(iv, sizeof(iv)) < 0) { + 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)= { + 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,23 +784,22 @@ virSecretObjGetValue(virSecretObj *obj) 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 */ @@ -807,11 +853,23 @@ virSecretLoadValidateUUID(virSecretDef *def, =20 =20 static int -virSecretLoadValue(virSecretObj *obj) +virSecretLoadValue(virSecretObj *obj, + virSecretDaemonConfig *driverConfig) { - int ret =3D -1, fd =3D -1; + int ret =3D -1; + VIR_AUTOCLOSE 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; + 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; + virSecretEncryptionSchemeType latestEncryptionScheme; =20 if ((fd =3D open(obj->secretValueFile, O_RDONLY)) =3D=3D -1) { if (errno =3D=3D ENOENT) { @@ -841,25 +899,60 @@ virSecretLoadValue(virSecretObj *obj) goto cleanup; } =20 - contents =3D g_new0(char, st.st_size + 1); + /* Iterate over the encryption schemes starting with the latest one and + * decrypt the contents of the file on the disk, by matching the file + * extention with the encryption scheme. If there is no scheme matching + * the file extention, then that secret is not loaded. */ =20 - if (saferead(fd, contents, st.st_size) !=3D st.st_size) { - virReportSystemError(errno, _("cannot read '%1$s'"), - obj->secretValueFile); - goto cleanup; + if (virStringHasSuffix(obj->secretValueFile, ".base64")) { + 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'; + obj->value =3D g_base64_decode(contents, &obj->value_size); + } else { + for (latestEncryptionScheme =3D VIR_SECRET_ENCRYPTION_SCHEME_LAST-= 1; latestEncryptionScheme > 0; latestEncryptionScheme--) { + encryptionScheme =3D g_strdup(virSecretEncryptionSchemeTypeToS= tring(latestEncryptionScheme)); + if (virStringHasSuffix(obj->secretValueFile, encryptionScheme)= ) { + 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; + } + if ((st.st_size) < sizeof(iv)) { + virReportError(VIR_ERR_INVALID_SECRET, "%s", + _("Encrypted secret size is invalid")); + goto cleanup; + } + contents[st.st_size] =3D '\0'; + contents_encrypted =3D g_base64_decode(contents, &obj->val= ue_size); + + 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, driverConfig->secretsKeyLen, + iv, sizeof(iv), + ciphertext, ciphertextLen, + &decryptedValue, &decryptedValueL= en) < 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); - VIR_FORCE_CLOSE(fd); + virSecureErase(iv, sizeof(iv)); return ret; } =20 @@ -868,7 +961,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; @@ -879,10 +973,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, driv= erConfig))) return NULL; =20 - if (virSecretLoadValue(obj) < 0) { + if (virSecretLoadValue(obj, driverConfig) < 0) { virSecretObjListRemove(secrets, obj); g_clear_pointer(&obj, virObjectUnref); return NULL; @@ -894,7 +988,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 +1010,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..78a1fb1a39 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 @@ -51,7 +52,8 @@ virSecretObj * virSecretObjListAdd(virSecretObjList *secrets, virSecretDef **newdef, const char *configDir, - virSecretDef **oldDef); + virSecretDef **oldDef, + virSecretDaemonConfig *driverConfig); =20 typedef bool (*virSecretObjListACLFilter)(virConnectPtr conn, @@ -86,7 +88,8 @@ int virSecretObjSaveConfig(virSecretObj *obj); =20 int -virSecretObjSaveData(virSecretObj *obj); +virSecretObjSaveData(virSecretObj *obj, + virSecretDaemonConfig *driverConfig); =20 virSecretDef * virSecretObjGetDef(virSecretObj *obj); @@ -101,7 +104,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 +116,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..ba781e241e 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,9 @@ struct _virSecretDriverState { =20 /* Immutable pointer, self-locking APIs */ virInhibitor *inhibitor; + + /* Settings from secrets.conf file */ + virSecretDaemonConfig *config; }; =20 static virSecretDriverState *driver; @@ -218,13 +226,14 @@ secretDefineXML(virConnectPtr conn, goto cleanup; =20 if (!(obj =3D virSecretObjListAdd(driver->secrets, &def, - driver->configDir, &backup))) + driver->configDir, &backup, + driver->config))) goto cleanup; objDef =3D virSecretObjGetDef(obj); =20 if (!objDef->isephemeral) { if (backup && backup->isephemeral) { - if (virSecretObjSaveData(obj) < 0) + if (virSecretObjSaveData(obj, driver->config) < 0) goto restore_backup; } =20 @@ -307,7 +316,6 @@ secretGetXMLDesc(virSecretPtr secret, return ret; } =20 - static int secretSetValue(virSecretPtr secret, const unsigned char *value, @@ -327,8 +335,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 +461,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 +526,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 +544,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 +563,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