forked from osmocom/wireshark
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 <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl>
This commit is contained in:
parent
53d8e6dcf8
commit
ac58eafa32
|
@ -59,8 +59,9 @@ before_install:
|
||||||
- echo $TRAVIS_OS_NAME
|
- echo $TRAVIS_OS_NAME
|
||||||
# macos
|
# macos
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then ./tools/macos-setup-brew.sh; fi
|
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then ./tools/macos-setup-brew.sh; fi
|
||||||
|
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install softhsm; fi
|
||||||
# linux
|
# 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
|
- sudo gem install asciidoctor --no-ri --no-rdoc
|
||||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install -y python3-pip; fi
|
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install -y python3-pip; fi
|
||||||
- sudo pip3 install pytest pytest-xdist
|
- sudo pip3 install pytest pytest-xdist
|
||||||
|
|
|
@ -990,7 +990,7 @@ if(ENABLE_SMI)
|
||||||
set(PACKAGELIST ${PACKAGELIST} SMI)
|
set(PACKAGELIST ${PACKAGELIST} SMI)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# GNU SSL/TLS support
|
# Support for TLS decryption using RSA private keys.
|
||||||
if(ENABLE_GNUTLS)
|
if(ENABLE_GNUTLS)
|
||||||
set(PACKAGELIST ${PACKAGELIST} GNUTLS)
|
set(PACKAGELIST ${PACKAGELIST} GNUTLS)
|
||||||
# Minimum version needed.
|
# Minimum version needed.
|
||||||
|
@ -1167,6 +1167,22 @@ if(HAVE_LIBLUA)
|
||||||
set(HAVE_LUA_H 1)
|
set(HAVE_LUA_H 1)
|
||||||
set(HAVE_LUA 1)
|
set(HAVE_LUA 1)
|
||||||
endif()
|
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)
|
if(HAVE_LIBKERBEROS)
|
||||||
set(HAVE_KERBEROS 1)
|
set(HAVE_KERBEROS 1)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -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_NGHTTP2 "Build with HTTP/2 header decompression support" ON)
|
||||||
option(ENABLE_LUA "Build with Lua dissector support" ON)
|
option(ENABLE_LUA "Build with Lua dissector support" ON)
|
||||||
option(ENABLE_SMI "Build with libsmi snmp 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)
|
if(WIN32)
|
||||||
option(ENABLE_WINSPARKLE "Enable WinSparkle support" ON)
|
option(ENABLE_WINSPARKLE "Enable WinSparkle support" ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -29,6 +29,9 @@ install:
|
||||||
# Py2 fails the test_tshark_unicode_display_filter test, so use Py3.
|
# Py2 fails the test_tshark_unicode_display_filter test, so use Py3.
|
||||||
- set PATH=C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%
|
- set PATH=C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%
|
||||||
- pip install pytest pytest-xdist
|
- 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.
|
# Note: the NSIS installer lacks debug dlls for Debug builds.
|
||||||
configuration: RelWithDebInfo
|
configuration: RelWithDebInfo
|
||||||
|
|
|
@ -112,6 +112,9 @@
|
||||||
/* Define to use GnuTLS library */
|
/* Define to use GnuTLS library */
|
||||||
#cmakedefine HAVE_LIBGNUTLS 1
|
#cmakedefine HAVE_LIBGNUTLS 1
|
||||||
|
|
||||||
|
/* Define to 1 if GnuTLS was built with pkcs11 support. */
|
||||||
|
#cmakedefine HAVE_GNUTLS_PKCS11 1
|
||||||
|
|
||||||
/* Enable libnl support */
|
/* Enable libnl support */
|
||||||
#cmakedefine HAVE_LIBNL 1
|
#cmakedefine HAVE_LIBNL 1
|
||||||
|
|
||||||
|
|
|
@ -715,6 +715,9 @@ epan_get_compiled_version_info(GString *str)
|
||||||
g_string_append(str, ", ");
|
g_string_append(str, ", ");
|
||||||
#ifdef HAVE_LIBGNUTLS
|
#ifdef HAVE_LIBGNUTLS
|
||||||
g_string_append(str, "with GnuTLS " LIBGNUTLS_VERSION);
|
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
|
#else
|
||||||
g_string_append(str, "without GnuTLS");
|
g_string_append(str, "without GnuTLS");
|
||||||
#endif /* HAVE_LIBGNUTLS */
|
#endif /* HAVE_LIBGNUTLS */
|
||||||
|
|
221
epan/secrets.c
221
epan/secrets.c
|
@ -51,6 +51,19 @@ static guint uat_num_rsa_privkeys;
|
||||||
static void register_rsa_uats(void);
|
static void register_rsa_uats(void);
|
||||||
#endif /* HAVE_LIBGNUTLS */
|
#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
|
void
|
||||||
secrets_init(void)
|
secrets_init(void)
|
||||||
{
|
{
|
||||||
|
@ -69,6 +82,10 @@ secrets_cleanup(void)
|
||||||
#ifdef HAVE_LIBGNUTLS
|
#ifdef HAVE_LIBGNUTLS
|
||||||
g_hash_table_destroy(rsa_privkeys);
|
g_hash_table_destroy(rsa_privkeys);
|
||||||
rsa_privkeys = NULL;
|
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 */
|
#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]));
|
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_FILENAME_CB_DEF(rsa_privkeys_uats, uri, rsa_privkey_record_t)
|
||||||
UAT_CSTRING_CB_DEF(rsa_privkeys_uats, password, 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. */
|
/* Clear previous keys. */
|
||||||
g_hash_table_remove_all(rsa_privkeys);
|
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;
|
GString *errors = NULL;
|
||||||
|
|
||||||
for (guint i = 0; i < uat_num_rsa_privkeys; i++) {
|
for (guint i = 0; i < uat_num_rsa_privkeys; i++) {
|
||||||
|
@ -210,6 +407,9 @@ uat_rsa_privkeys_post_update(void)
|
||||||
char *err = NULL;
|
char *err = NULL;
|
||||||
|
|
||||||
if (g_str_has_prefix(token_uri, "pkcs11:")) {
|
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 {
|
} else {
|
||||||
load_rsa_keyfile(token_uri, rec->password, &err);
|
load_rsa_keyfile(token_uri, rec->password, &err);
|
||||||
}
|
}
|
||||||
|
@ -236,6 +436,27 @@ uat_rsa_privkeys_post_update(void)
|
||||||
static void
|
static void
|
||||||
register_rsa_uats(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[] = {
|
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, 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"),
|
UAT_FLD_FILENAME_OTHER(rsa_privkeys_uats, password, "Password", NULL, "RSA Key File password or PKCS #11 Token PIN"),
|
||||||
|
|
|
@ -145,12 +145,14 @@ def features(cmd_tshark, make_env):
|
||||||
tshark_v = ''
|
tshark_v = ''
|
||||||
gcry_m = re.search(r'with +Gcrypt +([0-9]+\.[0-9]+)', tshark_v)
|
gcry_m = re.search(r'with +Gcrypt +([0-9]+\.[0-9]+)', tshark_v)
|
||||||
return types.SimpleNamespace(
|
return types.SimpleNamespace(
|
||||||
|
have_x64='Compiled (64-bit)' in tshark_v,
|
||||||
have_lua='with Lua' in tshark_v,
|
have_lua='with Lua' in tshark_v,
|
||||||
have_nghttp2='with nghttp2' 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_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_libgcrypt16=gcry_m and float(gcry_m.group(1)) >= 1.6,
|
||||||
have_libgcrypt17=gcry_m and float(gcry_m.group(1)) >= 1.7,
|
have_libgcrypt17=gcry_m and float(gcry_m.group(1)) >= 1.7,
|
||||||
have_gnutls='with GnuTLS' in tshark_v,
|
have_gnutls='with GnuTLS' in tshark_v,
|
||||||
|
have_pkcs11='and PKCS #11 support' in tshark_v,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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-----
|
|
@ -10,7 +10,12 @@
|
||||||
'''Decryption tests'''
|
'''Decryption tests'''
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
import subprocesstest
|
import subprocesstest
|
||||||
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
import fixtures
|
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 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 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$'))
|
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)
|
||||||
|
|
|
@ -18,6 +18,7 @@ then
|
||||||
printf "Usage: %s [--install-optional] [--install-deb-deps] [...other options...]\\n" "$0"
|
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-optional: install optional software as well\\n"
|
||||||
printf "\\t--install-deb-deps: install packages required to build the .deb file\\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"
|
printf "\\t[other]: other options are passed as-is to apt\\n"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -29,17 +30,24 @@ then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for op
|
ADDITIONAL=0
|
||||||
do
|
DEBDEPS=0
|
||||||
if [ "$op" = "--install-optional" ]
|
TESTDEPS=0
|
||||||
then
|
for arg; do
|
||||||
ADDITIONAL=1
|
case $arg in
|
||||||
elif [ "$op" = "--install-deb-deps" ]
|
--install-optional)
|
||||||
then
|
ADDITIONAL=1
|
||||||
DEBDEPS=1
|
;;
|
||||||
else
|
--install-deb-deps)
|
||||||
OPTIONS="$OPTIONS $op"
|
DEBDEPS=1
|
||||||
fi
|
;;
|
||||||
|
--install-test-deps)
|
||||||
|
TESTDEPS=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
OPTIONS="$OPTIONS $arg"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
BASIC_LIST="libglib2.0-dev \
|
BASIC_LIST="libglib2.0-dev \
|
||||||
|
@ -83,6 +91,8 @@ DEBDEPS_LIST="debhelper \
|
||||||
libxml2-utils \
|
libxml2-utils \
|
||||||
quilt"
|
quilt"
|
||||||
|
|
||||||
|
TESTDEPS_LIST=
|
||||||
|
|
||||||
# Adds package $2 to list variable $1 if the package is found.
|
# Adds package $2 to list variable $1 if the package is found.
|
||||||
# If $3 is given, then this version requirement must be satisfied.
|
# If $3 is given, then this version requirement must be satisfied.
|
||||||
add_package() {
|
add_package() {
|
||||||
|
@ -122,7 +132,7 @@ echo "libssh-gcrypt-dev and libssh-dev are unavailable" >&2
|
||||||
add_package ADDITIONAL_LIST libgnutls28-dev ||
|
add_package ADDITIONAL_LIST libgnutls28-dev ||
|
||||||
echo "libgnutls28-dev is unavailable" >&2
|
echo "libgnutls28-dev is unavailable" >&2
|
||||||
|
|
||||||
# mmdbresolve
|
# Debian >= jessie-backports, Ubuntu >= 16.04
|
||||||
add_package ADDITIONAL_LIST libmaxminddb-dev ||
|
add_package ADDITIONAL_LIST libmaxminddb-dev ||
|
||||||
echo "libmaxminddb-dev is unavailable" >&2
|
echo "libmaxminddb-dev is unavailable" >&2
|
||||||
|
|
||||||
|
@ -132,6 +142,18 @@ add_package DEBDEPS_LIST libsystemd-dev ||
|
||||||
add_package DEBDEPS_LIST libsystemd-journal-dev ||
|
add_package DEBDEPS_LIST libsystemd-journal-dev ||
|
||||||
echo "libsystemd-dev is unavailable"
|
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
|
ACTUAL_LIST=$BASIC_LIST
|
||||||
|
|
||||||
# Now arrange for optional support libraries
|
# Now arrange for optional support libraries
|
||||||
|
@ -145,6 +167,11 @@ then
|
||||||
ACTUAL_LIST="$ACTUAL_LIST $DEBDEPS_LIST"
|
ACTUAL_LIST="$ACTUAL_LIST $DEBDEPS_LIST"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ $TESTDEPS ]
|
||||||
|
then
|
||||||
|
ACTUAL_LIST="$ACTUAL_LIST $TESTDEPS_LIST"
|
||||||
|
fi
|
||||||
|
|
||||||
apt-get update || exit 2
|
apt-get update || exit 2
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
apt-get install $ACTUAL_LIST $OPTIONS || exit 2
|
apt-get install $ACTUAL_LIST $OPTIONS || exit 2
|
||||||
|
|
Loading…
Reference in New Issue