/* * COPYRIGHT (c) International Business Machines Corp. 2001-2017 * * This program is provided under the terms of the Common Public License, * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this * software constitutes recipient's acceptance of CPL-1.0 terms which can be * found in the file LICENSE file or at * https://opensource.org/licenses/cpl1.0.php */ // File: object.c // // Object manager related functions // // Functions contained within: // // object_create // object_free // object_is_modifiable // object_is_private // object_is_token_object // object_is_session_object // #include #include #include #include #include "pkcs11types.h" #include "defs.h" #include "host_defs.h" #include "h_extern.h" #include "tok_spec_struct.h" #include "pkcs32.h" #include "trace.h" // object_create() // // Args: void * attributes : (INPUT) pointer to data block containing // ATTRIBUTEs // OBJECT * obj : (OUTPUT) destination object // // Creates an object with the specified attributes. Verifies that all required // attributes are present and adds any missing attributes that have // Cryptoki-defined default values. This routine does not check whether the // session is authorized to create the object. That is done elsewhere // (see object_mgr_create()) // CK_RV object_create(STDLL_TokData_t * tokdata, CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount, OBJECT ** obj) { OBJECT *o = NULL; CK_ATTRIBUTE *attr = NULL; CK_BBOOL class_given = FALSE; CK_BBOOL subclass_given = FALSE; CK_ULONG class = 0xFFFFFFFF, subclass = 0xFFFFFFFF; CK_RV rc; unsigned int i; if (!pTemplate) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } // extract the object class and subclass // attr = pTemplate; for (i = 0; i < ulCount; i++, attr++) { if (attr->type == CKA_CLASS) { class = *(CK_OBJECT_CLASS *) attr->pValue; class_given = TRUE; } if (attr->type == CKA_CERTIFICATE_TYPE) { subclass = *(CK_CERTIFICATE_TYPE *) attr->pValue; subclass_given = TRUE; } if (attr->type == CKA_KEY_TYPE) { subclass = *(CK_KEY_TYPE *) attr->pValue; subclass_given = TRUE; } if (attr->type == CKA_HW_FEATURE_TYPE) { subclass = *(CK_HW_FEATURE_TYPE *) attr->pValue; subclass_given = TRUE; } } if (class_given == FALSE) { TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE)); return CKR_TEMPLATE_INCOMPLETE; } // Return CKR_ATTRIBUTE_TYPE_INVALID when trying to create a // vendor-defined object. if (class >= CKO_VENDOR_DEFINED) { TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_TYPE_INVALID)); return CKR_ATTRIBUTE_TYPE_INVALID; } if (subclass_given != TRUE && class != CKO_DATA && class != CKO_PROFILE) { TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE)); return CKR_TEMPLATE_INCOMPLETE; } rc = object_create_skel(tokdata, pTemplate, ulCount, MODE_CREATE, class, subclass, &o); if (rc != CKR_OK) { TRACE_DEVEL("object_create_skel failed.\n"); return rc; } *obj = o; return CKR_OK; } // object_copy() // // Args: OBJECT * old_obj : (INPUT) pointer to the source object // void * attributes : (INPUT) pointer to data block containing // additional ATTRIBUTEs // CK_ULONG count : (INPUT) number of new attributes // OBJECT ** new_obj : (OUTPUT) destination object // // Builds a copy of the specified object. The new object gets the original // object's attribute template plus any additional attributes that are specified // Verifies that all required attributes are present. This routine does not // check whether the session is authorized to copy the object -- routines at // the individual object level don't have the concept of "session". These checks // are done by the object manager. // // The old_obj must hold the READ lock! // CK_RV object_copy(STDLL_TokData_t * tokdata, CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount, OBJECT * old_obj, OBJECT ** new_obj) { TEMPLATE *tmpl = NULL; TEMPLATE *new_tmpl = NULL; OBJECT *o = NULL; CK_BBOOL found; CK_ULONG class, subclass; CK_RV rc; if (!old_obj || (!pTemplate && ulCount) || !new_obj) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } o = (OBJECT *) malloc(sizeof(OBJECT)); tmpl = (TEMPLATE *) malloc(sizeof(TEMPLATE)); new_tmpl = (TEMPLATE *) malloc(sizeof(TEMPLATE)); if (!o || !tmpl || !new_tmpl) { rc = CKR_HOST_MEMORY; TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); if (o) free(o); if (tmpl) free(tmpl); if (new_tmpl) free(new_tmpl); return rc; // do not goto done -- memory might not be initialized } memset(o, 0x0, sizeof(OBJECT)); memset(tmpl, 0x0, sizeof(TEMPLATE)); memset(new_tmpl, 0x0, sizeof(TEMPLATE)); rc = object_init_lock(o); if (rc != CKR_OK) goto error; // copy the original object's attribute template // rc = template_copy(tmpl, old_obj->template); if (rc != CKR_OK) { TRACE_DEVEL("Failed to copy template.\n"); goto error; } rc = template_add_attributes(new_tmpl, pTemplate, ulCount); if (rc != CKR_OK) { TRACE_DEVEL("template_add_attributes failed.\n"); goto error; } // at this point, the new object has the list of attributes. we need // to do some more checking now: // 1) invalid attribute values // 2) missing required attributes // 3) attributes inappropriate for the object class // 4) conflicting attributes/values // found = template_get_class(tmpl, &class, &subclass); if (found == FALSE) { TRACE_ERROR("Could not find CKA_CLASS in object's template.\n"); rc = CKR_TEMPLATE_INCONSISTENT; goto error; } // the user cannot change object classes so we assume the existing // object attributes are valid. we still need to check the new attributes. // we cannot merge the new attributes in with the old ones and then check // for validity because some attributes are added internally and are not // allowed to be specified by the user (ie. CKA_LOCAL for key types) but // may still be part of the old template. // rc = template_validate_attributes(tokdata, new_tmpl, class, subclass, MODE_COPY); if (rc != CKR_OK) { TRACE_DEVEL("template_validate_attributes failed.\n"); goto error; } // merge in the new attributes // rc = template_merge(tmpl, &new_tmpl); if (rc != CKR_OK) { TRACE_DEVEL("template_merge failed.\n"); goto error; } // do we need this? since an attribute cannot be removed, the original // object's template (contained in tmpl) already has the required attributes // present // rc = template_check_required_attributes(tmpl, class, subclass, MODE_COPY); if (rc != CKR_OK) { TRACE_ERROR("template_check_required_attributes failed.\n"); goto error; } // at this point, we should have a valid object with correct attributes // o->template = tmpl; *new_obj = o; return CKR_OK; error: if (tmpl) template_free(tmpl); if (new_tmpl) template_free(new_tmpl); if (o) object_free(o); return rc; } // object_flatten() - this is still used when saving token objects // CK_RV object_flatten(OBJECT * obj, CK_BYTE ** data, CK_ULONG * len) { CK_BYTE *buf = NULL; CK_ULONG tmpl_len, total_len; CK_ULONG offset; CK_ULONG_32 count; CK_OBJECT_CLASS_32 class32; long rc; if (!obj) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } count = template_get_count(obj->template); tmpl_len = template_get_compressed_size(obj->template); total_len = tmpl_len + sizeof(CK_OBJECT_CLASS_32) + sizeof(CK_ULONG_32) + 8; buf = (CK_BYTE *) malloc(total_len); if (!buf) { // SAB XXX FIXME This was DATA TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); return CKR_HOST_MEMORY; } memset((CK_BYTE *) buf, 0x0, total_len); offset = 0; class32 = obj->class; memcpy(buf + offset, &class32, sizeof(CK_OBJECT_CLASS_32)); offset += sizeof(CK_OBJECT_CLASS_32); memcpy(buf + offset, &count, sizeof(CK_ULONG_32)); offset += sizeof(CK_ULONG_32); memcpy(buf + offset, &obj->name, sizeof(CK_BYTE) * 8); offset += 8; rc = template_flatten(obj->template, buf + offset); if (rc != CKR_OK) { free(buf); return rc; } *data = buf; *len = total_len; return CKR_OK; } // object_free() // // does what it says... // void object_free(OBJECT * obj) { /* refactorization here to do actual free - fix from coverity scan */ if (obj) { if (obj->template) template_free(obj->template); object_destroy_lock(obj); free(obj); } } //call_object_free() //This function is added to silence the compiler during implicit void (*)(void*) //function pointer casting in call back functions. // void call_object_free(void *ptr) { if (ptr) object_free((OBJECT *) ptr); } // object_is_modifiable() // CK_BBOOL object_is_modifiable(OBJECT * obj) { CK_ATTRIBUTE *attr = NULL; CK_BBOOL modifiable; CK_BBOOL found; found = template_attribute_find(obj->template, CKA_MODIFIABLE, &attr); if (found == FALSE) return TRUE; // should always be found but we default to TRUE //axelrh: prevent dereferencing NULL from bad parse if (attr->pValue == NULL) return TRUE; //default to TRUE modifiable = *(CK_BBOOL *) attr->pValue; return modifiable; } // object_is_private() // // an is_private member should probably be added to OBJECT // CK_BBOOL object_is_private(OBJECT * obj) { CK_ATTRIBUTE *attr = NULL; CK_BBOOL priv; CK_BBOOL found; found = template_attribute_find(obj->template, CKA_PRIVATE, &attr); if (found == FALSE) { return TRUE; // should always be found but we default to TRUE } if (attr == NULL) return TRUE; //axelrh: prevent segfault caused by 0-len attribute //that has a null pValue CK_BBOOL *bboolPtr = (CK_BBOOL *) attr->pValue; if (bboolPtr == NULL) return TRUE; //default priv = *(bboolPtr); return priv; } // object_is_public() // CK_BBOOL object_is_public(OBJECT * obj) { CK_BBOOL rc; rc = object_is_private(obj); if (rc) return FALSE; return TRUE; } // object_is_token_object() // CK_BBOOL object_is_token_object(OBJECT * obj) { CK_ATTRIBUTE *attr = NULL; CK_BBOOL is_token; CK_BBOOL found; found = template_attribute_find(obj->template, CKA_TOKEN, &attr); if (found == FALSE) return FALSE; //axelrh: prevent dereferencing NULL from bad parse if (attr->pValue == NULL) return FALSE; is_token = *(CK_BBOOL *) attr->pValue; return is_token; } // object_is_session_object() // CK_BBOOL object_is_session_object(OBJECT * obj) { CK_BBOOL rc; rc = object_is_token_object(obj); if (rc) return FALSE; else return TRUE; } // object_get_size() // CK_ULONG object_get_size(OBJECT * obj) { CK_ULONG size; size = sizeof(OBJECT) + template_get_size(obj->template); return size; } // // CK_RV object_get_attribute_values(OBJECT * obj, CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount) { TEMPLATE *obj_tmpl = NULL; CK_ATTRIBUTE *attr = NULL; CK_ULONG i; CK_BBOOL flag; CK_RV rc; rc = CKR_OK; obj_tmpl = obj->template; for (i = 0; i < ulCount; i++) { flag = template_check_exportability(obj_tmpl, pTemplate[i].type); if (flag == FALSE) { TRACE_ERROR("%s: %lx\n", ock_err(ERR_ATTRIBUTE_SENSITIVE), pTemplate[i].type); rc = CKR_ATTRIBUTE_SENSITIVE; pTemplate[i].ulValueLen = (CK_ULONG) - 1; continue; } flag = template_attribute_find(obj_tmpl, pTemplate[i].type, &attr); if (flag == FALSE) { TRACE_ERROR("%s: %lx\n", ock_err(ERR_ATTRIBUTE_TYPE_INVALID), pTemplate[i].type); rc = CKR_ATTRIBUTE_TYPE_INVALID; pTemplate[i].ulValueLen = (CK_ULONG) - 1; continue; } if (pTemplate[i].pValue == NULL) { pTemplate[i].ulValueLen = attr->ulValueLen; } else if (pTemplate[i].ulValueLen >= attr->ulValueLen) { if (attr->pValue != NULL) memcpy(pTemplate[i].pValue, attr->pValue, attr->ulValueLen); pTemplate[i].ulValueLen = attr->ulValueLen; } else { TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL)); rc = CKR_BUFFER_TOO_SMALL; pTemplate[i].ulValueLen = (CK_ULONG) - 1; } } return rc; } // object_set_attribute_values() // CK_RV object_set_attribute_values(STDLL_TokData_t * tokdata, OBJECT * obj, CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount) { TEMPLATE *new_tmpl = NULL; CK_BBOOL found; CK_ULONG class, subclass; CK_RV rc; if (!obj || !pTemplate) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } found = template_get_class(obj->template, &class, &subclass); if (found == FALSE) { TRACE_ERROR("Failed to find CKA_CLASS in object template.\n"); rc = CKR_FUNCTION_FAILED; goto error; } new_tmpl = (TEMPLATE *) malloc(sizeof(TEMPLATE)); if (!new_tmpl) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); return CKR_HOST_MEMORY; } memset(new_tmpl, 0x0, sizeof(TEMPLATE)); rc = template_add_attributes(new_tmpl, pTemplate, ulCount); if (rc != CKR_OK) { TRACE_DEVEL("template_add_attributes failed.\n"); goto error; } // the user cannot change object classes so we assume the existing // object attributes are valid. we still need to check the new attributes. // we cannot merge the new attributes in with the old ones and then check // for validity because some attributes are added internally and are not // allowed to be specified by the user (ie. CKA_LOCAL for key types) but // may still be part of the old template. // rc = template_validate_attributes(tokdata, new_tmpl, class, subclass, MODE_MODIFY); if (rc != CKR_OK) { TRACE_DEVEL("template_validate_attributes failed.\n"); goto error; } // merge in the new attributes // rc = template_merge(obj->template, &new_tmpl); if (rc != CKR_OK) { TRACE_DEVEL("template_merge failed.\n"); return rc; } return CKR_OK; error: // we only free the template if there was an error...otherwise the // object "owns" the template // if (new_tmpl) template_free(new_tmpl); return rc; } // // CK_RV object_restore(CK_BYTE * data, OBJECT ** new_obj, CK_BBOOL replace) { return object_restore_withSize(data, new_obj, replace, -1); } // //Modified object_restore to prevent buffer overflow //If data_size=-1, won't do bounds checking CK_RV object_restore_withSize(CK_BYTE * data, OBJECT ** new_obj, CK_BBOOL replace, int data_size) { TEMPLATE *tmpl = NULL; OBJECT *obj = NULL; CK_ULONG offset = 0; CK_ULONG_32 count = 0; CK_RV rc; CK_OBJECT_CLASS_32 class32; if (!data || !new_obj) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } obj = (OBJECT *) malloc(sizeof(OBJECT)); if (!obj) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto error; } memset(obj, 0x0, sizeof(OBJECT)); memcpy(&class32, data + offset, sizeof(CK_OBJECT_CLASS_32)); obj->class = class32; offset += sizeof(CK_OBJECT_CLASS_32); memcpy(&count, data + offset, sizeof(CK_ULONG_32)); offset += sizeof(CK_ULONG_32); memcpy(&obj->name, data + offset, 8); offset += 8; rc = template_unflatten_withSize(&tmpl, data + offset, count, data_size); if (rc != CKR_OK) { TRACE_DEVEL("template_unflatten_withSize failed.\n"); goto error; } obj->template = tmpl; tmpl = NULL; if (replace == FALSE) { rc = object_init_lock(obj); if (rc != CKR_OK) goto error; *new_obj = obj; } else { /* Reload of existing object only changes the template */ template_free((*new_obj)->template); (*new_obj)->template = obj->template; free(obj); // don't want to do object_free() here! } return CKR_OK; error: if (obj) object_free(obj); if (tmpl) template_free(tmpl); return rc; } // // CK_RV object_create_skel(STDLL_TokData_t * tokdata, CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount, CK_ULONG mode, CK_ULONG class, CK_ULONG subclass, OBJECT ** obj) { TEMPLATE *tmpl = NULL; TEMPLATE *tmpl2 = NULL; OBJECT *o = NULL; CK_RV rc; if (!obj) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } if (!pTemplate && (ulCount != 0)) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } o = (OBJECT *) calloc(1, sizeof(OBJECT)); tmpl = (TEMPLATE *) calloc(1, sizeof(TEMPLATE)); tmpl2 = (TEMPLATE *) calloc(1, sizeof(TEMPLATE)); if (!o || !tmpl || !tmpl2) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } rc = template_add_attributes(tmpl2, pTemplate, ulCount); if (rc != CKR_OK) goto done; // at this point, the new template has the list of attributes. we need // to do some more checking now: // 1) invalid attribute values // 2) missing required attributes // 3) attributes inappropriate for the object class // 4) conflicting attributes/values // rc = template_validate_attributes(tokdata, tmpl2, class, subclass, mode); if (rc != CKR_OK) { TRACE_DEVEL("template_validate_attributes failed.\n"); goto done; } rc = template_check_required_attributes(tmpl2, class, subclass, mode); if (rc != CKR_OK) { TRACE_DEVEL("template_check_required_attributes failed.\n"); goto done; } rc = template_add_default_attributes(tmpl, tmpl2, class, subclass, mode); if (rc != CKR_OK) goto done; rc = template_merge(tmpl, &tmpl2); if (rc != CKR_OK) { TRACE_DEVEL("template_merge failed.\n"); goto done; } // at this point, we should have a valid object with correct attributes // o->template = tmpl; tmpl = NULL; rc = object_init_lock(o); if (rc != CKR_OK) goto done; *obj = o; return CKR_OK; done: if (o) free(o); if (tmpl) template_free(tmpl); if (tmpl2) template_free(tmpl2); return rc; } CK_RV object_init_lock(OBJECT *obj) { if (pthread_rwlock_init(&obj->template_rwlock, NULL) != 0) { TRACE_DEVEL("Object Lock init failed.\n"); return CKR_CANT_LOCK; } return CKR_OK; } CK_RV object_destroy_lock(OBJECT *obj) { if (pthread_rwlock_destroy(&obj->template_rwlock) != 0) { TRACE_DEVEL("Object Lock destroy failed.\n"); return CKR_CANT_LOCK; } return CKR_OK; } /* * Do NOT try to get an object lock, if the current thread holds the * XProcLock! This might case a deadlock ! * Always first acquire the Object lock, and then the XProcLock. */ CK_RV object_lock(OBJECT *obj, OBJ_LOCK_TYPE type) { switch (type) { case NO_LOCK: break; case READ_LOCK: if (pthread_rwlock_rdlock(&obj->template_rwlock) != 0) { TRACE_DEVEL("Object Read-Lock failed.\n"); return CKR_CANT_LOCK; } break; case WRITE_LOCK: if (pthread_rwlock_wrlock(&obj->template_rwlock) != 0) { TRACE_DEVEL("Object Write-Lock failed.\n"); return CKR_CANT_LOCK; } break; } return CKR_OK; } CK_RV object_unlock(OBJECT *obj) { if (pthread_rwlock_unlock(&obj->template_rwlock) != 0) { TRACE_DEVEL("Object Unlock failed.\n"); return CKR_CANT_LOCK; } return CKR_OK; }