xref: /optee_os/ta/pkcs11/src/attributes.c (revision 6cfa381e534b362afbd103f526b132048e54ba47)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2017-2020, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <compiler.h>
8 #include <pkcs11_ta.h>
9 #include <stddef.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <string_ext.h>
13 #include <tee_internal_api.h>
14 #include <tee_internal_api_extensions.h>
15 #include <trace.h>
16 #include <util.h>
17 
18 #include "attributes.h"
19 #include "pkcs11_helpers.h"
20 #include "serializer.h"
21 
22 enum pkcs11_rc init_attributes_head(struct obj_attrs **head)
23 {
24 	*head = TEE_Malloc(sizeof(**head), TEE_MALLOC_FILL_ZERO);
25 	if (!*head)
26 		return PKCS11_CKR_DEVICE_MEMORY;
27 
28 	return PKCS11_CKR_OK;
29 }
30 
31 enum pkcs11_rc add_attribute(struct obj_attrs **head, uint32_t attribute,
32 			     void *data, size_t size)
33 {
34 	size_t buf_len = sizeof(struct obj_attrs) + (*head)->attrs_size;
35 	char **bstart = (void *)head;
36 	enum pkcs11_rc rc = PKCS11_CKR_OK;
37 	uint32_t data32 = 0;
38 
39 	data32 = attribute;
40 	rc = serialize(bstart, &buf_len, &data32, sizeof(uint32_t));
41 	if (rc)
42 		return rc;
43 
44 	data32 = size;
45 	rc = serialize(bstart, &buf_len, &data32, sizeof(uint32_t));
46 	if (rc)
47 		return rc;
48 
49 	rc = serialize(bstart, &buf_len, data, size);
50 	if (rc)
51 		return rc;
52 
53 	/* Alloced buffer is always well aligned */
54 	head = (void *)bstart;
55 	(*head)->attrs_size += 2 * sizeof(uint32_t) + size;
56 	(*head)->attrs_count++;
57 
58 	return rc;
59 }
60 
61 static enum pkcs11_rc _remove_attribute(struct obj_attrs **head,
62 					uint32_t attribute, bool empty)
63 {
64 	struct obj_attrs *h = *head;
65 	char *cur = NULL;
66 	char *end = NULL;
67 	size_t next_off = 0;
68 
69 	/* Let's find the target attribute */
70 	cur = (char *)h + sizeof(struct obj_attrs);
71 	end = cur + h->attrs_size;
72 	for (; cur < end; cur += next_off) {
73 		struct pkcs11_attribute_head pkcs11_ref = { };
74 
75 		TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref));
76 		next_off = sizeof(pkcs11_ref) + pkcs11_ref.size;
77 
78 		if (pkcs11_ref.id != attribute)
79 			continue;
80 
81 		if (empty && pkcs11_ref.size)
82 			return PKCS11_CKR_FUNCTION_FAILED;
83 
84 		TEE_MemMove(cur, cur + next_off, end - (cur + next_off));
85 
86 		h->attrs_count--;
87 		h->attrs_size -= next_off;
88 		end -= next_off;
89 		next_off = 0;
90 
91 		return PKCS11_CKR_OK;
92 	}
93 
94 	DMSG("Attribute %s (%#x) not found", id2str_attr(attribute), attribute);
95 	return PKCS11_RV_NOT_FOUND;
96 }
97 
98 enum pkcs11_rc remove_empty_attribute(struct obj_attrs **head,
99 				      uint32_t attribute)
100 {
101 	return _remove_attribute(head, attribute, true /* empty */);
102 }
103 
104 void get_attribute_ptrs(struct obj_attrs *head, uint32_t attribute,
105 			void **attr, uint32_t *attr_size, size_t *count)
106 {
107 	char *cur = (char *)head + sizeof(struct obj_attrs);
108 	char *end = cur + head->attrs_size;
109 	size_t next_off = 0;
110 	size_t max_found = *count;
111 	size_t found = 0;
112 	void **attr_ptr = attr;
113 	uint32_t *attr_size_ptr = attr_size;
114 
115 	for (; cur < end; cur += next_off) {
116 		/* Structure aligned copy of the pkcs11_ref in the object */
117 		struct pkcs11_attribute_head pkcs11_ref = { };
118 
119 		TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref));
120 		next_off = sizeof(pkcs11_ref) + pkcs11_ref.size;
121 
122 		if (pkcs11_ref.id != attribute)
123 			continue;
124 
125 		found++;
126 
127 		if (!max_found)
128 			continue;	/* only count matching attributes */
129 
130 		if (attr) {
131 			if (pkcs11_ref.size)
132 				*attr_ptr++ = cur + sizeof(pkcs11_ref);
133 			else
134 				*attr_ptr++ = NULL;
135 		}
136 
137 		if (attr_size)
138 			*attr_size_ptr++ = pkcs11_ref.size;
139 
140 		if (found == max_found)
141 			break;
142 	}
143 
144 	/* Sanity */
145 	if (cur > end) {
146 		DMSG("Exceeding serial object length");
147 		TEE_Panic(0);
148 	}
149 
150 	*count = found;
151 }
152 
153 enum pkcs11_rc get_attribute_ptr(struct obj_attrs *head, uint32_t attribute,
154 				 void **attr_ptr, uint32_t *attr_size)
155 {
156 	size_t count = 1;
157 
158 	get_attribute_ptrs(head, attribute, attr_ptr, attr_size, &count);
159 
160 	if (!count)
161 		return PKCS11_RV_NOT_FOUND;
162 
163 	if (count != 1)
164 		return PKCS11_CKR_GENERAL_ERROR;
165 
166 	return PKCS11_CKR_OK;
167 }
168 
169 enum pkcs11_rc get_attribute(struct obj_attrs *head, uint32_t attribute,
170 			     void *attr, uint32_t *attr_size)
171 {
172 	enum pkcs11_rc rc = PKCS11_CKR_OK;
173 	void *attr_ptr = NULL;
174 	uint32_t size = 0;
175 
176 	rc = get_attribute_ptr(head, attribute, &attr_ptr, &size);
177 	if (rc)
178 		return rc;
179 
180 	if (attr_size && *attr_size < size) {
181 		*attr_size = size;
182 		/* This reuses buffer-to-small for any bad size matching */
183 		return PKCS11_CKR_BUFFER_TOO_SMALL;
184 	}
185 
186 	if (attr)
187 		TEE_MemMove(attr, attr_ptr, size);
188 
189 	if (attr_size)
190 		*attr_size = size;
191 
192 	return PKCS11_CKR_OK;
193 }
194 
195 enum pkcs11_rc set_attribute(struct obj_attrs **head, uint32_t attribute,
196 			     void *data, size_t size)
197 {
198 	enum pkcs11_rc rc = PKCS11_CKR_OK;
199 
200 	rc = _remove_attribute(head, attribute, false);
201 	if (rc != PKCS11_CKR_OK && rc != PKCS11_RV_NOT_FOUND)
202 		return rc;
203 
204 	return add_attribute(head, attribute, data, size);
205 }
206 
207 enum pkcs11_rc modify_attributes_list(struct obj_attrs **dst,
208 				      struct obj_attrs *head)
209 {
210 	char *cur = (char *)head + sizeof(struct obj_attrs);
211 	char *end = cur + head->attrs_size;
212 	size_t len = 0;
213 	enum pkcs11_rc rc = PKCS11_CKR_OK;
214 
215 	for (; cur < end; cur += len) {
216 		struct pkcs11_attribute_head *cli_ref = (void *)cur;
217 		/* Structure aligned copy of the pkcs11_ref in the object */
218 		struct pkcs11_attribute_head cli_head = { };
219 
220 		TEE_MemMove(&cli_head, cur, sizeof(cli_head));
221 		len = sizeof(cli_head) + cli_head.size;
222 
223 		rc = set_attribute(dst, cli_head.id,
224 				   cli_head.size ? cli_ref->data : NULL,
225 				   cli_head.size);
226 		if (rc)
227 			return rc;
228 	}
229 
230 	return PKCS11_CKR_OK;
231 }
232 
233 bool get_bool(struct obj_attrs *head, uint32_t attribute)
234 {
235 	enum pkcs11_rc rc = PKCS11_CKR_OK;
236 	uint8_t bbool = 0;
237 	uint32_t size = sizeof(bbool);
238 
239 	rc = get_attribute(head, attribute, &bbool, &size);
240 
241 	if (rc == PKCS11_RV_NOT_FOUND)
242 		return false;
243 
244 	assert(rc == PKCS11_CKR_OK);
245 	return bbool;
246 }
247 
248 bool attributes_match_reference(struct obj_attrs *candidate,
249 				struct obj_attrs *ref)
250 {
251 	size_t count = ref->attrs_count;
252 	unsigned char *ref_attr = ref->attrs;
253 	uint32_t rc = PKCS11_CKR_GENERAL_ERROR;
254 
255 	if (!ref->attrs_count) {
256 		DMSG("Empty reference match all");
257 		return true;
258 	}
259 
260 	for (count = 0; count < ref->attrs_count; count++) {
261 		struct pkcs11_attribute_head pkcs11_ref = { };
262 		void *value = NULL;
263 		uint32_t size = 0;
264 
265 		TEE_MemMove(&pkcs11_ref, ref_attr, sizeof(pkcs11_ref));
266 
267 		/* Hidden attributes cannot be matched */
268 		if (attribute_is_hidden(&pkcs11_ref))
269 			return false;
270 
271 		rc = get_attribute_ptr(candidate, pkcs11_ref.id, &value, &size);
272 
273 		if (rc || !value || size != pkcs11_ref.size ||
274 		    TEE_MemCompare(ref_attr + sizeof(pkcs11_ref), value, size))
275 			return false;
276 
277 		ref_attr += sizeof(pkcs11_ref) + pkcs11_ref.size;
278 	}
279 
280 	return true;
281 }
282 
283 enum pkcs11_rc attributes_match_add_reference(struct obj_attrs **head,
284 					      struct obj_attrs *ref)
285 {
286 	size_t count = ref->attrs_count;
287 	unsigned char *ref_attr = ref->attrs;
288 	enum pkcs11_rc rc = PKCS11_CKR_OK;
289 
290 	if (!ref->attrs_count)
291 		return PKCS11_CKR_OK;
292 
293 	for (count = 0; count < ref->attrs_count; count++) {
294 		struct pkcs11_attribute_head pkcs11_ref = { };
295 		void *value = NULL;
296 		uint32_t size = 0;
297 
298 		TEE_MemMove(&pkcs11_ref, ref_attr, sizeof(pkcs11_ref));
299 
300 		rc = get_attribute_ptr(*head, pkcs11_ref.id, &value, &size);
301 		if (rc == PKCS11_RV_NOT_FOUND) {
302 			rc = add_attribute(head, pkcs11_ref.id,
303 					   ref_attr + sizeof(pkcs11_ref),
304 					   pkcs11_ref.size);
305 			if (rc)
306 				return rc;
307 		} else {
308 			if (rc || !value || size != pkcs11_ref.size ||
309 			    TEE_MemCompare(ref_attr + sizeof(pkcs11_ref), value,
310 					   size))
311 				return PKCS11_CKR_TEMPLATE_INCONSISTENT;
312 		}
313 
314 		ref_attr += sizeof(pkcs11_ref) + pkcs11_ref.size;
315 	}
316 
317 	return PKCS11_CKR_OK;
318 }
319 
320 #if CFG_TEE_TA_LOG_LEVEL > 0
321 /*
322  * Debug: dump CK attribute array to output trace
323  */
324 #define ATTR_TRACE_FMT	"%s attr %s / %s\t(0x%04"PRIx32" %"PRIu32"-byte"
325 #define ATTR_FMT_0BYTE	ATTR_TRACE_FMT ")"
326 #define ATTR_FMT_1BYTE	ATTR_TRACE_FMT ": %02x)"
327 #define ATTR_FMT_2BYTE	ATTR_TRACE_FMT ": %02x %02x)"
328 #define ATTR_FMT_3BYTE	ATTR_TRACE_FMT ": %02x %02x %02x)"
329 #define ATTR_FMT_4BYTE	ATTR_TRACE_FMT ": %02x %02x %02x %02x)"
330 #define ATTR_FMT_ARRAY	ATTR_TRACE_FMT ": %02x %02x %02x %02x ...)"
331 
332 static void __trace_attributes(char *prefix, void *src, void *end)
333 {
334 	size_t next_off = 0;
335 	char *prefix2 = NULL;
336 	size_t prefix_len = strlen(prefix);
337 	char *cur = src;
338 
339 	/* append 4 spaces to the prefix plus terminal '\0' */
340 	prefix2 = TEE_Malloc(prefix_len + 1 + 4, TEE_MALLOC_FILL_ZERO);
341 	if (!prefix2)
342 		return;
343 
344 	TEE_MemMove(prefix2, prefix, prefix_len + 1);
345 	TEE_MemFill(prefix2 + prefix_len, ' ', 4);
346 	*(prefix2 + prefix_len + 4) = '\0';
347 
348 	for (; cur < (char *)end; cur += next_off) {
349 		struct pkcs11_attribute_head pkcs11_ref = { };
350 		uint8_t data[4] = { 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 
356 		next_off = sizeof(pkcs11_ref) + pkcs11_ref.size;
357 
358 		switch (pkcs11_ref.size) {
359 		case 0:
360 			IMSG_RAW(ATTR_FMT_0BYTE,
361 				 prefix, id2str_attr(pkcs11_ref.id), "*",
362 				 pkcs11_ref.id, pkcs11_ref.size);
363 			break;
364 		case 1:
365 			IMSG_RAW(ATTR_FMT_1BYTE,
366 				 prefix, id2str_attr(pkcs11_ref.id),
367 				 id2str_attr_value(pkcs11_ref.id,
368 						   pkcs11_ref.size,
369 						   cur + sizeof(pkcs11_ref)),
370 				 pkcs11_ref.id, pkcs11_ref.size, data[0]);
371 			break;
372 		case 2:
373 			IMSG_RAW(ATTR_FMT_2BYTE,
374 				 prefix, id2str_attr(pkcs11_ref.id),
375 				 id2str_attr_value(pkcs11_ref.id,
376 						   pkcs11_ref.size,
377 						   cur + sizeof(pkcs11_ref)),
378 				 pkcs11_ref.id, pkcs11_ref.size, data[0],
379 				 data[1]);
380 			break;
381 		case 3:
382 			IMSG_RAW(ATTR_FMT_3BYTE,
383 				 prefix, id2str_attr(pkcs11_ref.id),
384 				 id2str_attr_value(pkcs11_ref.id,
385 						   pkcs11_ref.size,
386 						   cur + sizeof(pkcs11_ref)),
387 				 pkcs11_ref.id, pkcs11_ref.size,
388 				 data[0], data[1], data[2]);
389 			break;
390 		case 4:
391 			IMSG_RAW(ATTR_FMT_4BYTE,
392 				 prefix, id2str_attr(pkcs11_ref.id),
393 				 id2str_attr_value(pkcs11_ref.id,
394 						   pkcs11_ref.size,
395 						   cur + sizeof(pkcs11_ref)),
396 				 pkcs11_ref.id, pkcs11_ref.size,
397 				 data[0], data[1], data[2], data[3]);
398 			break;
399 		default:
400 			IMSG_RAW(ATTR_FMT_ARRAY,
401 				 prefix, id2str_attr(pkcs11_ref.id),
402 				 id2str_attr_value(pkcs11_ref.id,
403 						   pkcs11_ref.size,
404 						   cur + sizeof(pkcs11_ref)),
405 				 pkcs11_ref.id, pkcs11_ref.size,
406 				 data[0], data[1], data[2], data[3]);
407 			break;
408 		}
409 
410 		switch (pkcs11_ref.id) {
411 		case PKCS11_CKA_WRAP_TEMPLATE:
412 		case PKCS11_CKA_UNWRAP_TEMPLATE:
413 		case PKCS11_CKA_DERIVE_TEMPLATE:
414 			if (pkcs11_ref.size)
415 				trace_attributes(prefix2,
416 						 cur + sizeof(pkcs11_ref));
417 			break;
418 		default:
419 			break;
420 		}
421 	}
422 
423 	/* Sanity */
424 	if (cur != end)
425 		EMSG("Warning: unexpected alignment in object attributes");
426 
427 	TEE_Free(prefix2);
428 }
429 
430 void trace_attributes(const char *prefix, void *ref)
431 {
432 	struct obj_attrs head = { };
433 	char *pre = NULL;
434 
435 	TEE_MemMove(&head, ref, sizeof(head));
436 
437 	if (!head.attrs_count)
438 		return;
439 
440 	pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO);
441 	if (!pre) {
442 		EMSG("%s: out of memory", prefix);
443 		return;
444 	}
445 
446 	if (prefix)
447 		TEE_MemMove(pre, prefix, strlen(prefix));
448 
449 	IMSG_RAW("%s,--- (serial object) Attributes list --------", pre);
450 	IMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes",
451 		 pre, head.attrs_count, head.attrs_size);
452 
453 	pre[prefix ? strlen(prefix) : 0] = '|';
454 	__trace_attributes(pre, (char *)ref + sizeof(head),
455 			   (char *)ref + sizeof(head) + head.attrs_size);
456 
457 	IMSG_RAW("%s`-----------------------", prefix ? prefix : "");
458 
459 	TEE_Free(pre);
460 }
461 #endif /*CFG_TEE_TA_LOG_LEVEL*/
462