Ha pasado bastante tiempo desde la introducción de UEFI y Secure Boot , que garantiza que el firmware solo ejecutará el código que lleve una firma de una parte confiable. Después de un breve período de incertidumbre, esto ahora está bien soportado de forma predeterminada en muchas distribuciones de Linux. El modelo de seguridad Zero Trust que está ganando popularidad últimamente también estipula: “nunca confíes, verifica siempre”, y hazlo en cada paso, siempre. Para que esto sea efectivo, debe iniciarse en el momento en que enciende su computadora.
El proceso de arranque habitual para una PC que ejecuta Linux hoy en día es que UEFI carga una pequeña pieza de software (llamada shim), que a su vez carga el gestor de arranque (generalmente GRUB), y en cada paso el anterior se asegura de que el siguiente esté firmado digitalmente. por una parte de confianza. Para ampliar esta “cadena de confianza” un paso más, el kernel de Linux también ha obtenido soporte para firmar digitalmente imágenes y módulos del kernel. Entonces, ahora el gestor de arranque (o incluso el firmware UEFI directamente) puede verificar la firma en la imagen del kernel que está cargando. Con las opciones de configuración correctas habilitadas (CONFIG_MODULE_SIG_FORCE y CONFIG_KEXEC_SIG_FORCE), el kernel verificará las firmas al cargar un módulo o realizar un kexec.
Desafortunadamente, esta seguridad adicional tiene el precio de una complejidad adicional. Cuando falla la carga de un módulo o la realización de un kexec debido a un problema con la verificación de la firma, puede que no sea trivial descubrir exactamente por qué. Los detalles sangrientos de cómo funciona toda esta intrincada maquinaria están fuera del alcance de este artículo, pero una posible razón del fallo es que el módulo o la imagen del núcleo no contiene ninguna firma válida o sí la contiene, pero el firmante no está en la lista de partes confiables del núcleo.
Teniendo en cuenta lo anterior, al depurar un problema de este tipo, un primer paso lógico es comprobar si el módulo o kernel contiene una firma válida y, en caso afirmativo, quién la ha enviado.
Existe cierta documentación sobre cómo hacer esto para los módulos, e incluso hay algunos scripts de ayuda en las fuentes del kernel, pero no encontré nada sobre las imágenes del kernel.
Primero, supongo que tienes un archivo bzImage en tus manos. Para saber si contiene una firma y por quién, debemos comprender un poco más sobre su formato de archivo. Y es posible que se sorprenda al descubrir que en realidad se trata de un archivo ejecutable portátil . Sí, la imagen del kernel de Linux está en un formato de archivo ejecutable de Microsoft Windows. O, más precisamente, pretende ser uno lo suficientemente parecido, de modo que si tiene la opción CONFIG_EFI_STUB habilitada, UEFI puede cargar el kernel de Linux directamente y verificar su firma.
Como la firma del formato de archivo PE ya estaba allí, los desarrolladores del kernel de Linux decidieron reutilizarla también para kexec . Se pueden encontrar más detalles sobre las firmas en un ejecutable de PE en este documento (¡Microsoft Word!) .
Hay bibliotecas para trabajar con archivos PE para varios idiomas, pero me ha resultado más sencillo utilizar la utilidad independiente osslsigncode . Si está utilizando openSUSE Tumbleweed, está a solo un comando 'zypper in osslsigncode' de distancia y, si no, consulte el administrador de paquetes de su distribución favorita o compílelo desde las fuentes. Tenga en cuenta que debería utilizar una versión bastante reciente de osslsigncode (>=2.6), ya que he encontrado problemas tanto con la extracción como con la verificación de firmas utilizando versiones anteriores.
Una vez que tenga esto en su lugar, veamos si la imagen del kernel con la que estamos tratando tiene una firma válida y, en caso afirmativo, extráigala a un archivo. Usaré el kernel que viene con la distribución para los ejemplos siguientes y ejecutaré este comando:
osslsigncode extract-signature -in /boot/vmlinuz-6.5.2-1-default -out kernel.sig
Succeeded
Si la imagen está firmada, obtenemos un mensaje "Exitoso" y la firma se almacena en el archivo kernel.sig. Y si no, verá el mensaje "No se puede extraer la firma existente, error".
Ahora podemos usar openssl para ver una versión legible por humanos de los certificados en la firma pkcs7:
openssl pkcs7 -in kernel.sig -inform der -print_certs -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
fa:be:d8:bf:40:9a:5e:65
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=openSUSE Secure Boot CA, C=DE, L=Nuremberg, O=openSUSE Project/emailAddress=build@opensuse.org
Validity
Not Before: Jun 13 13:22:16 2022 GMT
Not After : Apr 21 13:22:16 2032 GMT
Subject: CN=openSUSE Secure Boot Signkey, C=DE, L=Nuremberg, O=openSUSE Project/emailAddress=build@opensuse.org
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b2:29:40:7e:ad:9f:73:5e:44:6a:e4:56:0a:f3:
6e:7a:05:49:68:3f:b2:4f:13:41:82:3d:dd:80:c4:
f1:cb:11:06:48:fd:a5:90:19:0e:91:7d:da:6e:98:
ee:97:e1:43:90:bc:78:e5:30:c0:19:91:c6:3f:dc:
29:e0:af:a3:d8:41:84:dd:fd:90:19:cf:d1:4d:1f:
1f:97:84:e5:64:81:93:6f:87:d8:34:f9:4d:e1:8a:
87:7e:69:c6:a3:d4:5c:6c:b3:e7:01:6d:21:d5:46:
94:37:92:3b:e5:ef:15:bf:36:49:ed:8c:48:98:67:
04:ed:00:1c:c3:f4:8d:da:a6:f0:ce:95:3b:03:95:
79:86:fd:f5:84:c9:70:24:69:82:59:8c:86:59:2a:
ca:d0:a0:86:60:0b:5d:d9:fc:01:c2:39:73:0b:88:
9e:47:83:1b:29:ec:8d:82:66:81:a0:0e:5a:1e:95:
97:60:f5:1e:f0:b3:27:66:d5:13:0f:31:df:8e:9b:
b7:40:0a:cd:2f:22:31:8a:49:e5:30:cb:59:f5:79:
eb:92:fa:2d:35:6a:9e:2d:48:3c:67:e9:a4:3b:4e:
77:d2:fb:a1:cf:ff:4a:e7:c6:31:6a:69:61:3f:05:
04:38:c3:aa:e3:52:9d:78:7c:d1:01:3e:bd:61:d5:
17:e7
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
FD:9F:2C:12:E5:99:D6:7C:C7:F9:06:75:41:AD:F4:26:B7:12:46:9E
X509v3 Authority Key Identifier:
keyid:68:42:60:0D:E2:2C:4C:47:7E:95:BE:23:DF:EA:95:13:E5:97:17:62
DirName:/CN=openSUSE Secure Boot CA/C=DE/L=Nuremberg/O=openSUSE Project/emailAddress=build@opensuse.org
serial:01
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
Code Signing
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
5b:93:17:61:67:e9:2b:4a:f2:54:30:5d:5c:63:cb:5a:93:91:
80:a8:7a:25:3e:27:4b:bb:d4:e4:15:b9:7d:7d:43:21:cd:1f:
84:4f:6a:3e:6c:70:31:7e:f8:3a:35:d9:df:9d:b4:35:f5:75:
8e:b0:20:fd:d9:b2:cd:41:ae:e2:9c:af:99:37:d1:6f:05:f0:
78:39:c6:d6:dd:f3:6f:43:d4:d6:7b:5f:cd:18:6d:c2:77:d0:
1a:6d:74:78:80:99:34:a4:f0:c6:9f:43:f5:c6:ba:8c:83:f4:
a5:02:57:8a:54:52:05:2a:99:a7:0d:29:34:13:de:5f:91:41:
f3:b0:c1:26:70:e4:a6:cc:55:ec:5a:f3:47:e5:e3:21:9c:05:
7c:11:8d:79:cc:90:74:20:62:09:7b:46:51:4e:de:0d:32:aa:
b4:84:cb:d1:6c:f0:27:5f:21:78:52:ac:97:0b:5c:a0:44:a2:
eb:14:92:8e:c5:43:b2:4d:20:c8:bc:5b:1a:50:09:cb:45:5e:
11:bd:58:86:52:55:6e:f6:62:09:c1:18:ab:95:be:53:e4:b7:
d7:cd:3b:53:eb:33:d6:70:7e:cc:0c:9c:ec:91:11:26:04:87:
c0:f2:b4:d9:5c:0d:95:8f:bd:e7:9f:15:f7:92:e4:a7:e1:99:
45:23:49:10
En el resultado anterior podemos ver que el certificado utilizado para firmar nuestra imagen del kernel lleva el nombre común “openSUSE Secure Boot Signkey” y que fue emitido por la autoridad certificadora “openSUSE Secure Boot CA”. Para verificar la firma también necesitaremos que esta última complete la 'cadena de confianza'.
Si tiene su propia PKI, utilizará sus propios certificados, pero en este caso el certificado CA openSUSE se puede obtener fácilmente instalando el paquete shim (zypper en shim) y se encuentra en /usr/share/efi/x86_64/shim-opensuse. der. Primero debemos convertirlo del formato DER a PEM, ya que es el que espera osslsigncode:
openssl x509 -in /usr/share/efi/x86_64/shim-opensuse.der -inform DER -out opensuse-ca.pem -outform PEM
Finalmente podemos usar osslsigncode para verificar la firma en nuestra imagen del kernel:
osslsigncode verify -in /boot/vmlinuz-6.5.2-1-default -CAfile opensuse-ca.pem
Current PE checksum : 00000000
Calculated PE checksum: 00DC76A1
Warning: invalid PE checksum
Message digest algorithm : SHA256
Current message digest : 9564082AFF7452005D600F5D7C9884CD2F2D7C63544BEBAEC4C9DA1FF7A7EB21
Calculated message digest : 9564082AFF7452005D600F5D7C9884CD2F2D7C63544BEBAEC4C9DA1FF7A7EB21
Signature Index: 0 (Primary Signature)
Signer's certificate:
Signer #0:
Subject: /CN=openSUSE Secure Boot Signkey/C=DE/L=Nuremberg/O=openSUSE Project/emailAddress=build@opensuse.org
Issuer : /CN=openSUSE Secure Boot CA/C=DE/L=Nuremberg/O=openSUSE Project/emailAddress=build@opensuse.org
Serial : FABED8BF409A5E65
Certificate expiration date:
notBefore : Jun 13 13:22:16 2022 GMT
notAfter : Apr 21 13:22:16 2032 GMT
Number of certificates: 1
Signer #0:
Subject: /CN=openSUSE Secure Boot Signkey/C=DE/L=Nuremberg/O=openSUSE Project/emailAddress=build@opensuse.org
Issuer : /CN=openSUSE Secure Boot CA/C=DE/L=Nuremberg/O=openSUSE Project/emailAddress=build@opensuse.org
Serial : FABED8BF409A5E65
Certificate expiration date:
notBefore : Jun 13 13:22:16 2022 GMT
notAfter : Apr 21 13:22:16 2032 GMT
Message digest algorithm: SHA256
Authenticated attributes:
Signing time: Sep 11 20:04:11 2023 GMT
Message digest: C852448C1DBAC25B0F18D4F9711DA864F0E75A1A8FDD207315353042B84AC576
CAfile: osslsigncode/osslsigncode-2.7/build/opensuse-ca.pem
Timestamp is not available
Signature verification: ok
Number of verified signatures: 1
Succeeded
Si todo lo anterior se ve bien (no se alarme por la advertencia de 'suma de verificación PE' no válida) y aún no puede ejecutar la imagen, entonces el problema debe ser que falta un certificado en la lista de 'firmantes confiables' del archivo en ejecución. núcleo. Los certificados en esta lista se pueden agregar en tiempo de compilación del kernel o durante el tiempo de ejecución usando la utilidad 'keyctl', pero ese puede ser un tema para otro artículo...