xref: /optee_os/ta/pkcs11/src/sanitize_object.c (revision a1d5c81f8834a9d2c6f4372cce2e59e70e709121)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2017-2020, Linaro Limited
4  */
5 
6 #include <bitstring.h>
7 #include <pkcs11_ta.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <util.h>
11 #include <tee_internal_api.h>
12 #include <tee_internal_api_extensions.h>
13 #include <trace.h>
14 
15 #include "attributes.h"
16 #include "pkcs11_helpers.h"
17 #include "sanitize_object.h"
18 #include "serializer.h"
19 #include "token_capabilities.h"
20 
21 /*
22  * Functions to generate a serialized object.
23  * References are pointers to struct serializer.
24  */
25 
26 bool sanitize_consistent_class_and_type(struct obj_attrs *attrs)
27 {
28 	switch (get_class(attrs)) {
29 	case PKCS11_CKO_DATA:
30 		return true;
31 	case PKCS11_CKO_SECRET_KEY:
32 		return key_type_is_symm_key(get_key_type(attrs));
33 	case PKCS11_CKO_MECHANISM:
34 		return mechanism_is_valid(get_mechanism_type(attrs));
35 	case PKCS11_CKO_PUBLIC_KEY:
36 	case PKCS11_CKO_PRIVATE_KEY:
37 		return key_type_is_asymm_key(get_key_type(attrs));
38 	case PKCS11_CKO_OTP_KEY:
39 	case PKCS11_CKO_CERTIFICATE:
40 	case PKCS11_CKO_DOMAIN_PARAMETERS:
41 	case PKCS11_CKO_HW_FEATURE:
42 	default:
43 		return false;
44 	}
45 
46 	return false;
47 }
48 
49 static enum pkcs11_rc read_attr_advance(void *buf, size_t blen, size_t *pos,
50 					struct pkcs11_attribute_head *attr,
51 					void **data)
52 {
53 	uint8_t *b = buf;
54 	size_t data_pos = 0;
55 	size_t next_pos = 0;
56 
57 	if (ADD_OVERFLOW(*pos, sizeof(*attr), &data_pos) || data_pos > blen)
58 		return PKCS11_CKR_FUNCTION_FAILED;
59 	TEE_MemMove(attr, b + *pos, sizeof(*attr));
60 
61 	if (ADD_OVERFLOW(data_pos, attr->size, &next_pos) || next_pos > blen)
62 		return PKCS11_CKR_FUNCTION_FAILED;
63 
64 	*data = b + data_pos;
65 	*pos = next_pos;
66 
67 	return PKCS11_CKR_OK;
68 }
69 
70 /* Sanitize class/type in a client attribute list */
71 static enum pkcs11_rc sanitize_class_and_type(struct obj_attrs **dst, void *src,
72 					      size_t src_size)
73 {
74 	uint32_t class_found = PKCS11_CKO_UNDEFINED_ID;
75 	size_t pos = sizeof(struct pkcs11_object_head);
76 	struct pkcs11_attribute_head cli_ref = { };
77 	uint32_t type_found = PKCS11_UNDEFINED_ID;
78 	enum pkcs11_rc rc = PKCS11_CKR_OK;
79 	void *data = NULL;
80 
81 	while (pos != src_size) {
82 		rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data);
83 		if (rc)
84 			goto err;
85 
86 		if (cli_ref.id == PKCS11_CKA_CLASS) {
87 			uint32_t class = 0;
88 
89 			if (cli_ref.size != sizeof(class)) {
90 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
91 				goto err;
92 			}
93 
94 			TEE_MemMove(&class, data, sizeof(class));
95 
96 			if (class_found != PKCS11_CKO_UNDEFINED_ID &&
97 			    class_found != class) {
98 				EMSG("Conflicting class value");
99 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
100 				goto err;
101 			}
102 
103 			class_found = class;
104 			continue;
105 		}
106 
107 		/* The attribute is a type-in-class */
108 		if (pkcs11_attr_is_type(cli_ref.id)) {
109 			uint32_t type = 0;
110 
111 			if (cli_ref.size != sizeof(type)) {
112 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
113 				goto err;
114 			}
115 
116 			TEE_MemMove(&type, data, sizeof(type));
117 
118 			if (type_found != PKCS11_CKK_UNDEFINED_ID &&
119 			    type_found != type) {
120 				EMSG("Conflicting type-in-class value");
121 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
122 				goto err;
123 			}
124 
125 			type_found = type;
126 		}
127 	}
128 
129 	if (class_found != PKCS11_CKO_UNDEFINED_ID) {
130 		rc = add_attribute(dst, PKCS11_CKA_CLASS,
131 				   &class_found, sizeof(class_found));
132 		if (rc)
133 			return rc;
134 	}
135 
136 	if (type_found != PKCS11_UNDEFINED_ID) {
137 		rc = add_attribute(dst, PKCS11_CKA_KEY_TYPE,
138 				   &type_found, sizeof(type_found));
139 		if (rc)
140 			return rc;
141 	}
142 
143 	return PKCS11_CKR_OK;
144 
145 err:
146 	trace_attributes_from_api_head("bad-template", src, src_size);
147 
148 	return rc;
149 }
150 
151 static enum pkcs11_rc sanitize_boolprops(struct obj_attrs **dst, void *src,
152 					 size_t src_size)
153 {
154 	bitstr_t bit_decl(seen_attrs, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 };
155 	bitstr_t bit_decl(boolprops, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 };
156 	size_t pos = sizeof(struct pkcs11_object_head);
157 	struct pkcs11_attribute_head cli_ref = { };
158 	enum pkcs11_rc rc = PKCS11_CKR_OK;
159 	bool value = false;
160 	void *data = NULL;
161 	int idx = 0;
162 
163 	/*
164 	 * We're keeping track of seen boolean attributes in the bitstring
165 	 * seen_attrs. The bitstring boolprops holds the recorded value
166 	 * once seen_attrs has been updated.
167 	 */
168 
169 	while (pos != src_size) {
170 		rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data);
171 		if (rc)
172 			return rc;
173 
174 		idx = pkcs11_attr2boolprop_shift(cli_ref.id);
175 		if (idx < 0)
176 			continue; /* skipping non-boolean attributes */
177 
178 		if (idx >= PKCS11_BOOLPROPS_MAX_COUNT ||
179 		    cli_ref.size != sizeof(uint8_t))
180 			return PKCS11_CKR_FUNCTION_FAILED;
181 
182 		value = *(uint8_t *)data;
183 
184 		/*
185 		 * If this attribute has already been seen, check that it
186 		 * still holds the same value as last time.
187 		 */
188 		if (bit_test(seen_attrs, idx) &&
189 		    value != (bool)bit_test(boolprops, idx))
190 			return PKCS11_CKR_TEMPLATE_INCONSISTENT;
191 
192 		if (value)
193 			bit_set(boolprops, idx);
194 
195 		if (!bit_test(seen_attrs, idx)) {
196 			uint8_t pkcs11_bool = value;
197 
198 			rc = add_attribute(dst, cli_ref.id, &pkcs11_bool,
199 					   sizeof(pkcs11_bool));
200 			if (rc)
201 				return rc;
202 		}
203 		bit_set(seen_attrs, idx);
204 	}
205 
206 	return PKCS11_CKR_OK;
207 }
208 
209 static uint32_t sanitize_indirect_attr(struct obj_attrs **dst,
210 				       struct pkcs11_attribute_head *cli_ref,
211 				       char *data)
212 {
213 	struct obj_attrs *obj2 = NULL;
214 	enum pkcs11_rc rc = PKCS11_CKR_OK;
215 	enum pkcs11_class_id class = get_class(*dst);
216 
217 	if (class == PKCS11_CKO_UNDEFINED_ID)
218 		return PKCS11_CKR_GENERAL_ERROR;
219 
220 	/*
221 	 * Serialized attributes: current applicable only to the key
222 	 * templates which are tables of attributes.
223 	 */
224 	switch (cli_ref->id) {
225 	case PKCS11_CKA_WRAP_TEMPLATE:
226 	case PKCS11_CKA_UNWRAP_TEMPLATE:
227 	case PKCS11_CKA_DERIVE_TEMPLATE:
228 		break;
229 	default:
230 		return PKCS11_RV_NOT_FOUND;
231 	}
232 	/* Such attributes are expected only for keys (and vendor defined) */
233 	if (pkcs11_attr_class_is_key(class))
234 		return PKCS11_CKR_TEMPLATE_INCONSISTENT;
235 
236 	rc = init_attributes_head(&obj2);
237 	if (rc)
238 		return rc;
239 
240 	/* Build a new serial object while sanitizing the attributes list */
241 	rc = sanitize_client_object(&obj2, data, cli_ref->size);
242 	if (rc)
243 		goto out;
244 
245 	rc = add_attribute(dst, cli_ref->id, obj2,
246 			   sizeof(*obj2) + obj2->attrs_size);
247 out:
248 	TEE_Free(obj2);
249 	return rc;
250 }
251 
252 enum pkcs11_rc sanitize_client_object(struct obj_attrs **dst, void *src,
253 				      size_t size)
254 {
255 	struct pkcs11_attribute_head cli_ref = { };
256 	struct pkcs11_object_head head = { };
257 	enum pkcs11_rc rc = PKCS11_CKR_OK;
258 	size_t pos = sizeof(head);
259 	size_t sz_from_hdr = 0;
260 	void *data = NULL;
261 
262 	if (size < sizeof(head))
263 		return PKCS11_CKR_ARGUMENTS_BAD;
264 
265 	TEE_MemMove(&head, src, sizeof(head));
266 
267 	if (ADD_OVERFLOW(sizeof(head), head.attrs_size, &sz_from_hdr) ||
268 	    size < sz_from_hdr)
269 		return PKCS11_CKR_ARGUMENTS_BAD;
270 
271 	rc = init_attributes_head(dst);
272 	if (rc)
273 		return rc;
274 
275 	rc = sanitize_class_and_type(dst, src, sz_from_hdr);
276 	if (rc)
277 		return rc;
278 
279 	rc = sanitize_boolprops(dst, src, sz_from_hdr);
280 	if (rc)
281 		return rc;
282 
283 	while (pos != sz_from_hdr) {
284 		rc = read_attr_advance(src, sz_from_hdr, &pos, &cli_ref, &data);
285 		if (rc)
286 			return rc;
287 
288 		if (cli_ref.id == PKCS11_CKA_CLASS ||
289 		    pkcs11_attr_is_type(cli_ref.id) ||
290 		    pkcs11_attr_is_boolean(cli_ref.id))
291 			continue;
292 
293 		rc = sanitize_indirect_attr(dst, &cli_ref, data);
294 		if (rc == PKCS11_CKR_OK)
295 			continue;
296 		if (rc != PKCS11_RV_NOT_FOUND)
297 			return rc;
298 
299 		if (!valid_pkcs11_attribute_id(cli_ref.id, cli_ref.size)) {
300 			EMSG("Invalid attribute id %#"PRIx32, cli_ref.id);
301 			return PKCS11_CKR_TEMPLATE_INCONSISTENT;
302 		}
303 
304 		rc = add_attribute(dst, cli_ref.id, data, cli_ref.size);
305 		if (rc)
306 			return rc;
307 	}
308 
309 	return rc;
310 }
311 
312 /*
313  * Debug: dump object attribute array to output trace
314  */
315 
316 static void __trace_attributes(char *prefix, void *src, void *end)
317 {
318 	size_t next = 0;
319 	char *prefix2 = NULL;
320 	size_t prefix_len = strlen(prefix);
321 	char *cur = src;
322 
323 	/* append 4 spaces to the prefix plus terminal '\0' */
324 	prefix2 = TEE_Malloc(prefix_len + 1 + 4, TEE_MALLOC_FILL_ZERO);
325 	if (!prefix2)
326 		return;
327 
328 	TEE_MemMove(prefix2, prefix, prefix_len + 1);
329 	TEE_MemFill(prefix2 + prefix_len, ' ', 4);
330 	*(prefix2 + prefix_len + 4) = '\0';
331 
332 	for (; cur < (char *)end; cur += next) {
333 		struct pkcs11_attribute_head pkcs11_ref;
334 		uint8_t data[4] = { 0 };
335 		uint32_t data_u32 = 0;
336 
337 		TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref));
338 		TEE_MemMove(&data[0], cur + sizeof(pkcs11_ref),
339 			    MIN(pkcs11_ref.size, sizeof(data)));
340 		TEE_MemMove(&data_u32, cur + sizeof(pkcs11_ref),
341 			    sizeof(data_u32));
342 
343 		next = sizeof(pkcs11_ref) + pkcs11_ref.size;
344 
345 		DMSG_RAW("%s Attr %s / %s (%#04"PRIx32" %"PRIu32"-byte)",
346 			 prefix, id2str_attr(pkcs11_ref.id),
347 			 id2str_attr_value(pkcs11_ref.id, pkcs11_ref.size,
348 					   cur + sizeof(pkcs11_ref)),
349 			 pkcs11_ref.id, pkcs11_ref.size);
350 
351 		switch (pkcs11_ref.size) {
352 		case 0:
353 			break;
354 		case 1:
355 			DMSG_RAW("%s Attr byte value: %02x", prefix, data[0]);
356 			break;
357 		case 2:
358 			DMSG_RAW("%s Attr byte value: %02x %02x",
359 				 prefix, data[0], data[1]);
360 			break;
361 		case 3:
362 			DMSG_RAW("%s Attr byte value: %02x %02x %02x",
363 				 prefix, data[0], data[1], data[2]);
364 			break;
365 		case 4:
366 			DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x",
367 				 prefix, data[0], data[1], data[2], data[3]);
368 			break;
369 		default:
370 			DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x ...",
371 				 prefix, data[0], data[1], data[2], data[3]);
372 			break;
373 		}
374 
375 		switch (pkcs11_ref.id) {
376 		case PKCS11_CKA_WRAP_TEMPLATE:
377 		case PKCS11_CKA_UNWRAP_TEMPLATE:
378 		case PKCS11_CKA_DERIVE_TEMPLATE:
379 			trace_attributes_from_api_head(prefix2,
380 						       cur + sizeof(pkcs11_ref),
381 						       (char *)end - cur);
382 			break;
383 		default:
384 			break;
385 		}
386 	}
387 
388 	/* Sanity */
389 	if (cur != (char *)end)
390 		EMSG("Warning: unexpected alignment issue");
391 
392 	TEE_Free(prefix2);
393 }
394 
395 void trace_attributes_from_api_head(const char *prefix, void *ref, size_t size)
396 {
397 	struct pkcs11_object_head head = { };
398 	char *pre = NULL;
399 	size_t offset = 0;
400 
401 	TEE_MemMove(&head, ref, sizeof(head));
402 
403 	if (size > sizeof(head) + head.attrs_size) {
404 		EMSG("template overflows client buffer (%zu/%zu)",
405 		     size, sizeof(head) + head.attrs_size);
406 		return;
407 	}
408 
409 	pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO);
410 	if (!pre) {
411 		EMSG("%s: out of memory", prefix);
412 		return;
413 	}
414 	if (prefix)
415 		TEE_MemMove(pre, prefix, strlen(prefix));
416 
417 	DMSG_RAW("%s,--- (serial object) Attributes list --------", pre);
418 	DMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes",
419 		 pre, head.attrs_count, head.attrs_size);
420 
421 	offset = sizeof(head);
422 	pre[prefix ? strlen(prefix) : 0] = '|';
423 	__trace_attributes(pre, (char *)ref + offset,
424 			   (char *)ref + offset + head.attrs_size);
425 
426 	DMSG_RAW("%s`-----------------------", prefix ? prefix : "");
427 
428 	TEE_Free(pre);
429 }
430