xref: /optee_os/ta/pkcs11/src/sanitize_object.c (revision 36bb435f5be571b4b4c0792de1cea85013afa728)
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 					      uint32_t class_hint,
74 					      uint32_t type_hint)
75 {
76 	uint32_t class_found = PKCS11_CKO_UNDEFINED_ID;
77 	size_t pos = sizeof(struct pkcs11_object_head);
78 	struct pkcs11_attribute_head cli_ref = { };
79 	uint32_t type_found = PKCS11_UNDEFINED_ID;
80 	enum pkcs11_rc rc = PKCS11_CKR_OK;
81 	void *data = NULL;
82 
83 	while (pos != src_size) {
84 		rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data);
85 		if (rc)
86 			goto err;
87 
88 		if (cli_ref.id == PKCS11_CKA_CLASS) {
89 			uint32_t class = 0;
90 
91 			if (cli_ref.size != sizeof(class)) {
92 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
93 				goto err;
94 			}
95 
96 			TEE_MemMove(&class, data, sizeof(class));
97 
98 			if (class_found != PKCS11_CKO_UNDEFINED_ID &&
99 			    class_found != class) {
100 				EMSG("Conflicting class value");
101 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
102 				goto err;
103 			}
104 
105 			class_found = class;
106 			continue;
107 		}
108 
109 		/* The attribute is a type-in-class */
110 		if (pkcs11_attr_is_type(cli_ref.id)) {
111 			uint32_t type = 0;
112 
113 			if (cli_ref.size != sizeof(type)) {
114 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
115 				goto err;
116 			}
117 
118 			TEE_MemMove(&type, data, sizeof(type));
119 
120 			if (type_found != PKCS11_CKK_UNDEFINED_ID &&
121 			    type_found != type) {
122 				EMSG("Conflicting type-in-class value");
123 				rc = PKCS11_CKR_TEMPLATE_INCONSISTENT;
124 				goto err;
125 			}
126 
127 			type_found = type;
128 		}
129 	}
130 
131 	if (class_found != PKCS11_CKO_UNDEFINED_ID) {
132 		rc = add_attribute(dst, PKCS11_CKA_CLASS,
133 				   &class_found, sizeof(class_found));
134 		if (rc)
135 			return rc;
136 	} else {
137 		if (class_hint != PKCS11_CKO_UNDEFINED_ID) {
138 			rc = add_attribute(dst, PKCS11_CKA_CLASS,
139 					   &class_hint, sizeof(class_hint));
140 			if (rc)
141 				return rc;
142 		}
143 	}
144 
145 	if (type_found != PKCS11_UNDEFINED_ID) {
146 		rc = add_attribute(dst, PKCS11_CKA_KEY_TYPE,
147 				   &type_found, sizeof(type_found));
148 		if (rc)
149 			return rc;
150 	} else {
151 		if (type_hint != PKCS11_UNDEFINED_ID) {
152 			rc = add_attribute(dst, PKCS11_CKA_KEY_TYPE,
153 					   &type_hint, sizeof(type_hint));
154 			if (rc)
155 				return rc;
156 		}
157 	}
158 
159 	return PKCS11_CKR_OK;
160 
161 err:
162 	trace_attributes_from_api_head("bad-template", src, src_size);
163 
164 	return rc;
165 }
166 
167 static enum pkcs11_rc sanitize_boolprops(struct obj_attrs **dst, void *src,
168 					 size_t src_size)
169 {
170 	bitstr_t bit_decl(seen_attrs, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 };
171 	bitstr_t bit_decl(boolprops, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 };
172 	size_t pos = sizeof(struct pkcs11_object_head);
173 	struct pkcs11_attribute_head cli_ref = { };
174 	enum pkcs11_rc rc = PKCS11_CKR_OK;
175 	bool value = false;
176 	void *data = NULL;
177 	int idx = 0;
178 
179 	/*
180 	 * We're keeping track of seen boolean attributes in the bitstring
181 	 * seen_attrs. The bitstring boolprops holds the recorded value
182 	 * once seen_attrs has been updated.
183 	 */
184 
185 	while (pos != src_size) {
186 		rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data);
187 		if (rc)
188 			return rc;
189 
190 		idx = pkcs11_attr2boolprop_shift(cli_ref.id);
191 		if (idx < 0)
192 			continue; /* skipping non-boolean attributes */
193 
194 		if (idx >= PKCS11_BOOLPROPS_MAX_COUNT ||
195 		    cli_ref.size != sizeof(uint8_t))
196 			return PKCS11_CKR_FUNCTION_FAILED;
197 
198 		value = *(uint8_t *)data;
199 
200 		/*
201 		 * If this attribute has already been seen, check that it
202 		 * still holds the same value as last time.
203 		 */
204 		if (bit_test(seen_attrs, idx) &&
205 		    value != (bool)bit_test(boolprops, idx))
206 			return PKCS11_CKR_TEMPLATE_INCONSISTENT;
207 
208 		if (value)
209 			bit_set(boolprops, idx);
210 
211 		if (!bit_test(seen_attrs, idx)) {
212 			uint8_t pkcs11_bool = value;
213 
214 			rc = add_attribute(dst, cli_ref.id, &pkcs11_bool,
215 					   sizeof(pkcs11_bool));
216 			if (rc)
217 				return rc;
218 		}
219 		bit_set(seen_attrs, idx);
220 	}
221 
222 	return PKCS11_CKR_OK;
223 }
224 
225 static uint32_t sanitize_indirect_attr(struct obj_attrs **dst,
226 				       struct pkcs11_attribute_head *cli_ref,
227 				       char *data)
228 {
229 	struct obj_attrs *obj2 = NULL;
230 	enum pkcs11_rc rc = PKCS11_CKR_OK;
231 	enum pkcs11_class_id class = get_class(*dst);
232 
233 	/*
234 	 * Serialized attributes: current applicable only to the key
235 	 * templates which are tables of attributes.
236 	 */
237 	switch (cli_ref->id) {
238 	case PKCS11_CKA_WRAP_TEMPLATE:
239 	case PKCS11_CKA_UNWRAP_TEMPLATE:
240 	case PKCS11_CKA_DERIVE_TEMPLATE:
241 		break;
242 	default:
243 		return PKCS11_RV_NOT_FOUND;
244 	}
245 
246 	if (class == PKCS11_CKO_UNDEFINED_ID) {
247 		DMSG("Template without CLASS not supported yet");
248 		return PKCS11_CKR_TEMPLATE_INCOMPLETE;
249 	}
250 
251 	/* Such attributes are expected only for keys (and vendor defined) */
252 	if (pkcs11_attr_class_is_key(class))
253 		return PKCS11_CKR_TEMPLATE_INCONSISTENT;
254 
255 	rc = init_attributes_head(&obj2);
256 	if (rc)
257 		return rc;
258 
259 	/* Build a new serial object while sanitizing the attributes list */
260 	rc = sanitize_client_object(&obj2, data, cli_ref->size,
261 				    PKCS11_CKO_UNDEFINED_ID,
262 				    PKCS11_UNDEFINED_ID);
263 	if (rc)
264 		goto out;
265 
266 	rc = add_attribute(dst, cli_ref->id, obj2,
267 			   sizeof(*obj2) + obj2->attrs_size);
268 out:
269 	TEE_Free(obj2);
270 	return rc;
271 }
272 
273 enum pkcs11_rc sanitize_client_object(struct obj_attrs **dst, void *src,
274 				      size_t size, uint32_t class_hint,
275 				      uint32_t type_hint)
276 {
277 	struct pkcs11_attribute_head cli_ref = { };
278 	struct pkcs11_object_head head = { };
279 	enum pkcs11_rc rc = PKCS11_CKR_OK;
280 	size_t pos = sizeof(head);
281 	size_t sz_from_hdr = 0;
282 	void *data = NULL;
283 
284 	if (size < sizeof(head))
285 		return PKCS11_CKR_ARGUMENTS_BAD;
286 
287 	TEE_MemMove(&head, src, sizeof(head));
288 
289 	if (ADD_OVERFLOW(sizeof(head), head.attrs_size, &sz_from_hdr) ||
290 	    size < sz_from_hdr)
291 		return PKCS11_CKR_ARGUMENTS_BAD;
292 
293 	rc = init_attributes_head(dst);
294 	if (rc)
295 		return rc;
296 
297 	rc = sanitize_class_and_type(dst, src, sz_from_hdr, class_hint,
298 				     type_hint);
299 	if (rc)
300 		return rc;
301 
302 	rc = sanitize_boolprops(dst, src, sz_from_hdr);
303 	if (rc)
304 		return rc;
305 
306 	while (pos != sz_from_hdr) {
307 		rc = read_attr_advance(src, sz_from_hdr, &pos, &cli_ref, &data);
308 		if (rc)
309 			return rc;
310 
311 		if (cli_ref.id == PKCS11_CKA_CLASS ||
312 		    pkcs11_attr_is_type(cli_ref.id) ||
313 		    pkcs11_attr_is_boolean(cli_ref.id))
314 			continue;
315 
316 		rc = sanitize_indirect_attr(dst, &cli_ref, data);
317 		if (rc == PKCS11_CKR_OK)
318 			continue;
319 		if (rc != PKCS11_RV_NOT_FOUND)
320 			return rc;
321 
322 		if (!valid_pkcs11_attribute_id(cli_ref.id, cli_ref.size)) {
323 			EMSG("Invalid attribute id %#"PRIx32, cli_ref.id);
324 			return PKCS11_CKR_TEMPLATE_INCONSISTENT;
325 		}
326 
327 		rc = add_attribute(dst, cli_ref.id, data, cli_ref.size);
328 		if (rc)
329 			return rc;
330 	}
331 
332 	return rc;
333 }
334 
335 /*
336  * Debug: dump object attribute array to output trace
337  */
338 
339 static void __trace_attributes(char *prefix, void *src, void *end)
340 {
341 	size_t next = 0;
342 	char *prefix2 = NULL;
343 	size_t prefix_len = strlen(prefix);
344 	char *cur = src;
345 
346 	/* append 4 spaces to the prefix plus terminal '\0' */
347 	prefix2 = TEE_Malloc(prefix_len + 1 + 4, TEE_MALLOC_FILL_ZERO);
348 	if (!prefix2)
349 		return;
350 
351 	TEE_MemMove(prefix2, prefix, prefix_len + 1);
352 	TEE_MemFill(prefix2 + prefix_len, ' ', 4);
353 	*(prefix2 + prefix_len + 4) = '\0';
354 
355 	for (; cur < (char *)end; cur += next) {
356 		struct pkcs11_attribute_head pkcs11_ref;
357 		uint8_t data[4] = { 0 };
358 		uint32_t data_u32 = 0;
359 
360 		TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref));
361 		TEE_MemMove(&data[0], cur + sizeof(pkcs11_ref),
362 			    MIN(pkcs11_ref.size, sizeof(data)));
363 		TEE_MemMove(&data_u32, cur + sizeof(pkcs11_ref),
364 			    sizeof(data_u32));
365 
366 		next = sizeof(pkcs11_ref) + pkcs11_ref.size;
367 
368 		DMSG_RAW("%s Attr %s / %s (%#04"PRIx32" %"PRIu32"-byte)",
369 			 prefix, id2str_attr(pkcs11_ref.id),
370 			 id2str_attr_value(pkcs11_ref.id, pkcs11_ref.size,
371 					   cur + sizeof(pkcs11_ref)),
372 			 pkcs11_ref.id, pkcs11_ref.size);
373 
374 		switch (pkcs11_ref.size) {
375 		case 0:
376 			break;
377 		case 1:
378 			DMSG_RAW("%s Attr byte value: %02x", prefix, data[0]);
379 			break;
380 		case 2:
381 			DMSG_RAW("%s Attr byte value: %02x %02x",
382 				 prefix, data[0], data[1]);
383 			break;
384 		case 3:
385 			DMSG_RAW("%s Attr byte value: %02x %02x %02x",
386 				 prefix, data[0], data[1], data[2]);
387 			break;
388 		case 4:
389 			DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x",
390 				 prefix, data[0], data[1], data[2], data[3]);
391 			break;
392 		default:
393 			DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x ...",
394 				 prefix, data[0], data[1], data[2], data[3]);
395 			break;
396 		}
397 
398 		switch (pkcs11_ref.id) {
399 		case PKCS11_CKA_WRAP_TEMPLATE:
400 		case PKCS11_CKA_UNWRAP_TEMPLATE:
401 		case PKCS11_CKA_DERIVE_TEMPLATE:
402 			trace_attributes_from_api_head(prefix2,
403 						       cur + sizeof(pkcs11_ref),
404 						       (char *)end - cur);
405 			break;
406 		default:
407 			break;
408 		}
409 	}
410 
411 	/* Sanity */
412 	if (cur != (char *)end)
413 		EMSG("Warning: unexpected alignment issue");
414 
415 	TEE_Free(prefix2);
416 }
417 
418 void trace_attributes_from_api_head(const char *prefix, void *ref, size_t size)
419 {
420 	struct pkcs11_object_head head = { };
421 	char *pre = NULL;
422 	size_t offset = 0;
423 
424 	TEE_MemMove(&head, ref, sizeof(head));
425 
426 	if (size > sizeof(head) + head.attrs_size) {
427 		EMSG("template overflows client buffer (%zu/%zu)",
428 		     size, sizeof(head) + head.attrs_size);
429 		return;
430 	}
431 
432 	pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO);
433 	if (!pre) {
434 		EMSG("%s: out of memory", prefix);
435 		return;
436 	}
437 	if (prefix)
438 		TEE_MemMove(pre, prefix, strlen(prefix));
439 
440 	DMSG_RAW("%s,--- (serial object) Attributes list --------", pre);
441 	DMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes",
442 		 pre, head.attrs_count, head.attrs_size);
443 
444 	offset = sizeof(head);
445 	pre[prefix ? strlen(prefix) : 0] = '|';
446 	__trace_attributes(pre, (char *)ref + offset,
447 			   (char *)ref + offset + head.attrs_size);
448 
449 	DMSG_RAW("%s`-----------------------", prefix ? prefix : "");
450 
451 	TEE_Free(pre);
452 }
453