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 <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
sanitize_consistent_class_and_type(struct obj_attrs * attrs)26 bool sanitize_consistent_class_and_type(struct obj_attrs *attrs)
27 {
28 switch (get_class(attrs)) {
29 case PKCS11_CKO_DATA:
30 case PKCS11_CKO_CERTIFICATE:
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_DOMAIN_PARAMETERS:
41 case PKCS11_CKO_HW_FEATURE:
42 default:
43 return false;
44 }
45
46 return false;
47 }
48
read_attr_advance(void * buf,size_t blen,size_t * pos,struct pkcs11_attribute_head * attr,void ** data)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 */
sanitize_class_and_type(struct obj_attrs ** dst,void * src,size_t src_size,uint32_t class_hint,uint32_t type_hint)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
sanitize_boolprops(struct obj_attrs ** dst,void * src,size_t src_size)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
sanitize_indirect_attr(struct obj_attrs ** dst,struct pkcs11_attribute_head * cli_ref,char * data)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
232 assert(pkcs11_attr_has_indirect_attributes(cli_ref->id));
233
234 /* Build a new serial object while sanitizing the attributes list */
235 rc = sanitize_client_object(&obj2, data, cli_ref->size,
236 PKCS11_CKO_UNDEFINED_ID,
237 PKCS11_UNDEFINED_ID);
238 if (rc)
239 goto out;
240
241 rc = add_attribute(dst, cli_ref->id, obj2,
242 sizeof(*obj2) + obj2->attrs_size);
243 out:
244 TEE_Free(obj2);
245 return rc;
246 }
247
sanitize_client_object(struct obj_attrs ** dst,void * src,size_t size,uint32_t class_hint,uint32_t type_hint)248 enum pkcs11_rc sanitize_client_object(struct obj_attrs **dst, void *src,
249 size_t size, uint32_t class_hint,
250 uint32_t type_hint)
251 {
252 struct pkcs11_attribute_head cli_ref = { };
253 struct pkcs11_object_head head = { };
254 enum pkcs11_rc rc = PKCS11_CKR_OK;
255 size_t pos = sizeof(head);
256 size_t sz_from_hdr = 0;
257 void *data = NULL;
258
259 if (size < sizeof(head))
260 return PKCS11_CKR_ARGUMENTS_BAD;
261
262 TEE_MemMove(&head, src, sizeof(head));
263
264 if (ADD_OVERFLOW(sizeof(head), head.attrs_size, &sz_from_hdr) ||
265 size < sz_from_hdr)
266 return PKCS11_CKR_ARGUMENTS_BAD;
267
268 rc = init_attributes_head(dst);
269 if (rc)
270 return rc;
271
272 rc = sanitize_class_and_type(dst, src, sz_from_hdr, class_hint,
273 type_hint);
274 if (rc)
275 return rc;
276
277 rc = sanitize_boolprops(dst, src, sz_from_hdr);
278 if (rc)
279 return rc;
280
281 while (pos != sz_from_hdr) {
282 rc = read_attr_advance(src, sz_from_hdr, &pos, &cli_ref, &data);
283 if (rc)
284 return rc;
285
286 if (cli_ref.id == PKCS11_CKA_CLASS ||
287 pkcs11_attr_is_type(cli_ref.id) ||
288 pkcs11_attr_is_boolean(cli_ref.id))
289 continue;
290
291 if (pkcs11_attr_has_indirect_attributes(cli_ref.id)) {
292 rc = sanitize_indirect_attr(dst, &cli_ref, data);
293 if (rc)
294 return rc;
295
296 continue;
297 }
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
__trace_attributes(char * prefix,void * src,void * end)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 char *start = NULL;
337
338 TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref));
339 TEE_MemMove(&data[0], cur + sizeof(pkcs11_ref),
340 MIN(pkcs11_ref.size, sizeof(data)));
341 TEE_MemMove(&data_u32, cur + sizeof(pkcs11_ref),
342 sizeof(data_u32));
343
344 next = sizeof(pkcs11_ref) + pkcs11_ref.size;
345
346 DMSG_RAW("%s Attr %s / %s (%#04"PRIx32" %"PRIu32"-byte)",
347 prefix, id2str_attr(pkcs11_ref.id),
348 id2str_attr_value(pkcs11_ref.id, pkcs11_ref.size,
349 cur + sizeof(pkcs11_ref)),
350 pkcs11_ref.id, pkcs11_ref.size);
351
352 switch (pkcs11_ref.size) {
353 case 0:
354 break;
355 case 1:
356 DMSG_RAW("%s Attr byte value: %02x", prefix, data[0]);
357 break;
358 case 2:
359 DMSG_RAW("%s Attr byte value: %02x %02x",
360 prefix, data[0], data[1]);
361 break;
362 case 3:
363 DMSG_RAW("%s Attr byte value: %02x %02x %02x",
364 prefix, data[0], data[1], data[2]);
365 break;
366 case 4:
367 DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x",
368 prefix, data[0], data[1], data[2], data[3]);
369 break;
370 default:
371 DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x ...",
372 prefix, data[0], data[1], data[2], data[3]);
373 break;
374 }
375
376 switch (pkcs11_ref.id) {
377 case PKCS11_CKA_WRAP_TEMPLATE:
378 case PKCS11_CKA_UNWRAP_TEMPLATE:
379 case PKCS11_CKA_DERIVE_TEMPLATE:
380 start = cur + sizeof(pkcs11_ref);
381 trace_attributes_from_api_head(prefix2, start,
382 (char *)end - start);
383 break;
384 default:
385 break;
386 }
387 }
388
389 /* Sanity */
390 if (cur != (char *)end)
391 EMSG("Warning: unexpected alignment issue");
392
393 TEE_Free(prefix2);
394 }
395
trace_attributes_from_api_head(const char * prefix,void * ref,size_t size)396 void trace_attributes_from_api_head(const char *prefix, void *ref, size_t size)
397 {
398 struct pkcs11_object_head head = { };
399 char *pre = NULL;
400 size_t offset = 0;
401
402 TEE_MemMove(&head, ref, sizeof(head));
403
404 if (size > sizeof(head) + head.attrs_size) {
405 EMSG("template overflows client buffer (%zu/%zu)",
406 size, sizeof(head) + head.attrs_size);
407 return;
408 }
409
410 pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO);
411 if (!pre) {
412 EMSG("%s: out of memory", prefix);
413 return;
414 }
415 if (prefix)
416 TEE_MemMove(pre, prefix, strlen(prefix));
417
418 DMSG_RAW("%s,--- (serial object) Attributes list --------", pre);
419 DMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes",
420 pre, head.attrs_count, head.attrs_size);
421
422 offset = sizeof(head);
423 pre[prefix ? strlen(prefix) : 0] = '|';
424 __trace_attributes(pre, (char *)ref + offset,
425 (char *)ref + offset + head.attrs_size);
426
427 DMSG_RAW("%s`-----------------------", prefix ? prefix : "");
428
429 TEE_Free(pre);
430 }
431