1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2019 IBM Corporation |
4 | * Author: Nayna Jain |
5 | * |
6 | * - loads keys and hashes stored and controlled by the firmware. |
7 | */ |
8 | #include <linux/kernel.h> |
9 | #include <linux/sched.h> |
10 | #include <linux/cred.h> |
11 | #include <linux/err.h> |
12 | #include <linux/slab.h> |
13 | #include <asm/secure_boot.h> |
14 | #include <asm/secvar.h> |
15 | #include "keyring_handler.h" |
16 | #include "../integrity.h" |
17 | |
18 | #define (db, data, size, offset) \ |
19 | do { db = data + offset; size = size - offset; } while (0) |
20 | |
21 | /* |
22 | * Get a certificate list blob from the named secure variable. |
23 | * |
24 | * Returns: |
25 | * - a pointer to a kmalloc'd buffer containing the cert list on success |
26 | * - NULL if the key does not exist |
27 | * - an ERR_PTR on error |
28 | */ |
29 | static __init void *get_cert_list(u8 *key, unsigned long keylen, u64 *size) |
30 | { |
31 | int rc; |
32 | void *db; |
33 | |
34 | rc = secvar_ops->get(key, keylen, NULL, size); |
35 | if (rc) { |
36 | if (rc == -ENOENT) |
37 | return NULL; |
38 | return ERR_PTR(error: rc); |
39 | } |
40 | |
41 | db = kmalloc(size: *size, GFP_KERNEL); |
42 | if (!db) |
43 | return ERR_PTR(error: -ENOMEM); |
44 | |
45 | rc = secvar_ops->get(key, keylen, db, size); |
46 | if (rc) { |
47 | kfree(objp: db); |
48 | return ERR_PTR(error: rc); |
49 | } |
50 | |
51 | return db; |
52 | } |
53 | |
54 | /* |
55 | * Load the certs contained in the keys databases into the platform trusted |
56 | * keyring and the blacklisted X.509 cert SHA256 hashes into the blacklist |
57 | * keyring. |
58 | */ |
59 | static int __init load_powerpc_certs(void) |
60 | { |
61 | void *db = NULL, *dbx = NULL, *data = NULL; |
62 | void *trustedca; |
63 | void *moduledb; |
64 | u64 dsize = 0; |
65 | u64 offset = 0; |
66 | int rc = 0; |
67 | ssize_t len; |
68 | char buf[32]; |
69 | |
70 | if (!secvar_ops) |
71 | return -ENODEV; |
72 | |
73 | len = secvar_ops->format(buf, sizeof(buf)); |
74 | if (len <= 0) |
75 | return -ENODEV; |
76 | |
77 | // Check for known secure boot implementations from OPAL or PLPKS |
78 | if (strcmp("ibm,edk2-compat-v1" , buf) && strcmp("ibm,plpks-sb-v1" , buf)) { |
79 | pr_err("Unsupported secvar implementation \"%s\", not loading certs\n" , buf); |
80 | return -ENODEV; |
81 | } |
82 | |
83 | if (strcmp("ibm,plpks-sb-v1" , buf) == 0) |
84 | /* PLPKS authenticated variables ESL data is prefixed with 8 bytes of timestamp */ |
85 | offset = 8; |
86 | |
87 | /* |
88 | * Get db, and dbx. They might not exist, so it isn't an error if we |
89 | * can't get them. |
90 | */ |
91 | data = get_cert_list(key: "db" , keylen: 3, size: &dsize); |
92 | if (!data) { |
93 | pr_info("Couldn't get db list from firmware\n" ); |
94 | } else if (IS_ERR(ptr: data)) { |
95 | rc = PTR_ERR(ptr: data); |
96 | pr_err("Error reading db from firmware: %d\n" , rc); |
97 | return rc; |
98 | } else { |
99 | extract_esl(db, data, dsize, offset); |
100 | |
101 | rc = parse_efi_signature_list(source: "powerpc:db" , data: db, size: dsize, |
102 | get_handler_for_guid: get_handler_for_db); |
103 | if (rc) |
104 | pr_err("Couldn't parse db signatures: %d\n" , rc); |
105 | kfree(objp: data); |
106 | } |
107 | |
108 | data = get_cert_list(key: "dbx" , keylen: 4, size: &dsize); |
109 | if (!data) { |
110 | pr_info("Couldn't get dbx list from firmware\n" ); |
111 | } else if (IS_ERR(ptr: data)) { |
112 | rc = PTR_ERR(ptr: data); |
113 | pr_err("Error reading dbx from firmware: %d\n" , rc); |
114 | return rc; |
115 | } else { |
116 | extract_esl(dbx, data, dsize, offset); |
117 | |
118 | rc = parse_efi_signature_list(source: "powerpc:dbx" , data: dbx, size: dsize, |
119 | get_handler_for_guid: get_handler_for_dbx); |
120 | if (rc) |
121 | pr_err("Couldn't parse dbx signatures: %d\n" , rc); |
122 | kfree(objp: data); |
123 | } |
124 | |
125 | data = get_cert_list(key: "trustedcadb" , keylen: 12, size: &dsize); |
126 | if (!data) { |
127 | pr_info("Couldn't get trustedcadb list from firmware\n" ); |
128 | } else if (IS_ERR(ptr: data)) { |
129 | rc = PTR_ERR(ptr: data); |
130 | pr_err("Error reading trustedcadb from firmware: %d\n" , rc); |
131 | } else { |
132 | extract_esl(trustedca, data, dsize, offset); |
133 | |
134 | rc = parse_efi_signature_list(source: "powerpc:trustedca" , data: trustedca, size: dsize, |
135 | get_handler_for_guid: get_handler_for_ca_keys); |
136 | if (rc) |
137 | pr_err("Couldn't parse trustedcadb signatures: %d\n" , rc); |
138 | kfree(objp: data); |
139 | } |
140 | |
141 | data = get_cert_list(key: "moduledb" , keylen: 9, size: &dsize); |
142 | if (!data) { |
143 | pr_info("Couldn't get moduledb list from firmware\n" ); |
144 | } else if (IS_ERR(ptr: data)) { |
145 | rc = PTR_ERR(ptr: data); |
146 | pr_err("Error reading moduledb from firmware: %d\n" , rc); |
147 | } else { |
148 | extract_esl(moduledb, data, dsize, offset); |
149 | |
150 | rc = parse_efi_signature_list(source: "powerpc:moduledb" , data: moduledb, size: dsize, |
151 | get_handler_for_guid: get_handler_for_code_signing_keys); |
152 | if (rc) |
153 | pr_err("Couldn't parse moduledb signatures: %d\n" , rc); |
154 | kfree(objp: data); |
155 | } |
156 | |
157 | return rc; |
158 | } |
159 | late_initcall(load_powerpc_certs); |
160 | |