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
sanitize_consistent_class_and_type(struct obj_attrs * attrs)27 bool sanitize_consistent_class_and_type(struct obj_attrs *attrs)
28 {
29 switch (get_class(attrs)) {
30 case PKCS11_CKO_DATA:
31 case PKCS11_CKO_CERTIFICATE:
32 return true;
33 case PKCS11_CKO_SECRET_KEY:
34 return key_type_is_symm_key(get_key_type(attrs));
35 case PKCS11_CKO_MECHANISM:
36 return mechanism_is_valid(get_mechanism_type(attrs));
37 case PKCS11_CKO_PUBLIC_KEY:
38 case PKCS11_CKO_PRIVATE_KEY:
39 return key_type_is_asymm_key(get_key_type(attrs));
40 case PKCS11_CKO_OTP_KEY:
41 case PKCS11_CKO_DOMAIN_PARAMETERS:
42 case PKCS11_CKO_HW_FEATURE:
43 default:
44 return false;
45 }
46
47 return false;
48 }
49
read_attr_advance(void * buf,size_t blen,size_t * pos,struct pkcs11_attribute_head * attr,void ** data)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 */
sanitize_class_and_type(struct obj_attrs ** dst,void * src,size_t src_size,uint32_t class_hint,uint32_t type_hint)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
sanitize_boolprops(struct obj_attrs ** dst,void * src,size_t src_size)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
sanitize_indirect_attr(struct obj_attrs ** dst,struct pkcs11_attribute_head * cli_ref,char * data)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
233 assert(pkcs11_attr_has_indirect_attributes(cli_ref->id));
234
235 /* Build a new serial object while sanitizing the attributes list */
236 rc = sanitize_client_object(&obj2, data, cli_ref->size,
237 PKCS11_CKO_UNDEFINED_ID,
238 PKCS11_UNDEFINED_ID);
239 if (rc)
240 goto out;
241
242 rc = add_attribute(dst, cli_ref->id, obj2,
243 sizeof(*obj2) + obj2->attrs_size);
244 out:
245 TEE_Free(obj2);
246 return rc;
247 }
248
sanitize_client_object(struct obj_attrs ** dst,void * src,size_t size,uint32_t class_hint,uint32_t type_hint)249 enum pkcs11_rc sanitize_client_object(struct obj_attrs **dst, void *src,
250 size_t size, uint32_t class_hint,
251 uint32_t type_hint)
252 {
253 struct pkcs11_attribute_head cli_ref = { };
254 struct pkcs11_object_head head = { };
255 enum pkcs11_rc rc = PKCS11_CKR_OK;
256 size_t pos = sizeof(head);
257 size_t sz_from_hdr = 0;
258 void *data = NULL;
259
260 if (size < sizeof(head))
261 return PKCS11_CKR_ARGUMENTS_BAD;
262
263 TEE_MemMove(&head, src, sizeof(head));
264
265 if (ADD_OVERFLOW(sizeof(head), head.attrs_size, &sz_from_hdr) ||
266 size < sz_from_hdr)
267 return PKCS11_CKR_ARGUMENTS_BAD;
268
269 rc = init_attributes_head(dst);
270 if (rc)
271 return rc;
272
273 rc = sanitize_class_and_type(dst, src, sz_from_hdr, class_hint,
274 type_hint);
275 if (rc)
276 return rc;
277
278 rc = sanitize_boolprops(dst, src, sz_from_hdr);
279 if (rc)
280 return rc;
281
282 while (pos != sz_from_hdr) {
283 rc = read_attr_advance(src, sz_from_hdr, &pos, &cli_ref, &data);
284 if (rc)
285 return rc;
286
287 if (cli_ref.id == PKCS11_CKA_CLASS ||
288 pkcs11_attr_is_type(cli_ref.id) ||
289 pkcs11_attr_is_boolean(cli_ref.id))
290 continue;
291
292 if (pkcs11_attr_has_indirect_attributes(cli_ref.id)) {
293 rc = sanitize_indirect_attr(dst, &cli_ref, data);
294 if (rc)
295 return rc;
296
297 continue;
298 }
299
300 if (!valid_pkcs11_attribute_id(cli_ref.id, cli_ref.size)) {
301 EMSG("Invalid attribute id %#"PRIx32, cli_ref.id);
302 return PKCS11_CKR_TEMPLATE_INCONSISTENT;
303 }
304
305 rc = add_attribute(dst, cli_ref.id, data, cli_ref.size);
306 if (rc)
307 return rc;
308 }
309
310 return rc;
311 }
312
313 /*
314 * Debug: dump object attribute array to output trace
315 */
316
__trace_attributes(char * prefix,void * src,void * end)317 static void __trace_attributes(char *prefix, void *src, void *end)
318 {
319 size_t next = 0;
320 char *prefix2 = NULL;
321 size_t prefix_len = strlen(prefix);
322 char *cur = src;
323
324 /* append 4 spaces to the prefix plus terminal '\0' */
325 prefix2 = TEE_Malloc(prefix_len + 1 + 4, TEE_MALLOC_FILL_ZERO);
326 if (!prefix2)
327 return;
328
329 TEE_MemMove(prefix2, prefix, prefix_len + 1);
330 TEE_MemFill(prefix2 + prefix_len, ' ', 4);
331 *(prefix2 + prefix_len + 4) = '\0';
332
333 for (; cur < (char *)end; cur += next) {
334 struct pkcs11_attribute_head pkcs11_ref;
335 uint8_t data[4] = { 0 };
336 uint32_t data_u32 = 0;
337 char *start = NULL;
338
339 TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref));
340 TEE_MemMove(&data[0], cur + sizeof(pkcs11_ref),
341 MIN(pkcs11_ref.size, sizeof(data)));
342 TEE_MemMove(&data_u32, cur + sizeof(pkcs11_ref),
343 sizeof(data_u32));
344
345 next = sizeof(pkcs11_ref) + pkcs11_ref.size;
346
347 DMSG_RAW("%s Attr %s / %s (%#04"PRIx32" %"PRIu32"-byte)",
348 prefix, id2str_attr(pkcs11_ref.id),
349 id2str_attr_value(pkcs11_ref.id, pkcs11_ref.size,
350 cur + sizeof(pkcs11_ref)),
351 pkcs11_ref.id, pkcs11_ref.size);
352
353 switch (pkcs11_ref.size) {
354 case 0:
355 break;
356 case 1:
357 DMSG_RAW("%s Attr byte value: %02x", prefix, data[0]);
358 break;
359 case 2:
360 DMSG_RAW("%s Attr byte value: %02x %02x",
361 prefix, data[0], data[1]);
362 break;
363 case 3:
364 DMSG_RAW("%s Attr byte value: %02x %02x %02x",
365 prefix, data[0], data[1], data[2]);
366 break;
367 case 4:
368 DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x",
369 prefix, data[0], data[1], data[2], data[3]);
370 break;
371 default:
372 DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x ...",
373 prefix, data[0], data[1], data[2], data[3]);
374 break;
375 }
376
377 switch (pkcs11_ref.id) {
378 case PKCS11_CKA_WRAP_TEMPLATE:
379 case PKCS11_CKA_UNWRAP_TEMPLATE:
380 case PKCS11_CKA_DERIVE_TEMPLATE:
381 start = cur + sizeof(pkcs11_ref);
382 trace_attributes_from_api_head(prefix2, start,
383 (char *)end - start);
384 break;
385 default:
386 break;
387 }
388 }
389
390 /* Sanity */
391 if (cur != (char *)end)
392 EMSG("Warning: unexpected alignment issue");
393
394 TEE_Free(prefix2);
395 }
396
trace_attributes_from_api_head(const char * prefix,void * ref,size_t size)397 void trace_attributes_from_api_head(const char *prefix, void *ref, size_t size)
398 {
399 struct pkcs11_object_head head = { };
400 char *pre = NULL;
401 size_t offset = 0;
402
403 TEE_MemMove(&head, ref, sizeof(head));
404
405 if (size > sizeof(head) + head.attrs_size) {
406 EMSG("template overflows client buffer (%zu/%zu)",
407 size, sizeof(head) + head.attrs_size);
408 return;
409 }
410
411 pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO);
412 if (!pre) {
413 EMSG("%s: out of memory", prefix);
414 return;
415 }
416 if (prefix)
417 TEE_MemMove(pre, prefix, strlen(prefix));
418
419 DMSG_RAW("%s,--- (serial object) Attributes list --------", pre);
420 DMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes",
421 pre, head.attrs_count, head.attrs_size);
422
423 offset = sizeof(head);
424 pre[prefix ? strlen(prefix) : 0] = '|';
425 __trace_attributes(pre, (char *)ref + offset,
426 (char *)ref + offset + head.attrs_size);
427
428 DMSG_RAW("%s`-----------------------", prefix ? prefix : "");
429
430 TEE_Free(pre);
431 }
432