From cb1f02502d80eeaa1b886a80559d8ea6653bf16d Mon Sep 17 00:00:00 2001 From: Christian Hohnstaedt Date: Sat, 14 Sep 2024 23:28:08 +0200 Subject: [PATCH] Close #90, #361: Name Constraints (RFC5280 section 4.2.1.10) Conversion of name constraints from certificate to Template (Advanced Tab) and using them from there was working since long. Now there is an additional input line, like (and next to) SubjectAlternativeName with live validation and edit button with guided input. The conversion from cert to template now puts the extension into the correct LineEdit and not on the Advanced Tab anymore. However, XCA does NOT follow the constraints, yet. It is still possible to issue certificates with arbitrary names, independent of any "Name Constraints" in the CA. This is what issue #239 asks for. --- lib/func_base.cpp | 1 + lib/pki_temp.cpp | 2 + lib/x509v3ext.cpp | 13 +- ui/NewX509.ui | 255 +++++++++++++++++++++++++++------------- widgets/NewX509.cpp | 26 ++++ widgets/NewX509.h | 3 + widgets/NewX509_ext.cpp | 6 + 7 files changed, 218 insertions(+), 88 deletions(-) diff --git a/lib/func_base.cpp b/lib/func_base.cpp index 6b15ac6f..7c37cce9 100644 --- a/lib/func_base.cpp +++ b/lib/func_base.cpp @@ -244,6 +244,7 @@ D[NID_pkcs9_unstructuredName] = QObject::tr("Unstructured name"); D[NID_pkcs9_challengePassword] = QObject::tr("Challenge password"); D[NID_basic_constraints] = QObject::tr("Basic Constraints"); +D[NID_name_constraints] = QObject::tr("Name Constraints"); D[NID_subject_alt_name] = QObject::tr("Subject alternative name"); D[NID_issuer_alt_name] = QObject::tr("issuer alternative name"); D[NID_subject_key_identifier] = QObject::tr("Subject key identifier"); diff --git a/lib/pki_temp.cpp b/lib/pki_temp.cpp index 0b368775..a2cabefe 100644 --- a/lib/pki_temp.cpp +++ b/lib/pki_temp.cpp @@ -58,6 +58,7 @@ namespace db { }; const QList pki_temp::tmpl_keys = { + "nameCons", "subAltName", "issAltName", "crlDist", @@ -223,6 +224,7 @@ extList pki_temp::fromCert(pki_x509super *cert_or_req) xname.addEntryByNid(nid, n.getEntry(i)); } + fromExtList(&el, NID_name_constraints, "nameCons"); fromExtList(&el, NID_subject_alt_name, "subAltName"); fromExtList(&el, NID_issuer_alt_name, "issAltName"); fromExtList(&el, NID_crl_distribution_points, "crlDist"); diff --git a/lib/x509v3ext.cpp b/lib/x509v3ext.cpp index 4f57ecb6..c3c5b880 100644 --- a/lib/x509v3ext.cpp +++ b/lib/x509v3ext.cpp @@ -924,7 +924,7 @@ static bool nameConstraint(STACK_OF(GENERAL_SUBTREE) *trees, return true; } -bool x509v3ext::parse_nameConstraints(QString *, QString *adv) const +bool x509v3ext::parse_nameConstraints(QString *single, QString *adv) const { bool retval = true; QString sect, ret; @@ -947,11 +947,14 @@ bool x509v3ext::parse_nameConstraints(QString *, QString *adv) const if (ret.size() > 0) permEx << ret; - if (adv && retval &&permEx.size() > 0) { + if (retval && permEx.size() > 0) { ret = permEx.join(", "); - qDebug("%s %d '%s'\n", __func__, retval, CCHAR(ret)); - *adv = QString("%1=%2\n").arg(tag). - arg(parse_critical() +ret) + *adv + sect; + qDebug() << retval << ret; + if (single) + *single = ret; + else if (adv) + *adv = QString("%1=%2\n").arg(tag). + arg(parse_critical() +ret) + *adv + sect; } NAME_CONSTRAINTS_free(cons); return retval; diff --git a/ui/NewX509.ui b/ui/NewX509.ui index 6faab403..d19cccaf 100644 --- a/ui/NewX509.ui +++ b/ui/NewX509.ui @@ -7,7 +7,7 @@ 0 0 747 - 637 + 640 @@ -100,7 +100,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -141,8 +150,7 @@ - - + @@ -537,7 +545,16 @@ Key identifier - + + 8 + + + 8 + + + 8 + + 8 @@ -584,7 +601,16 @@ true - + + 0 + + + 0 + + + 0 + + 0 @@ -652,6 +678,20 @@ + + + + Local time + + + + + + + No well-defined expiration + + + @@ -675,20 +715,6 @@ - - - - Local time - - - - - - - No well-defined expiration - - - @@ -709,11 +735,15 @@ - - - + + + + + OCSP Must Staple + + - + @@ -732,7 +762,7 @@ - + DNS: IP: URI: email: RID: @@ -740,30 +770,108 @@ + + + Edit + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + Edit - - + + - - + + DNS: IP: URI: email: RID: - + + + + + + + + + + Edit + + + + DNS: IP: URI: email: RID: + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + + + DNS: IP: URI: email: RID: + + + + + + + Edit + + + + @@ -782,24 +890,14 @@ - - - - URI: - - - - + Edit - - - - + @@ -819,45 +917,9 @@ - + - DNS: IP: URI: email: RID: - - - - - - - Edit - - - - - - - - 20 - 20 - - - - - 20 - 20 - - - - - - - - - - - - - - OCSP Must Staple + URI: @@ -870,7 +932,16 @@ Key usage - + + 8 + + + 8 + + + 8 + + 8 @@ -878,7 +949,16 @@ - + + 8 + + + 8 + + + 8 + + 8 @@ -949,7 +1029,16 @@ - + + 8 + + + 8 + + + 8 + + 8 diff --git a/widgets/NewX509.cpp b/widgets/NewX509.cpp index 4c6ce49f..e5f67e7c 100644 --- a/widgets/NewX509.cpp +++ b/widgets/NewX509.cpp @@ -134,6 +134,8 @@ NewX509::NewX509(QWidget *w) : XcaDetail(w) connect(extDNlist->itemDelegateForColumn(1), SIGNAL(setupLineEdit(const QString &, QLineEdit *)), this, SLOT(setupExtDNwidget(const QString &, QLineEdit *))); + connect(nameCons, SIGNAL(textChanged(const QString &)), + this, SLOT(checkNameConstraints(const QString &))); connect(subAltName, SIGNAL(textChanged(const QString &)), this, SLOT(checkSubAltName(const QString &))); connect(issAltName, SIGNAL(textChanged(const QString &)), @@ -210,6 +212,7 @@ NewX509::NewX509(QWidget *w) : XcaDetail(w) basicPath->setValidator(new QIntValidator(0, 1000, this)); QMap nidWidget; + nidWidget[NID_name_constraints] = nameConsLbl; nidWidget[NID_subject_alt_name] = sanLbl; nidWidget[NID_issuer_alt_name] = ianLbl; nidWidget[NID_crl_distribution_points] = crldpLbl; @@ -277,6 +280,7 @@ NewX509::NewX509(QWidget *w) : XcaDetail(w) // Setup widget <-> Template mapping #define MAP_LE(name) templateLineEdits[#name] = name; + MAP_LE(nameCons); MAP_LE(subAltName); MAP_LE(issAltName); MAP_LE(crlDist); @@ -537,6 +541,7 @@ void NewX509::extensionsFromTemplate(pki_temp *temp) QMapIterator l(templateLineEdits); while (l.hasNext()) { l.next(); + qDebug() << "APPLY LineEdits" << l.key() << temp->getSetting(l.key()); l.value()->setText(temp->getSetting(l.key())); } QMapIterator i(templateCheckBoxes); @@ -963,6 +968,9 @@ void NewX509::checkIcon(const QString &text, int nid, QLabel *img) setupTmpCtx(); ign_openssl_error(); switch (nid) { + case NID_name_constraints: + ext = getNameConstraints(); + break; case NID_subject_alt_name: ext = getSubAltName(); break; @@ -979,6 +987,11 @@ void NewX509::checkIcon(const QString &text, int nid, QLabel *img) img->setPixmap(ext.isValid() ? QPixmap(":doneIco") : QPixmap(":warnIco")); } +void NewX509::checkNameConstraints(const QString & text) +{ + checkIcon(text, NID_name_constraints, nameConsIco); +} + void NewX509::checkSubAltName(const QString & text) { checkIcon(text, NID_subject_alt_name, subAltIco); @@ -1102,6 +1115,8 @@ enum NewX509::extension_error NewX509::validateExtensions(QString &result) result = errtxt + result; } QString lineext; + if (!nameCons->text().isEmpty() && !getNameConstraints().isValid()) + lineext += tr("The Name Constraints are invalid") + "
\n"; if (!subAltName->text().isEmpty() && !getSubAltName().isValid()) lineext += tr("The Subject Alternative Name is invalid") + "
\n"; if (!issAltName->text().isEmpty() && !getIssAltName().isValid()) @@ -1123,6 +1138,17 @@ enum NewX509::extension_error NewX509::validateExtensions(QString &result) return ee; } +void NewX509::on_editNameCons_clicked() +{ + QStringList permut; + for (const QString &group : QStringList { "permitted", "excluded" }) { + for(const QString &type : QStringList { "URI", "email", "RID", "DNS", "IP" }) { + permut << QString("%1;%2").arg(group).arg(type); + } + } + editV3ext(nameCons, permut.join(","), NID_name_constraints); +} + void NewX509::on_editSubAlt_clicked() { QString s = "URI,email,RID,DNS,IP,UPN,otherName"; diff --git a/widgets/NewX509.h b/widgets/NewX509.h index 79ec6076..217b59a4 100644 --- a/widgets/NewX509.h +++ b/widgets/NewX509.h @@ -96,6 +96,7 @@ class NewX509: public XcaDetail, public Ui::NewX509 x509v3ext getOCSPstaple(); x509v3ext getKeyUsage(); x509v3ext getEkeyUsage(); + x509v3ext getNameConstraints(); x509v3ext getSubAltName(); x509v3ext getIssAltName(); x509v3ext getCrlDist(); @@ -132,6 +133,7 @@ class NewX509: public XcaDetail, public Ui::NewX509 void on_reqList_currentIndexChanged(int); void newKeyDone(pki_key *nkey); void on_applyTime_clicked(); + void on_editNameCons_clicked(); void on_editSubAlt_clicked(); void on_editIssAlt_clicked(); void on_editCrlDist_clicked(); @@ -150,6 +152,7 @@ class NewX509: public XcaDetail, public Ui::NewX509 void accept(); void itemChanged(pki_base*); void setupExtDNwidget(const QString &s, QLineEdit *w); + void checkNameConstraints(const QString & text); void checkSubAltName(const QString & text); void checkIssAltName(const QString & text); void checkCrlDist(const QString & text); diff --git a/widgets/NewX509_ext.cpp b/widgets/NewX509_ext.cpp index 3f0c7a0c..f020f1fc 100644 --- a/widgets/NewX509_ext.cpp +++ b/widgets/NewX509_ext.cpp @@ -105,6 +105,11 @@ x509v3ext NewX509::getEkeyUsage() return x509v3ext(NID_ext_key_usage, cont.join(", "), &ext_ctx); } +x509v3ext NewX509::getNameConstraints() +{ + return x509v3ext(NID_name_constraints, nameCons->text(), &ext_ctx); +} + x509v3ext NewX509::getSubAltName() { QString s = subAltName->text(); @@ -211,6 +216,7 @@ extList NewX509::getGuiExt() ne << getAuthKeyIdent(); ne << getKeyUsage(); ne << getEkeyUsage(); + ne << getNameConstraints(); ne << getSubAltName(); ne << getIssAltName(); ne << getCrlDist();