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