/* * Copyright (c) 2016, Red Hat Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or * other materials provided with the distribution. * * The names of contributors to this software may not be * used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * * CONTRIBUTORS * Daiki Ueno */ #include "config.h" #include "attrs.h" #include "buffer.h" #include "constants.h" #include "debug.h" #include "filter.h" #include "iter.h" #include "message.h" #include "p11-kit.h" #include "virtual.h" #include #include #include #include #include #include typedef struct { CK_SLOT_ID slot; const CK_TOKEN_INFO *token; } FilterSlot; typedef struct { p11_virtual virt; CK_X_FUNCTION_LIST *lower; p11_destroyer destroyer; p11_array *entries; bool allowed; bool initialized; FilterSlot *slots; CK_ULONG n_slots; CK_ULONG max_slots; } FilterData; extern int p11_match_uri_token_info (CK_TOKEN_INFO_PTR one, CK_TOKEN_INFO_PTR two); static const CK_TOKEN_INFO * filter_match_token (FilterData *filter, CK_TOKEN_INFO *token) { unsigned int i; for (i = 0; i < filter->entries->num; i++) { CK_TOKEN_INFO *entry = filter->entries->elem[i]; bool matched = p11_match_uri_token_info (entry, token); if ((filter->allowed && matched) || (!filter->allowed && !matched)) return entry; } return NULL; } static bool filter_add_slot (FilterData *filter, CK_SLOT_ID slot, const CK_TOKEN_INFO *token) { if (filter->n_slots >= filter->max_slots) { filter->max_slots = filter->max_slots * 2 + 1; filter->slots = realloc (filter->slots, filter->max_slots * sizeof (FilterSlot)); if (filter->slots == NULL) return false; } filter->slots[filter->n_slots].slot = slot; filter->slots[filter->n_slots].token = token; filter->n_slots++; return true; } static CK_RV filter_ensure (FilterData *filter) { CK_FUNCTION_LIST *lower = NULL; P11KitIter *iter = NULL; CK_RV rv = CKR_OK; if (filter->slots != NULL) { free (filter->slots); filter->slots = NULL; } filter->n_slots = 0; filter->max_slots = 0; iter = p11_kit_iter_new (NULL, P11_KIT_ITER_WITH_TOKENS | P11_KIT_ITER_WITHOUT_OBJECTS); if (iter == NULL) { rv = CKR_HOST_MEMORY; goto out; } lower = p11_virtual_wrap (filter->virt.lower_module, NULL); if (lower == NULL) { rv = CKR_HOST_MEMORY; goto out; } p11_kit_iter_begin_with (iter, lower, 0, CK_INVALID_HANDLE); while (p11_kit_iter_next (iter) == CKR_OK) { CK_TOKEN_INFO *token; const CK_TOKEN_INFO *match; token = p11_kit_iter_get_token (iter); match = filter_match_token (filter, token); if (match) { CK_SLOT_ID slot; slot = p11_kit_iter_get_slot (iter); if (!filter_add_slot (filter, slot, match)) { rv = CKR_HOST_MEMORY; goto out; } } } rv = CKR_OK; out: p11_kit_iter_free (iter); if (lower) p11_virtual_unwrap (lower); return rv; } static void filter_reinit (FilterData *filter) { CK_RV rv; rv = filter_ensure (filter); if (rv == CKR_OK) filter->initialized = true; else { filter->initialized = false; p11_message ("filter cannot be initialized"); } } static CK_RV filter_C_Initialize (CK_X_FUNCTION_LIST *self, CK_VOID_PTR pInitArgs) { FilterData *filter = (FilterData *)self; CK_RV rv; rv = filter->lower->C_Initialize (filter->lower, pInitArgs); if (rv == CKR_OK) filter_reinit (filter); return rv; } static CK_RV filter_C_Finalize (CK_X_FUNCTION_LIST *self, CK_VOID_PTR pReserved) { FilterData *filter = (FilterData *)self; free (filter->slots); filter->n_slots = 0; p11_array_clear (filter->entries); filter->initialized = false; filter->allowed = false; return filter->lower->C_Finalize (filter->lower, pReserved); } static CK_RV filter_C_GetSlotList (CK_X_FUNCTION_LIST *self, CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { FilterData *filter = (FilterData *)self; CK_ULONG count; if (pulCount == NULL) return CKR_ARGUMENTS_BAD; count = *pulCount; *pulCount = filter->n_slots; if (pSlotList == NULL) return CKR_OK; if (filter->n_slots > count) return CKR_BUFFER_TOO_SMALL; for (count = 0; count < filter->n_slots; count++) pSlotList[count] = count; *pulCount = filter->n_slots; return CKR_OK; } static CK_RV filter_C_GetSlotInfo (CK_X_FUNCTION_LIST *self, CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { FilterData *filter = (FilterData *)self; if (slotID >= filter->n_slots) return CKR_SLOT_ID_INVALID; return filter->lower->C_GetSlotInfo (filter->lower, filter->slots[slotID].slot, pInfo); } static CK_RV filter_C_GetTokenInfo (CK_X_FUNCTION_LIST *self, CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { FilterData *filter = (FilterData *)self; if (slotID >= filter->n_slots) return CKR_SLOT_ID_INVALID; return filter->lower->C_GetTokenInfo (filter->lower, filter->slots[slotID].slot, pInfo); } static CK_RV filter_C_GetMechanismList (CK_X_FUNCTION_LIST *self, CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { FilterData *filter = (FilterData *)self; if (slotID >= filter->n_slots) return CKR_SLOT_ID_INVALID; return filter->lower->C_GetMechanismList (filter->lower, filter->slots[slotID].slot, pMechanismList, pulCount); } static CK_RV filter_C_GetMechanismInfo (CK_X_FUNCTION_LIST *self, CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { FilterData *filter = (FilterData *)self; if (slotID >= filter->n_slots) return CKR_SLOT_ID_INVALID; return filter->lower->C_GetMechanismInfo (filter->lower, filter->slots[slotID].slot, type, pInfo); } static CK_RV filter_C_InitToken (CK_X_FUNCTION_LIST *self, CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { FilterData *filter = (FilterData *)self; if (slotID >= filter->n_slots) return CKR_SLOT_ID_INVALID; if (filter->slots[slotID].token->flags & CKF_WRITE_PROTECTED) return CKR_TOKEN_WRITE_PROTECTED; return filter->lower->C_InitToken (filter->lower, filter->slots[slotID].slot, pPin, ulPinLen, pLabel); } static CK_RV filter_C_WaitForSlotEvent (CK_X_FUNCTION_LIST *self, CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) { return CKR_FUNCTION_NOT_SUPPORTED; } static CK_RV filter_C_OpenSession (CK_X_FUNCTION_LIST *self, CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) { FilterData *filter = (FilterData *)self; if (slotID >= filter->n_slots) return CKR_SLOT_ID_INVALID; if ((flags & CKF_RW_SESSION) && (filter->slots[slotID].token->flags & CKF_WRITE_PROTECTED)) return CKR_TOKEN_WRITE_PROTECTED; return filter->lower->C_OpenSession (filter->lower, filter->slots[slotID].slot, flags, pApplication, Notify, phSession); } static CK_RV filter_C_CloseAllSessions (CK_X_FUNCTION_LIST *self, CK_SLOT_ID slotID) { FilterData *filter = (FilterData *)self; if (slotID >= filter->n_slots) return CKR_SLOT_ID_INVALID; return filter->lower->C_CloseAllSessions (filter->lower, filter->slots[slotID].slot); } void p11_filter_release (void *data) { FilterData *filter = (FilterData *)data; return_if_fail (data != NULL); p11_virtual_uninit (&filter->virt); p11_array_free (filter->entries); free (filter); } p11_virtual * p11_filter_subclass (p11_virtual *lower, p11_destroyer destroyer) { FilterData *filter; CK_X_FUNCTION_LIST functions; filter = calloc (1, sizeof (FilterData)); return_val_if_fail (filter != NULL, NULL); memcpy (&functions, &p11_virtual_stack, sizeof (CK_X_FUNCTION_LIST)); functions.C_Initialize = filter_C_Initialize; functions.C_Finalize = filter_C_Finalize; functions.C_GetSlotList = filter_C_GetSlotList; functions.C_GetSlotInfo = filter_C_GetSlotInfo; functions.C_GetTokenInfo = filter_C_GetTokenInfo; functions.C_GetMechanismList = filter_C_GetMechanismList; functions.C_GetMechanismInfo = filter_C_GetMechanismInfo; functions.C_InitToken = filter_C_InitToken; functions.C_WaitForSlotEvent = filter_C_WaitForSlotEvent; functions.C_OpenSession = filter_C_OpenSession; functions.C_CloseAllSessions = filter_C_CloseAllSessions; p11_virtual_init (&filter->virt, &functions, lower, destroyer); filter->lower = &lower->funcs; filter->entries = p11_array_new ((p11_destroyer)free); return &filter->virt; } void p11_filter_allow_token (p11_virtual *virt, CK_TOKEN_INFO *token) { FilterData *filter = (FilterData *)virt; CK_TOKEN_INFO *token_copy; return_if_fail (filter->allowed || filter->entries->num == 0); filter->allowed = true; token_copy = memdup (token, sizeof (CK_TOKEN_INFO)); return_if_fail (token_copy != NULL); if (!p11_array_push (filter->entries, token_copy)) return_if_reached (); if (filter->initialized) filter_reinit (filter); } void p11_filter_deny_token (p11_virtual *virt, CK_TOKEN_INFO *token) { FilterData *filter = (FilterData *)virt; CK_TOKEN_INFO *token_copy; return_if_fail (!filter->allowed || filter->entries->num == 0); filter->allowed = false; token_copy = memdup (token, sizeof (CK_TOKEN_INFO)); return_if_fail (token_copy != NULL); if (!p11_array_push (filter->entries, token_copy)) return_if_reached (); if (filter->initialized) filter_reinit (filter); }