xca/widgets/NewX509.cpp
Christian Hohnstaedt 5b7aa117fa NewX509: select correct help context by tab
Especially removing tabs will shuffle the numbers.
Set the help context as tab-widget objects name.
2021-05-02 23:11:22 +02:00

1410 lines
34 KiB
C++

/* vi: set sw=4 ts=4:
*
* Copyright (C) 2001 - 2020 Christian Hohnstaedt.
*
* All rights reserved.
*/
#include "NewX509.h"
#include <QCheckBox>
#include <QComboBox>
#include <QRadioButton>
#include <QMessageBox>
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QPixmap>
#include <QPushButton>
#include <QValidator>
#include <QMap>
#include <QPair>
#include "XcaWarning.h"
#include "NewKey.h"
#include "CertDetail.h"
#include "OidResolver.h"
#include "MainWindow.h"
#include "Help.h"
#include "v3ext.h"
#include "lib/x509name.h"
#include "lib/db_key.h"
#include "lib/db_x509req.h"
#include "lib/db_x509.h"
#include "lib/db_temp.h"
#include "lib/oid.h"
#include "lib/func.h"
void NewX509::setupExplicitDN(NIDlist my_dn_nid = NIDlist())
{
NIDlist expl_dn_nid;
/* Create configured explicit_dn list */
if (!Settings["explicit_dn"].empty()) {
foreach(QString dn, Settings["explicit_dn"].split(",")) {
int nid = OBJ_sn2nid(CCHAR(dn));
if (!my_dn_nid.contains(nid))
expl_dn_nid << nid;
}
}
nameEdits = setupExplicitInputs(my_dn_nid + expl_dn_nid,
dnWidget, description, 2);
}
QList<nameEdit> NewX509::setupExplicitInputs(NIDlist nid_list,
QWidget *parent, QWidget *old, int columns)
{
QList<nameEdit> edits;
QGridLayout *layout = dynamic_cast<QGridLayout *>(parent->layout());
if (layout) {
QLayoutItem *child;
while ((child = layout->takeAt(0))) {
delete child->widget();
delete child;
}
} else {
layout = new QGridLayout(parent);
layout->setAlignment(Qt::AlignTop);
layout->setSpacing(6);
layout->setMargin(0);
}
int n = 0, col = 0;
foreach(int nid, nid_list) {
DoubleClickLabel *label;
QLineEdit *edit;
QString trans = dn_translations[nid];
QString ln = OBJ_nid2ln(nid), sn = OBJ_nid2sn(nid);
label = new DoubleClickLabel(parent);
if (Settings["translate_dn"] && !trans.isEmpty()) {
label->setText(trans);
label->setToolTip(QString("[%1] %2").arg(sn, ln));
if (sn == ln)
label->setToolTip(ln);
} else {
label->setText(ln);
label->setToolTip(QString("[%1] %2").arg(sn, trans));
if (trans == sn)
label->setToolTip(trans);
}
label->setClickText(OBJ_nid2sn(nid));
connect(label, SIGNAL(doubleClicked(QString)),
MainWindow::getResolver(), SLOT(searchOid(QString)));
edit = new QLineEdit(parent);
setupLineEditByNid(nid, edit);
edits << nameEdit(nid, edit, label);
layout->addWidget(label, n, col);
layout->addWidget(edit, n, col +1);
n++;
if (n > (nid_list.size()-1)/columns) {
col += 2;
n = 0;
}
QWidget::setTabOrder(old, edit);
old = edit;
}
return edits;
}
NewX509::NewX509(QWidget *w) : QDialog(w ?: mainwin)
{
int i;
QStringList keys;
db_key *keymodel = Database.model<db_key>();
db_x509req *reqmodel = Database.model<db_x509req>();
attr_nid << NID_pkcs9_unstructuredName << NID_pkcs9_challengePassword;
setupUi(this);
mainwin->helpdlg->register_ctxhelp_button(this, "wizard");
/* temporary storage for creating temporary X509V3_CTX */
ctx_cert = NULL;
pkiSource = generated;
foreach(int nid, distname_nid)
keys << QString(OBJ_nid2ln(nid));
tabnames = QStringList({
"wizard_src", "wizard_subject", "wizard_extensions",
"wizard_keyusage", "wizard_netscape", "wizard_advanced",
"comment"});
extDNlist->setKeys(keys);
extDNlist->setInfoLabel(extDNinfo);
connect(extDNlist->itemDelegateForColumn(1),
SIGNAL(setupLineEdit(const QString &, QLineEdit *)),
this, SLOT(setupExtDNwidget(const QString &, QLineEdit *)));
connect(subAltName, SIGNAL(textChanged(const QString &)),
this, SLOT(checkSubAltName(const QString &)));
connect(issAltName, SIGNAL(textChanged(const QString &)),
this, SLOT(checkIssAltName(const QString &)));
connect(crlDist, SIGNAL(textChanged(const QString &)),
this, SLOT(checkCrlDist(const QString &)));
connect(authInfAcc, SIGNAL(textChanged(const QString &)),
this, SLOT(checkAuthInfAcc(const QString &)));
connect(keymodel, SIGNAL(keyDone(pki_key*)),
this, SLOT(newKeyDone(pki_key*)));
connect(reqmodel, SIGNAL(pkiChanged(pki_base*)),
this, SLOT(itemChanged(pki_base*)));
setWindowTitle(XCA_TITLE);
for (i=0; i<tabWidget->count(); i++) {
tabWidget->widget(i)->setObjectName(tabnames[i]);
qDebug() << "TAB:" << i << tabWidget->tabText(i);
}
nsImg->setPixmap(QPixmap(":nsImg"));
// are there any useable private keys ?
newKeyDone(NULL);
// any PKCS#10 requests to be used ?
QList<pki_x509req *> requests = getAllRequests();
if (requests.isEmpty()) {
fromReqCB->setDisabled(true);
fromReqCB->setChecked(false);
} else {
reqList->insertPkiItems(requests);
}
on_fromReqCB_clicked();
// How about signing certificates ?
QList<pki_x509*> issuers = getAllIssuers();
if (issuers.isEmpty()) {
foreignSignRB->setDisabled(true);
} else {
certList->insertPkiItems(issuers);
}
// set dates to now and now + 1 year
validN->setText("1");
validRange->setCurrentIndex(2);
on_applyTime_clicked();
// settings for the templates ....
tempList->insertPkiItems(getAllTempsAndPredefs());
// setup Extended keyusage
foreach(int nid, extkeyuse_nid)
ekeyUsage->addItem(OBJ_nid2ln(nid));
// init the X509 v3 context
X509V3_set_ctx(&ext_ctx, NULL , NULL, NULL, NULL, 0);
X509V3_set_ctx_nodb(&ext_ctx);
// Setup dnWidget
setupExplicitDN();
// Setup Request Attributes
attrEdits = setupExplicitInputs(attr_nid, attrWidget, reqSubChange, 1);
// last polish
on_certList_currentIndexChanged(0);
certList->setDisabled(true);
tabWidget->setCurrentIndex(0);
attrWidget->hide();
pt = none;
notAfter->setEndDate(true);
QMap<int, QWidget*> nidWidget;
nidWidget[NID_subject_alt_name] = sanLbl;
nidWidget[NID_issuer_alt_name] = ianLbl;
nidWidget[NID_crl_distribution_points] = crldpLbl;
nidWidget[NID_info_access] = aiaLbl;
nidWidget[NID_netscape_base_url] = nsBaseLbl;
nidWidget[NID_netscape_revocation_url] = nsRevLbl;
nidWidget[NID_netscape_ca_revocation_url] = nsCaRevLbl;
nidWidget[NID_netscape_renewal_url] = nsRenewLbl;
nidWidget[NID_netscape_ca_policy_url] = nsCaPolicyLbl;
nidWidget[NID_netscape_ssl_server_name] = nsSslServerLbl;
nidWidget[NID_netscape_comment] = nsCommentLbl;
nidWidget[NID_basic_constraints] = bcBox;
nidWidget[NID_key_usage] = kuBox;
nidWidget[NID_ext_key_usage] = ekuBox;
nidWidget[NID_netscape_cert_type] = nsCertTypeBox;
nidWidget[NID_subject_key_identifier] = subKey;
nidWidget[NID_authority_key_identifier] = authKey;
foreach(int nid, nidWidget.keys()) {
QString text = OBJ_nid2ln(nid);
QString tooltip = dn_translations[nid];
QWidget *w = nidWidget[nid];
QString tt = w->toolTip();
if (Settings["translate_dn"])
text.swap(tooltip);
if (!tt.isEmpty())
tooltip = QString("%1 (%2)").arg(tt).arg(tooltip);
w->setToolTip(tooltip);
DoubleClickLabel *l = dynamic_cast<DoubleClickLabel*>(w);
QGroupBox *g = dynamic_cast<QGroupBox*>(w);
QCheckBox *c = dynamic_cast<QCheckBox*>(w);
if (l) {
l->setText(text);
l->setClickText(OBJ_nid2sn(nid));
connect(l, SIGNAL(doubleClicked(QString)),
MainWindow::getResolver(),
SLOT(searchOid(QString)));
} else if (g) {
g->setTitle(text);
} else if (c) {
c->setText(text);
}
}
if (Settings["translate_dn"]) {
QList<QGroupBox*> gb { distNameBox, keyIdentBox };
foreach(QGroupBox *g, gb) {
QString tt = g->toolTip();
g->setToolTip(g->title());
g->setTitle(tt);
}
QList<QCheckBox*> cbList { bcCritical,kuCritical,ekuCritical };
foreach(QCheckBox* cb, cbList) {
cb->setText(tr("Critical"));
}
}
if (Settings["disable_netscape"])
tabWidget->removeTab(4);
// Setup widget <-> Template mapping
#define MAP_LE(name) templateLineEdits[#name] = name;
MAP_LE(subAltName);
MAP_LE(issAltName);
MAP_LE(crlDist);
MAP_LE(authInfAcc);
MAP_LE(nsComment);
MAP_LE(nsBaseUrl);
MAP_LE(nsRevocationUrl);
MAP_LE(nsCARevocationUrl);
MAP_LE(nsRenewalUrl);
MAP_LE(nsCaPolicyUrl);
MAP_LE(nsSslServerName);
MAP_LE(validN);
MAP_LE(basicPath);
#define MAP_CB(name) templateCheckBoxes[#name] = name;
MAP_CB(bcCritical);
MAP_CB(kuCritical);
MAP_CB(ekuCritical);
MAP_CB(subKey);
MAP_CB(authKey);
MAP_CB(OCSPstaple);
MAP_CB(validMidn);
MAP_CB(noWellDefinedExpDate);
}
void NewX509::setRequest()
{
reqWidget->hide();
attrWidget->show();
signerBox->setEnabled(false);
timewidget->setEnabled(false);
capt->setText(tr("Create Certificate signing request"));
authKey->setEnabled(false);
image->setPixmap(QPixmap(":csrImg"));
pt = x509_req;
}
NewX509::~NewX509()
{
delete ctx_cert;
}
void NewX509::setupExtDNwidget(const QString &s, QLineEdit *l)
{
setupLineEditByNid(OBJ_txt2nid(CCHAR(s)), l);
}
void NewX509::setupLineEditByNid(int nid, QLineEdit *l)
{
ASN1_STRING_TABLE *tab = ASN1_STRING_TABLE_get(nid);
QValidator *validator = NULL;
QStringList info;
info << QString("[%1]").arg(OBJ_nid2sn(nid));
if (tab) {
if (tab->minsize > 1)
info << tr("minimum size: %1").arg(tab->minsize);
if (tab->maxsize != -1)
info << tr("maximum size: %1").arg(tab->maxsize);
if (tab->mask == B_ASN1_PRINTABLESTRING) {
info << tr("only a-z A-Z 0-9 '()+,-./:=?");
QRegExp rx("[a-zA-Z0-9'()+,-./:=?]+");
validator = new QRegExpValidator(rx, this);
} else if (tab->mask == B_ASN1_IA5STRING) {
info << tr("only 7-bit clean characters");
}
}
l->setToolTip(info.join(" "));
l->setValidator(validator);
}
void NewX509::getReqAttributes(pki_x509req *req)
{
foreach(nameEdit e, attrEdits) {
req->addAttribute(e.nid, e.edit->text());
}
}
void NewX509::setReqAttributes(pki_x509req *req)
{
foreach(nameEdit e, attrEdits) {
e.edit->setText(req->getAttribute(e.nid));
}
}
/* Initialize dialog for Template creation */
void NewX509::setTemp(pki_temp *temp)
{
description->setText(temp->getIntName());
capt->setText(tr("Edit XCA template"));
tabWidget->removeTab(0);
privKeyBox->setEnabled(false);
validityBox->setEnabled(false);
image->setPixmap(QPixmap(":tempImg"));
pt = tmpl;
fromTemplate(temp);
comment->setPlainText(temp->getComment());
}
/* Initialize dialog for Certificate creation */
void NewX509::setCert()
{
capt->setText(tr("Create x509 Certificate"));
image->setPixmap(QPixmap(":certImg"));
pt = x509;
}
/* Select a template and apply it */
void NewX509::defineTemplate(pki_temp *temp)
{
fromTemplate(temp);
templateChanged(temp);
pkiSource = transformed;
}
/* Select a Request for signing it */
void NewX509::defineRequest(pki_x509req *req)
{
fromReqCB->setEnabled(true);
fromReqCB->setChecked(true);
reqList->setCurrentPkiItem(req);
pkiSource = transformed;
on_fromReqCB_clicked();
}
/* Preset all values from another request to create a similar one */
void NewX509::fromX509super(pki_x509super *cert_or_req, bool applyTemp)
{
pki_temp *temp = new pki_temp("");
temp->fromCert(cert_or_req);
defineTemplate(temp);
delete temp;
description->setText(cert_or_req->getIntName());
pki_key *key = cert_or_req->getRefKey();
if (key) {
usedKeysToo->setChecked(true);
keyList->setCurrentPkiItem(key);
}
hashAlgo->setCurrentMD(cert_or_req->getDigest());
switch(cert_or_req->getType()) {
case x509: {
pki_x509 *cert = (pki_x509*)cert_or_req;
pki_x509 *signer = cert->getSigner();
if (signer == cert) {
foreignSignRB->setChecked(false);
} else if (signer) {
defineSigner(signer, applyTemp);
}
notBefore->setDate(cert->getNotBefore());
notAfter->setDate(cert->getNotAfter());
break;
}
case x509_req: {
pki_x509req *req = (pki_x509req*)cert_or_req;
setReqAttributes(req);
break;
}
default:
break;
}
}
pki_temp *NewX509::caTemplate(pki_x509 *ca) const
{
QVariant sqlId = ca->getTemplateSqlId();
if (!sqlId.isValid())
return NULL;
return Store.lookupPki<pki_temp>(sqlId);
}
/* Preset the signing certificate */
void NewX509::defineSigner(pki_x509 *defcert, bool applyTemp)
{
// suggested from: Andrey Brindeew <abr@abr.pp.ru>
if (defcert && defcert->canSign() ) {
if (certList->setCurrentPkiItem(defcert) != -1) {
foreignSignRB->setChecked(true);
certList->setEnabled(true);
if (applyTemp &&
defcert->getTemplateSqlId().isValid())
{
on_applyTemplate_clicked();
}
}
}
}
static int lb2int(QListWidget *lb)
{
int i, x=0, c=lb->count();
for (i=0; i<c; i++) {
if (lb->item(i)->isSelected())
x |= 1<<i;
}
return x;
}
static void int2lb(QListWidget *lb, int x)
{
for (int i=0; i<lb->count(); i++)
lb->item(i)->setSelected((1<<i) & x);
}
static void QString2lb(QListWidget *lb, QString x)
{
QStringList li = x.split(",");
QList<QListWidgetItem *> items;
for (int i=0; i<li.size(); i++) {
QString lname = OBJ_sn2ln(CCHAR(li[i].trimmed()));
items = lb->findItems(lname, Qt::MatchExactly);
if (items.size() > 0)
items[0]->setSelected(true);
}
}
static QString lb2QString(QListWidget *lb)
{
QStringList sl;
for (int i=0; i<lb->count(); i++) {
QListWidgetItem *item = lb->item(i);
if (item->isSelected())
sl << QString(OBJ_ln2sn(CCHAR(item->text())));
}
return sl.join(", ");
}
void NewX509::subjectFromTemplate(pki_temp *temp)
{
if (temp)
setX509name(temp->getSubject());
}
void NewX509::extensionsFromTemplate(pki_temp *temp)
{
if (!temp)
return;
QMapIterator<QString, QLineEdit*> l(templateLineEdits);
while (l.hasNext()) {
l.next();
l.value()->setText(temp->getSetting(l.key()));
}
QMapIterator<QString, QCheckBox*> i(templateCheckBoxes);
while (i.hasNext()) {
i.next();
i.value()->setChecked(temp->getSettingInt(i.key()));
}
int2lb(nsCertType, temp->getSettingInt("nsCertType"));
basicCA->setCurrentIndex(temp->getSettingInt("ca"));
int2lb(keyUsage, temp->getSettingInt("keyUse"));
QString2lb(ekeyUsage, temp->getSetting("eKeyUse"));
validRange->setCurrentIndex(temp->getSettingInt("validM"));
nconf_data->document()->setPlainText(temp->getSetting("adv_ext"));
on_applyTime_clicked();
}
void NewX509::fromTemplate(pki_temp *temp)
{
subjectFromTemplate(temp);
extensionsFromTemplate(temp);
}
void NewX509::toTemplate(pki_temp *temp)
{
temp->setIntName(description->text());
temp->setSubject(getX509name());
QMapIterator<QString, QLineEdit*> l(templateLineEdits);
while (l.hasNext()) {
l.next();
temp->setSetting(l.key(), l.value()->text());
}
QMapIterator<QString, QCheckBox*> i(templateCheckBoxes);
while (i.hasNext()) {
i.next();
temp->setSetting(i.key(), i.value()->isChecked());
}
temp->setSetting("nsCertType", lb2int(nsCertType));
temp->setSetting("ca", basicCA->currentIndex());
temp->setSetting("keyUse", lb2int(keyUsage));
temp->setSetting("eKeyUse", lb2QString(ekeyUsage));
temp->setSetting("validN", validN->text().toInt());
temp->setSetting("validM", validRange->currentIndex());
if (!temp->getSetting("basicPath").isEmpty())
temp->setSetting("basicPath", temp->getSettingInt("basicPath"));
if (nconf_data->isReadOnly()) {
temp->setSetting("adv_ext", v3ext_backup);
} else {
temp->setSetting("adv_ext", nconf_data->toPlainText());
}
temp->setComment(comment->toPlainText());
}
void NewX509::on_fromReqCB_clicked()
{
bool request = fromReqCB->isChecked();
bool subj_tab_present = tabWidget->widget(1) == tab_1;
bool subChange = reqSubChange->isChecked();
if (request && subj_tab_present && !subChange)
tabWidget->removeTab(1);
else if ((!request || subChange) && !subj_tab_present)
tabWidget->insertTab(1, tab_1, tr("Subject"));
reqList->setEnabled(request);
copyReqExtCB->setEnabled(request);
showReqBut->setEnabled(request);
reqSubChange->setEnabled(request);
switchHashAlgo();
}
void NewX509::on_reqSubChange_clicked()
{
if (reqSubChange->isChecked()) {
pki_x509req *req = getSelectedReq();
description->setText(req->getIntName());
setX509name(req->getSubject());
usedKeysToo->setEnabled(false);
keyList->setEnabled(false);
genKeyBut->setEnabled(false);
}
on_fromReqCB_clicked();
}
void NewX509::on_keyList_currentIndexChanged(const QString &)
{
switchHashAlgo();
}
void NewX509::on_reqList_currentIndexChanged(const QString &)
{
switchHashAlgo();
}
void NewX509::switchHashAlgo()
{
pki_key *key;
pki_x509super *sig;
if (foreignSignRB->isChecked())
sig = getSelectedSigner();
else if (fromReqCB->isChecked())
sig = getSelectedReq();
else
sig = NULL;
key = sig ? sig->getRefKey() : getSelectedKey();
if (key) {
hashAlgo->setKeyType(key->getKeyType());
hashAlgo->setupHashes(key->possibleHashNids());
} else {
hashAlgo->setKeyType(EVP_PKEY_RSA);
hashAlgo->setupAllHashes();
}
}
void NewX509::on_showReqBut_clicked()
{
CertDetail::showCert(this, reqList->currentPkiItem());
}
QList<pki_x509req *> NewX509::getAllRequests() const
{
return Database.model<db_x509req>()->getAllRequests();
}
QList<pki_x509*> NewX509::getAllIssuers() const
{
return Database.model<db_x509>()->getAllIssuers();
}
QList<pki_temp*> NewX509::getAllTempsAndPredefs() const
{
return Database.model<db_temp>()->getPredefs() +
Store.getAll<pki_temp>();
}
QList<pki_key*> NewX509::getAllKeys() const
{
return Database.model<db_key>()->getAllKeys();
}
QList<pki_key*> NewX509::getUnusedKeys() const
{
return Database.model<db_key>()->getUnusedKeys();
}
void NewX509::itemChanged(pki_base* req)
{
reqList->insertPkiItems(getAllRequests());
reqList->setCurrentPkiItem(dynamic_cast<pki_x509req*>(req));
}
void NewX509::on_genKeyBut_clicked()
{
QString name = description->text();
if (name.isEmpty())
name = getX509name().getMostPopular();
NewKey *dlg = new NewKey(this, name);
if (dlg->exec()) {
db_key *keys = Database.model<db_key>();
keys->newKey(dlg->getKeyJob(), dlg->keyDesc->text());
}
delete dlg;
}
void NewX509::on_certList_currentIndexChanged(int)
{
a1time snb, sna;
pki_x509 *cert = getSelectedSigner();
switchHashAlgo();
if (!cert)
return;
pki_temp *templ = caTemplate(cert);
snb = cert->getNotBefore();
sna = cert->getNotAfter();
if (snb > notBefore->getDate())
notBefore->setDate(snb);
if (sna < notAfter->getDate())
notAfter->setDate(sna);
if (templ)
templateChanged(templ);
}
void NewX509::templateChanged(QString tempname)
{
int index;
if (!tempList->isEnabled())
return;
if ((index = tempList->findText(tempname)) <0)
return;
tempList->setCurrentIndex(index);
}
void NewX509::templateChanged(pki_temp *templ)
{
tempList->setCurrentPkiItem(templ);
}
pki_temp *NewX509::currentTemplate()
{
if (!tempList->isEnabled())
return NULL;
return tempList->currentPkiItem();
}
void NewX509::selfComment(QString msg)
{
comment->setPlainText(appendXcaComment(comment->toPlainText(), msg));
}
void NewX509::on_applyTemplate_clicked()
{
pki_temp *t = currentTemplate();
if (!t)
return;
fromTemplate(t);
selfComment(tr("Template '%1' applied").arg(t->comboText()));
}
void NewX509::on_applySubject_clicked()
{
pki_temp *t = currentTemplate();
subjectFromTemplate(t);
selfComment(tr("Subject applied from template '%1'")
.arg(t->comboText()));
}
void NewX509::on_applyExtensions_clicked()
{
pki_temp *t = currentTemplate();
extensionsFromTemplate(t);
selfComment(tr("Extensions applied from template '%1'")
.arg(t->comboText()));
}
void NewX509::on_foreignSignRB_toggled(bool)
{
switchHashAlgo();
}
void NewX509::newKeyDone(pki_key *nkey)
{
allKeys = getAllKeys();
unusedKeys = getUnusedKeys();
on_usedKeysToo_toggled(true);
if (nkey) {
selfComment(tr("New key '%1' created")
.arg(nkey->comboText()));
keyList->setCurrentPkiItem(nkey);
} else {
keyList->setCurrentIndex(0);
}
}
void NewX509::on_usedKeysToo_toggled(bool)
{
pki_key *cur = keyList->currentPkiItem();
keyList->clear();
keyList->insertPkiItems(usedKeysToo->isChecked() ?
allKeys : unusedKeys);
keyList->setCurrentPkiItem(cur);
}
pki_key *NewX509::getSelectedKey()
{
return keyList->currentPkiItem();
}
pki_x509 *NewX509::getSelectedSigner()
{
return certList->currentPkiItem();
}
pki_x509req *NewX509::getSelectedReq()
{
return reqList->currentPkiItem();
}
x509name NewX509::getX509name(int _throw)
{
x509name x;
int j, row, nid;
if (fromReqCB->isChecked() && !reqSubChange->isChecked())
return getSelectedReq()->getSubject();
try {
foreach(nameEdit ne, nameEdits) {
x.addEntryByNid(ne.nid, ne.edit->text());
}
row = extDNlist->rowCount();
for (j=0; j<row; j++) {
QStringList l = extDNlist->getRow(j);
nid = OBJ_txt2nid(CCHAR(l[0]));
x.addEntryByNid(nid, l[1]);
}
} catch (errorEx &err) {
if (!err.isEmpty()) {
if (_throw)
throw err;
else
XCA_WARN(err.getString());
}
}
return x;
}
void NewX509::setX509name(const x509name &n)
{
extDNlist->deleteAllRows();
foreach(nameEdit ne, nameEdits) {
ne.edit->setText("");
}
if (Settings["adapt_explicit_subject"]) {
NIDlist mydn;
for (int i=0; i< n.entryCount(); i++)
mydn << n.nid(i);
setupExplicitDN(mydn);
}
for (int i=0, j=0; i< n.entryCount(); i++) {
int nid = n.nid(i);
bool done = false;
QStringList sl = n.entryList(i);
for ( ; j < nameEdits.size(); j++) {
nameEdit ne(nameEdits[j]);
if (nid == ne.nid && ne.edit->text().isEmpty()) {
ne.edit->setText(sl[2]);
done = true;
break;
}
}
if (!done) {
extDNlist->addRow(sl.mid(1, 2));
}
}
}
void NewX509::on_applyTime_clicked()
{
notAfter->setDiff(notBefore, validN->text().toInt(),
validRange->currentIndex());
}
void NewX509::setupTmpCtx()
{
pki_x509 *signcert;
pki_x509req *req = NULL;
pki_key *key = NULL;
a1int serial(1);
QString errtxt;
// initially create temporary ctx cert
delete ctx_cert;
ctx_cert = new pki_x509();
ctx_cert->setSubject(getX509name());
if (fromReqCB->isChecked()) {
req = getSelectedReq();
if (req)
key = req->getRefKey();
} else {
key = getSelectedKey();
}
if (key)
ctx_cert->setPubKey(key);
// Step 2 - select Signing
if (foreignSignRB->isChecked()) {
signcert = getSelectedSigner();
ctx_cert->setIssuer(signcert->getSubject());
} else {
signcert = ctx_cert;
ctx_cert->setIssuer(ctx_cert->getSubject());
}
ctx_cert->setSerial(serial);
initCtx(ctx_cert, signcert, req);
}
void NewX509::editV3ext(QLineEdit *le, QString types, int n)
{
v3ext *dlg;
dlg = new v3ext(this);
setupTmpCtx();
dlg->addInfo(le, types.split(','), n, &ext_ctx);
dlg->exec();
delete dlg;
}
void NewX509::on_adv_validate_clicked()
{
if (!nconf_data->isReadOnly()) {
/* switch from edit to display mode */
do_validateExtensions();
} else {
/* switch back to edit mode */
undo_validateExtensions();
}
}
void NewX509::checkIcon(const QString &text, int nid, QLabel *img)
{
if (text.isEmpty()) {
img->clear();
return;
}
ign_openssl_error();
switch (nid) {
case NID_subject_alt_name:
getSubAltName();
break;
case NID_issuer_alt_name:
getIssAltName();
break;
case NID_crl_distribution_points:
getCrlDist();
break;
case NID_info_access:
getAuthInfAcc();
break;
}
if (ign_openssl_error()) {
img->setPixmap(QPixmap(":warnIco"));
return;
}
img->setPixmap(QPixmap(":doneIco"));
}
void NewX509::checkSubAltName(const QString & text)
{
checkIcon(text, NID_subject_alt_name, subAltIco);
}
void NewX509::checkIssAltName(const QString & text)
{
checkIcon(text, NID_issuer_alt_name, issAltIco);
}
void NewX509::checkCrlDist(const QString & text)
{
checkIcon(text, NID_crl_distribution_points, crlDistIco);
}
void NewX509::checkAuthInfAcc(const QString & text)
{
checkIcon(text, NID_info_access, authInfAccIco);
}
int NewX509::do_validateExtensions()
{
QString result;
int ret = 0;
if (!nconf_data->isReadOnly()) {
v3ext_backup = nconf_data->toPlainText();
}
ret = validateExtensions(v3ext_backup, result);
nconf_data->document()->setHtml(result);
nconf_data->setReadOnly(true);
adv_validate->setText(tr("Edit"));
return ret;
}
void NewX509::undo_validateExtensions()
{
if (nconf_data->isReadOnly()) {
nconf_data->document()->setPlainText(v3ext_backup);
}
nconf_data->setReadOnly(false);
adv_validate->setText(tr("Validate"));
}
int NewX509::validateExtensions(QString nconf, QString &result)
{
int ret = 0, ext_count = 0;
QStringList errors;
extList el, req_el;
ign_openssl_error();
setupTmpCtx();
(void)nconf;
try {
el = getGuiExt();
if (!Settings["disable_netscape"])
el += getNetscapeExt();
el.delInvalid();
} catch (errorEx &err) {
errors += err.getString();
el.clear();
}
ext_count += el.size();
if (el.size() > 0) {
result += "<h2><center>";
result += tr("Other Tabs") + "</center></h2><p>\n";
result += el.getHtml("<br>");
}
try {
el = getAdvanced();
} catch (errorEx &err) {
errors += err.getString();
el.clear();
}
ext_count += el.size();
if (el.size() > 0) {
if (!result.isEmpty())
result += "\n<hr>\n";
result += "<h2><center>";
result += tr("Advanced Tab") + "</center></h2><p>\n";
result += el.getHtml("<br>");
}
if (errors.size()) {
if (!result.isEmpty())
result += "\n<hr>\n";
result += "<h2><center>";
result += tr("Errors") + "</center></h2><p><ul><li>\n";
result += errors.join("</li><li>\n");
result += "</li></ul>";
ret = 1;
}
el.clear();
if (fromReqCB->isChecked() && copyReqExtCB->isChecked()) {
req_el = getSelectedReq()->getV3ext();
for (int i=0; i<req_el.count(); i++) {
if (ctx_cert && ctx_cert->addV3ext(req_el[i], true))
el += req_el[i];
}
}
ext_count += el.size();
if (el.size() > 0) {
if (!result.isEmpty())
result += "\n<hr>\n";
result += "<h2><center>";
result += tr("From PKCS#10 request") +"</center></h2><p>\n";
result += el.getHtml("<br>");
}
el = getExtDuplicates();
if (el.size() > 0) {
QString errtxt;
ret = 1;
errtxt = "<h2><center><font color=\"red\">Error:</font>"
"duplicate extensions:</center></h2><p><ul>\n";
for(int i = 0; i< el.size(); i++) {
errtxt += "<li>" +el[i].getObject() +"</li>\n";
}
errtxt += "</ul>\n<hr>\n";
result = errtxt + result;
}
ign_openssl_error();
return ret == 1 ? 1 : ext_count == 0 && pt == x509 ? 2 : 0;
}
void NewX509::on_editSubAlt_clicked()
{
QString s = "URI,email,RID,DNS,IP,otherName";
editV3ext(subAltName, s, NID_subject_alt_name);
}
void NewX509::on_editIssAlt_clicked()
{
QString s = "URI,email,RID,DNS,IP,otherName,issuer";
editV3ext(issAltName, s, NID_issuer_alt_name);
}
void NewX509::on_editCrlDist_clicked()
{
editV3ext(crlDist, "URI", NID_crl_distribution_points);
}
void NewX509::on_editAuthInfAcc_clicked()
{
QStringList permut, groups { "OCSP", "caIssuers" },
types{ "URI", "email", "RID", "DNS", "IP" };
foreach(QString group, groups) {
foreach(QString type, types) {
permut << QString("%1;%2").arg(group).arg(type);
}
}
editV3ext(authInfAcc, permut.join(","), NID_info_access);
}
void NewX509::on_tabWidget_currentChanged(int tab)
{
QString tab_name = tabWidget->widget(tab)->objectName();
if (tab_name == tabnames[5])
do_validateExtensions();
buttonBox->setProperty("help_ctx", QVariant(tab_name));
}
QString NewX509::mandatoryDnRemain()
{
QStringList remain, dnl = QString(Settings["mandatory_dn"]).split(",");
x509name n;
int i;
if (QString(Settings["mandatory_dn"]).isEmpty())
return QString();
n = getX509name();
for (i=0; i< n.entryCount(); i++) {
int j = dnl.indexOf(QString(OBJ_nid2sn(n.nid(i))));
if (j>=0)
dnl.removeAt(j);
}
if (dnl.size() == 0)
return QString();
foreach(QString x, dnl)
remain << QString(OBJ_sn2ln(x.toLatin1()));
return QString("'%1'").arg(remain.join("','"));
}
void NewX509::gotoTab(int tab)
{
for (int i=0; i<tabWidget->count(); i++) {
if (tabWidget->tabText(i) == tabnames[tab]) {
tabWidget->setCurrentIndex(i);
break;
}
}
}
enum pki_source NewX509::getPkiSource() const
{
return pkiSource;
}
void NewX509::accept()
{
x509name xn;
on_tabWidget_currentChanged(0);
try {
xn = getX509name(1);
} catch (errorEx &err) {
gotoTab(1);
xcaWarning msg(this, err.getString());
msg.addButton(QMessageBox::Ok);
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
if (msg.exec() == QMessageBox::Close) {
reject();
}
return;
}
QString lenErr = xn.checkLength();
if (!lenErr.isEmpty()) {
gotoTab(1);
lenErr = tr("The following length restrictions of RFC3280 are violated:") +
"\n" + lenErr;
xcaWarning msg(this, lenErr);
msg.addButton(QMessageBox::Ok, tr("Edit subject"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
msg.addButton(QMessageBox::Apply, tr("Continue rollout"));
switch (msg.exec())
{
case QMessageBox::Ok:
case QMessageBox::Cancel:
return;
case QMessageBox::Close:
reject();
return;
case QMessageBox::Apply:
break;
}
}
if (fromReqCB->isChecked() && !getSelectedReq()->verify()) {
gotoTab(0);
xcaWarning msg(this,
tr("The verification of the Certificate request failed.\nThe rollout should be aborted."));
msg.addButton(QMessageBox::Ok, tr("Continue anyway"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
if (msg.exec() == QMessageBox::Close) {
reject();
}
}
if (description->text().isEmpty() && !fromReqCB->isChecked()) {
QString cn = getX509name().getMostPopular();
if (cn.isEmpty()) {
gotoTab(1);
xcaWarning msg(this,
tr("The internal name and the common name are empty.\nPlease set at least the internal name."));
msg.addButton(QMessageBox::Ok, tr("Edit name"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
if (msg.exec() == QMessageBox::Close) {
reject();
}
return;
} else {
description->setText(cn);
}
}
if (keyList->count() == 0 && keyList->isEnabled() &&
!fromReqCB->isChecked())
{
gotoTab(1);
xcaWarning msg(this,
tr("There is no Key selected for signing."));
msg.addButton(QMessageBox::Ok, tr("Select key"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
if (msg.exec() == QMessageBox::Close) {
reject();
}
return;
}
QString unsetDN;
if (pt != tmpl)
unsetDN = mandatoryDnRemain();
if (!unsetDN.isEmpty()) {
gotoTab(1);
QString text = tr("The following distinguished name entries are empty:\n%1\nthough you have declared them as mandatory in the options menu.").arg(unsetDN);
xcaWarning msg(this, text);
msg.addButton(QMessageBox::Ok, tr("Edit subject"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
msg.addButton(QMessageBox::Apply, tr("Continue rollout"));
switch (msg.exec())
{
case QMessageBox::Ok:
case QMessageBox::Cancel:
return;
case QMessageBox::Close:
reject();
return;
case QMessageBox::Apply:
break;
}
}
pki_key *signkey = NULL;
pki_x509 *signer = NULL;
if (foreignSignRB->isChecked()) {
signer = getSelectedSigner();
if (signer)
signkey = signer->getRefKey();
} else if (fromReqCB->isChecked()) {
pki_x509req *req = getSelectedReq();
if (req)
signkey = req->getRefKey();
} else {
signkey = getSelectedKey();
}
if ((!signkey || signkey->isPubKey()) && pt != tmpl) {
QString txt;
gotoTab(signer ? 0 : 1);
xcaWarning msg(this,
tr("The key you selected for signing is not a private one."));
txt = signer ? tr("Select other signer"):tr("Select other key");
msg.addButton(QMessageBox::Ok, txt);
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
if (msg.exec() == QMessageBox::Close) {
reject();
}
return;
}
if (hashAlgo->count() > 0 && hashAlgo->isInsecure()) {
gotoTab(0);
xcaWarning msg(this, tr("The currently selected hash algorithm '%1' is insecure and should not be used.").arg(hashAlgo->currentHashName()));
msg.addButton(QMessageBox::Ok, tr("Select other algorithm"));
msg.addButton(QMessageBox::Yes, tr("Use algorithm anyway"));
if (msg.exec() == QMessageBox::Ok)
return;
}
if (signer && notBefore->getDate() < signer->getNotBefore()) {
gotoTab(2);
QString text = tr("The certificate will be earlier valid than the signer. This is probably not what you want.");
xcaWarning msg(this, text);
msg.addButton(QMessageBox::Ok, tr("Edit dates"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
msg.addButton(QMessageBox::Apply, tr("Continue rollout"));
msg.addButton(QMessageBox::Yes, tr("Adjust date and continue"));
switch (msg.exec())
{
case QMessageBox::Ok:
case QMessageBox::Cancel:
return;
case QMessageBox::Close:
reject();
return;
case QMessageBox::Apply:
break;
case QMessageBox::Yes:
notBefore->setDate(signer->getNotBefore());
}
}
if (signer && notAfter->getDate() > signer->getNotAfter() &&
!noWellDefinedExpDate->isChecked()) {
gotoTab(2);
QString text = tr("The certificate will be longer valid than the signer. This is probably not what you want.");
xcaWarning msg(this, text);
msg.addButton(QMessageBox::Ok, tr("Edit dates"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
msg.addButton(QMessageBox::Apply, tr("Continue rollout"));
msg.addButton(QMessageBox::Yes, tr("Adjust date and continue"));
switch (msg.exec())
{
case QMessageBox::Ok:
case QMessageBox::Cancel:
return;
case QMessageBox::Close:
reject();
return;
case QMessageBox::Apply:
break;
case QMessageBox::Yes:
notAfter->setDate(signer->getNotAfter());
}
}
if (validityBox->isEnabled() &&
notBefore->getDate() > notAfter->getDate()) {
gotoTab(2);
QString text = tr("The certificate will be out of date before it becomes valid. You most probably mixed up both dates.");
xcaWarning msg(this, text);
msg.addButton(QMessageBox::Ok, tr("Edit dates"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
msg.addButton(QMessageBox::Apply, tr("Continue rollout"));
switch (msg.exec())
{
case QMessageBox::Ok:
case QMessageBox::Cancel:
return;
case QMessageBox::Close:
reject();
return;
case QMessageBox::Apply:
break;
}
}
int r = do_validateExtensions();
if (r) {
QString text;
if (r == 1) {
text = tr("The certificate contains invalid or duplicate extensions. Check the validation on the advanced tab.");
gotoTab(5);
} else {
text = tr("The certificate contains no extensions. You may apply the extensions of one of the templates to define the purpose of the certificate.");
gotoTab(0);
}
xcaWarning msg(this, text);
msg.addButton(QMessageBox::Ok, tr("Edit extensions"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
msg.addButton(QMessageBox::Apply, tr("Continue rollout"));
switch (msg.exec())
{
case QMessageBox::Ok:
case QMessageBox::Cancel:
return;
case QMessageBox::Close:
reject();
return;
case QMessageBox::Apply:
break;
}
}
QString cn = xn.getEntryByNid(NID_commonName);
QStringList san = subAltName->text().split(QRegExp(" *, *"));
if (cn.isEmpty() && san.contains("DNS:copycn") && pt != tmpl) {
gotoTab(2);
xcaWarning msg(this, tr("The subject alternative name shall contain a copy of the common name. However, the common name is empty."));
msg.addButton(QMessageBox::Ok, tr("Edit extensions"));
msg.addButton(QMessageBox::Close, tr("Abort rollout"));
msg.addButton(QMessageBox::Apply, tr("Continue rollout"));
switch (msg.exec())
{
case QMessageBox::Ok:
case QMessageBox::Cancel:
return;
case QMessageBox::Close:
reject();
return;
case QMessageBox::Apply:
break;
}
}
QDialog::accept();
}