From ac58eafa3223ef40b9b60765b0b3d118f338fffc Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 12 Dec 2018 14:34:00 +0100 Subject: [PATCH] Add support for RSA decryption using PKCS #11 tokens Add support for loading RSA private key files from PKCS #11 tokens, identified by PKCS #11 URIs. Add a new 'pkcs11_libs' UAT which can dynamically load PKCS #11 provider libraries that are not found by p11-kit. The configuration GUI will need additional code to discover available PKCS #11 tokens and will be added later. This feature requires GnuTLS 3.4 with PKCS #11 support, so Windows, macOS via Homebrew, Ubuntu 16.04, Debian Stretch. Not supported: RHEL7. Currently macOS via official packages disables PKCS #11 support, so that will also not work. Change-Id: I20646bfd69c6bd13c8c2d27cb65c164a4b0b7a66 Reviewed-on: https://code.wireshark.org/review/30855 Petri-Dish: Peter Wu Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu --- .travis.yml | 3 +- CMakeLists.txt | 18 +++- CMakeOptions.txt | 2 +- appveyor.yml | 3 + cmakeconfig.h.in | 3 + epan/epan.c | 3 + epan/secrets.c | 221 +++++++++++++++++++++++++++++++++++++++ test/fixtures_ws.py | 2 + test/keys/rsa-p-lt-q.p8 | 16 +++ test/suite_decryption.py | 124 ++++++++++++++++++++++ tools/debian-setup.sh | 51 ++++++--- 11 files changed, 431 insertions(+), 15 deletions(-) create mode 100644 test/keys/rsa-p-lt-q.p8 diff --git a/.travis.yml b/.travis.yml index c31ed16a96..ed47fbd7d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,8 +59,9 @@ before_install: - echo $TRAVIS_OS_NAME # macos - if [ "$TRAVIS_OS_NAME" == "osx" ]; then ./tools/macos-setup-brew.sh; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install softhsm; fi # linux - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo ./tools/debian-setup.sh --install-optional -q; fi + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo ./tools/debian-setup.sh --install-optional --install-test-deps -q; fi - sudo gem install asciidoctor --no-ri --no-rdoc - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install -y python3-pip; fi - sudo pip3 install pytest pytest-xdist diff --git a/CMakeLists.txt b/CMakeLists.txt index a228d3bbcf..e175bc6177 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -990,7 +990,7 @@ if(ENABLE_SMI) set(PACKAGELIST ${PACKAGELIST} SMI) endif() -# GNU SSL/TLS support +# Support for TLS decryption using RSA private keys. if(ENABLE_GNUTLS) set(PACKAGELIST ${PACKAGELIST} GNUTLS) # Minimum version needed. @@ -1167,6 +1167,22 @@ if(HAVE_LIBLUA) set(HAVE_LUA_H 1) set(HAVE_LUA 1) endif() +if(GNUTLS_FOUND AND NOT GNUTLS_VERSION VERSION_LESS "3.4.0") + # While all Linux and Windows builds have PKCS #11 support enabled, + # macos-setup.sh explicitly disables it using --without-p11-kit. + # + # Require at least GnuTLS 3.4.0 such that public keys can be calculated + # from PKCS #11 private keys. + include(CheckSymbolExists) + cmake_push_check_state() + if(WIN32) + set(CMAKE_REQUIRED_DEFINITIONS -Dssize_t=int) + endif() + set(CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES}) + check_symbol_exists(gnutls_pkcs11_obj_list_import_url4 gnutls/pkcs11.h HAVE_GNUTLS_PKCS11) + cmake_pop_check_state() +endif() if(HAVE_LIBKERBEROS) set(HAVE_KERBEROS 1) endif() diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 2cfe6419d4..f4d4ce1f31 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -69,7 +69,7 @@ option(ENABLE_SNAPPY "Build with Snappy compression support" ON) option(ENABLE_NGHTTP2 "Build with HTTP/2 header decompression support" ON) option(ENABLE_LUA "Build with Lua dissector support" ON) option(ENABLE_SMI "Build with libsmi snmp support" ON) -option(ENABLE_GNUTLS "Build with GNU TLS support" ON) +option(ENABLE_GNUTLS "Build with RSA decryption support" ON) if(WIN32) option(ENABLE_WINSPARKLE "Enable WinSparkle support" ON) endif() diff --git a/appveyor.yml b/appveyor.yml index c20b82735c..82059e4dc6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,6 +29,9 @@ install: # Py2 fails the test_tshark_unicode_display_filter test, so use Py3. - set PATH=C:\Python37-x64;C:\Python37-x64\Scripts;%PATH% - pip install pytest pytest-xdist + - ps: | # For pkcs11 tests. + Invoke-WebRequest -Uri https://github.com/disig/SoftHSM2-for-Windows/releases/download/v2.5.0/SoftHSM2-2.5.0.msi -OutFile $Env:WIRESHARK_BASE_DIR\SoftHSM2-2.5.0.msi + & msiexec /qn /i $Env:WIRESHARK_BASE_DIR\SoftHSM2-2.5.0.msi # Note: the NSIS installer lacks debug dlls for Debug builds. configuration: RelWithDebInfo diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index 5adaab7121..e5bc4a1937 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -112,6 +112,9 @@ /* Define to use GnuTLS library */ #cmakedefine HAVE_LIBGNUTLS 1 +/* Define to 1 if GnuTLS was built with pkcs11 support. */ +#cmakedefine HAVE_GNUTLS_PKCS11 1 + /* Enable libnl support */ #cmakedefine HAVE_LIBNL 1 diff --git a/epan/epan.c b/epan/epan.c index 0b3a969a18..e20a9cb28e 100644 --- a/epan/epan.c +++ b/epan/epan.c @@ -715,6 +715,9 @@ epan_get_compiled_version_info(GString *str) g_string_append(str, ", "); #ifdef HAVE_LIBGNUTLS g_string_append(str, "with GnuTLS " LIBGNUTLS_VERSION); +#ifdef HAVE_GNUTLS_PKCS11 + g_string_append(str, " and PKCS #11 support"); +#endif /* HAVE_GNUTLS_PKCS11 */ #else g_string_append(str, "without GnuTLS"); #endif /* HAVE_LIBGNUTLS */ diff --git a/epan/secrets.c b/epan/secrets.c index bfbbad2b6e..a16913768c 100644 --- a/epan/secrets.c +++ b/epan/secrets.c @@ -51,6 +51,19 @@ static guint uat_num_rsa_privkeys; static void register_rsa_uats(void); #endif /* HAVE_LIBGNUTLS */ +#ifdef HAVE_GNUTLS_PKCS11 +/** PINs for PKCS #11 keys in rsa_privkeys. Must be cleared after rsa_privkeys. */ +static GSList *rsa_privkeys_pkcs11_pins; + +typedef struct { + char *library_path; /**< PKCS #11 library path. */ +} pkcs11_lib_record_t; + +static uat_t *pkcs11_libs_uat; +static pkcs11_lib_record_t *uat_pkcs11_libs; +static guint uat_num_pkcs11_libs; +#endif /* HAVE_GNUTLS_PKCS11 */ + void secrets_init(void) { @@ -69,6 +82,10 @@ secrets_cleanup(void) #ifdef HAVE_LIBGNUTLS g_hash_table_destroy(rsa_privkeys); rsa_privkeys = NULL; +#ifdef HAVE_GNUTLS_PKCS11 + g_slist_free_full(rsa_privkeys_pkcs11_pins, g_free); + rsa_privkeys_pkcs11_pins = NULL; +#endif /* HAVE_GNUTLS_PKCS11 */ #endif /* HAVE_LIBGNUTLS */ } @@ -125,6 +142,182 @@ rsa_privkey_add(const cert_key_id_t *key_id, gnutls_privkey_t pkey) g_htonl(dw[1]), g_htonl(dw[2]), g_htonl(dw[3]), g_htonl(dw[4])); } +#ifdef HAVE_GNUTLS_PKCS11 +/** Provides a fixed PIN to the caller (or failure if the fixed PIN is NULL). */ +static int +set_pin_callback(void *userdata, int attempt _U_, + const char *token_url _U_, const char *token_label _U_, + unsigned int flags, char *pin, size_t pin_max) +{ + const char *fixed_pin = (const char *)userdata; + size_t fixed_pin_len = fixed_pin ? strlen(fixed_pin) : 0; + + /* Fail if the PIN was not provided, wrong or too long. */ + if (!fixed_pin || (flags & GNUTLS_PIN_WRONG) || fixed_pin_len >= pin_max) { + return GNUTLS_E_PKCS11_PIN_ERROR; + } + + memcpy(pin, fixed_pin, fixed_pin_len + 1); + return 0; +} + +/** + * Load private RSA keys from a PKCS #11 token. Returns zero on success and a + * negative error code on failure. + */ +static int +pkcs11_load_keys_from_token(const char *token_uri, const char *pin, char **err) +{ + gnutls_pkcs11_obj_t *list = NULL; + unsigned int nlist = 0; + int ret; + /* An empty/NULL PIN means that none is necessary. */ + char *fixed_pin = pin && pin[0] ? g_strdup(pin) : NULL; + gboolean pin_in_use = FALSE; + + /* Set PIN via a global callback since import_url can prompt for one. */ + gnutls_pkcs11_set_pin_function(set_pin_callback, fixed_pin); + + /* This might already result in callback for the PIN. */ + ret = gnutls_pkcs11_obj_list_import_url4(&list, &nlist, token_uri, + GNUTLS_PKCS11_OBJ_FLAG_PRIVKEY|GNUTLS_PKCS11_OBJ_FLAG_LOGIN); + if (ret < 0) { + *err = g_strdup_printf("Failed to iterate through objects for %s: %s", token_uri, gnutls_strerror(ret)); + goto cleanup; + } + + for (unsigned j = 0; j < nlist; j++) { + char *obj_uri = NULL; + gnutls_privkey_t privkey = NULL; + gnutls_pubkey_t pubkey = NULL; + cert_key_id_t key_id; + size_t size; + + if (gnutls_pkcs11_obj_get_type(list[j]) != GNUTLS_PKCS11_OBJ_PRIVKEY) { + /* Should not happen since we requested private keys only. */ + goto cont; + } + + ret = gnutls_pkcs11_obj_export_url(list[j], GNUTLS_PKCS11_URL_GENERIC, &obj_uri); + if (ret < 0) { + /* Should not happen either if the object is valid. */ + goto cont; + } + + ret = gnutls_privkey_init(&privkey); + if (ret < 0) { + /* Out of memory? */ + goto cont; + } + + /* Set the PIN to be used during decryption. */ + gnutls_privkey_set_pin_function(privkey, set_pin_callback, fixed_pin); + + /* Can prompt for PIN. Can also invoke the token function set by + * gnutls_pkcs11_set_token_function (if not set, it will just fail + * immediately rather than retrying). */ + ret = gnutls_privkey_import_url(privkey, obj_uri, 0); + if (ret < 0) { + /* Bad PIN or some other system error? */ + g_debug("Failed to import private key %s: %s", obj_uri, gnutls_strerror(ret)); + goto cont; + } + + if (gnutls_privkey_get_pk_algorithm(privkey, NULL) != GNUTLS_PK_RSA) { + g_debug("Skipping private key %s, not RSA.", obj_uri); + goto cont; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) { + /* Out of memory? */ + goto cont; + } + + /* This requires GnuTLS 3.4.0 and will fail on older versions. */ + ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0); + if (ret < 0) { + g_debug("Failed to import public key %s: %s", obj_uri, gnutls_strerror(ret)); + goto cont; + } + + size = sizeof(key_id); + ret = gnutls_pubkey_get_key_id(pubkey, GNUTLS_KEYID_USE_SHA1, key_id.key_id, &size); + if (ret < 0 || size != sizeof(key_id)) { + g_debug("Failed to calculate Key ID for %s: %s", obj_uri, gnutls_strerror(ret)); + goto cont; + } + + /* Remember the private key. */ + rsa_privkey_add(&key_id, privkey); + privkey = NULL; + pin_in_use = TRUE; + +cont: + gnutls_privkey_deinit(privkey); + gnutls_pubkey_deinit(pubkey); + gnutls_free(obj_uri); + gnutls_pkcs11_obj_deinit(list[j]); + } + gnutls_free(list); + if (pin_in_use) { + /* Remember PINs such they can be freed later. */ + rsa_privkeys_pkcs11_pins = g_slist_prepend(rsa_privkeys_pkcs11_pins, fixed_pin); + fixed_pin = NULL; + } + ret = 0; + +cleanup: + /* Forget about the PIN. */ + gnutls_pkcs11_set_pin_function(NULL, NULL); + g_free(fixed_pin); + return ret; +} + +/** Load all libraries specified in a UAT. */ +static void +uat_pkcs11_libs_load_all(void) +{ + int ret; + GString *err = NULL; + + for (guint i = 0; i < uat_num_pkcs11_libs; i++) { + const pkcs11_lib_record_t *rec = &uat_pkcs11_libs[i]; + const char *libname = rec->library_path; + /* Note: should return success for already loaded libraries. */ + ret = gnutls_pkcs11_add_provider(libname, NULL); + if (ret) { + if (!err) { + err = g_string_new("Error loading PKCS #11 libraries:"); + } + g_string_append_printf(err, "\n%s: %s", libname, gnutls_strerror(ret)); + } + } + if (err) { + report_failure("%s", err->str); + g_string_free(err, TRUE); + } +} + +UAT_FILENAME_CB_DEF(pkcs11_libs_uats, library_path, pkcs11_lib_record_t) + +static void * +uat_pkcs11_lib_copy_str_cb(void *dest, const void *source, size_t len _U_) +{ + pkcs11_lib_record_t *d = (pkcs11_lib_record_t *)dest; + const pkcs11_lib_record_t *s = (const pkcs11_lib_record_t *)source; + d->library_path = g_strdup(s->library_path); + return dest; +} + +static void +uat_pkcs11_lib_free_str_cb(void *record) +{ + pkcs11_lib_record_t *rec = (pkcs11_lib_record_t *)record; + g_free(rec->library_path); +} +#endif /* HAVE_GNUTLS_PKCS11 */ + UAT_FILENAME_CB_DEF(rsa_privkeys_uats, uri, rsa_privkey_record_t) UAT_CSTRING_CB_DEF(rsa_privkeys_uats, password, rsa_privkey_record_t) @@ -202,6 +395,10 @@ uat_rsa_privkeys_post_update(void) { /* Clear previous keys. */ g_hash_table_remove_all(rsa_privkeys); +#ifdef HAVE_GNUTLS_PKCS11 + g_slist_free_full(rsa_privkeys_pkcs11_pins, g_free); + rsa_privkeys_pkcs11_pins = NULL; +#endif /* HAVE_GNUTLS_PKCS11 */ GString *errors = NULL; for (guint i = 0; i < uat_num_rsa_privkeys; i++) { @@ -210,6 +407,9 @@ uat_rsa_privkeys_post_update(void) char *err = NULL; if (g_str_has_prefix(token_uri, "pkcs11:")) { +#ifdef HAVE_GNUTLS_PKCS11 + pkcs11_load_keys_from_token(token_uri, rec->password, &err); +#endif /* HAVE_GNUTLS_PKCS11 */ } else { load_rsa_keyfile(token_uri, rec->password, &err); } @@ -236,6 +436,27 @@ uat_rsa_privkeys_post_update(void) static void register_rsa_uats(void) { +#ifdef HAVE_GNUTLS_PKCS11 + static uat_field_t uat_pkcs11_libs_fields[] = { + UAT_FLD_FILENAME_OTHER(pkcs11_libs_uats, library_path, "Library Path", NULL, "PKCS #11 provider library file"), + UAT_END_FIELDS + }; + pkcs11_libs_uat = uat_new("PKCS #11 Provider Libraries", + sizeof(pkcs11_lib_record_t), + "pkcs11_libs", /* filename */ + FALSE, /* from_profile */ + &uat_pkcs11_libs, /* data_ptr */ + &uat_num_pkcs11_libs, /* numitems_ptr */ + 0, /* does not directly affect dissection */ + NULL, /* Help section (currently a wiki page) */ + uat_pkcs11_lib_copy_str_cb, /* copy_cb */ + NULL, /* update_cb */ + uat_pkcs11_lib_free_str_cb, /* free_cb */ + uat_pkcs11_libs_load_all, /* post_update_cb */ + NULL, /* reset_cb */ + uat_pkcs11_libs_fields); +#endif /* HAVE_GNUTLS_PKCS11 */ + static uat_field_t uat_rsa_privkeys_fields[] = { UAT_FLD_FILENAME_OTHER(rsa_privkeys_uats, uri, "Keyfile or Token URI", NULL, "RSA Key File or PKCS #11 URI for token"), UAT_FLD_FILENAME_OTHER(rsa_privkeys_uats, password, "Password", NULL, "RSA Key File password or PKCS #11 Token PIN"), diff --git a/test/fixtures_ws.py b/test/fixtures_ws.py index cfadaf0ab8..6fd5d791d1 100644 --- a/test/fixtures_ws.py +++ b/test/fixtures_ws.py @@ -145,12 +145,14 @@ def features(cmd_tshark, make_env): tshark_v = '' gcry_m = re.search(r'with +Gcrypt +([0-9]+\.[0-9]+)', tshark_v) return types.SimpleNamespace( + have_x64='Compiled (64-bit)' in tshark_v, have_lua='with Lua' in tshark_v, have_nghttp2='with nghttp2' in tshark_v, have_kerberos='with MIT Kerberos' in tshark_v or 'with Heimdal Kerberos' in tshark_v, have_libgcrypt16=gcry_m and float(gcry_m.group(1)) >= 1.6, have_libgcrypt17=gcry_m and float(gcry_m.group(1)) >= 1.7, have_gnutls='with GnuTLS' in tshark_v, + have_pkcs11='and PKCS #11 support' in tshark_v, ) diff --git a/test/keys/rsa-p-lt-q.p8 b/test/keys/rsa-p-lt-q.p8 new file mode 100644 index 0000000000..d9fca7ee08 --- /dev/null +++ b/test/keys/rsa-p-lt-q.p8 @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOE/58jJArmp9xd6 +M42ttJk+tbzG5U47pHfj9wFVMoPPUP5/9Mfl9PGBm7GdnGxW//9wQrphEjrqrwya +st6OBO5yb1DotLAl8FecN6eJxSTMPPS108A2D52kXdZohFgn8OjDhi3wR09L42vC +1w7hPJOHIIuzPbywLauhqB7H/OazAgMBAAECgYEAgEnza0oBAVmqX3a8Ef9TEszC +mWf2hd42SApQTjQF90iGaszZz1hLb5lP4ZNQ2eubFhMMDjbnOSMc2+Ln6RWd/Fb1 +Oy8mYkXYwPH+a2PHwa0yQ6IWuqRgZ09EijO+DxRlk8pn5hBj7YC8gU9hIce/PAkx +z50FS6yEPT3U7xouJ9ECQQDm2BAoJasJBOKpYTHbPN4fhZ9m5DV65+nvu9ZgbxeR +FfrBbYRva9KX9q/E03sSPkoEEHV21KYCMYz2g0FipqqXAkEA+cvGNz2upzwX8Ty5 +WKR323NHEVSMerEbbAQikamcCYnR5vdeJ2w3OMYNWTOQrNE9+Rk3gfaMCxwqER1l +eW/0RQJAIzPLsvObk3KFRiMmQTKVBOWRm1UtuqJnEEHqvSXzyBI7/QdAbOVaZgYe +Y7uERxHso5YG86oV7ruzrVvyuqKD3QJAH4do7XALq3AaVYiknFumBTz3q2hQkuvn +2iprcpdF6q5KoCx45eDy12eoJ6oqiKWgfOCB8RV9d6mGZcKgHEPVQQJALFi49VGr +qyX5SCcfS6c+XgX/VuJGS6eFxhpXHolAG3O1FmbQMKJ618bOGt8jYjGYR0bM5Kgd +sDXwcu06rODBIA== +-----END PRIVATE KEY----- diff --git a/test/suite_decryption.py b/test/suite_decryption.py index aad2b2dde0..1cbdaf33bb 100644 --- a/test/suite_decryption.py +++ b/test/suite_decryption.py @@ -10,7 +10,12 @@ '''Decryption tests''' import os.path +import shutil +import subprocess import subprocesstest +import sys +import sysconfig +import types import unittest import fixtures @@ -862,3 +867,122 @@ class case_decrypt_knxip(subprocesstest.SubprocessTestCase): self.assertTrue(self.grepOutput('^IA 1[.]1[.]4 SeqNr 12345$')) self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$')) self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 SeqNr 1234$')) + + +@fixtures.fixture(scope='session') +def softhsm_paths(features): + if sys.platform == 'win32': + search_path = os.getenv('PATH') + r';C:\SoftHSM2\bin' + else: + search_path = None + softhsm_tool = shutil.which('softhsm2-util', path=search_path) + if not softhsm_tool: + # Note: do not fallback to SoftHSMv1. While available on Ubuntu 14.04 + # (and 16.04), it is built with botan < 1.11.10 which causes a crash due + # to a conflict with the GMP library that is also used by GnuTLS/nettle. + # See https://github.com/randombit/botan/issues/1090 + fixtures.skip('SoftHSM is not found') + # Find provider library path. + bindir = os.path.dirname(softhsm_tool) + libdir = os.path.join(os.path.dirname(bindir), 'lib') + if sys.platform == 'win32': + libdirs = [libdir, bindir] + if features.have_x64: + name = 'softhsm2-x64.dll' + else: + name = 'softhsm2.dll' + else: + # Debian/Ubuntu-specific paths + madir = sysconfig.get_config_var('multiarchsubdir') + libdir64_sub = os.path.join(libdir + '64', 'softhsm') + libdir_sub = os.path.join(libdir, 'softhsm') + libdirs = [os.path.join(libdir + madir, 'softhsm')] if madir else [] + libdirs += [libdir_sub, libdir64_sub] + name = 'libsofthsm2.so' + for libdir in libdirs: + provider = os.path.join(libdir, name) + if os.path.exists(provider): + break + else: + # Even if p11-kit can automatically locate it, do not rely on it. + fixtures.skip('SoftHSM provider library not detected') + # Now check whether the import tool is usable. SoftHSM < 2.3.0 did not + # set CKA_DECRYPT when using softhsm2-tool --import and therefore cannot be + # used to import keys for decryption. Use GnuTLS p11tool as workaround. + softhsm_version = subprocess.check_output([softhsm_tool, '--version'], + universal_newlines=True).strip() + use_p11tool = softhsm_version in ('2.0.0', '2.1.0', '2.2.0') + if use_p11tool and not shutil.which('p11tool'): + fixtures.skip('SoftHSM available, but GnuTLS p11tool is unavailable') + return use_p11tool, softhsm_tool, provider + + +@fixtures.fixture +def softhsm(softhsm_paths, home_path, base_env): + '''Creates a temporary SoftHSM token store (and set it in the environment), + returns a function to populate that token store and the path to the PKCS #11 + provider library.''' + use_p11tool, softhsm_tool, provider = softhsm_paths + conf_path = os.path.join(home_path, 'softhsm-test.conf') + db_path = os.path.join(home_path, 'softhsm-test-tokens') + os.makedirs(db_path) + with open(conf_path, 'w') as f: + f.write('directories.tokendir = %s\n' % db_path) + f.write('objectstore.backend = file\n') + # Avoid syslog spam + f.write('log.level = ERROR\n') + base_env['SOFTHSM2_CONF'] = conf_path + + tool_env = base_env.copy() + if sys.platform == 'win32': + # Ensure that softhsm2-util can find the library. + tool_env['PATH'] += ';%s' % os.path.dirname(provider) + + # Initialize tokens store. + token_name = 'Wireshark-Test-Tokens' + pin = 'Secret' + subprocess.check_call([softhsm_tool, '--init-token', '--slot', '0', + '--label', token_name, '--so-pin', 'Supersecret', '--pin', pin], + env=tool_env) + if use_p11tool: + tool_env['GNUTLS_PIN'] = pin + + # Arbitrary IDs and labels. + ids = iter(range(0xab12, 0xffff)) + def import_key(keyfile): + '''Returns a PKCS #11 URI to identify the imported key.''' + label = os.path.basename(keyfile) + obj_id = '%x' % next(ids) + if not use_p11tool: + tool_args = [softhsm_tool, '--import', keyfile, '--label', label, + '--id', obj_id, '--pin', pin, '--token', token_name] + else: + # Fallback for SoftHSM < 2.3.0 + tool_args = ['p11tool', '--provider', provider, '--batch', + '--login', '--write', 'pkcs11:token=%s' % token_name, + '--load-privkey', keyfile, '--label', label, '--id', obj_id] + subprocess.check_call(tool_args, env=tool_env) + id_str = '%{}{}%{}{}'.format(*obj_id) + return 'pkcs11:token=%s;id=%s;type=private' % (token_name, id_str) + + return types.SimpleNamespace(import_key=import_key, provider=provider, pin=pin) + + +@fixtures.mark_usefixtures('test_env') +@fixtures.uses_fixtures +class case_decrypt_pkcs11(subprocesstest.SubprocessTestCase): + def test_tls_pkcs11(self, cmd_tshark, dirs, capture_file, features, softhsm): + '''Check that a RSA key in a PKCS #11 token enables decryption.''' + if not features.have_pkcs11: + self.skipTest('Requires GnuTLS with PKCS #11 support.') + key_file = os.path.join(dirs.key_dir, 'rsa-p-lt-q.p8') + key_uri = softhsm.import_key(key_file) + proc = self.assertRun((cmd_tshark, + '-r', capture_file('rsa-p-lt-q.pcap'), + '-o', 'uat:pkcs11_libs:"{}"'.format(softhsm.provider.replace('\\', '\\x5c')), + '-o', 'uat:rsa_keys:"{}","{}"'.format(key_uri, softhsm.pin), + '-Tfields', + '-e', 'http.request.uri', + '-Y', 'http', + )) + self.assertIn('/', proc.stdout_str) diff --git a/tools/debian-setup.sh b/tools/debian-setup.sh index 678ddf8a2d..51b3bce8ab 100755 --- a/tools/debian-setup.sh +++ b/tools/debian-setup.sh @@ -18,6 +18,7 @@ then printf "Usage: %s [--install-optional] [--install-deb-deps] [...other options...]\\n" "$0" printf "\\t--install-optional: install optional software as well\\n" printf "\\t--install-deb-deps: install packages required to build the .deb file\\n" + printf "\\t--install-test-deps: install packages required to run all tests\\n" printf "\\t[other]: other options are passed as-is to apt\\n" exit 1 fi @@ -29,17 +30,24 @@ then exit 1 fi -for op -do - if [ "$op" = "--install-optional" ] - then - ADDITIONAL=1 - elif [ "$op" = "--install-deb-deps" ] - then - DEBDEPS=1 - else - OPTIONS="$OPTIONS $op" - fi +ADDITIONAL=0 +DEBDEPS=0 +TESTDEPS=0 +for arg; do + case $arg in + --install-optional) + ADDITIONAL=1 + ;; + --install-deb-deps) + DEBDEPS=1 + ;; + --install-test-deps) + TESTDEPS=1 + ;; + *) + OPTIONS="$OPTIONS $arg" + ;; + esac done BASIC_LIST="libglib2.0-dev \ @@ -83,6 +91,8 @@ DEBDEPS_LIST="debhelper \ libxml2-utils \ quilt" +TESTDEPS_LIST= + # Adds package $2 to list variable $1 if the package is found. # If $3 is given, then this version requirement must be satisfied. add_package() { @@ -122,7 +132,7 @@ echo "libssh-gcrypt-dev and libssh-dev are unavailable" >&2 add_package ADDITIONAL_LIST libgnutls28-dev || echo "libgnutls28-dev is unavailable" >&2 -# mmdbresolve +# Debian >= jessie-backports, Ubuntu >= 16.04 add_package ADDITIONAL_LIST libmaxminddb-dev || echo "libmaxminddb-dev is unavailable" >&2 @@ -132,6 +142,18 @@ add_package DEBDEPS_LIST libsystemd-dev || add_package DEBDEPS_LIST libsystemd-journal-dev || echo "libsystemd-dev is unavailable" +# softhsm2 2.0.0: Ubuntu 16.04 +# softhsm2 2.2.0: Debian >= jessie-backports, Ubuntu 18.04 +# softhsm2 >= 2.4.0: Debian >= buster, Ubuntu >= 18.10 +if ! add_package TESTDEPS_LIST softhsm2 '>= 2.3.0'; then + if add_package TESTDEPS_LIST softhsm2; then + # If SoftHSM 2.3.0 is unavailble, install p11tool. + TESTDEPS_LIST="$TESTDEPS_LIST gnutls-bin" + else + echo "softhsm2 is unavailable" >&2 + fi +fi + ACTUAL_LIST=$BASIC_LIST # Now arrange for optional support libraries @@ -145,6 +167,11 @@ then ACTUAL_LIST="$ACTUAL_LIST $DEBDEPS_LIST" fi +if [ $TESTDEPS ] +then + ACTUAL_LIST="$ACTUAL_LIST $TESTDEPS_LIST" +fi + apt-get update || exit 2 # shellcheck disable=SC2086 apt-get install $ACTUAL_LIST $OPTIONS || exit 2