4
0
mirror of https://github.com/QuasarApp/qca.git synced 2025-05-12 10:49:32 +00:00
Albert Astals Cid f62a8ee8f7 Run clang-format
find . \( -name "*.cpp" -or -name "*.h"  -or -name "*.c"  -or -name "*.cc" \) -exec clang-format -i {} \;

If you reached this file doing a git blame, please see README.clang-format (added 2 commits in the future of this one)
2020-09-07 02:13:47 +02:00

775 lines
26 KiB
C++

/*
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "pkcs11configdlg.h"
#include "ui_pkcs11config.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QtCore>
#include <QtCrypto>
#include <QtGui>
//----------------------------------------------------------------------------
// Pkcs11ProviderConfig
//----------------------------------------------------------------------------
class Pkcs11ProviderConfig
{
public:
bool allow_protected_authentication;
bool cert_private;
bool enabled;
QString library;
QString name;
int private_mask;
QString slotevent_method;
int slotevent_timeout;
Pkcs11ProviderConfig()
: allow_protected_authentication(true)
, cert_private(false)
, enabled(false)
, private_mask(0)
, slotevent_method("auto")
, slotevent_timeout(0)
{
}
QVariantMap toVariantMap() const
{
QVariantMap out;
out["allow_protected_authentication"] = allow_protected_authentication;
out["cert_private"] = cert_private;
out["enabled"] = enabled;
out["library"] = library;
out["name"] = name;
out["private_mask"] = private_mask;
out["slotevent_method"] = slotevent_method;
out["slotevent_timeout"] = slotevent_timeout;
return out;
}
bool fromVariantMap(const QVariantMap &in)
{
allow_protected_authentication = in["allow_protected_authentication"].toBool();
cert_private = in["cert_private"].toBool();
enabled = in["enabled"].toBool();
library = in["library"].toString();
name = in["name"].toString();
private_mask = in["private_mask"].toInt();
slotevent_method = in["slotevent_method"].toString();
slotevent_timeout = in["slotevent_timeout"].toInt();
return true;
}
};
//----------------------------------------------------------------------------
// Pkcs11Config
//----------------------------------------------------------------------------
class Pkcs11Config
{
public:
bool allow_load_rootca;
bool allow_protected_authentication;
int log_level;
int pin_cache;
QList<Pkcs11ProviderConfig> providers;
QVariantMap orig_config;
Pkcs11Config()
: allow_load_rootca(false)
, allow_protected_authentication(true)
, log_level(0)
, pin_cache(-1)
{
}
QVariantMap toVariantMap() const
{
QVariantMap out = orig_config;
// form type
out["formtype"] = "http://affinix.com/qca/forms/qca-pkcs11#1.0";
// base settings
out["allow_load_rootca"] = allow_load_rootca;
out["allow_protected_authentication"] = allow_protected_authentication;
out["log_level"] = log_level;
out["pin_cache"] = pin_cache;
// provider settings (always write at least 10 providers)
for (int n = 0; n < 10 || n < providers.count(); ++n) {
QString prefix = QString().sprintf("provider_%02d_", n);
Pkcs11ProviderConfig provider;
if (n < providers.count())
provider = providers[n];
QVariantMap subconfig = provider.toVariantMap();
QMapIterator<QString, QVariant> it(subconfig);
while (it.hasNext()) {
it.next();
out.insert(prefix + it.key(), it.value());
}
}
return out;
}
bool fromVariantMap(const QVariantMap &in)
{
if (in["formtype"] != "http://affinix.com/qca/forms/qca-pkcs11#1.0")
return false;
allow_load_rootca = in["allow_load_rootca"].toBool();
allow_protected_authentication = in["allow_protected_authentication"].toBool();
log_level = in["log_level"].toInt();
pin_cache = in["pin_cache"].toInt();
for (int n = 0;; ++n) {
QString prefix = QString().sprintf("provider_%02d_", n);
// collect all key/values with this prefix into a
// a separate container, leaving out the prefix
// from the keys.
QVariantMap subconfig;
QMapIterator<QString, QVariant> it(in);
while (it.hasNext()) {
it.next();
if (it.key().startsWith(prefix))
subconfig.insert(it.key().mid(prefix.length()), it.value());
}
// if there are no config items with this prefix, we're done
if (subconfig.isEmpty())
break;
Pkcs11ProviderConfig provider;
if (!provider.fromVariantMap(subconfig))
return false;
// skip unnamed entries
if (provider.name.isEmpty())
continue;
// skip duplicate entries
bool have_name_already = false;
foreach (const Pkcs11ProviderConfig &i, providers) {
if (i.name == provider.name) {
have_name_already = true;
break;
}
}
if (have_name_already)
continue;
providers += provider;
}
orig_config = in;
return true;
}
};
//----------------------------------------------------------------------------
// ModuleListModel
//----------------------------------------------------------------------------
class ModuleListModel : public QAbstractListModel
{
Q_OBJECT
public:
QList<Pkcs11ProviderConfig> list;
ModuleListModel(QObject *parent = 0)
: QAbstractListModel(parent)
{
}
int rowCount(const QModelIndex &parent = QModelIndex()) const
{
Q_UNUSED(parent);
return list.count();
}
QVariant data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= list.count())
return QVariant();
if (role == Qt::DisplayRole)
return list[index.row()].name;
else if (role == Qt::EditRole)
return list[index.row()].name;
else
return QVariant();
}
Qt::ItemFlags flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
bool setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
QString str = value.toString();
if (str.isEmpty()) {
emit editFailed(index, tr("Module name cannot be blank."));
return false;
}
bool have_name_already = false;
int at = index.row();
for (int n = 0; n < list.count(); ++n) {
const Pkcs11ProviderConfig &i = list[n];
// skip self
if (n == at)
continue;
if (i.name == str) {
have_name_already = true;
break;
}
}
if (have_name_already) {
emit editFailed(index, tr("There is already a module with this name."));
return false;
}
list[index.row()].name = str;
emit dataChanged(index, index);
return true;
}
return false;
}
void addItems(const QList<Pkcs11ProviderConfig> &items)
{
if (items.isEmpty())
return;
beginInsertRows(QModelIndex(), list.size(), list.size() + items.count() - 1);
list += items;
endInsertRows();
}
void addItem(const Pkcs11ProviderConfig &i)
{
beginInsertRows(QModelIndex(), list.size(), list.size());
list += i;
endInsertRows();
}
void removeItem(int at)
{
beginRemoveRows(QModelIndex(), at, at);
list.removeAt(at);
endRemoveRows();
}
Q_SIGNALS:
void editFailed(const QModelIndex &index, const QString &reasonString);
};
//----------------------------------------------------------------------------
// Pkcs11ConfigDlg
//----------------------------------------------------------------------------
static QCA::Provider *get_pkcs11_provider(QVariantMap *_config = 0)
{
QCA::ProviderList providers = QCA::providers();
providers += QCA::defaultProvider();
QCA::Provider *provider = 0;
QVariantMap config;
foreach (QCA::Provider *p, providers) {
config = QCA::getProviderConfig(p->name());
if (!config.isEmpty() && config["formtype"] == "http://affinix.com/qca/forms/qca-pkcs11#1.0") {
provider = p;
break;
}
}
if (provider && _config)
*_config = config;
return provider;
}
class Pkcs11ConfigDlg::Private : public QObject
{
Q_OBJECT
public:
Pkcs11ConfigDlg *q;
Ui_Pkcs11Config ui;
QString providerName;
ModuleListModel *model;
Pkcs11Config config;
bool dirty;
// for safe dialog closing behavior during QListView editing
bool allow_close;
bool done;
// for ignoring modifications that we cause when populating fields
bool ignore_dataChanged;
Private(Pkcs11ConfigDlg *_q, const QString &_providerName, const QVariantMap &configmap)
: QObject(_q)
, q(_q)
, providerName(_providerName)
, dirty(false)
, allow_close(true)
, done(false)
, ignore_dataChanged(true)
{
ui.setupUi(q);
q->resize(q->minimumSize());
model = new ModuleListModel(this);
qRegisterMetaType<QModelIndex>("QModelIndex");
// do this queued for two reasons:
// 1) if we throw an error dialog, it will occur after the
// row text has reverted, and the call stack completed
// (the latter may not be required, but it helps me
// sleep).
// 2) if the user accepts/rejects the dialog while editing,
// it is easy to ensure that the signal is not processed
// (if it gets delivered at all).
connect(model,
SIGNAL(editFailed(const QModelIndex &, const QString &)),
SLOT(model_editFailed(const QModelIndex &, const QString &)),
Qt::QueuedConnection);
// set up widgets
ui.rb_pincache_nolimit->setChecked(true);
ui.sb_pincache_time->setEnabled(false);
ui.sb_pincache_time->setValue(300);
ui.lv_modules->setModel(model);
ui.lv_modules->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked |
QAbstractItemView::EditKeyPressed);
ui.pb_remove->setEnabled(false);
ui.tb_details->setEnabled(false);
ui.gb_poll->setEnabled(false);
ui.rb_polldefault->setChecked(true);
ui.sb_pollcustom->setEnabled(false);
ui.sb_pollcustom->setValue(5);
ui.ck_modeauto->setChecked(true);
// disable this by default, enable on dataChanged
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
// general
connect(ui.ck_allowroot, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.ck_allowprotected, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.sb_loglevel, SIGNAL(valueChanged(int)), SLOT(dataChanged()));
connect(ui.gb_pincache, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.rb_pincache_nolimit, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.rb_pincache_time, SIGNAL(toggled(bool)), ui.sb_pincache_time, SLOT(setEnabled(bool)));
connect(ui.rb_pincache_time, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.sb_pincache_time, SIGNAL(valueChanged(int)), SLOT(dataChanged()));
// modules
connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), SLOT(dataChanged()));
connect(ui.lv_modules->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
SLOT(module_selectionChanged(const QItemSelection &, const QItemSelection &)));
connect(ui.pb_add, SIGNAL(clicked()), SLOT(module_add()));
connect(ui.pb_remove, SIGNAL(clicked()), SLOT(module_remove()));
connect(ui.le_library, SIGNAL(textChanged(const QString &)), SLOT(dataChanged()));
connect(ui.pb_browse, SIGNAL(clicked()), SLOT(library_browse()));
connect(ui.cb_slotmethod, SIGNAL(currentIndexChanged(int)), SLOT(slotmethod_currentIndexChanged(int)));
connect(ui.rb_polldefault, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.rb_pollcustom, SIGNAL(toggled(bool)), ui.sb_pollcustom, SLOT(setEnabled(bool)));
connect(ui.rb_pollcustom, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.sb_pollcustom, SIGNAL(valueChanged(int)), SLOT(dataChanged()));
connect(ui.ck_modallowprotected, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.ck_certprivate, SIGNAL(toggled(bool)), SLOT(dataChanged()));
connect(ui.ck_modeauto, SIGNAL(toggled(bool)), SLOT(modeauto_toggled(bool)));
connect(ui.ck_modesign, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
connect(ui.ck_modesignrecover, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
connect(ui.ck_modedecrypt, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
connect(ui.ck_modeunwrap, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
// is this a valid config?
if (!providerName.isEmpty() && config.fromVariantMap(configmap)) {
// if so, load everything up
ui.ck_allowroot->setChecked(config.allow_load_rootca);
ui.ck_allowprotected->setChecked(config.allow_protected_authentication);
ui.sb_loglevel->setValue(config.log_level);
if (config.pin_cache != 0) {
ui.gb_pincache->setChecked(true);
if (config.pin_cache <= -1)
ui.rb_pincache_nolimit->setChecked(true);
else {
ui.rb_pincache_time->setChecked(true);
ui.sb_pincache_time->setValue(config.pin_cache);
}
}
model->addItems(config.providers);
if (!model->list.isEmpty()) {
QModelIndex index = model->index(0);
ui.lv_modules->setCurrentIndex(index);
ui.lv_modules->selectionModel()->select(
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
}
ui.buttonBox->setFocus();
ui.buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
} else {
// otherwise, disable everything
ui.gb_general->setEnabled(false);
ui.gb_modules->setEnabled(false);
ui.buttonBox->setFocus();
ui.buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
}
ignore_dataChanged = false;
}
void save_module(int at)
{
// save all options (except the name, which is handled by the model)
Pkcs11ProviderConfig &i = model->list[at];
i.library = ui.le_library->text();
i.enabled = true;
int x = ui.cb_slotmethod->currentIndex();
if (x == 0)
i.slotevent_method = "auto";
else if (x == 1)
i.slotevent_method = "trigger";
else // 2
i.slotevent_method = "poll";
if (x == 2) {
if (ui.rb_polldefault->isChecked())
i.slotevent_timeout = 0;
else
i.slotevent_timeout = ui.sb_pollcustom->value();
} else
i.slotevent_timeout = 0;
i.allow_protected_authentication = ui.ck_modallowprotected->isChecked();
i.cert_private = ui.ck_certprivate->isChecked();
i.private_mask = 0;
if (ui.ck_modesign->isChecked())
i.private_mask |= 1;
if (ui.ck_modesignrecover->isChecked())
i.private_mask |= 2;
if (ui.ck_modedecrypt->isChecked())
i.private_mask |= 4;
if (ui.ck_modeunwrap->isChecked())
i.private_mask |= 8;
}
void save()
{
// save currently selected module, which may not be saved yet
QItemSelection selection = ui.lv_modules->selectionModel()->selection();
if (!selection.indexes().isEmpty()) {
QModelIndex index = selection.indexes().first();
save_module(index.row());
}
config.allow_load_rootca = ui.ck_allowroot->isChecked();
config.allow_protected_authentication = ui.ck_allowprotected->isChecked();
config.log_level = ui.sb_loglevel->value();
if (ui.gb_pincache->isChecked()) {
if (ui.rb_pincache_nolimit->isChecked())
config.pin_cache = -1;
else
config.pin_cache = ui.sb_pincache_time->value();
} else
config.pin_cache = 0;
config.providers = model->list;
QVariantMap configmap = config.toVariantMap();
QCA::setProviderConfig(providerName, configmap);
QCA::saveProviderConfig(providerName);
}
private Q_SLOTS:
void model_editFailed(const QModelIndex &index, const QString &reasonString)
{
// if the dialog has already been dismissed, then don't
// bother with handling the editing failure
if (done)
return;
// show error dialog, and don't allow dimissing the dialog
// during. we need this, because the dismiss request
// can be queued, and end up being invoked during the
// QMessageBox nested eventloop.
allow_close = false;
QMessageBox::information(q, tr("Module Configuration"), reasonString);
allow_close = true;
// return to edit mode for the item
ui.lv_modules->setFocus();
ui.lv_modules->setCurrentIndex(index);
ui.lv_modules->selectionModel()->select(
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
ui.lv_modules->edit(index);
}
void dataChanged()
{
if (ignore_dataChanged)
return;
if (dirty)
return;
dirty = true;
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
}
void module_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
if (!deselected.indexes().isEmpty()) {
QModelIndex index = deselected.indexes().first();
save_module(index.row());
}
ignore_dataChanged = true;
if (!selected.indexes().isEmpty()) {
if (deselected.indexes().isEmpty()) {
ui.pb_remove->setEnabled(true);
ui.tb_details->setEnabled(true);
}
QModelIndex index = selected.indexes().first();
const Pkcs11ProviderConfig &i = model->list[index.row()];
ui.le_library->setText(i.library);
if (i.slotevent_method == "trigger")
ui.cb_slotmethod->setCurrentIndex(1);
else if (i.slotevent_method == "poll") {
ui.cb_slotmethod->setCurrentIndex(2);
if (i.slotevent_timeout <= 0)
ui.rb_polldefault->setChecked(true);
else {
ui.rb_pollcustom->setChecked(true);
ui.sb_pollcustom->setValue(i.slotevent_timeout);
}
} else // auto
ui.cb_slotmethod->setCurrentIndex(0);
if (i.slotevent_method != "poll") {
ui.rb_polldefault->setChecked(true);
ui.sb_pollcustom->setValue(5);
}
ui.ck_modallowprotected->setChecked(i.allow_protected_authentication);
ui.ck_certprivate->setChecked(i.cert_private);
if (i.private_mask == 0)
ui.ck_modeauto->setChecked(true);
else {
ui.ck_modesign->setChecked(i.private_mask & 1);
ui.ck_modesignrecover->setChecked(i.private_mask & 2);
ui.ck_modedecrypt->setChecked(i.private_mask & 4);
ui.ck_modeunwrap->setChecked(i.private_mask & 8);
}
} else if (selected.indexes().isEmpty() && !deselected.indexes().isEmpty()) {
// restore defaults for all details widgets
ui.le_library->setText(QString());
ui.cb_slotmethod->setCurrentIndex(0);
ui.rb_polldefault->setChecked(true);
ui.sb_pollcustom->setValue(5);
ui.ck_modallowprotected->setChecked(false);
ui.ck_certprivate->setChecked(false);
ui.ck_modeauto->setChecked(true);
// flip to first page, disable
ui.tb_details->setCurrentIndex(0);
ui.pb_remove->setEnabled(false);
ui.tb_details->setEnabled(false);
}
ignore_dataChanged = false;
}
void module_add()
{
// find unused default name
QString name;
for (int n = 1;; ++n) {
if (n == 1)
name = tr("New Module");
else
name = tr("New Module (%1)").arg(n);
bool have_name_already = false;
for (int n = 0; n < model->list.count(); ++n) {
const Pkcs11ProviderConfig &i = model->list[n];
if (i.name == name) {
have_name_already = true;
break;
}
}
if (!have_name_already)
break;
}
Pkcs11ProviderConfig i;
i.name = name;
i.enabled = true;
model->addItem(i);
dataChanged();
QModelIndex index = model->index(model->list.count() - 1);
// flip to first page
ui.tb_details->setCurrentIndex(0);
// edit this item
ui.lv_modules->setFocus();
ui.lv_modules->setCurrentIndex(index);
ui.lv_modules->selectionModel()->select(
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
ui.lv_modules->edit(index);
}
void module_remove()
{
QItemSelection selection = ui.lv_modules->selectionModel()->selection();
if (selection.indexes().isEmpty())
return;
QModelIndex index = selection.indexes().first();
model->removeItem(index.row());
dataChanged();
}
void library_browse()
{
QString fileName =
QFileDialog::getOpenFileName(q, tr("Select PKCS#11 Module"), QString(), tr("PKCS#11 Modules (*.*)"));
if (fileName.isEmpty())
return;
ui.le_library->setText(fileName);
}
void slotmethod_currentIndexChanged(int index)
{
if (index == 2) // Polling
ui.gb_poll->setEnabled(true);
else
ui.gb_poll->setEnabled(false);
dataChanged();
}
void modeauto_toggled(bool checked)
{
if (checked) {
if (ui.ck_modesign->isChecked())
ui.ck_modesign->setChecked(false);
if (ui.ck_modesignrecover->isChecked())
ui.ck_modesignrecover->setChecked(false);
if (ui.ck_modedecrypt->isChecked())
ui.ck_modedecrypt->setChecked(false);
if (ui.ck_modeunwrap->isChecked())
ui.ck_modeunwrap->setChecked(false);
} else {
if (!ui.ck_modesign->isChecked() && !ui.ck_modesignrecover->isChecked() &&
!ui.ck_modedecrypt->isChecked() && !ui.ck_modeunwrap->isChecked()) {
ui.ck_modesign->setChecked(true);
ui.ck_modesignrecover->setChecked(true);
ui.ck_modedecrypt->setChecked(true);
ui.ck_modeunwrap->setChecked(true);
}
}
dataChanged();
}
void modenonauto_toggled(bool checked)
{
if (checked) {
if (ui.ck_modeauto->isChecked())
ui.ck_modeauto->setChecked(false);
} else {
if (!ui.ck_modesign->isChecked() && !ui.ck_modesignrecover->isChecked() &&
!ui.ck_modedecrypt->isChecked() && !ui.ck_modeunwrap->isChecked()) {
ui.ck_modeauto->setChecked(true);
}
}
dataChanged();
}
};
Pkcs11ConfigDlg::Pkcs11ConfigDlg(QWidget *parent)
: QDialog(parent)
{
QVariantMap config;
QCA::Provider *p = get_pkcs11_provider(&config);
if (p)
d = new Private(this, p->name(), config);
else
d = new Private(this, QString(), QVariantMap());
}
Pkcs11ConfigDlg::Pkcs11ConfigDlg(const QString &providerName, const QVariantMap &config, QWidget *parent)
: QDialog(parent)
{
d = new Private(this, providerName, config);
}
Pkcs11ConfigDlg::~Pkcs11ConfigDlg()
{
delete d;
}
void Pkcs11ConfigDlg::done(int r)
{
if (!d->allow_close)
return;
d->done = true;
if (r == Accepted)
d->save();
QDialog::done(r);
}
bool Pkcs11ConfigDlg::isSupported()
{
return (get_pkcs11_provider() ? true : false);
}
#include "pkcs11configdlg.moc"