xref: /openbmc/linux/certs/blacklist.c (revision 6e7c2b4d)
1734114f8SDavid Howells /* System hash blacklist.
2734114f8SDavid Howells  *
3734114f8SDavid Howells  * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
4734114f8SDavid Howells  * Written by David Howells (dhowells@redhat.com)
5734114f8SDavid Howells  *
6734114f8SDavid Howells  * This program is free software; you can redistribute it and/or
7734114f8SDavid Howells  * modify it under the terms of the GNU General Public Licence
8734114f8SDavid Howells  * as published by the Free Software Foundation; either version
9734114f8SDavid Howells  * 2 of the Licence, or (at your option) any later version.
10734114f8SDavid Howells  */
11734114f8SDavid Howells 
12734114f8SDavid Howells #define pr_fmt(fmt) "blacklist: "fmt
13734114f8SDavid Howells #include <linux/module.h>
14734114f8SDavid Howells #include <linux/slab.h>
15734114f8SDavid Howells #include <linux/key.h>
16734114f8SDavid Howells #include <linux/key-type.h>
17734114f8SDavid Howells #include <linux/sched.h>
18734114f8SDavid Howells #include <linux/ctype.h>
19734114f8SDavid Howells #include <linux/err.h>
20734114f8SDavid Howells #include <linux/seq_file.h>
21734114f8SDavid Howells #include <keys/system_keyring.h>
22734114f8SDavid Howells #include "blacklist.h"
23734114f8SDavid Howells 
24734114f8SDavid Howells static struct key *blacklist_keyring;
25734114f8SDavid Howells 
26734114f8SDavid Howells /*
27734114f8SDavid Howells  * The description must be a type prefix, a colon and then an even number of
28734114f8SDavid Howells  * hex digits.  The hash is kept in the description.
29734114f8SDavid Howells  */
30734114f8SDavid Howells static int blacklist_vet_description(const char *desc)
31734114f8SDavid Howells {
32734114f8SDavid Howells 	int n = 0;
33734114f8SDavid Howells 
34734114f8SDavid Howells 	if (*desc == ':')
35734114f8SDavid Howells 		return -EINVAL;
36734114f8SDavid Howells 	for (; *desc; desc++)
37734114f8SDavid Howells 		if (*desc == ':')
38734114f8SDavid Howells 			goto found_colon;
39734114f8SDavid Howells 	return -EINVAL;
40734114f8SDavid Howells 
41734114f8SDavid Howells found_colon:
42734114f8SDavid Howells 	desc++;
43734114f8SDavid Howells 	for (; *desc; desc++) {
44734114f8SDavid Howells 		if (!isxdigit(*desc))
45734114f8SDavid Howells 			return -EINVAL;
46734114f8SDavid Howells 		n++;
47734114f8SDavid Howells 	}
48734114f8SDavid Howells 
49734114f8SDavid Howells 	if (n == 0 || n & 1)
50734114f8SDavid Howells 		return -EINVAL;
51734114f8SDavid Howells 	return 0;
52734114f8SDavid Howells }
53734114f8SDavid Howells 
54734114f8SDavid Howells /*
55734114f8SDavid Howells  * The hash to be blacklisted is expected to be in the description.  There will
56734114f8SDavid Howells  * be no payload.
57734114f8SDavid Howells  */
58734114f8SDavid Howells static int blacklist_preparse(struct key_preparsed_payload *prep)
59734114f8SDavid Howells {
60734114f8SDavid Howells 	if (prep->datalen > 0)
61734114f8SDavid Howells 		return -EINVAL;
62734114f8SDavid Howells 	return 0;
63734114f8SDavid Howells }
64734114f8SDavid Howells 
65734114f8SDavid Howells static void blacklist_free_preparse(struct key_preparsed_payload *prep)
66734114f8SDavid Howells {
67734114f8SDavid Howells }
68734114f8SDavid Howells 
69734114f8SDavid Howells static void blacklist_describe(const struct key *key, struct seq_file *m)
70734114f8SDavid Howells {
71734114f8SDavid Howells 	seq_puts(m, key->description);
72734114f8SDavid Howells }
73734114f8SDavid Howells 
74734114f8SDavid Howells static struct key_type key_type_blacklist = {
75734114f8SDavid Howells 	.name			= "blacklist",
76734114f8SDavid Howells 	.vet_description	= blacklist_vet_description,
77734114f8SDavid Howells 	.preparse		= blacklist_preparse,
78734114f8SDavid Howells 	.free_preparse		= blacklist_free_preparse,
79734114f8SDavid Howells 	.instantiate		= generic_key_instantiate,
80734114f8SDavid Howells 	.describe		= blacklist_describe,
81734114f8SDavid Howells };
82734114f8SDavid Howells 
83734114f8SDavid Howells /**
84734114f8SDavid Howells  * mark_hash_blacklisted - Add a hash to the system blacklist
85734114f8SDavid Howells  * @hash - The hash as a hex string with a type prefix (eg. "tbs:23aa429783")
86734114f8SDavid Howells  */
87734114f8SDavid Howells int mark_hash_blacklisted(const char *hash)
88734114f8SDavid Howells {
89734114f8SDavid Howells 	key_ref_t key;
90734114f8SDavid Howells 
91734114f8SDavid Howells 	key = key_create_or_update(make_key_ref(blacklist_keyring, true),
92734114f8SDavid Howells 				   "blacklist",
93734114f8SDavid Howells 				   hash,
94734114f8SDavid Howells 				   NULL,
95734114f8SDavid Howells 				   0,
96734114f8SDavid Howells 				   ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
97734114f8SDavid Howells 				    KEY_USR_VIEW),
98734114f8SDavid Howells 				   KEY_ALLOC_NOT_IN_QUOTA |
99734114f8SDavid Howells 				   KEY_ALLOC_BUILT_IN);
100734114f8SDavid Howells 	if (IS_ERR(key)) {
101734114f8SDavid Howells 		pr_err("Problem blacklisting hash (%ld)\n", PTR_ERR(key));
102734114f8SDavid Howells 		return PTR_ERR(key);
103734114f8SDavid Howells 	}
104734114f8SDavid Howells 	return 0;
105734114f8SDavid Howells }
106734114f8SDavid Howells 
107734114f8SDavid Howells /**
108734114f8SDavid Howells  * is_hash_blacklisted - Determine if a hash is blacklisted
109734114f8SDavid Howells  * @hash: The hash to be checked as a binary blob
110734114f8SDavid Howells  * @hash_len: The length of the binary hash
111734114f8SDavid Howells  * @type: Type of hash
112734114f8SDavid Howells  */
113734114f8SDavid Howells int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
114734114f8SDavid Howells {
115734114f8SDavid Howells 	key_ref_t kref;
116734114f8SDavid Howells 	size_t type_len = strlen(type);
117734114f8SDavid Howells 	char *buffer, *p;
118734114f8SDavid Howells 	int ret = 0;
119734114f8SDavid Howells 
120734114f8SDavid Howells 	buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
121734114f8SDavid Howells 	if (!buffer)
122734114f8SDavid Howells 		return -ENOMEM;
123734114f8SDavid Howells 	p = memcpy(buffer, type, type_len);
124734114f8SDavid Howells 	p += type_len;
125734114f8SDavid Howells 	*p++ = ':';
126734114f8SDavid Howells 	bin2hex(p, hash, hash_len);
127734114f8SDavid Howells 	p += hash_len * 2;
128734114f8SDavid Howells 	*p = 0;
129734114f8SDavid Howells 
130734114f8SDavid Howells 	kref = keyring_search(make_key_ref(blacklist_keyring, true),
131734114f8SDavid Howells 			      &key_type_blacklist, buffer);
132734114f8SDavid Howells 	if (!IS_ERR(kref)) {
133734114f8SDavid Howells 		key_ref_put(kref);
134734114f8SDavid Howells 		ret = -EKEYREJECTED;
135734114f8SDavid Howells 	}
136734114f8SDavid Howells 
137734114f8SDavid Howells 	kfree(buffer);
138734114f8SDavid Howells 	return ret;
139734114f8SDavid Howells }
140734114f8SDavid Howells EXPORT_SYMBOL_GPL(is_hash_blacklisted);
141734114f8SDavid Howells 
142734114f8SDavid Howells /*
1436e7c2b4dSMasahiro Yamada  * Initialise the blacklist
144734114f8SDavid Howells  */
145734114f8SDavid Howells static int __init blacklist_init(void)
146734114f8SDavid Howells {
147734114f8SDavid Howells 	const char *const *bl;
148734114f8SDavid Howells 
149734114f8SDavid Howells 	if (register_key_type(&key_type_blacklist) < 0)
150734114f8SDavid Howells 		panic("Can't allocate system blacklist key type\n");
151734114f8SDavid Howells 
152734114f8SDavid Howells 	blacklist_keyring =
153734114f8SDavid Howells 		keyring_alloc(".blacklist",
154734114f8SDavid Howells 			      KUIDT_INIT(0), KGIDT_INIT(0),
155734114f8SDavid Howells 			      current_cred(),
156734114f8SDavid Howells 			      (KEY_POS_ALL & ~KEY_POS_SETATTR) |
157734114f8SDavid Howells 			      KEY_USR_VIEW | KEY_USR_READ |
158734114f8SDavid Howells 			      KEY_USR_SEARCH,
159734114f8SDavid Howells 			      KEY_ALLOC_NOT_IN_QUOTA |
160734114f8SDavid Howells 			      KEY_FLAG_KEEP,
161734114f8SDavid Howells 			      NULL, NULL);
162734114f8SDavid Howells 	if (IS_ERR(blacklist_keyring))
163734114f8SDavid Howells 		panic("Can't allocate system blacklist keyring\n");
164734114f8SDavid Howells 
165734114f8SDavid Howells 	for (bl = blacklist_hashes; *bl; bl++)
166734114f8SDavid Howells 		if (mark_hash_blacklisted(*bl) < 0)
167734114f8SDavid Howells 			pr_err("- blacklisting failed\n");
168734114f8SDavid Howells 	return 0;
169734114f8SDavid Howells }
170734114f8SDavid Howells 
171734114f8SDavid Howells /*
172734114f8SDavid Howells  * Must be initialised before we try and load the keys into the keyring.
173734114f8SDavid Howells  */
174734114f8SDavid Howells device_initcall(blacklist_init);
175