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 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 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 */ 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 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 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 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 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 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