diff options
author | Peter Wu <peter@lekensteyn.nl> | 2018-12-09 17:28:48 +0100 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2018-12-13 05:28:02 +0000 |
commit | 48033906868075f43e511e4f906684c474454290 (patch) | |
tree | e1fc50ec9dd15cc352b37c192f221f6dcc55ddd1 /epan/secrets.c | |
parent | 43dfd45faa8af0b239a671b25ab6a398fa32f5c6 (diff) |
Add new "rsa_keys" UAT for storage of RSA private keys
This should eventually replace the "ssl_keys" UAT which additionally
contains a useless address, port and protocol field. This prepares for
HSM support through PKCS #11.
Change-Id: I59409c98aeedf260d19266d18e14ef7d9b40b582
Reviewed-on: https://code.wireshark.org/review/30977
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Peter Wu <peter@lekensteyn.nl>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/secrets.c')
-rw-r--r-- | epan/secrets.c | 200 |
1 files changed, 198 insertions, 2 deletions
diff --git a/epan/secrets.c b/epan/secrets.c index c539a95e06..34b7dabfff 100644 --- a/epan/secrets.c +++ b/epan/secrets.c @@ -11,22 +11,51 @@ #include "config.h" +/* Start with G_MESSAGES_DEBUG=secrets to see messages. */ +#define G_LOG_DOMAIN "secrets" + #include "secrets.h" #include <wiretap/wtap.h> #include <string.h> #ifdef HAVE_LIBGNUTLS -#include <gnutls/gnutls.h> -#include <gnutls/abstract.h> +# include <gnutls/gnutls.h> +# include <gnutls/abstract.h> +# include <wsutil/wsgcrypt.h> +# include <wsutil/rsa.h> +# include <epan/uat.h> +# include <wsutil/report_message.h> +# include <wsutil/file_util.h> +# include <errno.h> #endif /* HAVE_LIBGNUTLS */ /** Maps guint32 secrets_type -> secrets_block_callback_t. */ static GHashTable *secrets_callbacks; +#ifdef HAVE_LIBGNUTLS +/** Maps public key IDs (cert_key_id_t) -> gnutls_privkey_t. */ +static GHashTable *rsa_privkeys; + +typedef struct { + char *uri; /**< User-supplied PKCS #11 URI for token or RSA private key file. */ + char *password; /**< User-supplied PKCS #11 PIN or RSA private key file password. */ +} rsa_privkey_record_t; + +static uat_t *rsa_privkeys_uat; +static rsa_privkey_record_t *uat_rsa_privkeys; +static guint uat_num_rsa_privkeys; + +static void register_rsa_uats(void); +#endif /* HAVE_LIBGNUTLS */ + void secrets_init(void) { secrets_callbacks = g_hash_table_new(g_direct_hash, g_direct_equal); +#ifdef HAVE_LIBGNUTLS + rsa_privkeys = privkey_hash_table_new(); + register_rsa_uats(); +#endif /* HAVE_LIBGNUTLS */ } void @@ -34,6 +63,10 @@ secrets_cleanup(void) { g_hash_table_destroy(secrets_callbacks); secrets_callbacks = NULL; +#ifdef HAVE_LIBGNUTLS + g_hash_table_destroy(rsa_privkeys); + rsa_privkeys = NULL; +#endif /* HAVE_LIBGNUTLS */ } void @@ -78,6 +111,169 @@ privkey_hash_table_new(void) { return g_hash_table_new_full(key_id_hash, key_id_equal, g_free, (GDestroyNotify)gnutls_privkey_deinit); } + +static void +rsa_privkey_add(const cert_key_id_t *key_id, gnutls_privkey_t pkey) +{ + void *ht_key = g_memdup(key_id->key_id, sizeof(cert_key_id_t)); + const guint32 *dw = (const guint32 *)key_id->key_id; + g_hash_table_insert(rsa_privkeys, ht_key, pkey); + g_debug("Adding key %08x%08x%08x%08x%08x", dw[0], dw[1], dw[2], dw[3], dw[4]); +} + +UAT_FILENAME_CB_DEF(rsa_privkeys_uats, uri, rsa_privkey_record_t) +UAT_CSTRING_CB_DEF(rsa_privkeys_uats, password, rsa_privkey_record_t) + +static void * +uat_rsa_privkey_copy_str_cb(void *dest, const void *source, size_t len _U_) +{ + rsa_privkey_record_t *d = (rsa_privkey_record_t *)dest; + const rsa_privkey_record_t *s = (const rsa_privkey_record_t *)source; + d->uri = g_strdup(s->uri); + d->password = g_strdup(s->password); + return dest; +} + +static void +uat_rsa_privkey_free_str_cb(void *record) +{ + rsa_privkey_record_t *rec = (rsa_privkey_record_t *)record; + g_free(rec->uri); + g_free(rec->password); +} + +static void +load_rsa_keyfile(const char *filename, const char *password, char **err) +{ + gnutls_x509_privkey_t x509_priv_key; + gnutls_privkey_t privkey = NULL; + char *errmsg = NULL; + int ret; + cert_key_id_t key_id; + size_t size = sizeof(key_id); + + FILE *fp = ws_fopen(filename, "rb"); + if (!fp) { + *err = g_strdup_printf("Error loading RSA key file %s: %s", filename, g_strerror(errno)); + return; + } + + if (!password[0]) { + x509_priv_key = rsa_load_pem_key(fp, &errmsg); + } else { + /* Assume encrypted PKCS #12 container. */ + x509_priv_key = rsa_load_pkcs12(fp, password, &errmsg); + } + fclose(fp); + if (!x509_priv_key) { + *err = g_strdup_printf("Error loading RSA key file %s: %s", filename, errmsg); + g_free(errmsg); + return; + } + + gnutls_privkey_init(&privkey); + ret = gnutls_privkey_import_x509(privkey, x509_priv_key, + GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE|GNUTLS_PRIVKEY_IMPORT_COPY); + if (ret < 0) { + *err = g_strdup_printf("Error importing private key %s: %s", filename, gnutls_strerror(ret)); + goto end; + } + ret = gnutls_x509_privkey_get_key_id(x509_priv_key, GNUTLS_KEYID_USE_SHA1, key_id.key_id, &size); + if (ret < 0 || size != sizeof(key_id)) { + *err = g_strdup_printf("Error calculating Key ID for %s: %s", filename, gnutls_strerror(ret)); + goto end; + } + + /* Remember the private key. */ + rsa_privkey_add(&key_id, privkey); + privkey = NULL; + +end: + gnutls_x509_privkey_deinit(x509_priv_key); + gnutls_privkey_deinit(privkey); +} + +static void +uat_rsa_privkeys_post_update(void) +{ + /* Clear previous keys. */ + g_hash_table_remove_all(rsa_privkeys); + GString *errors = NULL; + + for (guint i = 0; i < uat_num_rsa_privkeys; i++) { + const rsa_privkey_record_t *rec = &uat_rsa_privkeys[i]; + const char *token_uri = rec->uri; + char *err = NULL; + + if (g_str_has_prefix(token_uri, "pkcs11:")) { + } else { + load_rsa_keyfile(token_uri, rec->password, &err); + } + if (err) { + if (!errors) { + errors = g_string_new("Error processing rsa_privkeys:"); + } + g_string_append_c(errors, '\n'); + g_string_append(errors, err); + g_free(err); + } + } + if (errors) { + report_failure("%s", errors->str); + g_string_free(errors, TRUE); + } +} + +/** + * Register the UAT definitions such that settings can be loaded from file. + * Note: relies on uat_load_all to invoke the post_update_cb in order of + * registration below such that libraries are loaded *before* keys are read. + */ +static void +register_rsa_uats(void) +{ + 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"), + UAT_END_FIELDS + }; + rsa_privkeys_uat = uat_new("RSA Private Keys", + sizeof(rsa_privkey_record_t), + "rsa_keys", /* filename */ + FALSE, /* from_profile */ + &uat_rsa_privkeys, /* data_ptr */ + &uat_num_rsa_privkeys, /* numitems_ptr */ + 0, /* does not directly affect dissection */ + NULL, /* Help section (currently a wiki page) */ + uat_rsa_privkey_copy_str_cb, /* copy_cb */ + NULL, /* update_cb */ + uat_rsa_privkey_free_str_cb, /* free_cb */ + uat_rsa_privkeys_post_update, /* post_update_cb */ + NULL, /* reset_cb */ + uat_rsa_privkeys_fields); +} + +int +secrets_rsa_decrypt(const cert_key_id_t *key_id, const guint8 *encr, int encr_len, guint8 **out, int *out_len) +{ + gboolean ret; + gnutls_datum_t ciphertext = { (guchar *)encr, encr_len }; + gnutls_datum_t plain = { 0 }; + + gnutls_privkey_t pkey = (gnutls_privkey_t)g_hash_table_lookup(rsa_privkeys, key_id->key_id); + if (!pkey) { + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + ret = gnutls_privkey_decrypt_data(pkey, 0, &ciphertext, &plain); + if (ret == 0) { + *out = (guint8 *)g_memdup(plain.data, plain.size); + *out_len = plain.size; + gnutls_free(plain.data); + } + + return ret; +} #endif /* HAVE_LIBGNUTLS */ /* |