Close #90, #361: Name Constraints (RFC5280 section 4.2.1.10)
Some checks failed
CMake / build (build/xca-*-Linux.tar.gz, Unix Makefiles, linux, /usr, ubuntu, 5.15.2, ubuntu-latest) (push) Has been cancelled
CMake / build (build/xca-*-Linux.tar.gz, Unix Makefiles, linux, /usr, ubuntu, 6.6.2, ubuntu-latest) (push) Has been cancelled
CMake / build (build/xca-*.dmg, Unix Makefiles, mac, /opt/homebrew/opt/openssl, macos, 6.6.2, macos-latest) (push) Has been cancelled
CMake / build (build/xca-*.msi build/xca-portable-*.zip , MinGW Makefiles, windows, D:\msys2\msys64\mingw64, windows, 5.15.2, windows-2019, win64_mingw81) (push) Has been cancelled

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.
This commit is contained in:
Christian Hohnstaedt 2024-09-14 23:28:08 +02:00
parent c6304a073d
commit cb1f02502d
7 changed files with 218 additions and 88 deletions

View File

@ -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");

View File

@ -58,6 +58,7 @@ namespace db {
};
const QList<QString> 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");

View File

@ -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;

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>747</width>
<height>637</height>
<height>640</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -100,7 +100,16 @@
<item>
<widget class="QWidget" name="reqWidget" native="true">
<layout class="QGridLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="1">
@ -141,8 +150,7 @@
</widget>
</item>
<item>
<widget class="QWidget" name="attrWidget" native="true">
</widget>
<widget class="QWidget" name="attrWidget" native="true"/>
</item>
</layout>
</widget>
@ -537,7 +545,16 @@
<string notr="true">Key identifier</string>
</property>
<layout class="QVBoxLayout">
<property name="margin">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
@ -584,7 +601,16 @@
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -652,6 +678,20 @@
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Local time</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QCheckBox" name="noWellDefinedExpDate">
<property name="text">
<string>No well-defined expiration</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="applyTime">
<property name="sizePolicy">
@ -675,20 +715,6 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Local time</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QCheckBox" name="noWellDefinedExpDate">
<property name="text">
<string>No well-defined expiration</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -709,11 +735,15 @@
</spacer>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_12">
<item row="0" column="0">
<widget class="DoubleClickLabel" name="sanLbl"/>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="2" colspan="2">
<widget class="QCheckBox" name="OCSPstaple">
<property name="text">
<string notr="true">OCSP Must Staple</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QLabel" name="subAltIco">
<property name="minimumSize">
<size>
@ -732,7 +762,7 @@
</property>
</widget>
</item>
<item row="0" column="2">
<item row="1" column="2">
<widget class="QLineEdit" name="subAltName">
<property name="toolTip">
<string>DNS: IP: URI: email: RID:</string>
@ -740,30 +770,108 @@
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="editNameCons">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="nameConsIco">
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="editSubAlt">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="DoubleClickLabel" name="ianLbl"/>
<item row="0" column="0">
<widget class="DoubleClickLabel" name="nameConsLbl"/>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="issAltName">
<item row="0" column="2">
<widget class="QLineEdit" name="nameCons">
<property name="toolTip">
<string>DNS: IP: URI: email: RID:</string>
</property>
</widget>
</item>
<item row="1" column="3">
<item row="1" column="0">
<widget class="DoubleClickLabel" name="sanLbl"/>
</item>
<item row="2" column="0">
<widget class="DoubleClickLabel" name="ianLbl"/>
</item>
<item row="3" column="0">
<widget class="DoubleClickLabel" name="crldpLbl"/>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="editIssAlt">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="issAltName">
<property name="toolTip">
<string>DNS: IP: URI: email: RID:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="issAltIco">
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="DoubleClickLabel" name="aiaLbl"/>
</item>
<item row="4" column="2">
<widget class="QLineEdit" name="authInfAcc">
<property name="toolTip">
<string>DNS: IP: URI: email: RID:</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="editAuthInfAcc">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="crlDistIco">
<property name="minimumSize">
<size>
@ -782,24 +890,14 @@
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="crlDist">
<property name="toolTip">
<string>URI:</string>
</property>
</widget>
</item>
<item row="2" column="3">
<item row="3" column="3">
<widget class="QPushButton" name="editCrlDist">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="DoubleClickLabel" name="aiaLbl"/>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QLabel" name="authInfAccIco">
<property name="minimumSize">
<size>
@ -819,45 +917,9 @@
</widget>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="authInfAcc">
<widget class="QLineEdit" name="crlDist">
<property name="toolTip">
<string>DNS: IP: URI: email: RID:</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="editAuthInfAcc">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="issAltIco">
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="DoubleClickLabel" name="crldpLbl"/>
</item>
<item row="4" column="2">
<widget class="QCheckBox" name="OCSPstaple">
<property name="text">
<string notr="true">OCSP Must Staple</string>
<string>URI:</string>
</property>
</widget>
</item>
@ -870,7 +932,16 @@
<string>Key usage</string>
</attribute>
<layout class="QVBoxLayout">
<property name="margin">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
@ -878,7 +949,16 @@
<item>
<widget class="QGroupBox" name="kuBox">
<layout class="QVBoxLayout">
<property name="margin">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
@ -949,7 +1029,16 @@
<item>
<widget class="QGroupBox" name="ekuBox">
<layout class="QVBoxLayout">
<property name="margin">
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>

View File

@ -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<int, QWidget*> 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<QString, QLineEdit*> l(templateLineEdits);
while (l.hasNext()) {
l.next();
qDebug() << "APPLY LineEdits" << l.key() << temp->getSetting(l.key());
l.value()->setText(temp->getSetting(l.key()));
}
QMapIterator<QString, QCheckBox*> 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") + "<br>\n";
if (!subAltName->text().isEmpty() && !getSubAltName().isValid())
lineext += tr("The Subject Alternative Name is invalid") + "<br>\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";

View File

@ -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);

View File

@ -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();