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 extract_esl(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(rc);
39 	}
40 
41 	db = kmalloc(*size, GFP_KERNEL);
42 	if (!db)
43 		return ERR_PTR(-ENOMEM);
44 
45 	rc = secvar_ops->get(key, keylen, db, size);
46 	if (rc) {
47 		kfree(db);
48 		return ERR_PTR(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 	u64 dsize = 0;
63 	u64 offset = 0;
64 	int rc = 0;
65 	ssize_t len;
66 	char buf[32];
67 
68 	if (!secvar_ops)
69 		return -ENODEV;
70 
71 	len = secvar_ops->format(buf, sizeof(buf));
72 	if (len <= 0)
73 		return -ENODEV;
74 
75 	// Check for known secure boot implementations from OPAL or PLPKS
76 	if (strcmp("ibm,edk2-compat-v1", buf) && strcmp("ibm,plpks-sb-v1", buf)) {
77 		pr_err("Unsupported secvar implementation \"%s\", not loading certs\n", buf);
78 		return -ENODEV;
79 	}
80 
81 	if (strcmp("ibm,plpks-sb-v1", buf) == 0)
82 		/* PLPKS authenticated variables ESL data is prefixed with 8 bytes of timestamp */
83 		offset = 8;
84 
85 	/*
86 	 * Get db, and dbx. They might not exist, so it isn't an error if we
87 	 * can't get them.
88 	 */
89 	data = get_cert_list("db", 3, &dsize);
90 	if (!data) {
91 		pr_info("Couldn't get db list from firmware\n");
92 	} else if (IS_ERR(data)) {
93 		rc = PTR_ERR(data);
94 		pr_err("Error reading db from firmware: %d\n", rc);
95 		return rc;
96 	} else {
97 		extract_esl(db, data, dsize, offset);
98 
99 		rc = parse_efi_signature_list("powerpc:db", db, dsize,
100 					      get_handler_for_db);
101 		if (rc)
102 			pr_err("Couldn't parse db signatures: %d\n", rc);
103 		kfree(data);
104 	}
105 
106 	data = get_cert_list("dbx", 4,  &dsize);
107 	if (!data) {
108 		pr_info("Couldn't get dbx list from firmware\n");
109 	} else if (IS_ERR(data)) {
110 		rc = PTR_ERR(data);
111 		pr_err("Error reading dbx from firmware: %d\n", rc);
112 		return rc;
113 	} else {
114 		extract_esl(dbx, data, dsize, offset);
115 
116 		rc = parse_efi_signature_list("powerpc:dbx", dbx, dsize,
117 					      get_handler_for_dbx);
118 		if (rc)
119 			pr_err("Couldn't parse dbx signatures: %d\n", rc);
120 		kfree(data);
121 	}
122 
123 	return rc;
124 }
125 late_initcall(load_powerpc_certs);
126