From nobody Sat Apr 5 01:21:10 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1739265906; cv=none; d=zohomail.com; s=zohoarc; b=Yr6xUMQ/soNB6ZQn6Vs03iDeGH8zn4yhZosjfMD71LlbBMO/eyz4gcczJwpCgxNS2ZjSeIWbmyHj44QpKw15U6fSqAadhO/kS90IuHTQzRvIFdsXbraBy1xJNXOPsplQ30Rf8hPcinAdBiFqXWnDlTy2lw9Uc4UeOa0r1p8Yn4w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1739265906; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=hgQBER1MJ2liAJuH4wPTGa8SyH6MCQm9SD6Ph8MTQ5Y=; b=S1//jEd+GiSjImsRhIhvGMb+Ydumh43InHHOMyDCahf8StNd0onLHI+jzggwp1+tQAon3YApgt5w5rYuztSSemBZEsm8pFpoXloUGWd777F7Wqn5dtbUU4lrm7a7bFJy1qVjaYBJ9XDoyFYsrPkNpPIQrWm1jiNIwGl/4TY1PTI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 173926590636147.78123976577285; Tue, 11 Feb 2025 01:25:06 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1thmUq-0002H7-Ri; Tue, 11 Feb 2025 04:24:00 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1thmUg-0002Ab-4W for qemu-devel@nongnu.org; Tue, 11 Feb 2025 04:23:53 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1thmUd-00030G-Ak for qemu-devel@nongnu.org; Tue, 11 Feb 2025 04:23:49 -0500 Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-190-rcnq64DIPxCP1OqI04Vi4g-1; Tue, 11 Feb 2025 04:23:42 -0500 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 33C291955DDD; Tue, 11 Feb 2025 09:23:41 +0000 (UTC) Received: from sirius.home.kraxel.org (unknown [10.44.32.57]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9F7D6195608D; Tue, 11 Feb 2025 09:23:40 +0000 (UTC) Received: by sirius.home.kraxel.org (Postfix, from userid 1000) id EBDF318003B4; Tue, 11 Feb 2025 10:23:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1739265826; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hgQBER1MJ2liAJuH4wPTGa8SyH6MCQm9SD6Ph8MTQ5Y=; b=eLlNFGiJEDy6M2Q11nFH4hpmV+IdsbOxYnzMTQtornCC6xyMwmWppBmcnrJk3uo3Zajzbq 56zfyv7PuNSCrMqwi+bx47Po/fC5jliIuxO8jGJbmtwOsE0l35eGT83ZPLewZ6RuldmgyA uq9ty2PC6TwqwuZfRnstFWTFUKZTnbs= X-MC-Unique: rcnq64DIPxCP1OqI04Vi4g-1 X-Mimecast-MFC-AGG-ID: rcnq64DIPxCP1OqI04Vi4g From: Gerd Hoffmann To: qemu-devel@nongnu.org Cc: Gerd Hoffmann , graf@amazon.com, Eric Blake , Peter Maydell , Paolo Bonzini , =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= , Thomas Huth , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-arm@nongnu.org, Michael Roth , Markus Armbruster , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Ard Biesheuvel Subject: [PATCH v3 07/23] hw/uefi: add var-service-auth.c Date: Tue, 11 Feb 2025 10:23:05 +0100 Message-ID: <20250211092324.965440-8-kraxel@redhat.com> In-Reply-To: <20250211092324.965440-1-kraxel@redhat.com> References: <20250211092324.965440-1-kraxel@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=kraxel@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -34 X-Spam_score: -3.5 X-Spam_bar: --- X-Spam_report: (-3.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1.388, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1739265909058019100 Content-Type: text/plain; charset="utf-8" This implements authenticated variable handling (see AuthVariableLib in edk2). The by far most common use case for auth variables is secure boot. The secure boot certificate databases ('PK', 'KEK', 'db' and 'dbx') are authenticated variables, with update rules being specified in the UEFI specification. Signed-off-by: Gerd Hoffmann --- hw/uefi/var-service-auth.c | 361 +++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 hw/uefi/var-service-auth.c diff --git a/hw/uefi/var-service-auth.c b/hw/uefi/var-service-auth.c new file mode 100644 index 000000000000..fba5a0956a57 --- /dev/null +++ b/hw/uefi/var-service-auth.c @@ -0,0 +1,361 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * uefi vars device - AuthVariableLib + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/dma.h" + +#include "hw/uefi/var-service.h" + +static const uint16_t name_pk[] =3D u"PK"; +static const uint16_t name_kek[] =3D u"KEK"; +static const uint16_t name_db[] =3D u"db"; +static const uint16_t name_dbx[] =3D u"dbx"; +static const uint16_t name_setup_mode[] =3D u"SetupMode"; +static const uint16_t name_sigs_support[] =3D u"SignatureSupport"; +static const uint16_t name_sb[] =3D u"SecureBoot"; +static const uint16_t name_sb_enable[] =3D u"SecureBootEnable"; +static const uint16_t name_custom_mode[] =3D u"CustomMode"; +static const uint16_t name_vk[] =3D u"VendorKeys"; +static const uint16_t name_vk_nv[] =3D u"VendorKeysNv"; + +static const uint32_t sigdb_attrs =3D + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + +static void set_secure_boot(uefi_vars_state *uv, uint8_t sb) +{ + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_sb, sizeof(name_sb), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &sb, sizeof(sb)); +} + +static void set_secure_boot_enable(uefi_vars_state *uv, uint8_t sbe) +{ + uefi_vars_set_variable(uv, EfiSecureBootEnableDisable, + name_sb_enable, sizeof(name_sb_enable), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + &sbe, sizeof(sbe)); +} + +static void set_setup_mode(uefi_vars_state *uv, uint8_t sm) +{ + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_setup_mode, sizeof(name_setup_mode), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &sm, sizeof(sm)); +} + +static void set_custom_mode(uefi_vars_state *uv, uint8_t cm) +{ + uefi_vars_set_variable(uv, EfiCustomModeEnable, + name_custom_mode, sizeof(name_custom_mode), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + &cm, sizeof(cm)); +} + +static void set_signature_support(uefi_vars_state *uv) +{ + QemuUUID sigs_support[5]; + + sigs_support[0] =3D EfiCertSha256Guid; + sigs_support[1] =3D EfiCertSha384Guid; + sigs_support[2] =3D EfiCertSha512Guid; + sigs_support[3] =3D EfiCertRsa2048Guid; + sigs_support[4] =3D EfiCertX509Guid; + + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_sigs_support, sizeof(name_sigs_support), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sigs_support, sizeof(sigs_support)); +} + +static bool setup_mode_is_active(uefi_vars_state *uv) +{ + uefi_variable *var; + uint8_t *value; + + var =3D uefi_vars_find_variable(uv, EfiGlobalVariable, + name_setup_mode, sizeof(name_setup_mode)= ); + if (var) { + value =3D var->data; + if (value[0] =3D=3D SETUP_MODE) { + return true; + } + } + return false; +} + +static bool custom_mode_is_active(uefi_vars_state *uv) +{ + uefi_variable *var; + uint8_t *value; + + var =3D uefi_vars_find_variable(uv, EfiCustomModeEnable, + name_custom_mode, sizeof(name_custom_mod= e)); + if (var) { + value =3D var->data; + if (value[0] =3D=3D CUSTOM_SECURE_BOOT_MODE) { + return true; + } + } + return false; +} + +bool uefi_vars_is_sb_pk(uefi_variable *var) +{ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, name_pk, sizeof(name_pk)= )) { + return true; + } + return false; +} + +static bool uefi_vars_is_sb_kek(uefi_variable *var) +{ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, name_kek, sizeof(name_ke= k))) { + return true; + } + return false; +} + +static bool uefi_vars_is_sb_db(uefi_variable *var) +{ + if (!qemu_uuid_is_equal(&var->guid, &EfiImageSecurityDatabase)) { + return false; + } + if (uefi_str_equal(var->name, var->name_size, name_db, sizeof(name_db)= )) { + return true; + } + if (uefi_str_equal(var->name, var->name_size, name_dbx, sizeof(name_db= x))) { + return true; + } + return false; +} + +bool uefi_vars_is_sb_any(uefi_variable *var) +{ + if (uefi_vars_is_sb_pk(var) || + uefi_vars_is_sb_kek(var) || + uefi_vars_is_sb_db(var)) { + return true; + } + return false; +} + +static uefi_variable *uefi_vars_find_siglist(uefi_vars_state *uv, + uefi_variable *var) +{ + if (uefi_vars_is_sb_pk(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + } + if (uefi_vars_is_sb_kek(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + } + if (uefi_vars_is_sb_db(var)) { + return uefi_vars_find_variable(uv, EfiGlobalVariable, + name_kek, sizeof(name_kek)); + } + + return NULL; +} + +static efi_status uefi_vars_check_auth_2_sb(uefi_vars_state *uv, + uefi_variable *var, + mm_variable_access *va, + void *data, + uint64_t data_offset) +{ + variable_auth_2 *auth =3D data; + uefi_variable *siglist; + + if (custom_mode_is_active(uv)) { + /* no authentication in custom mode */ + return EFI_SUCCESS; + } + + if (setup_mode_is_active(uv) && !uefi_vars_is_sb_pk(var)) { + /* no authentication in setup mode (except PK) */ + return EFI_SUCCESS; + } + + if (auth->hdr_length =3D=3D 24) { + /* no signature (auth->cert_data is empty) */ + return EFI_SECURITY_VIOLATION; + } + + siglist =3D uefi_vars_find_siglist(uv, var); + if (!siglist && setup_mode_is_active(uv) && uefi_vars_is_sb_pk(var)) { + /* check PK is self-signed */ + uefi_variable tmp =3D { + .guid =3D EfiGlobalVariable, + .name =3D (uint16_t *)name_pk, + .name_size =3D sizeof(name_pk), + .attributes =3D sigdb_attrs, + .data =3D data + data_offset, + .data_size =3D va->data_size - data_offset, + }; + return uefi_vars_check_pkcs7_2(&tmp, NULL, NULL, va, data); + } + + return uefi_vars_check_pkcs7_2(siglist, NULL, NULL, va, data); +} + +efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, + mm_variable_access *va, void *data) +{ + variable_auth_2 *auth =3D data; + uint64_t data_offset; + efi_status status; + + if (va->data_size < sizeof(*auth)) { + return EFI_SECURITY_VIOLATION; + } + if (uadd64_overflow(sizeof(efi_time), auth->hdr_length, &data_offset))= { + return EFI_SECURITY_VIOLATION; + } + if (va->data_size < data_offset) { + return EFI_SECURITY_VIOLATION; + } + + if (auth->hdr_revision !=3D 0x0200 || + auth->hdr_cert_type !=3D WIN_CERT_TYPE_EFI_GUID || + !qemu_uuid_is_equal(&auth->guid_cert_type, &EfiCertTypePkcs7Guid))= { + return EFI_UNSUPPORTED; + } + + if (uefi_vars_is_sb_any(var)) { + /* secure boot variables */ + status =3D uefi_vars_check_auth_2_sb(uv, var, va, data, data_offse= t); + if (status !=3D EFI_SUCCESS) { + return status; + } + } else { + /* other authenticated variables */ + status =3D uefi_vars_check_pkcs7_2(NULL, + &var->digest, &var->digest_size, + va, data); + if (status !=3D EFI_SUCCESS) { + return status; + } + } + + /* checks passed, set variable data */ + var->time =3D auth->timestamp; + if (va->data_size - data_offset > 0) { + var->data =3D g_malloc(va->data_size - data_offset); + memcpy(var->data, data + data_offset, va->data_size - data_offset); + var->data_size =3D va->data_size - data_offset; + } + + return EFI_SUCCESS; +} + +efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable = *var) +{ + uint8_t *value =3D var->data; + + if (uefi_vars_is_sb_any(var)) { + if (var->attributes !=3D sigdb_attrs) { + return EFI_INVALID_PARAMETER; + } + } + + /* reject SecureBootEnable updates if force_secure_boot is set */ + if (qemu_uuid_is_equal(&var->guid, &EfiSecureBootEnableDisable) && + uefi_str_equal(var->name, var->name_size, + name_sb_enable, sizeof(name_sb_enable)) && + uv->force_secure_boot && + value[0] !=3D SECURE_BOOT_ENABLE) { + return EFI_WRITE_PROTECTED; + } + + /* reject CustomMode updates if disable_custom_mode is set */ + if (qemu_uuid_is_equal(&var->guid, &EfiCustomModeEnable) && + uefi_str_equal(var->name, var->name_size, + name_custom_mode, sizeof(name_custom_mode)) && + uv->disable_custom_mode) { + return EFI_WRITE_PROTECTED; + } + + return EFI_SUCCESS; +} + +/* AuthVariableLibInitialize */ +void uefi_vars_auth_init(uefi_vars_state *uv) +{ + uefi_variable *pk_var, *sbe_var; + uint8_t platform_mode, sb, sbe, vk; + + /* SetupMode */ + pk_var =3D uefi_vars_find_variable(uv, EfiGlobalVariable, + name_pk, sizeof(name_pk)); + if (!pk_var) { + platform_mode =3D SETUP_MODE; + } else { + platform_mode =3D USER_MODE; + } + set_setup_mode(uv, platform_mode); + + /* SignatureSupport */ + set_signature_support(uv); + + /* SecureBootEnable */ + sbe =3D SECURE_BOOT_DISABLE; + sbe_var =3D uefi_vars_find_variable(uv, EfiSecureBootEnableDisable, + name_sb_enable, sizeof(name_sb_enabl= e)); + if (sbe_var) { + if (platform_mode =3D=3D USER_MODE) { + sbe =3D ((uint8_t *)sbe_var->data)[0]; + } + } else if (platform_mode =3D=3D USER_MODE) { + sbe =3D SECURE_BOOT_ENABLE; + set_secure_boot_enable(uv, sbe); + } + + if (uv->force_secure_boot && sbe !=3D SECURE_BOOT_ENABLE) { + sbe =3D SECURE_BOOT_ENABLE; + set_secure_boot_enable(uv, sbe); + } + + /* SecureBoot */ + if ((sbe =3D=3D SECURE_BOOT_ENABLE) && (platform_mode =3D=3D USER_MODE= )) { + sb =3D SECURE_BOOT_MODE_ENABLE; + } else { + sb =3D SECURE_BOOT_MODE_DISABLE; + } + set_secure_boot(uv, sb); + + /* CustomMode */ + set_custom_mode(uv, STANDARD_SECURE_BOOT_MODE); + + vk =3D 0; + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_vk_nv, sizeof(name_vk_nv), + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACC= ESS, + &vk, sizeof(vk)); + uefi_vars_set_variable(uv, EfiGlobalVariable, + name_vk, sizeof(name_vk), + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + &vk, sizeof(vk)); + + /* flush to disk */ + uefi_vars_json_save(uv); +} --=20 2.48.1