1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2017-2020, Linaro Limited 4 */ 5 6 #include <bitstring.h> 7 #include <pkcs11_ta.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <util.h> 11 #include <tee_internal_api.h> 12 #include <tee_internal_api_extensions.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 return true; 31 case PKCS11_CKO_SECRET_KEY: 32 return key_type_is_symm_key(get_key_type(attrs)); 33 case PKCS11_CKO_MECHANISM: 34 return mechanism_is_valid(get_mechanism_type(attrs)); 35 case PKCS11_CKO_PUBLIC_KEY: 36 case PKCS11_CKO_PRIVATE_KEY: 37 return key_type_is_asymm_key(get_key_type(attrs)); 38 case PKCS11_CKO_OTP_KEY: 39 case PKCS11_CKO_CERTIFICATE: 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 { 74 uint32_t class_found = PKCS11_CKO_UNDEFINED_ID; 75 size_t pos = sizeof(struct pkcs11_object_head); 76 struct pkcs11_attribute_head cli_ref = { }; 77 uint32_t type_found = PKCS11_UNDEFINED_ID; 78 enum pkcs11_rc rc = PKCS11_CKR_OK; 79 void *data = NULL; 80 81 while (pos != src_size) { 82 rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data); 83 if (rc) 84 goto err; 85 86 if (cli_ref.id == PKCS11_CKA_CLASS) { 87 uint32_t class = 0; 88 89 if (cli_ref.size != sizeof(class)) { 90 rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; 91 goto err; 92 } 93 94 TEE_MemMove(&class, data, sizeof(class)); 95 96 if (class_found != PKCS11_CKO_UNDEFINED_ID && 97 class_found != class) { 98 EMSG("Conflicting class value"); 99 rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; 100 goto err; 101 } 102 103 class_found = class; 104 continue; 105 } 106 107 /* The attribute is a type-in-class */ 108 if (pkcs11_attr_is_type(cli_ref.id)) { 109 uint32_t type = 0; 110 111 if (cli_ref.size != sizeof(type)) { 112 rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; 113 goto err; 114 } 115 116 TEE_MemMove(&type, data, sizeof(type)); 117 118 if (type_found != PKCS11_CKK_UNDEFINED_ID && 119 type_found != type) { 120 EMSG("Conflicting type-in-class value"); 121 rc = PKCS11_CKR_TEMPLATE_INCONSISTENT; 122 goto err; 123 } 124 125 type_found = type; 126 } 127 } 128 129 if (class_found != PKCS11_CKO_UNDEFINED_ID) { 130 rc = add_attribute(dst, PKCS11_CKA_CLASS, 131 &class_found, sizeof(class_found)); 132 if (rc) 133 return rc; 134 } 135 136 if (type_found != PKCS11_UNDEFINED_ID) { 137 rc = add_attribute(dst, PKCS11_CKA_KEY_TYPE, 138 &type_found, sizeof(type_found)); 139 if (rc) 140 return rc; 141 } 142 143 return PKCS11_CKR_OK; 144 145 err: 146 trace_attributes_from_api_head("bad-template", src, src_size); 147 148 return rc; 149 } 150 151 static enum pkcs11_rc sanitize_boolprops(struct obj_attrs **dst, void *src, 152 size_t src_size) 153 { 154 bitstr_t bit_decl(seen_attrs, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 }; 155 bitstr_t bit_decl(boolprops, PKCS11_BOOLPROPS_MAX_COUNT) = { 0 }; 156 size_t pos = sizeof(struct pkcs11_object_head); 157 struct pkcs11_attribute_head cli_ref = { }; 158 enum pkcs11_rc rc = PKCS11_CKR_OK; 159 bool value = false; 160 void *data = NULL; 161 int idx = 0; 162 163 /* 164 * We're keeping track of seen boolean attributes in the bitstring 165 * seen_attrs. The bitstring boolprops holds the recorded value 166 * once seen_attrs has been updated. 167 */ 168 169 while (pos != src_size) { 170 rc = read_attr_advance(src, src_size, &pos, &cli_ref, &data); 171 if (rc) 172 return rc; 173 174 idx = pkcs11_attr2boolprop_shift(cli_ref.id); 175 if (idx < 0) 176 continue; /* skipping non-boolean attributes */ 177 178 if (idx >= PKCS11_BOOLPROPS_MAX_COUNT || 179 cli_ref.size != sizeof(uint8_t)) 180 return PKCS11_CKR_FUNCTION_FAILED; 181 182 value = *(uint8_t *)data; 183 184 /* 185 * If this attribute has already been seen, check that it 186 * still holds the same value as last time. 187 */ 188 if (bit_test(seen_attrs, idx) && 189 value != (bool)bit_test(boolprops, idx)) 190 return PKCS11_CKR_TEMPLATE_INCONSISTENT; 191 192 if (value) 193 bit_set(boolprops, idx); 194 195 if (!bit_test(seen_attrs, idx)) { 196 uint8_t pkcs11_bool = value; 197 198 rc = add_attribute(dst, cli_ref.id, &pkcs11_bool, 199 sizeof(pkcs11_bool)); 200 if (rc) 201 return rc; 202 } 203 bit_set(seen_attrs, idx); 204 } 205 206 return PKCS11_CKR_OK; 207 } 208 209 static uint32_t sanitize_indirect_attr(struct obj_attrs **dst, 210 struct pkcs11_attribute_head *cli_ref, 211 char *data) 212 { 213 struct obj_attrs *obj2 = NULL; 214 enum pkcs11_rc rc = PKCS11_CKR_OK; 215 enum pkcs11_class_id class = get_class(*dst); 216 217 /* 218 * Serialized attributes: current applicable only to the key 219 * templates which are tables of attributes. 220 */ 221 switch (cli_ref->id) { 222 case PKCS11_CKA_WRAP_TEMPLATE: 223 case PKCS11_CKA_UNWRAP_TEMPLATE: 224 case PKCS11_CKA_DERIVE_TEMPLATE: 225 break; 226 default: 227 return PKCS11_RV_NOT_FOUND; 228 } 229 230 if (class == PKCS11_CKO_UNDEFINED_ID) { 231 DMSG("Template without CLASS not supported yet"); 232 return PKCS11_CKR_TEMPLATE_INCOMPLETE; 233 } 234 235 /* Such attributes are expected only for keys (and vendor defined) */ 236 if (pkcs11_attr_class_is_key(class)) 237 return PKCS11_CKR_TEMPLATE_INCONSISTENT; 238 239 rc = init_attributes_head(&obj2); 240 if (rc) 241 return rc; 242 243 /* Build a new serial object while sanitizing the attributes list */ 244 rc = sanitize_client_object(&obj2, data, cli_ref->size); 245 if (rc) 246 goto out; 247 248 rc = add_attribute(dst, cli_ref->id, obj2, 249 sizeof(*obj2) + obj2->attrs_size); 250 out: 251 TEE_Free(obj2); 252 return rc; 253 } 254 255 enum pkcs11_rc sanitize_client_object(struct obj_attrs **dst, void *src, 256 size_t size) 257 { 258 struct pkcs11_attribute_head cli_ref = { }; 259 struct pkcs11_object_head head = { }; 260 enum pkcs11_rc rc = PKCS11_CKR_OK; 261 size_t pos = sizeof(head); 262 size_t sz_from_hdr = 0; 263 void *data = NULL; 264 265 if (size < sizeof(head)) 266 return PKCS11_CKR_ARGUMENTS_BAD; 267 268 TEE_MemMove(&head, src, sizeof(head)); 269 270 if (ADD_OVERFLOW(sizeof(head), head.attrs_size, &sz_from_hdr) || 271 size < sz_from_hdr) 272 return PKCS11_CKR_ARGUMENTS_BAD; 273 274 rc = init_attributes_head(dst); 275 if (rc) 276 return rc; 277 278 rc = sanitize_class_and_type(dst, src, sz_from_hdr); 279 if (rc) 280 return rc; 281 282 rc = sanitize_boolprops(dst, src, sz_from_hdr); 283 if (rc) 284 return rc; 285 286 while (pos != sz_from_hdr) { 287 rc = read_attr_advance(src, sz_from_hdr, &pos, &cli_ref, &data); 288 if (rc) 289 return rc; 290 291 if (cli_ref.id == PKCS11_CKA_CLASS || 292 pkcs11_attr_is_type(cli_ref.id) || 293 pkcs11_attr_is_boolean(cli_ref.id)) 294 continue; 295 296 rc = sanitize_indirect_attr(dst, &cli_ref, data); 297 if (rc == PKCS11_CKR_OK) 298 continue; 299 if (rc != PKCS11_RV_NOT_FOUND) 300 return rc; 301 302 if (!valid_pkcs11_attribute_id(cli_ref.id, cli_ref.size)) { 303 EMSG("Invalid attribute id %#"PRIx32, cli_ref.id); 304 return PKCS11_CKR_TEMPLATE_INCONSISTENT; 305 } 306 307 rc = add_attribute(dst, cli_ref.id, data, cli_ref.size); 308 if (rc) 309 return rc; 310 } 311 312 return rc; 313 } 314 315 /* 316 * Debug: dump object attribute array to output trace 317 */ 318 319 static void __trace_attributes(char *prefix, void *src, void *end) 320 { 321 size_t next = 0; 322 char *prefix2 = NULL; 323 size_t prefix_len = strlen(prefix); 324 char *cur = src; 325 326 /* append 4 spaces to the prefix plus terminal '\0' */ 327 prefix2 = TEE_Malloc(prefix_len + 1 + 4, TEE_MALLOC_FILL_ZERO); 328 if (!prefix2) 329 return; 330 331 TEE_MemMove(prefix2, prefix, prefix_len + 1); 332 TEE_MemFill(prefix2 + prefix_len, ' ', 4); 333 *(prefix2 + prefix_len + 4) = '\0'; 334 335 for (; cur < (char *)end; cur += next) { 336 struct pkcs11_attribute_head pkcs11_ref; 337 uint8_t data[4] = { 0 }; 338 uint32_t data_u32 = 0; 339 340 TEE_MemMove(&pkcs11_ref, cur, sizeof(pkcs11_ref)); 341 TEE_MemMove(&data[0], cur + sizeof(pkcs11_ref), 342 MIN(pkcs11_ref.size, sizeof(data))); 343 TEE_MemMove(&data_u32, cur + sizeof(pkcs11_ref), 344 sizeof(data_u32)); 345 346 next = sizeof(pkcs11_ref) + pkcs11_ref.size; 347 348 DMSG_RAW("%s Attr %s / %s (%#04"PRIx32" %"PRIu32"-byte)", 349 prefix, id2str_attr(pkcs11_ref.id), 350 id2str_attr_value(pkcs11_ref.id, pkcs11_ref.size, 351 cur + sizeof(pkcs11_ref)), 352 pkcs11_ref.id, pkcs11_ref.size); 353 354 switch (pkcs11_ref.size) { 355 case 0: 356 break; 357 case 1: 358 DMSG_RAW("%s Attr byte value: %02x", prefix, data[0]); 359 break; 360 case 2: 361 DMSG_RAW("%s Attr byte value: %02x %02x", 362 prefix, data[0], data[1]); 363 break; 364 case 3: 365 DMSG_RAW("%s Attr byte value: %02x %02x %02x", 366 prefix, data[0], data[1], data[2]); 367 break; 368 case 4: 369 DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x", 370 prefix, data[0], data[1], data[2], data[3]); 371 break; 372 default: 373 DMSG_RAW("%s Attr byte value: %02x %02x %02x %02x ...", 374 prefix, data[0], data[1], data[2], data[3]); 375 break; 376 } 377 378 switch (pkcs11_ref.id) { 379 case PKCS11_CKA_WRAP_TEMPLATE: 380 case PKCS11_CKA_UNWRAP_TEMPLATE: 381 case PKCS11_CKA_DERIVE_TEMPLATE: 382 trace_attributes_from_api_head(prefix2, 383 cur + sizeof(pkcs11_ref), 384 (char *)end - cur); 385 break; 386 default: 387 break; 388 } 389 } 390 391 /* Sanity */ 392 if (cur != (char *)end) 393 EMSG("Warning: unexpected alignment issue"); 394 395 TEE_Free(prefix2); 396 } 397 398 void trace_attributes_from_api_head(const char *prefix, void *ref, size_t size) 399 { 400 struct pkcs11_object_head head = { }; 401 char *pre = NULL; 402 size_t offset = 0; 403 404 TEE_MemMove(&head, ref, sizeof(head)); 405 406 if (size > sizeof(head) + head.attrs_size) { 407 EMSG("template overflows client buffer (%zu/%zu)", 408 size, sizeof(head) + head.attrs_size); 409 return; 410 } 411 412 pre = TEE_Malloc(prefix ? strlen(prefix) + 2 : 2, TEE_MALLOC_FILL_ZERO); 413 if (!pre) { 414 EMSG("%s: out of memory", prefix); 415 return; 416 } 417 if (prefix) 418 TEE_MemMove(pre, prefix, strlen(prefix)); 419 420 DMSG_RAW("%s,--- (serial object) Attributes list --------", pre); 421 DMSG_RAW("%s| %"PRIu32" item(s) - %"PRIu32" bytes", 422 pre, head.attrs_count, head.attrs_size); 423 424 offset = sizeof(head); 425 pre[prefix ? strlen(prefix) : 0] = '|'; 426 __trace_attributes(pre, (char *)ref + offset, 427 (char *)ref + offset + head.attrs_size); 428 429 DMSG_RAW("%s`-----------------------", prefix ? prefix : ""); 430 431 TEE_Free(pre); 432 } 433