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