diff --git a/daemons/dnssec/ipa-dnskeysync-replica b/daemons/dnssec/ipa-dnskeysync-replica index 9bf19eeab..c7b9cf3dc 100755 --- a/daemons/dnssec/ipa-dnskeysync-replica +++ b/daemons/dnssec/ipa-dnskeysync-replica @@ -15,6 +15,7 @@ import os import sys import ipalib +from ipalib.constants import SOFTHSM_DNSSEC_TOKEN_LABEL from ipalib.install.kinit import kinit_keytab from ipapython.dn import DN from ipapython.ipa_log_manager import root_logger, standard_logging_setup @@ -158,8 +159,7 @@ ldapkeydb = LdapKeyDB(log, ldap, DN(('cn', 'keys'), ('cn', 'sec'), ipalib.api.env.container_dns, ipalib.api.env.basedn)) -# TODO: slot number could be configurable -localhsm = LocalHSM(paths.LIBSOFTHSM2_SO, 0, +localhsm = LocalHSM(paths.LIBSOFTHSM2_SO, SOFTHSM_DNSSEC_TOKEN_LABEL, open(paths.DNSSEC_SOFTHSM_PIN).read()) ldap2replica_master_keys_sync(log, ldapkeydb, localhsm) diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter index 260a7b651..6fe11dcc3 100755 --- a/daemons/dnssec/ipa-ods-exporter +++ b/daemons/dnssec/ipa-ods-exporter @@ -32,6 +32,7 @@ import sqlite3 import traceback import ipalib +from ipalib.constants import SOFTHSM_DNSSEC_TOKEN_LABEL from ipalib.install.kinit import kinit_keytab from ipapython.dn import DN from ipapython import ipaldap @@ -647,7 +648,7 @@ log.debug('Connected') ldapkeydb = LdapKeyDB(log, ldap, DN(('cn', 'keys'), ('cn', 'sec'), ipalib.api.env.container_dns, ipalib.api.env.basedn)) -localhsm = LocalHSM(paths.LIBSOFTHSM2_SO, 0, +localhsm = LocalHSM(paths.LIBSOFTHSM2_SO, SOFTHSM_DNSSEC_TOKEN_LABEL, open(paths.DNSSEC_SOFTHSM_PIN).read()) ldap2master_replica_keys_sync(log, ldapkeydb, localhsm) diff --git a/ipalib/constants.py b/ipalib/constants.py index f8a194c1f..e604bb430 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -313,3 +313,5 @@ USER_CACHE_PATH = ( '.cache' ) ) + +SOFTHSM_DNSSEC_TOKEN_LABEL = u'ipaDNSSEC' diff --git a/ipaserver/dnssec/localhsm.py b/ipaserver/dnssec/localhsm.py index c1e4887b5..12b40cc8d 100755 --- a/ipaserver/dnssec/localhsm.py +++ b/ipaserver/dnssec/localhsm.py @@ -89,10 +89,11 @@ class Key(collections.MutableMapping): def __repr__(self): return self.__str__() + class LocalHSM(AbstractHSM): - def __init__(self, library, slot, pin): + def __init__(self, library, label, pin): self.cache_replica_pubkeys = None - self.p11 = _ipap11helper.P11_Helper(slot, pin, library) + self.p11 = _ipap11helper.P11_Helper(label, pin, library) self.log = logging.getLogger() def __del__(self): diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py index 861a1702e..3849626e5 100644 --- a/ipaserver/install/dnskeysyncinstance.py +++ b/ipaserver/install/dnskeysyncinstance.py @@ -23,10 +23,9 @@ from ipapython import ipautil from ipaplatform.constants import constants from ipaplatform.paths import paths from ipalib import errors, api +from ipalib.constants import SOFTHSM_DNSSEC_TOKEN_LABEL from ipaserver.install.bindinstance import dns_container_exists -softhsm_token_label = u'ipaDNSSEC' -softhsm_slot = 0 replica_keylabel_template = u"dnssec-replica:%s" @@ -254,8 +253,8 @@ class DNSKeySyncInstance(service.Service): command = [ paths.SOFTHSM2_UTIL, '--init-token', - '--slot', str(softhsm_slot), - '--label', softhsm_token_label, + '--free', # use random free slot + '--label', SOFTHSM_DNSSEC_TOKEN_LABEL, '--pin', pin, '--so-pin', pin_so, ] @@ -274,7 +273,8 @@ class DNSKeySyncInstance(service.Service): pin = f.read() os.environ["SOFTHSM2_CONF"] = paths.DNSSEC_SOFTHSM2_CONF - p11 = _ipap11helper.P11_Helper(softhsm_slot, pin, paths.LIBSOFTHSM2_SO) + p11 = _ipap11helper.P11_Helper( + SOFTHSM_DNSSEC_TOKEN_LABEL, pin, paths.LIBSOFTHSM2_SO) try: # generate replica keypair diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py index 467f1f038..bc2974a2c 100644 --- a/ipaserver/install/opendnssecinstance.py +++ b/ipaserver/install/opendnssecinstance.py @@ -20,10 +20,9 @@ from ipaplatform.constants import constants from ipaplatform.paths import paths from ipalib import errors, api from ipaserver import p11helper -from ipaserver.install import dnskeysyncinstance +from ipalib.constants import SOFTHSM_DNSSEC_TOKEN_LABEL KEYMASTER = u'dnssecKeyMaster' -softhsm_slot = 0 def get_dnssec_key_masters(conn): @@ -68,7 +67,7 @@ class OpenDNSSECInstance(service.Service): self.ods_gid = None self.conf_file_dict = { 'SOFTHSM_LIB': paths.LIBSOFTHSM2_SO, - 'TOKEN_LABEL': dnskeysyncinstance.softhsm_token_label, + 'TOKEN_LABEL': SOFTHSM_DNSSEC_TOKEN_LABEL, 'KASP_DB': paths.OPENDNSSEC_KASP_DB, 'ODS_USER': constants.ODS_USER, 'ODS_GROUP': constants.ODS_GROUP, @@ -237,7 +236,8 @@ class OpenDNSSECInstance(service.Service): pin = f.read() os.environ["SOFTHSM2_CONF"] = paths.DNSSEC_SOFTHSM2_CONF - p11 = p11helper.P11_Helper(softhsm_slot, pin, paths.LIBSOFTHSM2_SO) + p11 = p11helper.P11_Helper( + SOFTHSM_DNSSEC_TOKEN_LABEL, pin, paths.LIBSOFTHSM2_SO) try: # generate master key root_logger.debug("Creating master key") diff --git a/ipaserver/p11helper.py b/ipaserver/p11helper.py index 5963c6d71..37abf7279 100644 --- a/ipaserver/p11helper.py +++ b/ipaserver/p11helper.py @@ -30,6 +30,7 @@ struct _CK_VERSION }; typedef unsigned long CK_SLOT_ID; +typedef CK_SLOT_ID *CK_SLOT_ID_PTR; typedef unsigned long CK_SESSION_HANDLE; @@ -43,6 +44,13 @@ typedef unsigned long CK_KEY_TYPE; typedef unsigned long CK_ATTRIBUTE_TYPE; +typedef unsigned long ck_flags_t; + +typedef unsigned char CK_BBOOL; + +typedef unsigned long int CK_ULONG; +typedef CK_ULONG *CK_ULONG_PTR; + struct _CK_ATTRIBUTE { CK_ATTRIBUTE_TYPE type; @@ -59,6 +67,31 @@ struct _CK_MECHANISM unsigned long ulParameterLen; }; +struct _CK_TOKEN_INFO +{ + unsigned char label[32]; + unsigned char manufacturer_id[32]; + unsigned char model[16]; + unsigned char serial_number[16]; + ck_flags_t flags; + unsigned long max_session_count; + unsigned long session_count; + unsigned long max_rw_session_count; + unsigned long rw_session_count; + unsigned long max_pin_len; + unsigned long min_pin_len; + unsigned long total_public_memory; + unsigned long free_public_memory; + unsigned long total_private_memory; + unsigned long free_private_memory; + struct _CK_VERSION hardware_version; + struct _CK_VERSION firmware_version; + unsigned char utc_time[16]; +}; + +typedef struct _CK_TOKEN_INFO CK_TOKEN_INFO; +typedef CK_TOKEN_INFO *CK_TOKEN_INFO_PTR; + typedef unsigned long CK_RV; typedef ... *CK_NOTIFY; @@ -70,9 +103,12 @@ typedef CK_RV (*CK_C_Finalize) (void *pReserved); typedef ... *CK_C_GetInfo; typedef ... *CK_C_GetFunctionList; CK_RV C_GetFunctionList (struct _CK_FUNCTION_LIST **function_list); -typedef ... *CK_C_GetSlotList; +typedef CK_RV (*CK_C_GetSlotList) (CK_BBOOL tokenPresent, + CK_SLOT_ID_PTR pSlotList, + CK_ULONG_PTR pulCount); typedef ... *CK_C_GetSlotInfo; -typedef ... *CK_C_GetTokenInfo; +typedef CK_RV (*CK_C_GetTokenInfo) (CK_SLOT_ID slotID, + CK_TOKEN_INFO_PTR pInfo); typedef ... *CK_C_WaitForSlotEvent; typedef ... *CK_C_GetMechanismList; typedef ... *CK_C_GetMechanismInfo; @@ -255,10 +291,7 @@ struct _CK_FUNCTION_LIST typedef unsigned char CK_BYTE; typedef unsigned char CK_UTF8CHAR; -typedef unsigned char CK_BBOOL; -typedef unsigned long int CK_ULONG; typedef CK_BYTE *CK_BYTE_PTR; -typedef CK_ULONG *CK_ULONG_PTR; typedef CK_OBJECT_HANDLE *CK_OBJECT_HANDLE_PTR; @@ -387,6 +420,7 @@ CKM_AES_KEY_GEN = 0x1080 CKR_OK = 0 CKR_ATTRIBUTE_TYPE_INVALID = 0x12 CKR_USER_NOT_LOGGED_IN = 0x101 +CKR_BUFFER_TOO_SMALL = 0x150 CK_BYTE = _ffi.typeof('CK_BYTE') CK_BBOOL = _ffi.typeof('CK_BBOOL') @@ -403,6 +437,10 @@ CK_MECHANISM = _ffi.typeof('CK_MECHANISM') CK_FUNCTION_LIST_PTR = _ffi.typeof('CK_FUNCTION_LIST_PTR') +CK_SLOT_ID = _ffi.typeof('CK_SLOT_ID') + +CK_TOKEN_INFO = _ffi.typeof('CK_TOKEN_INFO') + NULL_PTR = NULL @@ -796,11 +834,10 @@ class P11_Helper(object): # Object not found return False - def __init__(self, slot, user_pin, library_path): + def __init__(self, token_label, user_pin, library_path): self.p11_ptr = new_ptr(CK_FUNCTION_LIST_PTR) self.session_ptr = new_ptr(CK_SESSION_HANDLE) - self.slot = 0 self.session_ptr[0] = 0 self.p11_ptr[0] = NULL self.module_handle = None @@ -808,7 +845,7 @@ class P11_Helper(object): # Parse method args if isinstance(user_pin, unicode): user_pin = user_pin.encode() - self.slot = slot + self.token_label = token_label try: pGetFunctionList, module_handle = loadLibrary(library_path) @@ -828,10 +865,17 @@ class P11_Helper(object): rv = self.p11.C_Initialize(NULL) check_return_value(rv, "initialize") + # + # Get Slot + # + slot = self.get_slot() + if slot is None: + raise Error("No slot for label {} found".format(self.token_label)) + # # Start session # - rv = self.p11.C_OpenSession(self.slot, + rv = self.p11.C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, self.session_ptr) check_return_value(rv, "open session") @@ -842,6 +886,49 @@ class P11_Helper(object): rv = self.p11.C_Login(self.session, CKU_USER, user_pin, len(user_pin)) check_return_value(rv, "log in") + def get_slot(self): + """Get slot where then token is located + :return: slot number or None when slot not found + """ + object_count_ptr = new_ptr(CK_ULONG) + + # get slots ID + slots = None + for _i in range(0, 10): + # try max N times, then die to avoid infinite iteration + rv = self.p11.C_GetSlotList(CK_TRUE, NULL, object_count_ptr) + check_return_value(rv, "get slots IDs - prepare") + + result_ids_ptr = new_array(CK_SLOT_ID, object_count_ptr[0]) + + rv = self.p11.C_GetSlotList( + CK_TRUE, result_ids_ptr, object_count_ptr) + if rv == CKR_BUFFER_TOO_SMALL: + continue + check_return_value(rv, "get slots IDs") + slots = result_ids_ptr + break # we have slots !!! + + if slots is None: + raise Error("Failed to get slots") + + for slot in slots: + token_info_ptr = new_ptr(CK_TOKEN_INFO) + rv = self.p11.C_GetTokenInfo(slot, token_info_ptr) + check_return_value(rv, 'get token info') + + # softhsm always returns label 32 bytes long with padding made of + # white spaces (#32), so we have to rstrip() padding and compare + # Label was created by softhsm-util so it is not our fault that + # there are #32 as padding (cffi initializes structures with + # zeroes) + # In case that this is not valid anymore, keep in mind backward + # compatibility + + if self.token_label == char_array_to_unicode( + token_info_ptr[0].label, 32).rstrip(): + return slot + def finalize(self): """ Finalize operations with pkcs11 library @@ -868,7 +955,6 @@ class P11_Helper(object): self.p11_ptr[0] = NULL self.session_ptr[0] = 0 - self.slot = 0 self.module_handle = None ################################################################# diff --git a/ipatests/test_ipaserver/test_ipap11helper.py b/ipatests/test_ipaserver/test_ipap11helper.py index c0c8b24bb..e2ed06f91 100644 --- a/ipatests/test_ipaserver/test_ipap11helper.py +++ b/ipatests/test_ipaserver/test_ipap11helper.py @@ -55,12 +55,12 @@ def p11(request): with open('softhsm2.conf', 'w') as cfg: cfg.write(CONFIG_DATA % token_path) os.environ['SOFTHSM2_CONF'] = os.path.join(token_path, 'softhsm2.conf') - subprocess.check_call([SOFTHSM2_UTIL, '--init-token', '--slot', '0', + subprocess.check_call([SOFTHSM2_UTIL, '--init-token', '--free', '--label', 'test', '--pin', '1234', '--so-pin', '1234']) try: - p11 = _ipap11helper.P11_Helper(0, "1234", LIBSOFTHSM) + p11 = _ipap11helper.P11_Helper('test', "1234", LIBSOFTHSM) except _ipap11helper.Error: pytest.fail('Failed to initialize the helper object.', pytrace=False) @@ -70,6 +70,8 @@ def p11(request): except _ipap11helper.Error: pytest.fail('Failed to finalize the helper object.', pytrace=False) finally: + subprocess.call( + [SOFTHSM2_UTIL, '--delete-token', '--label', 'test']) del os.environ['SOFTHSM2_CONF'] request.addfinalizer(fin)