diff --git a/changelog b/changelog index 12f06733..525a1dff 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,7 @@ + * improve creating templates from cert + - enhance parsing of CRL-DP, SAN, IAN and AuthInfoAcc + - add support for CertificatePolicies * improve date handling. "notBefore" is not reset to now anymore when applyiong a time range * Support dropping files onto the application diff --git a/configure b/configure index 7bd30062..dae9d7be 100755 --- a/configure +++ b/configure @@ -29,7 +29,7 @@ DIRS="$QTDIR $DIRS /usr /usr/X11R6 /usr/local" STDINC="-I/usr/include" STDLIB="-L/usr/lib" prefix=${prefix:=/usr/local} -CFLAGS=${CFLAGS:=-Wall -ggdb -O2} +CFLAGS=${CFLAGS:=-Wall -ggdb -O0} CC=${CC:=gcc} CF="-I. -I.. -I\$(TOPDIR)/ui" LDIRS= @@ -191,9 +191,9 @@ test -z "$docdir" && docdir="${prefix}/share/xca" cat >Local.mak <data[i]); printf("\n"); #endif + if (quote) + qs.replace('\n', "\\n\\"); return qs; } @@ -240,6 +242,23 @@ const char *OBJ_sn2ln(const char *sn) return OBJ_nid2ln(OBJ_sn2nid(sn)); } +const char *OBJ_obj2sn(ASN1_OBJECT *a) +{ + OBJ_obj2nid(a); + openssl_error(); + return OBJ_nid2sn(OBJ_obj2nid(a)); +} + +QString OBJ_obj2QString(ASN1_OBJECT *a, int no_name) +{ + char buf[512]; + int len; + + len = OBJ_obj2txt(buf, 256, a, no_name); + openssl_error(); + return QString::fromAscii(buf, len); +} + QString changeFilenameSuffix(QString fn, const QStringList &suffixlist, int selected) { diff --git a/lib/func.h b/lib/func.h index 1fa04b63..ef039ef5 100644 --- a/lib/func.h +++ b/lib/func.h @@ -25,11 +25,13 @@ QString getFullFilename(const QString &filename, const QString &selectedFilter); QByteArray filename2bytearray(const QString &fname); QString filename2QString(const char *fname); -QString asn1ToQString(const ASN1_STRING *str); +QString asn1ToQString(const ASN1_STRING *str, bool quote = false); ASN1_STRING *QStringToAsn1(QString s, int nid); const char *OBJ_ln2sn(const char *ln); const char *OBJ_sn2ln(const char *sn); +const char *OBJ_obj2sn(ASN1_OBJECT *a); +QString OBJ_obj2QString(ASN1_OBJECT *a, int no_name = 0); QString changeFilenameSuffix(QString fn, const QStringList &suffixlist, int selected); diff --git a/lib/pki_temp.cpp b/lib/pki_temp.cpp index 5badfbd1..8d9b2b94 100644 --- a/lib/pki_temp.cpp +++ b/lib/pki_temp.cpp @@ -108,54 +108,6 @@ QString pki_temp::getMsg(msg_type msg) return pki_base::getMsg(msg); } -static QStringList extVlistToString(extList &el, int nid, bool *crit) -{ - int i = el.idxByNid(nid); - QStringList sl; - if (i != -1) { - if (crit) - *crit = el[i].getCritical(); - sl = el[i].i2v(); - el.removeAt(i); - } - return sl; -} - -static QString extVtoString(extList &el, int nid, QString *adv) -{ - bool crit; - QString critical; - const char *tag = OBJ_nid2sn(nid); - - QStringList vlist = extVlistToString(el, nid, &crit); - if (crit) - critical = "critical,"; - if (!vlist.join("").contains(",")) - return critical + vlist.join(", "); - - *adv = QString("%1=%2@%1_sect\n").arg(tag).arg(critical) + *adv + - QString("\n[%1_sect]\n").arg(tag); - - for (int i=0; igetV3ext(); + nsComment = ""; + n = cert_or_req->getSubject(); for (i=0; ica ? 0 : 1) +1; - a1int pl(bc->pathlen); - pathLen = pl.toDec(); - BASIC_CONSTRAINTS_free(bc); - } - el.removeAt(i); - } - i = el.idxByNid(NID_authority_key_identifier); - if (i != -1) { - el.removeAt(i); - authKey = true; - } - i = el.idxByNid(NID_subject_key_identifier); - if (i != -1) { - el.removeAt(i); - subKey = true; + QString r; + if (el.genConf(NID_basic_constraints, &r)) { + QStringList sl = r.split(","); + if (sl.contains("critical")) + bcCrit = true; + ca = sl.contains("CA:TRUE") ? 2 : 1; + pathLen = sl.filter("pathlen:").join("").mid(8, -1); + } else { + bcCrit = false; + ca = 0; } + authKey = el.delByNid(NID_authority_key_identifier); + subKey = el.delByNid(NID_subject_key_identifier); + nsCertType = bitsToInt(el, NID_netscape_cert_type, NULL); /* bit 4 is unused. Move higher bits down. */ nsCertType = (nsCertType & 0xf) | ((nsCertType & 0xf0) >> 1); keyUse = bitsToInt(el, NID_key_usage, &keyUseCrit); - QStringList sl = extVlistToString(el, NID_ext_key_usage, &eKeyUseCrit); - for (i=0; igetType() == x509) { pki_x509 *cert = (pki_x509*)cert_or_req; diff --git a/lib/x509name.cpp b/lib/x509name.cpp index 8f51fa7a..6c08fbfa 100644 --- a/lib/x509name.cpp +++ b/lib/x509name.cpp @@ -23,6 +23,17 @@ x509name::x509name(const X509_NAME *n) xn = X509_NAME_dup((X509_NAME *)n); } +x509name::x509name(STACK_OF(X509_NAME_ENTRY) *entries) +{ + X509_NAME *n = X509_NAME_new(); + STACK_OF(X509_NAME_ENTRY) *ba = n->entries; + xn = NULL; + n->entries = entries; + set(n); + n->entries = ba; + X509_NAME_free(n); +} + x509name::x509name(const x509name &n) { xn = NULL; @@ -42,7 +53,6 @@ x509name &x509name::set(const X509_NAME *n) return *this; } - QString x509name::oneLine(unsigned long flags) const { QString ret; @@ -144,14 +154,11 @@ int x509name::nid(int i) const QString x509name::getOid(int i) const { X509_NAME_ENTRY *ne; - char buf[256]; - int len; ne = sk_X509_NAME_ENTRY_value(xn->entries, i); if (ne == NULL) return QString(); - len = OBJ_obj2txt(buf, 256, ne->object, 1); - return QString::fromAscii(buf, len); + return OBJ_obj2QString(ne->object, 1); } void x509name::d2i(QByteArray &ba) @@ -217,6 +224,19 @@ QString x509name::checkLength() const return warn; } +QString x509name::taggedValues() const +{ + int i, max = entryCount(); + QString ret; + + for (i=0; i #include #include @@ -111,6 +114,23 @@ QString x509v3ext::getValue(bool html) const return text.trimmed(); } +static QString vlist2Section(QStringList vlist, QString tag, QString *sect) +{ + /* Check for commas in the text */ + if (!vlist.join("").contains(",")) + return vlist.join(", "); + + *sect += QString("\n[%1_sect]\n").arg(tag); + + for (int i=0; ii2s) + return false; + ext_str = ext_str_new(ext); if (!ext_str) - return str; - if (method->i2s) - str = QString(method->i2s(method, ext_str)); + return false; + ret = QString(method->i2s(method, ext_str)); + if (single) + *single = ret; + else + *adv = QString("%1=%2").arg(OBJ_nid2sn(nid())).arg(ret); ext_str_free(ext, ext_str); - return str; + return true; } -QStringList x509v3ext::i2v() +static bool genName2conf(GENERAL_NAME *gen, QString tag, QString *single, QString *sect) { - C_X509V3_EXT_METHOD *method = X509V3_EXT_get(ext); - void *ext_str = ext_str_new(ext); - QStringList sl; + unsigned char *p; + QString ret; - if (!ext_str) - return sl; - if (method->i2v) { - STACK_OF(CONF_VALUE) *val = method->i2v(method, ext_str, NULL); - for (int i = 0; i < sk_CONF_VALUE_num(val); i++) { - CONF_VALUE *nval = sk_CONF_VALUE_value(val, i); - const char *name = nval->name; - QString final; - if (name) { - const char *prep = strstr(name, " - "); - if (prep) { - final = QString::fromAscii(name, - prep -name); - name = prep +3; - } - if (!strcmp(name, "IP Address")) - name = "IP"; - else if (!strcmp(name, "Registered ID")) - name = "RID"; - if (!final.isEmpty()) { - int nid = OBJ_txt2nid(CCHAR(final)); - final = OBJ_nid2sn(nid); - final += ";"; - } - final += name; - } - if (!name) - sl << QString(nval->value); - else if (!nval->value) - sl << QString(final); - else - sl << QString("%1:%2").arg(final). - arg(nval->value); + switch (gen->type) { + case GEN_EMAIL: ret = "email:%1"; break; + case GEN_DNS: ret = "DNS:%1"; break; + case GEN_URI: ret = "URI:%1"; break; + + case GEN_DIRNAME: { + tag += "_dirname"; + x509name xn(gen->d.dirn); + *sect += QString("\n[%1]\n"). arg(tag); + *sect += xn.taggedValues(); + *single = QString("dirName:") + tag; + printf("GENN: '%s'\n'%s'\n", CCHAR(*single), CCHAR(*sect)); + return true; } - sk_CONF_VALUE_pop_free(val, X509V3_conf_free); - } + case GEN_IPADD: + p = gen->d.ip->data; + if (gen->d.ip->length == 4) { + *single = QString("IP:%1.%2.%3.%4"). + arg(p[0]).arg(p[1]).arg(p[2]).arg(p[3]); + return true; + } + return false; - ext_str_free(ext, ext_str); - return sl; + case GEN_RID: + *single = QString("RID:%1").arg(OBJ_obj2QString(gen->d.rid)); + return true; + case GEN_OTHERNAME: + if (gen->d.otherName->value->type != V_ASN1_UTF8STRING) + return false; + *single = QString("othername:%1;UTF8:%2"). + arg(OBJ_obj2QString(gen->d.otherName->type_id)). + arg(asn1ToQString( + gen->d.otherName->value->value.asn1_string, true)); + return true; + default: + return false; + } + if (!ret.isEmpty()) + *single = ret.arg(asn1ToQString(gen->d.ia5, true)); + return true; +} + +static bool genNameStack2conf(STACK_OF(GENERAL_NAME) *gens, QString tag, + QString *single, QString *sect) +{ + int i; + QStringList sl; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + QString one; + if (!genName2conf(sk_GENERAL_NAME_value(gens, i), + QString("%1_%2").arg(tag).arg(i), &one, sect)) + { + return false; + } + sl << one; + } + *single = vlist2Section(sl, tag, sect); + return true; +} + +QString x509v3ext::parse_critical() const +{ + return QString(getCritical() ? "critical," : ""); +} + +bool x509v3ext::parse_generalName(QString *single, QString *adv) const +{ + bool retval = true; + QString sect, ret; + QString tag = OBJ_nid2sn(nid()); + STACK_OF(GENERAL_NAME) *gens = (STACK_OF(GENERAL_NAME) *)ext_str_new(ext); + + if (!genNameStack2conf(gens, tag, &ret, §)) + retval = false; + else if (sect.isEmpty() && single) { + *single = parse_critical() + ret; + } else { + *adv = tag + "=" + parse_critical() + ret + *adv + sect; + } + ext_str_free(ext, gens); + return retval; +} + +bool x509v3ext::parse_eku(QString *single, QString *adv) const +{ + EXTENDED_KEY_USAGE *eku = ( EXTENDED_KEY_USAGE *)ext_str_new(ext); + QStringList sl; + int i; + + for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) { + sl << QString(OBJ_obj2sn(sk_ASN1_OBJECT_value(eku, i))); + } + QString r = parse_critical() + sl.join(", "); + if (single) + *single = r; + else + *adv = QString("%1=%2").arg(OBJ_nid2sn(nid())).arg(r); + return true; +} + +bool x509v3ext::parse_ainfo(QString *single, QString *adv) const +{ + bool retval = true; + QString sect, ret; + QString tag = OBJ_nid2sn(nid()); + QStringList sl; + int i; + + AUTHORITY_INFO_ACCESS *ainfo = (AUTHORITY_INFO_ACCESS *)ext_str_new(ext); + + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ainfo); i++) { + QString one; + ACCESS_DESCRIPTION *desc = sk_ACCESS_DESCRIPTION_value(ainfo, i); + if (!genName2conf(desc->location, + QString("%1_%2").arg(tag).arg(i), &one, §)) + { + retval = false; + break; + } + sl << QString("%1;%2").arg(OBJ_obj2sn(desc->method)).arg(one); + } + if (retval) { + ret = vlist2Section(sl, tag, §); + if (sect.isEmpty() && sk_ACCESS_DESCRIPTION_num(ainfo) == 1) { + *single = parse_critical() + ret; + } else { + *adv = tag + "=" + parse_critical() + ret + *adv + sect; + } + } + ext_str_free(ext, ainfo); + return retval; +} + +static const BIT_STRING_BITNAME reason_flags[] = { +{0, "", "unused"}, +{1, "", "keyCompromise"}, +{2, "", "CACompromise"}, +{3, "", "affiliationChanged"}, +{4, "", "superseded"}, +{5, "", "cessationOfOperation"}, +{6, "", "certificateHold"}, +{7, "", "privilegeWithdrawn"}, +{8, "", "AACompromise"}, +{-1, NULL, NULL} +}; + +bool x509v3ext::parse_Crldp(QString *single, QString *adv) const +{ + QString othersect; + QStringList crldps; + int i; + + STACK_OF(DIST_POINT) *crld = (STACK_OF(DIST_POINT)*)ext_str_new(ext); + if (sk_DIST_POINT_num(crld) == 1) { + DIST_POINT *point = sk_DIST_POINT_value(crld, 0); + if (point->distpoint && !point->reasons && !point->CRLissuer && + !point->distpoint->type && single) + { + QString sect, ret; + if (!genNameStack2conf(point->distpoint->name.fullname, + "", &ret, §)) + goto could_not_parse; + + if (sect.isEmpty()) { + *single = parse_critical() +ret; + return true; + } + } + } +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + for(i = 0; i < sk_DIST_POINT_num(crld); i++) { + DIST_POINT *point = sk_DIST_POINT_value(crld, i); + QString tag = QString("crlDistributionPoint%1_sect").arg(i); + QString crldpsect = QString("\n[%1]\n").arg(tag); + if (point->distpoint) { + if (!point->distpoint->type) { + QString ret; + if (!genNameStack2conf(point->distpoint->name.fullname, + tag + "_fullname", &ret, &othersect)) + goto could_not_parse; + + crldpsect += "fullname=" + ret +"\n"; + } else { + QString mysect = tag + "_relativename"; + x509name xn(point->distpoint->name.relativename); + crldpsect += "relativename=" + mysect + "\n"; + othersect += QString("\n[%1]\n").arg(mysect) + + xn.taggedValues(); + } + } + if (point->reasons) { + const BIT_STRING_BITNAME *pbn; + QStringList r; + for (pbn = reason_flags; pbn->lname; pbn++) { + if (ASN1_BIT_STRING_get_bit(point->reasons, + pbn->bitnum)) + r += pbn->sname; + } + crldpsect += "reasons=" + r.join(", ") + "\n"; + } + if (point->CRLissuer) { + QString ret; + if (genNameStack2conf(point->CRLissuer, + tag +"_crlissuer", &ret, &othersect)) + goto could_not_parse; + crldpsect += "CRLissuer=" + ret + "\n"; + } + crldps << tag; + othersect = crldpsect + othersect; + } + ext_str_free(ext, crld); + if (crldps.size() == 0) + return true; + *adv = "crlDistributionPoints=" + parse_critical() + + crldps.join(", ") + "\n" + *adv + othersect; + return true; + +could_not_parse: +#endif + ext_str_free(ext, crld); + return false; +} + +static void gen_cpol_notice(QString tag, USERNOTICE *notice, QString *adv) +{ + *adv += QString("\n[%1]\n").arg(tag); + if (notice->exptext) { + *adv += QString("explicitText=%1\n"). + arg(asn1ToQString(notice->exptext, true)); + } + if (notice->noticeref) { + NOTICEREF *ref = notice->noticeref; + QStringList sl; + int i; + *adv += QString("organization=%1\n"). + arg(asn1ToQString(ref->organization, true)); + for (i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) { + a1int num(sk_ASN1_INTEGER_value(ref->noticenos, i)); + sl << num.toDec(); + } + if (sl.size()) + *adv += QString("noticeNumbers=%1\n"). + arg(sl.join(", ")); + } +} + +static bool gen_cpol_qual_sect(QString tag, POLICYINFO *pinfo, QString *adv) +{ + QString polsect = QString("\n[%1]\n").arg(tag); + QString noticetag; + STACK_OF(POLICYQUALINFO) *quals = pinfo->qualifiers; + int i; + + polsect += QString("policyIdentifier=%1\n"). + arg(OBJ_obj2QString(pinfo->policyid)); + + for (i = 0; i < sk_POLICYQUALINFO_num(quals); i++) { + POLICYQUALINFO *qualinfo = sk_POLICYQUALINFO_value(quals, i); + switch (OBJ_obj2nid(qualinfo->pqualid)) { + case NID_id_qt_cps: + polsect += QString("CPS.%1=%2\n").arg(i). + arg(asn1ToQString(qualinfo->d.cpsuri, true)); + break; + case NID_id_qt_unotice: + noticetag = QString("%1_notice%2_sect").arg(tag).arg(i); + polsect += QString("userNotice.%1=@%2\n").arg(i). + arg(noticetag); + gen_cpol_notice(noticetag, qualinfo->d.usernotice, adv); + break; + default: + return false; + } + } + *adv = polsect + *adv; + return true; +} + + +bool x509v3ext::parse_certpol(QString *single, QString *adv) const +{ + bool retval = true; + QStringList pols; + QString myadv; + STACK_OF(POLICYINFO) *pol = (STACK_OF(POLICYINFO) *)ext_str_new(ext); + int i; + for (i = 0; i < sk_POLICYINFO_num(pol); i++) { + POLICYINFO *pinfo = sk_POLICYINFO_value(pol, i); + if (!pinfo->qualifiers) { + pols << OBJ_obj2QString(pinfo->policyid); + continue; + } + QString tag = QString("certpol%1_sect").arg(i); + pols << QString("@") + tag; + if (!gen_cpol_qual_sect(tag, pinfo, &myadv)) { + retval = false; + break; + } + } + if (retval) + *adv = QString("certificatePolicies=ia5org,%1\n"). + arg(pols.join(", ")) + *adv + myadv; + ext_str_free(ext, pol); + return retval; +} + +bool x509v3ext::parse_bc(QString *single, QString *adv) const +{ + BASIC_CONSTRAINTS *bc = (BASIC_CONSTRAINTS *)ext_str_new(ext); + QString ret = a1int(bc->pathlen).toDec(); + if (!ret.isEmpty()) + ret = ",pathlen:" + ret; + ret = parse_critical() + (bc->ca ? "CA:FALSE" : "CA:TRUE") + ret; + if (single) + *single = ret; + if (adv) + *adv = QString("%1=%2").arg(OBJ_nid2sn(nid())).arg(*adv); + return true; +} + +bool x509v3ext::genConf(QString *single, QString *adv) const +{ + int n = nid(); + switch (n) { + case NID_crl_distribution_points: + return parse_Crldp(single, adv); + case NID_subject_alt_name: + case NID_issuer_alt_name: + return parse_generalName(single, adv); + case NID_info_access: + return parse_ainfo(single, adv); + case NID_ext_key_usage: + return parse_eku(single, adv); + case NID_certificate_policies: + return parse_certpol(single, adv); + case NID_netscape_comment: + case NID_netscape_base_url: + case NID_netscape_revocation_url: + case NID_netscape_ca_revocation_url: + case NID_netscape_renewal_url: + case NID_netscape_ca_policy_url: + case NID_netscape_ssl_server_name: + return parse_i2s(single, adv); + case NID_basic_constraints: + return parse_bc(single, adv); + } + return false; } QString x509v3ext::getHtml() const @@ -225,6 +557,18 @@ bool x509v3ext::isValid() const /*************************************************************/ +bool extList::genConf(int nid, QString *single, QString *adv) +{ + int i = idxByNid(nid); + if (i != -1) { + if (at(i).genConf(single, adv)) + removeAt(i); + ign_openssl_error(); + return true; + } + return false; +} + void extList::setStack(STACK_OF(X509_EXTENSION) *st, int start) { clear(); @@ -256,16 +600,15 @@ QString extList::getHtml(const QString &sep) return a; } -int extList::delByNid(int nid) +bool extList::delByNid(int nid) { - int removed=0; for(int i = 0; i< size(); i++) { if (at(i).nid() == nid) { removeAt(i); - removed=1; + return true; } } - return removed; + return false; } int extList::idxByNid(int nid) diff --git a/lib/x509v3ext.h b/lib/x509v3ext.h index 6ac52912..c379f81e 100644 --- a/lib/x509v3ext.h +++ b/lib/x509v3ext.h @@ -36,8 +36,16 @@ class x509v3ext bool isValid() const; int nid() const; void *d2i(); - QString i2s(); - QStringList i2v(); + bool genConf(QString *single, QString *adv) const; + protected: + QString parse_critical() const; + bool parse_certpol(QString *single, QString *adv) const; + bool parse_ainfo(QString *single, QString *adv) const; + bool parse_Crldp(QString *single, QString *adv) const; + bool parse_eku(QString *single, QString *adv) const; + bool parse_generalName(QString *single, QString *adv) const; + bool parse_i2s(QString *single, QString *adv) const; + bool parse_bc(QString *single, QString *adv) const; }; class extList : public QList @@ -46,8 +54,9 @@ class extList : public QList void setStack(STACK_OF(X509_EXTENSION) *st, int start=0); STACK_OF(X509_EXTENSION) *getStack(); QString getHtml(const QString &sep); - int delByNid(int nid); + bool delByNid(int nid); int delInvalid(); int idxByNid(int nid); + bool genConf(int nid, QString *single, QString *adv = NULL); }; #endif diff --git a/widgets/NewX509_ext.cpp b/widgets/NewX509_ext.cpp index f4f35390..f3489fe3 100644 --- a/widgets/NewX509_ext.cpp +++ b/widgets/NewX509_ext.cpp @@ -161,8 +161,10 @@ void NewX509::setAuthInfAcc_string(QString aia_txt) int nid, idx; idx = aia_txt.indexOf(';'); + if (idx == -1) + return; - nid = OBJ_sn2nid(CCHAR(aia_txt.left(idx))); + nid = OBJ_txt2nid(CCHAR(aia_txt.left(idx))); for (int i=0; i < aia_nid.count(); i++) { if (aia_nid[i] == nid) {