forked from osmocom/wireshark
![Gerald Combs](/assets/img/avatar_default.png)
Remove the editor modeline blocks from most of the source files in ui/qt by running perl -i -p0e 's{ \n+ /[ *\n]+ editor \s+ modelines .* shiftwidth= .* \*/ \s+ } {\n}gsix' $( ag -g '\.(cpp|h)' ) then cleaning up the remaining files by hand. This *shouldn't* affect anyone since - All of the source files in ui/qt use 4 space indentation, which matches the default in our top-level .editorconfig - The one notable editor that's likely to be used on these files and *doesn't* support EditorConfig (Qt Creator) defaults to 4 space indentation.
274 lines
8.8 KiB
C++
274 lines
8.8 KiB
C++
/* rsa_keys_frame.cpp
|
|
*
|
|
* Copyright 2019 Peter Wu <peter@lekensteyn.nl>
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "rsa_keys_frame.h"
|
|
#include <ui_rsa_keys_frame.h>
|
|
|
|
#include "ui/qt/widgets/wireshark_file_dialog.h"
|
|
#include <wsutil/report_message.h>
|
|
#include <QMessageBox>
|
|
#include <ui/all_files_wildcard.h>
|
|
|
|
#include <epan/secrets.h>
|
|
#include <QInputDialog>
|
|
|
|
#ifdef HAVE_LIBGNUTLS
|
|
RsaKeysFrame::RsaKeysFrame(QWidget *parent) :
|
|
QFrame(parent),
|
|
ui(new Ui::RsaKeysFrame),
|
|
rsa_keys_model_(0),
|
|
pkcs11_libs_model_(0)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
#ifdef Q_OS_MAC
|
|
ui->addFileButton->setAttribute(Qt::WA_MacSmallSize, true);
|
|
ui->addItemButton->setAttribute(Qt::WA_MacSmallSize, true);
|
|
ui->deleteItemButton->setAttribute(Qt::WA_MacSmallSize, true);
|
|
ui->addLibraryButton->setAttribute(Qt::WA_MacSmallSize, true);
|
|
ui->deleteLibraryButton->setAttribute(Qt::WA_MacSmallSize, true);
|
|
#endif
|
|
|
|
#ifdef HAVE_GNUTLS_PKCS11
|
|
pkcs11_libs_model_ = new UatModel(this, "PKCS #11 Provider Libraries");
|
|
ui->libsView->setModel(pkcs11_libs_model_);
|
|
connect(ui->libsView->selectionModel(), &QItemSelectionModel::currentChanged,
|
|
this, &RsaKeysFrame::libCurrentChanged);
|
|
#else
|
|
ui->addLibraryButton->setEnabled(false);
|
|
#endif
|
|
|
|
rsa_keys_model_ = new UatModel(this, "RSA Private Keys");
|
|
ui->keysView->setModel(rsa_keys_model_);
|
|
connect(ui->keysView->selectionModel(), &QItemSelectionModel::currentChanged,
|
|
this, &RsaKeysFrame::keyCurrentChanged);
|
|
}
|
|
#else /* ! HAVE_LIBGNUTLS */
|
|
RsaKeysFrame::RsaKeysFrame(QWidget *parent) : QFrame(parent) { }
|
|
#endif /* ! HAVE_LIBGNUTLS */
|
|
|
|
#ifdef HAVE_LIBGNUTLS
|
|
RsaKeysFrame::~RsaKeysFrame()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
gboolean RsaKeysFrame::verifyKey(const char *uri, const char *password, gboolean *need_password, QString &error)
|
|
{
|
|
char *error_c = NULL;
|
|
gboolean key_ok = secrets_verify_key(qPrintable(uri), qPrintable(password), need_password, &error_c);
|
|
error = error_c ? error_c : "";
|
|
g_free(error_c);
|
|
return key_ok;
|
|
}
|
|
|
|
void RsaKeysFrame::addKey(const QString &uri, const QString &password)
|
|
{
|
|
// Create a new UAT entry with the given URI and PIN/password.
|
|
int row = rsa_keys_model_->rowCount();
|
|
rsa_keys_model_->insertRows(row, 1);
|
|
rsa_keys_model_->setData(rsa_keys_model_->index(row, 0), uri);
|
|
rsa_keys_model_->setData(rsa_keys_model_->index(row, 1), password);
|
|
ui->keysView->setCurrentIndex(rsa_keys_model_->index(row, 0));
|
|
}
|
|
|
|
void RsaKeysFrame::keyCurrentChanged(const QModelIndex ¤t, const QModelIndex & /* previous */)
|
|
{
|
|
ui->deleteItemButton->setEnabled(current.isValid());
|
|
}
|
|
|
|
void RsaKeysFrame::on_addItemButton_clicked()
|
|
{
|
|
GSList *keys_list = secrets_get_available_keys();
|
|
QStringList keys;
|
|
if (keys_list) {
|
|
for (GSList *uri = keys_list; uri; uri = uri->next) {
|
|
keys << (char *)uri->data;
|
|
}
|
|
g_slist_free_full(keys_list, g_free);
|
|
}
|
|
|
|
// Remove duplicates (keys that have already been added)
|
|
for (int row = rsa_keys_model_->rowCount() - 1; row >= 0; --row) {
|
|
QString item = rsa_keys_model_->data(rsa_keys_model_->index(row, 0)).toString();
|
|
keys.removeAll(item);
|
|
}
|
|
|
|
if (keys.isEmpty()) {
|
|
QMessageBox::information(this, tr("Add PKCS #11 token or key"),
|
|
tr("No new PKCS #11 tokens or keys found, consider adding a PKCS #11 provider."),
|
|
QMessageBox::Ok);
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
QString item = QInputDialog::getItem(this,
|
|
tr("Select a new PKCS #11 token or key"),
|
|
tr("PKCS #11 token or key"), keys, 0, false, &ok);
|
|
if (!ok || item.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Validate the token, is a PIN needed?
|
|
gboolean key_ok = false, needs_pin = true;
|
|
QString error;
|
|
if (!item.startsWith("pkcs11:")) {
|
|
// For keys other than pkcs11, try to verify the key without password.
|
|
// (The PIN must always be prompted for PKCS #11 tokens, otherwise it is
|
|
// possible that an already unlocked token will not trigger a prompt).
|
|
key_ok = verifyKey(qPrintable(item), NULL, &needs_pin, error);
|
|
}
|
|
QString pin;
|
|
while (!key_ok && needs_pin) {
|
|
// A PIN is possibly needed, prompt for one.
|
|
QString msg;
|
|
if (!error.isEmpty()) {
|
|
msg = error + "\n";
|
|
error.clear();
|
|
}
|
|
msg += tr("Enter PIN or password for %1 (it will be stored unencrypted)");
|
|
pin = QInputDialog::getText(this, tr("Enter PIN or password for key"),
|
|
msg.arg(item), QLineEdit::Password, "", &ok);
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
key_ok = verifyKey(qPrintable(item), qPrintable(pin), NULL, error);
|
|
}
|
|
if (!key_ok) {
|
|
QMessageBox::warning(this,
|
|
tr("Add PKCS #11 token or key"),
|
|
tr("Key could not be added: %1").arg(item),
|
|
QMessageBox::Ok);
|
|
return;
|
|
}
|
|
|
|
addKey(item, pin);
|
|
}
|
|
|
|
void RsaKeysFrame::on_addFileButton_clicked()
|
|
{
|
|
QString filter =
|
|
tr("RSA private key (*.pem *.p12 *.pfx *.key);;All Files (" ALL_FILES_WILDCARD ")");
|
|
QString file = WiresharkFileDialog::getOpenFileName(this,
|
|
tr("Select RSA private key file"), "", filter);
|
|
if (file.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Try to load the key as unencrypted key file. If any errors occur, assume
|
|
// an encrypted key file and prompt for a password.
|
|
QString password, error;
|
|
gboolean key_ok = secrets_verify_key(qPrintable(file), NULL, NULL, NULL);
|
|
while (!key_ok) {
|
|
QString msg;
|
|
if (!error.isEmpty()) {
|
|
msg = error + "\n";
|
|
error.clear();
|
|
}
|
|
msg += QString("Enter the password to open %1").arg(file);
|
|
|
|
bool ok;
|
|
password = QInputDialog::getText(this, tr("Select RSA private key file"), msg,
|
|
QLineEdit::Password, "", &ok);
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
key_ok = verifyKey(qPrintable(file), qPrintable(password), NULL, error);
|
|
}
|
|
|
|
addKey(file, password);
|
|
}
|
|
|
|
void RsaKeysFrame::on_deleteItemButton_clicked()
|
|
{
|
|
const QModelIndex ¤t = ui->keysView->currentIndex();
|
|
if (rsa_keys_model_ && current.isValid()) {
|
|
rsa_keys_model_->removeRows(current.row(), 1);
|
|
}
|
|
}
|
|
|
|
void RsaKeysFrame::acceptChanges()
|
|
{
|
|
// Save keys list mutations. The PKCS #11 provider list was already saved.
|
|
QString error;
|
|
if (rsa_keys_model_->applyChanges(error) && !error.isEmpty()) {
|
|
report_failure("%s", qPrintable(error));
|
|
}
|
|
}
|
|
|
|
void RsaKeysFrame::rejectChanges()
|
|
{
|
|
// Revert keys list mutations. The PKCS #11 provider list was already saved.
|
|
QString error;
|
|
if (rsa_keys_model_->revertChanges(error) && !error.isEmpty()) {
|
|
report_failure("%s", qPrintable(error));
|
|
}
|
|
}
|
|
|
|
void RsaKeysFrame::libCurrentChanged(const QModelIndex ¤t, const QModelIndex & /* previous */)
|
|
{
|
|
ui->deleteLibraryButton->setEnabled(current.isValid());
|
|
}
|
|
|
|
void RsaKeysFrame::on_addLibraryButton_clicked()
|
|
{
|
|
if (!pkcs11_libs_model_) return;
|
|
|
|
#ifdef Q_OS_WIN
|
|
QString filter(tr("Libraries (*.dll)"));
|
|
#else
|
|
QString filter(tr("Libraries (*.so)"));
|
|
#endif
|
|
QString file = WiresharkFileDialog::getOpenFileName(this, tr("Select PKCS #11 Provider Library"), "", filter);
|
|
if (file.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
int row = pkcs11_libs_model_->rowCount();
|
|
pkcs11_libs_model_->insertRows(row, 1);
|
|
pkcs11_libs_model_->setData(pkcs11_libs_model_->index(row, 0), file);
|
|
ui->libsView->setCurrentIndex(pkcs11_libs_model_->index(row, 0));
|
|
|
|
// As the libraries affect the availability of PKCS #11 tokens, we will
|
|
// immediately apply changes without waiting for the OK button to be
|
|
// activated.
|
|
QString error;
|
|
if (pkcs11_libs_model_->applyChanges(error) && error.isEmpty()) {
|
|
report_failure("%s", qPrintable(error));
|
|
}
|
|
}
|
|
|
|
void RsaKeysFrame::on_deleteLibraryButton_clicked()
|
|
{
|
|
if (!pkcs11_libs_model_) return;
|
|
|
|
const QModelIndex ¤t = ui->libsView->currentIndex();
|
|
if (!current.isValid()) {
|
|
return;
|
|
}
|
|
|
|
QString file = pkcs11_libs_model_->data(current, 0).toString();
|
|
pkcs11_libs_model_->removeRows(current.row(), 1);
|
|
// Due to technical limitations of GnuTLS, libraries cannot be unloaded or
|
|
// disabled once loaded. Inform the user of this caveat.
|
|
QMessageBox::information(this, tr("Changes will apply after a restart"),
|
|
tr("PKCS #11 provider %1 will be removed after the next restart.").arg(file),
|
|
QMessageBox::Ok);
|
|
// Make sure the UAT is actually saved to file.
|
|
QString error;
|
|
if (pkcs11_libs_model_->applyChanges(error) && error.isEmpty()) {
|
|
report_failure("%s", qPrintable(error));
|
|
}
|
|
}
|
|
#endif /* HAVE_LIBGNUTLS */
|