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