/* * 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: obj_mgr.c // // Object manager related functions // #include #include #include #include #include #include "pkcs11types.h" #include "defs.h" #include "host_defs.h" #include "h_extern.h" #include "tok_spec_struct.h" #include "trace.h" #include "../api/apiproto.h" CK_RV object_mgr_add(STDLL_TokData_t *tokdata, SESSION *sess, CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE *handle) { OBJECT *o = NULL; CK_BBOOL priv_obj, sess_obj, added = FALSE, locked = FALSE; CK_RV rc; unsigned long obj_handle; if (!sess || !pTemplate || !handle) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_ARGUMENTS_BAD; } rc = object_create(tokdata, pTemplate, ulCount, &o); if (rc != CKR_OK) { TRACE_DEVEL("Object Create failed.\n"); goto done; } if (token_specific.t_object_add != NULL) { rc = token_specific.t_object_add(tokdata, sess, o); if (rc != CKR_OK) { TRACE_DEVEL("Token specific object add failed.\n"); goto done; } } // check whether session has permissions to create the object, etc // // Object R/O R/W R/O R/W R/W // Type Public Public User User SO // ------------------------------------------------------------- // Public session R/W R/W R/W R/W R/W // Private session R/W R/W // Public token R/O R/W R/O R/W R/W // Private token R/O R/W // sess_obj = object_is_session_object(o); priv_obj = object_is_private(o); if (sess->session_info.state == CKS_RO_PUBLIC_SESSION) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } if (!sess_obj) { TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); rc = CKR_SESSION_READ_ONLY; goto done; } } if (sess->session_info.state == CKS_RO_USER_FUNCTIONS) { if (!sess_obj) { TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); rc = CKR_SESSION_READ_ONLY; goto done; } } if (sess->session_info.state == CKS_RW_PUBLIC_SESSION) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } } if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } } // okay, object is created and the session permissions look okay. // add the object to the appropriate list and assign an object handle // if (sess_obj) { o->session = sess; memset(o->name, 0x00, sizeof(CK_BYTE) * 8); if ((obj_handle = bt_node_add(&tokdata->sess_obj_btree, o)) == 0) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } else { CK_BYTE current[8]; CK_BYTE next[8]; // we'll be modifying nv_token_data so we should protect this part with // the 'XProcLock' // rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); goto done; } locked = TRUE; // Determine if we have already reached our Max Token Objects // if (priv_obj) { if (tokdata->global_shm->num_priv_tok_obj >= MAX_TOK_OBJS) { rc = CKR_HOST_MEMORY; TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); goto done; } } else { if (tokdata->global_shm->num_publ_tok_obj >= MAX_TOK_OBJS) { rc = CKR_HOST_MEMORY; TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); goto done; } } memcpy(current, &tokdata->nv_token_data->next_token_object_name, 8); o->session = NULL; memcpy(&o->name, current, 8); rc = compute_next_token_obj_name(current, next); if (rc != CKR_OK) { // TODO: handle error, check if rc is a valid per spec goto done; } memcpy(&tokdata->nv_token_data->next_token_object_name, next, 8); rc = save_token_object(tokdata, o); if (rc != CKR_OK) { // TODO: handle error, check if rc is a valid per spec goto done; } // add the object identifier to the shared memory segment // object_mgr_add_to_shm(o, tokdata->global_shm); // save_token_data has to lock the mutex itself because it's used // elsewhere rc = save_token_data(tokdata, sess->session_info.slotID); if (rc != CKR_OK) { // TODO: handle error, check if rc is a valid per spec goto done; } // now, store the object in the appropriate btree // if (priv_obj) obj_handle = bt_node_add(&tokdata->priv_token_obj_btree, o); else obj_handle = bt_node_add(&tokdata->publ_token_obj_btree, o); if (!obj_handle) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } added = TRUE; rc = object_mgr_add_to_map(tokdata, sess, o, obj_handle, handle); if (rc != CKR_OK) { // we need to remove the object from whatever btree we just added it to if (sess_obj) { // put the binary tree node which holds o on the free list, but // pass NULL here, so that o (the binary tree node's value pointer) // isn't touched. It is free'd below bt_node_free(&tokdata->sess_obj_btree, obj_handle, FALSE); } else { // we'll want to delete the token object file too! // delete_token_object(tokdata, o); if (priv_obj) { // put the binary tree node which holds o on the free list, but // pass NULL here, so that o (the binary tree node's value // pointer) isn't touched. It is free'd below bt_node_free(&tokdata->priv_token_obj_btree, obj_handle, FALSE); } else { // put the binary tree node which holds o on the free list, but // pass NULL here, so that o (the binary tree node's value // pointer) isn't touched. It is free'd below bt_node_free(&tokdata->publ_token_obj_btree, obj_handle, FALSE); } object_mgr_del_from_shm(o, tokdata->global_shm); } } done: if ((rc != CKR_OK) && (o != NULL)) { if (!added) object_free(o); else object_put(tokdata, o, FALSE); o = NULL; } if (locked) { if (rc == CKR_OK) { rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); } } else { /* return error that occurred first */ XProcUnLock(tokdata); } } return rc; } // object_mgr_add_to_map() // CK_RV object_mgr_add_to_map(STDLL_TokData_t *tokdata, SESSION *sess, OBJECT *obj, unsigned long obj_handle, CK_OBJECT_HANDLE *map_handle) { OBJECT_MAP *map_node = NULL; UNUSED(tokdata); if (!sess || !obj || !map_handle) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } // // this guy doesn't lock a mutex because it's calling routines should have // already locked it // map_node = (OBJECT_MAP *) malloc(sizeof(OBJECT_MAP)); if (!map_node) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); return CKR_HOST_MEMORY; } map_node->session = sess; if (obj->session != NULL) map_node->is_session_obj = TRUE; else map_node->is_session_obj = FALSE; map_node->is_private = object_is_private(obj); // map_node->obj_handle will store the index of the btree node in one of // these lists: // publ_token_obj_btree - for public token object // priv_token_obj_btree - for private token objects // sess_obj_btree - for session objects // // *map_handle, the application's CK_OBJECT_HANDLE, will then be the index // of the btree node in the object_map_btree // map_node->obj_handle = obj_handle; *map_handle = bt_node_add(&tokdata->object_map_btree, map_node); if (*map_handle == 0) { free(map_node); TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); return CKR_HOST_MEMORY; } obj->map_handle = *map_handle; return CKR_OK; } // object_mgr_copy() // // algorithm: // 1) find the old object // 2) get the template from the old object // 3) merge in the new object's template // 4) perform class-specific sanity checks // CK_RV object_mgr_copy(STDLL_TokData_t *tokdata, SESSION *sess, CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE old_handle, CK_OBJECT_HANDLE *new_handle) { OBJECT *old_obj = NULL; OBJECT *new_obj = NULL; CK_BBOOL priv_obj; CK_BBOOL sess_obj; CK_BBOOL added = FALSE, locked = FALSE; CK_RV rc; unsigned long obj_handle; if (!sess || (!pTemplate && ulCount) || !new_handle) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } rc = object_mgr_find_in_map1(tokdata, old_handle, &old_obj, READ_LOCK); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_find_in_map1 failed.\n"); goto done; } rc = object_copy(tokdata, pTemplate, ulCount, old_obj, &new_obj); if (rc != CKR_OK) { TRACE_DEVEL("Object Copy failed.\n"); goto done; } // check whether session has permissions to create the object, etc // // Object R/O R/W R/O R/W R/W // Type Public Public User User SO // ------------------------------------------------------------- // Public session R/W R/W R/W R/W R/W // Private session R/W R/W // Public token R/O R/W R/O R/W R/W // Private token R/O R/W // sess_obj = object_is_session_object(new_obj); priv_obj = object_is_private(new_obj); if (sess->session_info.state == CKS_RO_PUBLIC_SESSION) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } if (!sess_obj) { TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); rc = CKR_SESSION_READ_ONLY; goto done; } } if (sess->session_info.state == CKS_RO_USER_FUNCTIONS) { if (!sess_obj) { TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); rc = CKR_SESSION_READ_ONLY; goto done; } } if (sess->session_info.state == CKS_RW_PUBLIC_SESSION) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } } if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } } // okay, object is created and the session permissions look okay. // add the object to the appropriate list and assign an object handle // if (sess_obj) { new_obj->session = sess; memset(&new_obj->name, 0x00, sizeof(CK_BYTE) * 8); if ((obj_handle = bt_node_add(&tokdata->sess_obj_btree, new_obj)) == 0) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } else { CK_BYTE current[8]; CK_BYTE next[8]; // we'll be modifying nv_token_data so we should protect this part // with 'XProcLock' // rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); goto done; } locked = TRUE; // Determine if we have already reached our Max Token Objects // if (priv_obj) { if (tokdata->global_shm->num_priv_tok_obj >= MAX_TOK_OBJS) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } else { if (tokdata->global_shm->num_publ_tok_obj >= MAX_TOK_OBJS) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } memcpy(current, &tokdata->nv_token_data->next_token_object_name, 8); new_obj->session = NULL; memcpy(&new_obj->name, current, 8); compute_next_token_obj_name(current, next); memcpy(&tokdata->nv_token_data->next_token_object_name, next, 8); save_token_object(tokdata, new_obj); // add the object identifier to the shared memory segment // object_mgr_add_to_shm(new_obj, tokdata->global_shm); save_token_data(tokdata, sess->session_info.slotID); // now, store the object in the token object btree // if (priv_obj) obj_handle = bt_node_add(&tokdata->priv_token_obj_btree, new_obj); else obj_handle = bt_node_add(&tokdata->publ_token_obj_btree, new_obj); if (!obj_handle) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } added = TRUE; rc = object_mgr_add_to_map(tokdata, sess, new_obj, obj_handle, new_handle); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_add_to_map failed.\n"); // this is messy but we need to remove the object from whatever // list we just added it to // if (sess_obj) { // put the binary tree node which holds new_obj on the free list, // but pass NULL here, so that new_obj (the binary tree node's value // pointer) isn't touched. It is free'd below bt_node_free(&tokdata->sess_obj_btree, obj_handle, FALSE); } else { delete_token_object(tokdata, new_obj); if (priv_obj) { // put the binary tree node which holds new_obj on the free // list, but pass NULL here, so that new_obj (the binary tree // node's value pointer) isn't touched. It is free'd below bt_node_free(&tokdata->priv_token_obj_btree, obj_handle, FALSE); } else { // put the binary tree node which holds new_obj on the free // list, but pass NULL here, so that new_obj (the binary tree // node's value pointer) isn't touched. It is free'd below bt_node_free(&tokdata->publ_token_obj_btree, obj_handle, FALSE); } object_mgr_del_from_shm(new_obj, tokdata->global_shm); } } done: if ((rc != CKR_OK) && (new_obj != NULL)) { if (!added) object_free(new_obj); else object_put(tokdata, new_obj, FALSE); new_obj = NULL; } object_put(tokdata, old_obj, TRUE); old_obj = NULL; if (locked) { if (rc == CKR_OK) { rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); goto done; } } else { /* return error that occurred first */ XProcUnLock(tokdata); } } return rc; } // determines whether the session is allowed to create an object. creates // the object but doesn't add the object to any object lists or to the // process' object map. // CK_RV object_mgr_create_skel(STDLL_TokData_t *tokdata, SESSION *sess, CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount, CK_ULONG mode, CK_ULONG obj_type, CK_ULONG sub_class, OBJECT **obj) { OBJECT *o = NULL; CK_RV rc; CK_BBOOL priv_obj; CK_BBOOL sess_obj; if (!sess || !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; } // // we don't need to lock mutex for this routine // rc = object_create_skel(tokdata, pTemplate, ulCount, mode, obj_type, sub_class, &o); if (rc != CKR_OK) { TRACE_DEVEL("object_create_skel failed.\n"); return rc; } sess_obj = object_is_session_object(o); priv_obj = object_is_private(o); if (sess->session_info.state == CKS_RO_PUBLIC_SESSION) { if (priv_obj) { object_free(o); TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); return CKR_USER_NOT_LOGGED_IN; } if (!sess_obj) { object_free(o); TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); return CKR_SESSION_READ_ONLY; } } if (sess->session_info.state == CKS_RO_USER_FUNCTIONS) { if (!sess_obj) { object_free(o); TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); return CKR_SESSION_READ_ONLY; } } if (sess->session_info.state == CKS_RW_PUBLIC_SESSION) { if (priv_obj) { object_free(o); TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); return CKR_USER_NOT_LOGGED_IN; } } if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) { if (priv_obj) { object_free(o); TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); return CKR_USER_NOT_LOGGED_IN; } } *obj = o; return CKR_OK; } /* * Finalizes the object creation and adds the object into the appropriate * btree and also the object map btree. * When this function succeeds, object obj must not be freed! It has been added * to the btree and thus must be kept intact. * When this function fails, then the object obj must be freed by the caller * using object_free() (not object_put() nor bt_put_node_value() !) */ CK_RV object_mgr_create_final(STDLL_TokData_t *tokdata, SESSION *sess, OBJECT *obj, CK_OBJECT_HANDLE *handle) { CK_BBOOL sess_obj; CK_BBOOL priv_obj; CK_BBOOL locked = FALSE; CK_RV rc; unsigned long obj_handle; if (!sess || !obj || !handle) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } sess_obj = object_is_session_object(obj); priv_obj = object_is_private(obj); if (sess_obj) { obj->session = sess; memset(obj->name, 0x0, sizeof(CK_BYTE) * 8); if ((obj_handle = bt_node_add(&tokdata->sess_obj_btree, obj)) == 0) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); return CKR_HOST_MEMORY; } } else { CK_BYTE current[8]; CK_BYTE next[8]; // we'll be modifying nv_token_data so we should protect this part // with 'XProcLock' // rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); return rc; } locked = TRUE; // Determine if we have already reached our Max Token Objects // if (priv_obj) { if (tokdata->global_shm->num_priv_tok_obj >= MAX_TOK_OBJS) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } else { if (tokdata->global_shm->num_publ_tok_obj >= MAX_TOK_OBJS) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } memcpy(current, &tokdata->nv_token_data->next_token_object_name, 8); obj->session = NULL; memcpy(&obj->name, current, 8); compute_next_token_obj_name(current, next); memcpy(&tokdata->nv_token_data->next_token_object_name, next, 8); save_token_object(tokdata, obj); // add the object identifier to the shared memory segment // object_mgr_add_to_shm(obj, tokdata->global_shm); save_token_data(tokdata, sess->session_info.slotID); // now, store the object in the token object btree // if (priv_obj) obj_handle = bt_node_add(&tokdata->priv_token_obj_btree, obj); else obj_handle = bt_node_add(&tokdata->publ_token_obj_btree, obj); if (!obj_handle) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto done; } } rc = object_mgr_add_to_map(tokdata, sess, obj, obj_handle, handle); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_add_to_map failed.\n"); // this is messy but we need to remove the object from whatever // list we just added it to // if (sess_obj) { // put the binary tree node which holds obj on the free list, but // pass NULL here, so that obj (the binary tree node's value // pointer) isn't touched. // It is free'd by the caller of object_mgr_create_final bt_node_free(&tokdata->sess_obj_btree, obj_handle, FALSE); } else { delete_token_object(tokdata, obj); if (priv_obj) { // put the binary tree node which holds obj on the free list, // but pass NULL here, so that obj (the binary tree node's value // pointer) isn't touched. It is free'd by the caller of // object_mgr_create_final bt_node_free(&tokdata->priv_token_obj_btree, obj_handle, FALSE); } else { // put the binary tree node which holds obj on the free list, // but pass NULL here, so that obj (the binary tree node's value // pointer) isn't touched. It is free'd by the caller of // object_mgr_create_final bt_node_free(&tokdata->publ_token_obj_btree, obj_handle, FALSE); } object_mgr_del_from_shm(obj, tokdata->global_shm); } } done: if (locked) { if (rc == CKR_OK) { rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); } } else { /* return error that occurred first */ XProcUnLock(tokdata); } } return rc; } CK_RV object_mgr_destroy_object(STDLL_TokData_t *tokdata, SESSION *sess, CK_OBJECT_HANDLE handle) { CK_RV rc = CKR_OK; OBJECT_MAP *map; OBJECT *o = NULL; CK_BBOOL locked = FALSE; UNUSED(sess); /* Don't use a delete callback, the map will be freed below */ map = bt_node_free(&tokdata->object_map_btree, handle, FALSE); if (map == NULL) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } if (map->is_session_obj) { bt_node_free(&tokdata->sess_obj_btree, map->obj_handle, TRUE); } else { if (XProcLock(tokdata)) { TRACE_ERROR("Failed to get Process Lock.\n"); return CKR_CANT_LOCK; } locked = TRUE; if (map->is_private) o = bt_get_node_value(&tokdata->priv_token_obj_btree, map->obj_handle); else o = bt_get_node_value(&tokdata->publ_token_obj_btree, map->obj_handle); if (!o) { rc = CKR_OBJECT_HANDLE_INVALID; goto done; } delete_token_object(tokdata, o); DUMP_SHM(tokdata->global_shm, "before"); object_mgr_del_from_shm(o, tokdata->global_shm); DUMP_SHM(tokdata->global_shm, "after"); if (map->is_private) { bt_put_node_value(&tokdata->priv_token_obj_btree, o); bt_node_free(&tokdata->priv_token_obj_btree, map->obj_handle, TRUE); } else { bt_put_node_value(&tokdata->publ_token_obj_btree, o); bt_node_free(&tokdata->publ_token_obj_btree, map->obj_handle, TRUE); } o = NULL; } done: if (o != NULL) { if (map->is_private) bt_put_node_value(&tokdata->priv_token_obj_btree, o); else bt_put_node_value(&tokdata->publ_token_obj_btree, o); o = NULL; } bt_put_node_value(&tokdata->object_map_btree, map); if (locked) { if (rc == CKR_OK) { rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); } } else { /* return error that occurred first */ XProcUnLock(tokdata); } } return rc; } /* delete_token_obj_cb * * Callback to delete an object if its a token object */ void delete_token_obj_cb(STDLL_TokData_t *tokdata, void *node, unsigned long map_handle, void *p3) { OBJECT_MAP *map = (OBJECT_MAP *) node; OBJECT *o = NULL; CK_BBOOL locked = FALSE; UNUSED(p3); if (!(map->is_session_obj)) { if (map->is_private) o = bt_get_node_value(&tokdata->priv_token_obj_btree, map->obj_handle); else o = bt_get_node_value(&tokdata->publ_token_obj_btree, map->obj_handle); if (!o) goto done; /* Use the same calling convention as the old code, if * XProcLock fails, don't delete from shm and don't free * the object in its other btree */ if (XProcLock(tokdata)) { TRACE_ERROR("Failed to get Process Lock.\n"); goto done; } locked = TRUE; delete_token_object(tokdata, o); object_mgr_del_from_shm(o, tokdata->global_shm); if (map->is_private) { bt_put_node_value(&tokdata->priv_token_obj_btree, o); bt_node_free(&tokdata->priv_token_obj_btree, map->obj_handle, TRUE); } else { bt_put_node_value(&tokdata->publ_token_obj_btree, o); bt_node_free(&tokdata->publ_token_obj_btree, map->obj_handle, TRUE); } o = NULL; } done: if (o != NULL) { if (map->is_private) bt_put_node_value(&tokdata->priv_token_obj_btree, o); else bt_put_node_value(&tokdata->publ_token_obj_btree, o); o = NULL; } /* delete @node from this btree */ bt_node_free(&tokdata->object_map_btree, map_handle, TRUE); if (locked) { if (XProcUnLock(tokdata)) { TRACE_ERROR("Failed to release Process Lock.\n"); } } } // this routine will destroy all token objects in the system // CK_RV object_mgr_destroy_token_objects(STDLL_TokData_t *tokdata) { CK_RV rc; rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); goto done; } bt_for_each_node(tokdata, &tokdata->object_map_btree, delete_token_obj_cb, NULL); // now we want to purge the token object list in shared memory // tokdata->global_shm->num_priv_tok_obj = 0; tokdata->global_shm->num_publ_tok_obj = 0; memset(&tokdata->global_shm->publ_tok_objs, 0x0, MAX_TOK_OBJS * sizeof(TOK_OBJ_ENTRY)); memset(&tokdata->global_shm->priv_tok_objs, 0x0, MAX_TOK_OBJS * sizeof(TOK_OBJ_ENTRY)); rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); goto done; } done: return rc; } // object_mgr_find_in_map_nocache() // // Locates the specified object in the map // without going and checking for cache update // // The returned Object must be put back (using object_put()) by the // caller to decrease the reference count! // // The returned object is locked (depending on lock_type), and must be unlocked // by the caller! // CK_RV object_mgr_find_in_map_nocache(STDLL_TokData_t *tokdata, CK_OBJECT_HANDLE handle, OBJECT **ptr, OBJ_LOCK_TYPE lock_type) { OBJECT_MAP *map = NULL; OBJECT *obj = NULL; CK_RV rc = CKR_OK; if (!ptr) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } if (!handle) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } // // no mutex here. the calling function should have locked the mutex // map = bt_get_node_value(&tokdata->object_map_btree, handle); if (!map) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } if (map->is_session_obj) obj = bt_get_node_value(&tokdata->sess_obj_btree, map->obj_handle); else if (map->is_private) obj = bt_get_node_value(&tokdata->priv_token_obj_btree, map->obj_handle); else obj = bt_get_node_value(&tokdata->publ_token_obj_btree, map->obj_handle); bt_put_node_value(&tokdata->object_map_btree, map); map = NULL; if (!obj) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } rc = object_lock(obj, lock_type); if (rc != CKR_OK) { object_put(tokdata, obj, FALSE); obj = NULL; return rc; } *ptr = obj; return rc; } // object_mgr_find_in_map1() // // Locates the specified object in the map // // The returned Object must be put back (using object_put()) by the // caller to decrease the reference count! // // The returned object is locked (depending on lock_type), and must be unlocked // by the caller! // CK_RV object_mgr_find_in_map1(STDLL_TokData_t *tokdata, CK_OBJECT_HANDLE handle, OBJECT **ptr, OBJ_LOCK_TYPE lock_type) { OBJECT_MAP *map = NULL; OBJECT *obj = NULL; CK_RV rc = CKR_OK; CK_BBOOL session_obj, locked = FALSE; if (!ptr) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } map = bt_get_node_value(&tokdata->object_map_btree, handle); if (!map) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } session_obj = map->is_session_obj; if (map->is_session_obj) obj = bt_get_node_value(&tokdata->sess_obj_btree, map->obj_handle); else if (map->is_private) obj = bt_get_node_value(&tokdata->priv_token_obj_btree, map->obj_handle); else obj = bt_get_node_value(&tokdata->publ_token_obj_btree, map->obj_handle); bt_put_node_value(&tokdata->object_map_btree, map); map = NULL; if (!obj) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } /* SAB XXX Fix me.. need to make it more efficient than just looking * for the object to be changed. set a global flag that contains the * ref count to all objects.. if the shm ref count changes, then we * update the object. if not */ /* Note: Each C_Initialize call loads up the public token objects * and build corresponding tree(s). The same for private token objects * upon successful C_Login. Since token objects can be shared, it is * possible another process or session has deleted a token object. * Accounting is done in shm, so check shm to see if object still exists. */ if (!session_obj) { /* object_mgr_check_shm() needs the object to hold the READ lock */ rc = object_lock(obj, READ_LOCK); if (rc != CKR_OK) goto done; locked = TRUE; rc = object_mgr_check_shm(tokdata, obj); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_check_shm failed.\n"); goto done; } if (lock_type == READ_LOCK) { /* already have the desired object lock */ rc = CKR_OK; goto done; } rc = object_unlock(obj); if (rc != CKR_OK) goto done; locked = FALSE; } rc = object_lock(obj, lock_type); if (rc != CKR_OK) goto done; done: if (rc == CKR_OK) { *ptr = obj; } else { object_put(tokdata, obj, locked); obj = NULL; } return rc; } void find_obj_cb(STDLL_TokData_t *tokdata, void *node, unsigned long map_handle, void *p3) { OBJECT_MAP *map = (OBJECT_MAP *) node; OBJECT *obj; struct find_args *fa = (struct find_args *) p3; UNUSED(tokdata); if (fa->done) return; if (map->is_session_obj) obj = bt_get_node_value(&tokdata->sess_obj_btree, map->obj_handle); else if (map->is_private) obj = bt_get_node_value(&tokdata->priv_token_obj_btree, map->obj_handle); else obj = bt_get_node_value(&tokdata->publ_token_obj_btree, map->obj_handle); if (!obj) return; /* if this object is the one we're looking for (matches p3->obj), return * its map_handle in p3->map_handle */ if (obj == fa->obj) { fa->map_handle = map_handle; fa->done = TRUE; } if (map->is_session_obj) bt_put_node_value(&tokdata->sess_obj_btree, obj); else if (map->is_private) bt_put_node_value(&tokdata->priv_token_obj_btree, obj); else bt_put_node_value(&tokdata->publ_token_obj_btree, obj); obj = NULL; } // object_mgr_find_in_map2() // // The caller must already have locked the passed object (READ_LOCK)! // CK_RV object_mgr_find_in_map2(STDLL_TokData_t *tokdata, OBJECT *obj, CK_OBJECT_HANDLE *handle) { struct find_args fa; CK_RV rc; if (!obj || !handle) { TRACE_ERROR("Invalid function arguments.\n"); return CKR_FUNCTION_FAILED; } fa.done = FALSE; fa.obj = obj; fa.map_handle = 0; // pass the fa structure with the values to operate on in the find_obj_cb // function bt_for_each_node(tokdata, &tokdata->object_map_btree, find_obj_cb, &fa); if (fa.done == FALSE || fa.map_handle == 0) { return CKR_OBJECT_HANDLE_INVALID; } *handle = fa.map_handle; if (!object_is_session_object(obj)) { rc = object_mgr_check_shm(tokdata, obj); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_check_shm failed.\n"); return rc; } } return CKR_OK; } void find_build_list_cb(STDLL_TokData_t *tokdata, void *node, unsigned long obj_handle, void *p3) { OBJECT *obj = (OBJECT *) node; struct find_build_list_args *fa = (struct find_build_list_args *) p3; CK_OBJECT_HANDLE map_handle; CK_ATTRIBUTE *attr; CK_BBOOL match = FALSE; CK_RV rc; if (object_lock(obj, READ_LOCK) != CKR_OK) return; if ((object_is_private(obj) == FALSE) || (fa->public_only == FALSE)) { // if the user doesn't specify any template attributes then we return // all objects // if (fa->pTemplate == NULL || fa->ulCount == 0) match = TRUE; else match = template_compare(fa->pTemplate, fa->ulCount, obj->template); } // if we have a match, find the object in the map (add it if necessary) // then add the object to the list of found objects // if (match) { rc = object_mgr_find_in_map2(tokdata, obj, &map_handle); if (rc != CKR_OK) { rc = object_mgr_add_to_map(tokdata, fa->sess, obj, obj_handle, &map_handle); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_add_to_map failed.\n"); goto done; } } // If hw_feature is false here, we need to filter out all objects // that have the CKO_HW_FEATURE attribute set. - KEY if ((fa->hw_feature == FALSE) && (template_attribute_find(obj->template, CKA_CLASS, &attr) == TRUE)) { if (attr->pValue == NULL) { TRACE_DEVEL("%s\n", ock_err(ERR_GENERAL_ERROR)); goto done; } if (*(CK_OBJECT_CLASS *) attr->pValue == CKO_HW_FEATURE) goto done; } /* Don't find objects that have been created with the CKA_HIDDEN * attribute set */ if ((fa->hidden_object == FALSE) && (template_attribute_find(obj->template, CKA_HIDDEN, &attr) == TRUE)) { if (*(CK_BBOOL *) attr->pValue == TRUE) goto done; } fa->sess->find_list[fa->sess->find_count] = map_handle; fa->sess->find_count++; if (fa->sess->find_count >= fa->sess->find_len) { fa->sess->find_len += 15; fa->sess->find_list = (CK_OBJECT_HANDLE *) realloc(fa->sess->find_list, fa->sess->find_len * sizeof(CK_OBJECT_HANDLE)); if (!fa->sess->find_list) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); goto done; } } } done: object_unlock(obj); } CK_RV object_mgr_find_init(STDLL_TokData_t *tokdata, SESSION *sess, CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount) { struct find_build_list_args fa; CK_ULONG i; CK_RV rc; // it is possible the pTemplate == NULL // if (!sess) { TRACE_ERROR("Invalid function argument.\n"); return CKR_FUNCTION_FAILED; } if (sess->find_active != FALSE) { return CKR_OPERATION_ACTIVE; TRACE_ERROR("%s\n", ock_err(ERR_OPERATION_ACTIVE)); } // initialize the found object list. if it doesn't exist, allocate // a list big enough for 10 handles. we'll reallocate if we need more // if (sess->find_list != NULL) { memset(sess->find_list, 0x0, sess->find_len * sizeof(CK_OBJECT_HANDLE)); } else { sess->find_list = (CK_OBJECT_HANDLE *) malloc(10 * sizeof(CK_OBJECT_HANDLE)); if (!sess->find_list) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); return CKR_HOST_MEMORY; } else { memset(sess->find_list, 0x0, 10 * sizeof(CK_OBJECT_HANDLE)); sess->find_len = 10; } } sess->find_count = 0; sess->find_idx = 0; rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); return rc; } object_mgr_update_from_shm(tokdata); rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); return rc; } fa.hw_feature = FALSE; fa.hidden_object = FALSE; fa.sess = sess; fa.pTemplate = pTemplate; fa.ulCount = ulCount; // which objects can be returned: // // Public Session: public session objects, public token objects // User Session: all session objects, all token objects // SO session: public session objects, public token objects // // PKCS#11 v2.11 (pg. 79): "When searching using C_FindObjectsInit // and C_FindObjects, hardware feature objects are not returned // unless the CKA_CLASS attribute in the template has the value // CKO_HW_FEATURE." So, we check for CKO_HW_FEATURE and if its set, // we'll find these objects below. - KEY for (i = 0; i < ulCount; i++) { if (pTemplate[i].type == CKA_CLASS) { if (*(CK_ULONG *) pTemplate[i].pValue == CKO_HW_FEATURE) { fa.hw_feature = TRUE; } } /* only find CKA_HIDDEN objects if its specified in the template. */ if (pTemplate[i].type == CKA_HIDDEN) { if (*(CK_BBOOL *) pTemplate[i].pValue == TRUE) { fa.hidden_object = TRUE; } } } switch (sess->session_info.state) { case CKS_RO_PUBLIC_SESSION: case CKS_RW_PUBLIC_SESSION: case CKS_RW_SO_FUNCTIONS: fa.public_only = TRUE; bt_for_each_node(tokdata, &tokdata->publ_token_obj_btree, find_build_list_cb, &fa); bt_for_each_node(tokdata, &tokdata->sess_obj_btree, find_build_list_cb, &fa); break; case CKS_RO_USER_FUNCTIONS: case CKS_RW_USER_FUNCTIONS: fa.public_only = FALSE; bt_for_each_node(tokdata, &tokdata->priv_token_obj_btree, find_build_list_cb, &fa); bt_for_each_node(tokdata, &tokdata->publ_token_obj_btree, find_build_list_cb, &fa); bt_for_each_node(tokdata, &tokdata->sess_obj_btree, find_build_list_cb, &fa); break; } sess->find_active = TRUE; return CKR_OK; } // // CK_RV object_mgr_find_final(SESSION *sess) { if (!sess) { TRACE_ERROR("Invalid function argument.\n"); return CKR_FUNCTION_FAILED; } if (sess->find_active == FALSE) { TRACE_ERROR("%s\n", ock_err(ERR_OPERATION_NOT_INITIALIZED)); return CKR_OPERATION_NOT_INITIALIZED; } free(sess->find_list); sess->find_list = NULL; sess->find_count = 0; sess->find_idx = 0; sess->find_active = FALSE; return CKR_OK; } // // CK_RV object_mgr_get_attribute_values(STDLL_TokData_t *tokdata, SESSION *sess, CK_OBJECT_HANDLE handle, CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount) { OBJECT *obj; CK_BBOOL priv_obj; CK_RV rc; if (!pTemplate) { TRACE_ERROR("Invalid function argument.\n"); return CKR_FUNCTION_FAILED; } rc = object_mgr_find_in_map1(tokdata, handle, &obj, READ_LOCK); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_find_in_map1 failed.\n"); return rc; } priv_obj = object_is_private(obj); if (priv_obj == TRUE) { if (sess->session_info.state == CKS_RO_PUBLIC_SESSION || sess->session_info.state == CKS_RW_PUBLIC_SESSION) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } } rc = object_get_attribute_values(obj, pTemplate, ulCount); if (rc != CKR_OK) TRACE_DEVEL("object_get_attribute_values failed.\n"); done: object_put(tokdata, obj, TRUE); obj = NULL; return rc; } // // CK_RV object_mgr_get_object_size(STDLL_TokData_t *tokdata, CK_OBJECT_HANDLE handle, CK_ULONG *size) { OBJECT *obj; CK_RV rc; rc = object_mgr_find_in_map1(tokdata, handle, &obj, READ_LOCK); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_find_in_map1 failed.\n"); return rc; } *size = object_get_size(obj); object_put(tokdata, obj, TRUE); obj = NULL; return rc; } void purge_session_obj_cb(STDLL_TokData_t *tokdata, void *node, unsigned long obj_handle, void *p3) { OBJECT *obj = (OBJECT *) node; struct purge_args *pa = (struct purge_args *) p3; CK_BBOOL del = FALSE; UNUSED(tokdata); if (obj->session == pa->sess) { if (object_lock(obj, READ_LOCK) != CKR_OK) return; if (pa->type == PRIVATE) { if (object_is_private(obj)) del = TRUE; } else if (pa->type == PUBLIC) { if (object_is_public(obj)) del = TRUE; } else if (pa->type == ALL) { del = TRUE; } object_unlock(obj); if (del == TRUE) { if (obj->map_handle) bt_node_free(&tokdata->object_map_btree, obj->map_handle, TRUE); bt_node_free(&tokdata->sess_obj_btree, obj_handle, TRUE); } } } // object_mgr_purge_session_objects() // // Args: SESSION * // SESS_OBJ_TYPE: can be ALL, PRIVATE or PUBLIC // // Remove all session objects owned by the specified session satisfying // the 'type' requirements // CK_BBOOL object_mgr_purge_session_objects(STDLL_TokData_t *tokdata, SESSION *sess, SESS_OBJ_TYPE type) { struct purge_args pa = { sess, type }; UNUSED(tokdata); if (!sess) return FALSE; bt_for_each_node(tokdata, &tokdata->sess_obj_btree, purge_session_obj_cb, &pa); return TRUE; } /* purge_token_obj_cb * * @p3 is the btree we're purging from */ void purge_token_obj_cb(STDLL_TokData_t *tokdata, void *node, unsigned long obj_handle, void *p3) { OBJECT *obj = (OBJECT *) node; struct btree *t = (struct btree *) p3; UNUSED(tokdata); if (obj->map_handle) bt_node_free(&tokdata->object_map_btree, obj->map_handle, TRUE); bt_node_free(t, obj_handle, TRUE); } // this routine cleans up the list of token objects. in general, we don't // need to do this but when tracing memory leaks, it's best that we free // everything that we've allocated // CK_BBOOL object_mgr_purge_token_objects(STDLL_TokData_t *tokdata) { bt_for_each_node(tokdata, &tokdata->priv_token_obj_btree, purge_token_obj_cb, &tokdata->priv_token_obj_btree); bt_for_each_node(tokdata, &tokdata->publ_token_obj_btree, purge_token_obj_cb, &tokdata->publ_token_obj_btree); return TRUE; } CK_BBOOL object_mgr_purge_private_token_objects(STDLL_TokData_t *tokdata) { bt_for_each_node(tokdata, &tokdata->priv_token_obj_btree, purge_token_obj_cb, &tokdata->priv_token_obj_btree); return TRUE; } // // CK_RV object_mgr_restore_obj(STDLL_TokData_t *tokdata, CK_BYTE *data, OBJECT *oldObj) { return object_mgr_restore_obj_withSize(tokdata, data, oldObj, -1); } // //Modified verrsion of object_mgr_restore_obj to bounds check //If data_size==-1, won't check bounds CK_RV object_mgr_restore_obj_withSize(STDLL_TokData_t *tokdata, CK_BYTE *data, OBJECT *oldObj, int data_size) { OBJECT *obj = NULL; CK_BBOOL priv; CK_RV rc, tmp; if (!data) { TRACE_ERROR("Invalid function argument.\n"); return CKR_FUNCTION_FAILED; } // The calling stack MUST have the mutex // to many grab it now. if (oldObj != NULL) { obj = oldObj; rc = object_restore_withSize(data, &obj, TRUE, data_size); } else { rc = object_restore_withSize(data, &obj, FALSE, data_size); if (rc == CKR_OK) { rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); return rc; } priv = object_is_private(obj); if (priv) { if (!bt_node_add(&tokdata->priv_token_obj_btree, obj)) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto unlock; } } else { if (!bt_node_add(&tokdata->publ_token_obj_btree, obj)) { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; goto unlock; } } if (priv) { if (tokdata->global_shm->priv_loaded == FALSE) { if (tokdata->global_shm->num_priv_tok_obj < MAX_TOK_OBJS) { object_mgr_add_to_shm(obj, tokdata->global_shm); } else { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; } } } else { if (tokdata->global_shm->publ_loaded == FALSE) { if (tokdata->global_shm->num_publ_tok_obj < MAX_TOK_OBJS) { object_mgr_add_to_shm(obj, tokdata->global_shm); } else { TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY)); rc = CKR_HOST_MEMORY; } } } unlock: tmp = XProcUnLock(tokdata); if (tmp != CKR_OK) TRACE_ERROR("Failed to release Process Lock.\n"); if (rc == CKR_OK) rc = tmp; } else { TRACE_DEVEL("object_restore_withSize failed.\n"); } } return rc; } // // CK_RV object_mgr_set_attribute_values(STDLL_TokData_t *tokdata, SESSION *sess, CK_OBJECT_HANDLE handle, CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount) { OBJECT *obj; CK_BBOOL sess_obj, priv_obj; CK_BBOOL modifiable; CK_RV rc; if (!pTemplate) { TRACE_ERROR("Invalid function argument.\n"); return CKR_FUNCTION_FAILED; } rc = object_mgr_find_in_map1(tokdata, handle, &obj, WRITE_LOCK); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_find_in_map1 failed.\n"); return rc; } // determine whether the session is allowed to modify the object // modifiable = object_is_modifiable(obj); sess_obj = object_is_session_object(obj); priv_obj = object_is_private(obj); // if object is not modifiable, it doesn't matter what kind of session // is issuing the request... // if (!modifiable) { TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_READ_ONLY)); rc = CKR_ATTRIBUTE_READ_ONLY; goto done; } if (sess->session_info.state == CKS_RO_PUBLIC_SESSION) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } if (!sess_obj) { TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); rc = CKR_SESSION_READ_ONLY; goto done; } } if (sess->session_info.state == CKS_RO_USER_FUNCTIONS) { if (!sess_obj) { TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY)); rc = CKR_SESSION_READ_ONLY; goto done; } } if (sess->session_info.state == CKS_RW_PUBLIC_SESSION) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } } if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) { if (priv_obj) { TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN)); rc = CKR_USER_NOT_LOGGED_IN; goto done; } } rc = object_set_attribute_values(tokdata, obj, pTemplate, ulCount); if (rc != CKR_OK) { TRACE_DEVEL("object_set_attribute_values failed.\n"); goto done; } // okay. the object has been updated. if it's a session object, // we're finished. if it's a token object, we need to update // non-volatile storage. // if (!sess_obj) { TOK_OBJ_ENTRY *entry = NULL; CK_ULONG index; // I still think there's a race condition here if two processes are // updating the same token object at the same time. I don't know how // to solve this short of assigning each token object it's own mutex... // obj->count_lo++; if (obj->count_lo == 0) obj->count_hi++; rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); goto done; } save_token_object(tokdata, obj); if (priv_obj) { if (tokdata->global_shm->num_priv_tok_obj == 0) { TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); rc = CKR_OBJECT_HANDLE_INVALID; XProcUnLock(tokdata); goto done; } rc = object_mgr_search_shm_for_obj(tokdata->global_shm-> priv_tok_objs, 0, tokdata->global_shm-> num_priv_tok_obj - 1, obj, &index); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_search_shm_for_obj failed.\n"); XProcUnLock(tokdata); goto done; } entry = &tokdata->global_shm->priv_tok_objs[index]; } else { if (tokdata->global_shm->num_publ_tok_obj == 0) { TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); rc = CKR_OBJECT_HANDLE_INVALID; XProcUnLock(tokdata); goto done; } rc = object_mgr_search_shm_for_obj(tokdata->global_shm-> publ_tok_objs, 0, tokdata->global_shm-> num_publ_tok_obj - 1, obj, &index); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_search_shm_for_obj failed.\n"); XProcUnLock(tokdata); goto done; } entry = &tokdata->global_shm->publ_tok_objs[index]; } entry->count_lo = obj->count_lo; entry->count_hi = obj->count_hi; rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); goto done; } } done: object_put(tokdata, obj, TRUE); obj = NULL; return rc; } // // void object_mgr_add_to_shm(OBJECT *obj, LW_SHM_TYPE *global_shm) { // TODO: Can't this function fail? TOK_OBJ_ENTRY *entry = NULL; CK_BBOOL priv; // the calling routine is responsible for locking the global_shm mutex // priv = object_is_private(obj); if (priv) entry = &global_shm->priv_tok_objs[global_shm->num_priv_tok_obj]; else entry = &global_shm->publ_tok_objs[global_shm->num_publ_tok_obj]; entry->deleted = FALSE; entry->count_lo = 0; entry->count_hi = 0; memcpy(entry->name, obj->name, 8); if (priv) { global_shm->num_priv_tok_obj++; object_mgr_sort_priv_shm(); } else { global_shm->num_publ_tok_obj++; object_mgr_sort_publ_shm(); } return; } // // CK_RV object_mgr_del_from_shm(OBJECT *obj, LW_SHM_TYPE *global_shm) { CK_ULONG index, count; CK_BBOOL priv; CK_RV rc; // the calling routine is responsible for locking the global_shm mutex // priv = object_is_private(obj); if (priv) { if (global_shm->num_priv_tok_obj == 0) { TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } rc = object_mgr_search_shm_for_obj(global_shm->priv_tok_objs, 0, global_shm->num_priv_tok_obj - 1, obj, &index); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_search_shm_for_obj failed.\n"); return rc; } // Since the number of objects starts at 1 and index starts at zero, we // decrement before we get count. This eliminates the need to perform // this operation later as well as decrementing the number of objects. // (i.e. If we have 10 objects, num will be 10 but the last index is 9. // If we want to delete the last object we need to subtract 9 from 9 not // 10 from 9.) // global_shm->num_priv_tok_obj--; if (index > global_shm->num_priv_tok_obj) { count = index - global_shm->num_priv_tok_obj; } else { count = global_shm->num_priv_tok_obj - index; } if (count > 0) { // If we aren't deleting the last element in the list // Move up count number of elements effectively deleting the index // NB: memmove is required since the copied regions may overlap memmove((char *) &global_shm->priv_tok_objs[index], (char *) &global_shm->priv_tok_objs[index + 1], sizeof(TOK_OBJ_ENTRY) * count); // We need to zero out the last entry... Since the memcopy // does not zero it out... memset((char *) &global_shm-> priv_tok_objs[global_shm->num_priv_tok_obj + 1], 0, sizeof(TOK_OBJ_ENTRY)); } else { // We are deleting the last element which is in num_priv_tok_obj memset((char *) &global_shm-> priv_tok_objs[global_shm->num_priv_tok_obj], 0, sizeof(TOK_OBJ_ENTRY)); } } else { if (global_shm->num_publ_tok_obj == 0) { TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } rc = object_mgr_search_shm_for_obj(global_shm->publ_tok_objs, 0, global_shm->num_publ_tok_obj - 1, obj, &index); if (rc != CKR_OK) { TRACE_DEVEL("object_mgr_search_shm_for_obj failed.\n"); return rc; } global_shm->num_publ_tok_obj--; if (index > global_shm->num_publ_tok_obj) { count = index - global_shm->num_publ_tok_obj; } else { count = global_shm->num_publ_tok_obj - index; } if (count > 0) { // NB: memmove is required since the copied regions may overlap memmove((char *) &global_shm->publ_tok_objs[index], (char *) &global_shm->publ_tok_objs[index + 1], sizeof(TOK_OBJ_ENTRY) * count); // We need to zero out the last entry... Since the memcopy // does not zero it out... memset((char *) &global_shm-> publ_tok_objs[global_shm->num_publ_tok_obj + 1], 0, sizeof(TOK_OBJ_ENTRY)); } else { memset((char *) &global_shm-> publ_tok_objs[global_shm->num_publ_tok_obj], 0, sizeof(TOK_OBJ_ENTRY)); } } // // object list is still sorted...so no need to re-sort // return CKR_OK; } // The object must hold the READ lock when this function is called! // CK_RV object_mgr_check_shm(STDLL_TokData_t *tokdata, OBJECT *obj) { TOK_OBJ_ENTRY *entry = NULL; CK_BBOOL priv, rd_locked = TRUE, wr_locked = FALSE; CK_ULONG index; CK_RV rc; priv = object_is_private(obj); retry: rc = XProcLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to get Process Lock.\n"); return rc; } if (priv) { /* first check the object count. If it is 0, then just return. */ if (tokdata->global_shm->num_priv_tok_obj == 0) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); rc = CKR_OBJECT_HANDLE_INVALID; goto done; } rc = object_mgr_search_shm_for_obj(tokdata->global_shm->priv_tok_objs, 0, tokdata->global_shm-> num_priv_tok_obj - 1, obj, &index); if (rc != CKR_OK) { TRACE_ERROR("object_mgr_search_shm_for_obj failed.\n"); goto done; } entry = &tokdata->global_shm->priv_tok_objs[index]; } else { /* first check the object count. If it is 0, then just return. */ if (tokdata->global_shm->num_publ_tok_obj == 0) { TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); rc = CKR_OBJECT_HANDLE_INVALID; goto done; } rc = object_mgr_search_shm_for_obj(tokdata->global_shm->publ_tok_objs, 0, tokdata->global_shm->num_publ_tok_obj - 1, obj, &index); if (rc != CKR_OK) { TRACE_ERROR("object_mgr_search_shm_for_obj failed.\n"); goto done; } entry = &tokdata->global_shm->publ_tok_objs[index]; } if ((obj->count_hi == entry->count_hi) && (obj->count_lo == entry->count_lo)) { rc = CKR_OK; goto done; } /* We need to acquire the WRITE lock on the object because we are modifying * the attributes. Since the object already holds the READ lock, we need to * unlock it first, then get the WRITE lock, and finally unlock again and * get the READ lock again. The caller assumes that the object still holds * the READ lock when we return. * * We must not hold the XProcLock when trying to get the WRITE lock on the * Objects. This might cause a deadlock, if another thread holds a READ or * WRITE lock on the object, and is also trying to get the XProcLock. */ if (rd_locked) { rc = object_unlock(obj); if (rc != CKR_OK) goto done; rd_locked = FALSE; } if (!wr_locked) { /* Try to get the WRITE lock, although we hold the XProcLock. If we get * it we take the fast path, if not, we release the XProcLock, then get * the WRITE lock and then get the XProcLock again. Since we have * released the XProcLock, we then need to re-do the SHM checking. */ if (pthread_rwlock_trywrlock(&obj->template_rwlock) != 0) { /* Did not get the WRITE lock */ rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); goto done; } rc = object_lock(obj, WRITE_LOCK); if (rc != CKR_OK) goto done; wr_locked = TRUE; goto retry; } wr_locked = TRUE; } /* If we reach here, we do have the WRITE lock on the object */ rc = reload_token_object(tokdata, obj); if (rc != CKR_OK) goto done; rc = object_unlock(obj); if (rc != CKR_OK) goto done; wr_locked = FALSE; /* Re-acquire the READ lock only after we have released the XProcLock ! */ done: if (rc == CKR_OK) { rc = XProcUnLock(tokdata); if (rc != CKR_OK) { TRACE_ERROR("Failed to release Process Lock.\n"); } } else { XProcUnLock(tokdata); } if (wr_locked) object_unlock(obj); if (!rd_locked) { if (rc == CKR_OK) rc = object_lock(obj, READ_LOCK); else object_lock(obj, READ_LOCK); } return rc; } // I'd use the standard bsearch() routine but I want an index, not a pointer. // Converting the pointer to an index might cause problems when switching // to a 64-bit environment... // CK_RV object_mgr_search_shm_for_obj(TOK_OBJ_ENTRY *obj_list, CK_ULONG lo, CK_ULONG hi, OBJECT *obj, CK_ULONG *index) { // SAB XXX reduce the search time since this is what seems to be burning cycles CK_ULONG idx; UNUSED(lo); if (obj->index == 0) { for (idx = 0; idx <= hi; idx++) { if (memcmp(obj->name, obj_list[idx].name, 8) == 0) { *index = idx; obj->index = idx; return CKR_OK; } } } else { // SAB better double check if (memcmp(obj->name, obj_list[obj->index].name, 8) == 0) { *index = obj->index; return CKR_OK; } else { // something is hosed.. go back to the brute force method for (idx = 0; idx <= hi; idx++) { if (memcmp(obj->name, obj_list[idx].name, 8) == 0) { *index = idx; obj->index = idx; return CKR_OK; } } } } TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID)); return CKR_OBJECT_HANDLE_INVALID; } // // CK_RV object_mgr_sort_priv_shm(void) { // for now, we assume the list is sorted by design. this is not unreasonable // since new object handles are assigned in increasing order. problems // will arise after 36^8 token objects have been created... // return CKR_OK; } // // CK_RV object_mgr_sort_publ_shm(void) { // for now, we assume the list is sorted by design. this is not unreasonable // since new object handles are assigned in increasing order. problems // will arise after 36^8 token objects have been created... // return CKR_OK; } // this routine scans the local token object lists and updates any objects that // have changed. it also adds any new token objects that have been added by // other processes and deletes any objects that have been deleted by other // processes // CK_RV object_mgr_update_from_shm(STDLL_TokData_t *tokdata) { object_mgr_update_publ_tok_obj_from_shm(tokdata); object_mgr_update_priv_tok_obj_from_shm(tokdata); return CKR_OK; } void delete_objs_from_btree_cb(STDLL_TokData_t *tokdata, void *node, unsigned long obj_handle, void *p3) { struct update_tok_obj_args *ua = (struct update_tok_obj_args *) p3; TOK_OBJ_ENTRY *shm_te = NULL; OBJECT *obj = (OBJECT *) node; CK_ULONG index; UNUSED(tokdata); /* for each TOK_OBJ_ENTRY in the SHM list */ for (index = 0; index < *(ua->num_entries); index++) { shm_te = &(ua->entries[index]); /* found it, return */ if (!memcmp(obj->name, shm_te->name, 8)) { return; } } /* didn't find it in SHM, delete it from its btree and the object map */ bt_node_free(&tokdata->object_map_btree, obj->map_handle, TRUE); bt_node_free(ua->t, obj_handle, TRUE); } void find_by_name_cb(STDLL_TokData_t *tokdata, void *node, unsigned long obj_handle, void *p3) { OBJECT *obj = (OBJECT *) node; struct find_by_name_args *fa = (struct find_by_name_args *) p3; UNUSED(tokdata); UNUSED(obj_handle); if (fa->done) return; if (!memcmp(obj->name, fa->name, 8)) { fa->done = TRUE; } } CK_RV object_mgr_update_publ_tok_obj_from_shm(STDLL_TokData_t *tokdata) { struct update_tok_obj_args ua; struct find_by_name_args fa; TOK_OBJ_ENTRY *shm_te = NULL; CK_ULONG index; OBJECT *new_obj; CK_RV rc; ua.entries = tokdata->global_shm->publ_tok_objs; ua.num_entries = &(tokdata->global_shm->num_publ_tok_obj); ua.t = &tokdata->publ_token_obj_btree; /* delete any objects not in SHM from the btree */ bt_for_each_node(tokdata, &tokdata->publ_token_obj_btree, delete_objs_from_btree_cb, &ua); /* for each item in SHM, add it to the btree if its not there */ for (index = 0; index < tokdata->global_shm->num_publ_tok_obj; index++) { shm_te = &tokdata->global_shm->publ_tok_objs[index]; fa.done = FALSE; fa.name = shm_te->name; /* find an object from SHM in the btree */ bt_for_each_node(tokdata, &tokdata->publ_token_obj_btree, find_by_name_cb, &fa); /* we didn't find it in the btree, so add it */ if (fa.done == FALSE) { new_obj = (OBJECT *) malloc(sizeof(OBJECT)); memset(new_obj, 0x0, sizeof(OBJECT)); rc = object_init_lock(new_obj); if (rc != CKR_OK) { free(new_obj); continue; } memcpy(new_obj->name, shm_te->name, 8); rc = reload_token_object(tokdata, new_obj); if (rc == CKR_OK) bt_node_add(&tokdata->publ_token_obj_btree, new_obj); else object_free(new_obj); } } return CKR_OK; } CK_RV object_mgr_update_priv_tok_obj_from_shm(STDLL_TokData_t *tokdata) { struct update_tok_obj_args ua; struct find_by_name_args fa; TOK_OBJ_ENTRY *shm_te = NULL; CK_ULONG index; OBJECT *new_obj; CK_RV rc; // SAB XXX don't bother doing this call if we are not in the correct // login state if (!session_mgr_user_session_exists(tokdata)) return CKR_OK; ua.entries = tokdata->global_shm->priv_tok_objs; ua.num_entries = &(tokdata->global_shm->num_priv_tok_obj); ua.t = &tokdata->priv_token_obj_btree; /* delete any objects not in SHM from the btree */ bt_for_each_node(tokdata, &tokdata->priv_token_obj_btree, delete_objs_from_btree_cb, &ua); /* for each item in SHM, add it to the btree if its not there */ for (index = 0; index < tokdata->global_shm->num_priv_tok_obj; index++) { shm_te = &tokdata->global_shm->priv_tok_objs[index]; fa.done = FALSE; fa.name = shm_te->name; /* find an object from SHM in the btree */ bt_for_each_node(tokdata, &tokdata->priv_token_obj_btree, find_by_name_cb, &fa); /* we didn't find it in the btree, so add it */ if (fa.done == FALSE) { new_obj = (OBJECT *) malloc(sizeof(OBJECT)); memset(new_obj, 0x0, sizeof(OBJECT)); rc = object_init_lock(new_obj); if (rc != CKR_OK) { free(new_obj); continue; } memcpy(new_obj->name, shm_te->name, 8); rc = reload_token_object(tokdata, new_obj); if (rc == CKR_OK) bt_node_add(&tokdata->priv_token_obj_btree, new_obj); else object_free(new_obj); } } return CKR_OK; } // SAB FIXME FIXME void purge_map_by_type_cb(STDLL_TokData_t *tokdata, void *node, unsigned long map_handle, void *p3) { OBJECT_MAP *map = (OBJECT_MAP *) node; SESS_OBJ_TYPE type = *(SESS_OBJ_TYPE *) p3; if (type == PRIVATE) { if (map->is_private) { bt_node_free(&tokdata->object_map_btree, map_handle, TRUE); } } else if (type == PUBLIC) { if (!map->is_private) { bt_node_free(&tokdata->object_map_btree, map_handle, TRUE); } } } CK_BBOOL object_mgr_purge_map(STDLL_TokData_t *tokdata, SESSION *sess, SESS_OBJ_TYPE type) { UNUSED(sess); bt_for_each_node(tokdata, &tokdata->object_map_btree, purge_map_by_type_cb, &type); return TRUE; } /* Put back the object using its btree */ CK_RV object_put(STDLL_TokData_t *tokdata, OBJECT *obj, CK_BBOOL unlock) { CK_BBOOL sess, priv; CK_RV rc; if (obj == NULL) return CKR_OBJECT_HANDLE_INVALID; if (!unlock) { rc = object_lock(obj, READ_LOCK); if (rc != CKR_OK) return rc; } sess = object_is_session_object(obj); priv = object_is_private(obj); if (unlock) { rc= object_unlock(obj); if (rc != CKR_OK) return rc; } if (sess) bt_put_node_value(&tokdata->sess_obj_btree, obj); else if (priv) bt_put_node_value(&tokdata->priv_token_obj_btree, obj); else bt_put_node_value(&tokdata->publ_token_obj_btree, obj); return CKR_OK; } #ifdef DEBUG void dump_shm(LW_SHM_TYPE *global_shm, const char *s) { CK_ULONG i; TRACE_DEBUG("%s: dump_shm priv:\n", s); for (i = 0; i < global_shm->num_priv_tok_obj; i++) { TRACE_DEBUG("[%lu]: %.8s\n", i, global_shm->priv_tok_objs[i].name); } TRACE_DEBUG("%s: dump_shm publ:\n", s); for (i = 0; i < global_shm->num_publ_tok_obj; i++) { TRACE_DEBUG("[%lu]: %.8s\n", i, global_shm->publ_tok_objs[i].name); } } #endif