mirror of
https://github.com/chris2511/xca.git
synced 2026-09-12 19:51:05 +05:00
532 lines
13 KiB
C++
532 lines
13 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2001 - 2020 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "pki_temp.h"
|
|
#include "pki_x509.h"
|
|
#include "func.h"
|
|
#include "oid.h"
|
|
#include "exception.h"
|
|
#include <QDir>
|
|
#include <QBuffer>
|
|
#include <QDataStream>
|
|
#include <QRegularExpression>
|
|
|
|
#define TEMPLATE_DS_VERSION (QDataStream::Qt_4_2)
|
|
|
|
namespace db {
|
|
|
|
QByteArray intToData(uint32_t val)
|
|
{
|
|
uint32_t v = xhtonl(val);
|
|
return QByteArray((char*)&v, sizeof(uint32_t));
|
|
}
|
|
uint32_t intFromData(QByteArray &ba)
|
|
{
|
|
uint32_t ret;
|
|
if ((unsigned)(ba.size()) < sizeof(uint32_t)) {
|
|
throw errorEx(QObject::tr("Out of data"));
|
|
}
|
|
memcpy(&ret, ba.constData(), sizeof(uint32_t));
|
|
ba = ba.mid(sizeof(uint32_t));
|
|
return xntohl(ret);
|
|
}
|
|
bool boolFromData(QByteArray &ba)
|
|
{
|
|
unsigned char c;
|
|
if (ba.size() < 1)
|
|
throw errorEx(QObject::tr("Out of data"));
|
|
|
|
c = ba.constData()[0];
|
|
ba = ba.mid(1);
|
|
return c ? true : false;
|
|
}
|
|
QString stringFromData(QByteArray &ba)
|
|
{
|
|
int idx = ba.indexOf('\0');
|
|
|
|
if (idx == -1)
|
|
throw errorEx(QObject::tr("Error finding endmarker of string"));
|
|
|
|
QString ret = QString::fromUtf8(ba.constData(), idx);
|
|
ba = ba.mid(idx+1);
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
const QList<QString> pki_temp::tmpl_keys = {
|
|
"subAltName",
|
|
"issAltName",
|
|
"crlDist",
|
|
"authInfAcc",
|
|
"nsCertType",
|
|
"nsComment",
|
|
"nsBaseUrl",
|
|
"nsRevocationUrl",
|
|
"nsCARevocationUrl",
|
|
"nsRenewalUrl",
|
|
"nsCaPolicyUrl",
|
|
"nsSslServerName",
|
|
"ca",
|
|
"bcCritical",
|
|
"ekuCritical",
|
|
"kuCritical",
|
|
"subKey",
|
|
"authKey",
|
|
"basicPath",
|
|
"validN",
|
|
"validM",
|
|
"validMidn",
|
|
"keyUse",
|
|
"eKeyUse",
|
|
"adv_ext",
|
|
"noWellDefinedExpDate",
|
|
"OCSPstaple",
|
|
};
|
|
|
|
pki_temp::pki_temp(const pki_temp *pk)
|
|
:pki_x509name(pk->getIntName()), xname(pk->xname), settings(pk->settings)
|
|
{
|
|
}
|
|
|
|
pki_temp::pki_temp(const QString &d)
|
|
:pki_x509name(d)
|
|
{
|
|
pkiType = tmpl;
|
|
|
|
foreach(QString key, tmpl_keys) {
|
|
settings[key] = QString();
|
|
}
|
|
settings["nsComment"] = "xca certificate";
|
|
settings["validN"] = "365";
|
|
}
|
|
|
|
QString pki_temp::comboText() const
|
|
{
|
|
return pre_defined ? QString("[default] ") + pki_base::comboText() :
|
|
pki_base::comboText();
|
|
}
|
|
|
|
QSqlError pki_temp::insertSqlData()
|
|
{
|
|
XSqlQuery q;
|
|
QSqlError e = pki_x509name::insertSqlData();
|
|
if (e.isValid())
|
|
return e;
|
|
SQL_PREPARE(q, "INSERT INTO templates (item, version, template) "
|
|
"VALUES (?, ?, ?)");
|
|
q.bindValue(0, sqlItemId);
|
|
q.bindValue(1, TMPL_VERSION);
|
|
q.bindValue(2, toB64Data());
|
|
q.exec();
|
|
return q.lastError();
|
|
}
|
|
|
|
void pki_temp::restoreSql(const QSqlRecord &rec)
|
|
{
|
|
pki_base::restoreSql(rec);
|
|
int version = rec.value(VIEW_temp_version).toInt();
|
|
QByteArray ba = QByteArray::fromBase64(
|
|
rec.value(VIEW_temp_template).toByteArray());
|
|
fromData(ba, version);
|
|
}
|
|
|
|
QSqlError pki_temp::deleteSqlData()
|
|
{
|
|
XSqlQuery q;
|
|
QSqlError e = pki_x509name::deleteSqlData();
|
|
if (e.isValid())
|
|
return e;
|
|
SQL_PREPARE(q, "DELETE FROM templates WHERE item=?");
|
|
q.bindValue(0, sqlItemId);
|
|
q.exec();
|
|
return q.lastError();
|
|
}
|
|
|
|
QString pki_temp::getMsg(msg_type msg) const
|
|
{
|
|
/*
|
|
* We do not construct english sentences from fragments
|
|
* to allow proper translations.
|
|
*
|
|
* %1 will be replaced by the internal name of the template
|
|
*/
|
|
switch (msg) {
|
|
case msg_import: return tr("Successfully imported the XCA template '%1'");
|
|
case msg_delete: return tr("Delete the XCA template '%1'?");
|
|
case msg_create: return tr("Successfully created the XCA template '%1'");
|
|
/* %1: Number of ktemplates; %2: list of templatenames */
|
|
case msg_delete_multi: return tr("Delete the %1 XCA templates: %2?");
|
|
}
|
|
return pki_base::getMsg(msg);
|
|
}
|
|
|
|
x509name pki_temp::getSubject() const
|
|
{
|
|
return xname;
|
|
}
|
|
|
|
static int bitsToInt(extList &el, int nid, bool *crit)
|
|
{
|
|
int ret = 0, i = el.idxByNid(nid);
|
|
|
|
if (i != -1) {
|
|
if (crit)
|
|
*crit = el[i].getCritical();
|
|
ASN1_BIT_STRING *bits;
|
|
bits = (ASN1_BIT_STRING *)el[i].d2i();
|
|
|
|
for (int j=0; j<9; j++) {
|
|
if (ASN1_BIT_STRING_get_bit(bits, j))
|
|
ret |= 1 << j;
|
|
}
|
|
el.removeAt(i);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void pki_temp::fromExtList(extList *el, int nid, const char *item)
|
|
{
|
|
QString target;
|
|
el->genConf(nid, &target, &adv_ext);
|
|
settings[item] = target;
|
|
}
|
|
|
|
extList pki_temp::fromCert(pki_x509super *cert_or_req)
|
|
{
|
|
x509name n;
|
|
extList el = cert_or_req->getV3ext();
|
|
adv_ext.clear();
|
|
|
|
settings["nsComment"] = "";
|
|
|
|
n = cert_or_req->getSubject();
|
|
foreach(QString sn, Settings["explicit_dn"].split(",")) {
|
|
int nid = OBJ_sn2nid(CCHAR(sn));
|
|
QString ne = n.popEntryByNid(nid);
|
|
if (!ne.isNull())
|
|
xname.addEntryByNid(nid, ne);
|
|
}
|
|
for (int i=0; i<n.entryCount(); i++) {
|
|
int nid = n.nid(i);
|
|
if (nid != NID_undef)
|
|
xname.addEntryByNid(nid, n.getEntry(i));
|
|
}
|
|
|
|
fromExtList(&el, NID_subject_alt_name, "subAltName");
|
|
fromExtList(&el, NID_issuer_alt_name, "issAltName");
|
|
fromExtList(&el, NID_crl_distribution_points, "crlDist");
|
|
fromExtList(&el, NID_info_access, "authInfAcc");
|
|
fromExtList(&el, NID_netscape_comment, "nsComment");
|
|
fromExtList(&el, NID_netscape_base_url, "nsBaseUrl");
|
|
fromExtList(&el, NID_netscape_revocation_url, "nsRevocationUrl");
|
|
fromExtList(&el, NID_netscape_ca_revocation_url, "nsCARevocationUrl");
|
|
fromExtList(&el, NID_netscape_renewal_url, "nsRenewalUrl");
|
|
fromExtList(&el, NID_netscape_ca_policy_url, "nsCaPolicyUrl");
|
|
fromExtList(&el, NID_netscape_ssl_server_name, "nsSslServerName");
|
|
|
|
QString r;
|
|
if (el.genConf(NID_basic_constraints, &r)) {
|
|
QStringList sl = r.split(",");
|
|
if (sl.contains("critical"))
|
|
settings["bcCritical"] = "1";
|
|
settings["ca"] = sl.contains("CA:TRUE") ? "1" : "2";
|
|
settings["basicPath"]=sl.filter("pathlen:").join("") .mid(8,-1);
|
|
} else {
|
|
settings["bcCritical"] = "";
|
|
settings["ca"] = "";
|
|
settings["basicPath"] = "";
|
|
}
|
|
settings["authKey"] = el.delByNid(NID_authority_key_identifier) ? "1" : "0";
|
|
settings["subKey"] = el.delByNid(NID_subject_key_identifier) ? "1" : "0";
|
|
settings["OCSPstaple"] = el.delByNid(NID_tlsfeature) ? "1" : "0";
|
|
|
|
int nsCT = bitsToInt(el, NID_netscape_cert_type, NULL);
|
|
/* bit 4 is unused. Move higher bits down. */
|
|
settings["nsCertType"] = QString::number(
|
|
(nsCT & 0xf) | ((nsCT & 0xf0) >> 1));
|
|
|
|
bool kuCritical;
|
|
settings["keyUse"] = QString::number(
|
|
bitsToInt(el, NID_key_usage, &kuCritical));
|
|
|
|
settings["kuCritical"] = kuCritical ? "1" : "0";
|
|
fromExtList(&el, NID_ext_key_usage, "eKeyUse");
|
|
QStringList eKeyUse = settings["eKeyUse"]
|
|
.split(QRegularExpression(",\\s*"));
|
|
settings["ekuCritical"] = "0";
|
|
if (eKeyUse.contains("critical")) {
|
|
eKeyUse.removeOne("critical");
|
|
settings["eKeyUse"] = eKeyUse.join(", ");
|
|
settings["ekuCritical"] = "1";
|
|
}
|
|
qDebug() << "eKeyUse" << settings["kuCritical"] << settings["eKeyUse"];
|
|
|
|
el.genGenericConf(&adv_ext);
|
|
settings["adv_ext"] = adv_ext;
|
|
|
|
if (cert_or_req->getType() == x509) {
|
|
pki_x509 *cert = (pki_x509*)cert_or_req;
|
|
if (cert->getNotAfter().isUndefined()) {
|
|
settings["noWellDefinedExpDate"] = "1";
|
|
} else {
|
|
a1time notBefore = cert->getNotBefore();
|
|
a1time notAfter = cert->getNotAfter();
|
|
|
|
if (notBefore.toPlain().endsWith("000000Z") &&
|
|
notAfter.toPlain().endsWith("235959Z"))
|
|
{
|
|
settings["validMidn"] = "1";
|
|
}
|
|
|
|
int diff = notBefore.daysTo(notAfter);
|
|
settings["validM"] = "0";
|
|
if (diff > 60) {
|
|
settings["validM"] = "1";
|
|
diff /= 30;
|
|
if (diff > 24) {
|
|
settings["validM"] = "2";
|
|
diff /= 12;
|
|
}
|
|
}
|
|
settings["validN"] = QString::number(diff);
|
|
}
|
|
}
|
|
return el;
|
|
}
|
|
|
|
static QString old_eKeyUse2QString(int old)
|
|
{
|
|
QStringList sl;
|
|
|
|
for (int i = 0; i < extkeyuse_nid.size(); i++) {
|
|
if (old & (1<<i)) {
|
|
sl << OBJ_nid2sn(extkeyuse_nid[i]);
|
|
}
|
|
}
|
|
return sl.join(", ");
|
|
}
|
|
|
|
void pki_temp::old_fromData(const unsigned char *p, int size, int version)
|
|
{
|
|
QByteArray ba((const char*)p, size);
|
|
|
|
/* destination = */ db::stringFromData(ba);
|
|
settings["bcCritical"] = QString::number(db::boolFromData(ba));
|
|
settings["kuCritical"] = QString::number(db::boolFromData(ba));
|
|
settings["ekuCritical"] = QString::number(db::boolFromData(ba));
|
|
settings["subKey"] = QString::number(db::boolFromData(ba));
|
|
settings["authKey"] = QString::number(db::boolFromData(ba));
|
|
settings["ca"] = QString::number(db::intFromData(ba));
|
|
if (version > 5) {
|
|
settings["basicPath"] = db::stringFromData(ba);
|
|
} else {
|
|
settings["basicPath"] = QString::number(db::intFromData(ba));
|
|
if (settings["basicPath"] == "0")
|
|
settings["basicPath"] = "";
|
|
}
|
|
settings["validN"] = QString::number(db::intFromData(ba));
|
|
settings["validM"] = QString::number(db::intFromData(ba));
|
|
settings["keyUse"] = QString::number(db::intFromData(ba));
|
|
if (version > 4) {
|
|
settings["eKeyUse"] = db::stringFromData(ba);
|
|
} else {
|
|
int old = db::intFromData(ba);
|
|
settings["eKeyUse"] = old_eKeyUse2QString(old);
|
|
}
|
|
settings["nsCertType"] = QString::number(db::intFromData(ba));
|
|
settings["subAltName"] = db::stringFromData(ba);
|
|
settings["issAltName"] = db::stringFromData(ba);
|
|
settings["crlDist"] = db::stringFromData(ba);
|
|
settings["nsComment"] = db::stringFromData(ba);
|
|
settings["nsBaseUrl"] = db::stringFromData(ba);
|
|
settings["nsRevocationUrl"] = db::stringFromData(ba);
|
|
settings["nsCARevocationUrl"] = db::stringFromData(ba);
|
|
settings["nsRenewalUrl"] = db::stringFromData(ba);
|
|
settings["nsCaPolicyUrl"] = db::stringFromData(ba);
|
|
settings["nsSslServerName"] = db::stringFromData(ba);
|
|
xname.d2i(ba);
|
|
settings["authInfAcc"] = db::stringFromData(ba);
|
|
/* certPol = */ db::stringFromData(ba);
|
|
settings["validMidn"] = QString::number(db::boolFromData(ba));
|
|
if (version>2)
|
|
settings["adv_ext"] = db::stringFromData(ba);
|
|
if (version>3)
|
|
settings["noWellDefinedExpDate"] =
|
|
QString::number(db::boolFromData(ba));
|
|
|
|
if (ba.size() > 0) {
|
|
my_error(tr("Wrong Size %1").arg(ba.size()));
|
|
}
|
|
}
|
|
|
|
QByteArray pki_temp::toData() const
|
|
{
|
|
QByteArray ba;
|
|
|
|
ba += xname.i2d();
|
|
|
|
QBuffer buf(&ba);
|
|
buf.open(QIODevice::WriteOnly | QIODevice::Append);
|
|
QDataStream out(&buf);
|
|
out.setVersion(TEMPLATE_DS_VERSION);
|
|
out << settings;
|
|
buf.close();
|
|
return ba;
|
|
}
|
|
|
|
void pki_temp::fromData(QByteArray &ba, int version)
|
|
{
|
|
xname.d2i(ba);
|
|
QBuffer buf(&ba);
|
|
buf.open(QIODevice::ReadOnly);
|
|
QDataStream in(&buf);
|
|
in.setVersion(TEMPLATE_DS_VERSION);
|
|
in >> settings;
|
|
QMap<QString, QString> translate;
|
|
translate["eKyUseCritical"] = "ekuCritical";
|
|
translate["keyUseCritical"] ="kuCritical";
|
|
|
|
foreach(QString key, translate.keys()) {
|
|
if (settings.contains(key))
|
|
settings[translate[key]] = settings.take(key);
|
|
}
|
|
buf.close();
|
|
(void)version;
|
|
//if (version < 11) ....
|
|
}
|
|
|
|
void pki_temp::fromData(const unsigned char *p, int size, int version)
|
|
{
|
|
if (version < 10) {
|
|
old_fromData(p, size, version);
|
|
} else {
|
|
QByteArray ba((const char*)p, size);
|
|
fromData(ba, version);
|
|
}
|
|
}
|
|
|
|
QByteArray pki_temp::toExportData() const
|
|
{
|
|
QByteArray data, header;
|
|
BioByteArray b;
|
|
|
|
data = toData();
|
|
header = db::intToData(data.size());
|
|
header += db::intToData(TMPL_VERSION);
|
|
header += data;
|
|
PEM_write_bio(b, PEM_STRING_XCA_TEMPLATE, (char*)"",
|
|
(unsigned char*)(header.data()), header.size());
|
|
return b.byteArray();
|
|
}
|
|
|
|
void pki_temp::writeTemp(XFile &file) const
|
|
{
|
|
file.write(PEM_comment() + toExportData());
|
|
}
|
|
|
|
void pki_temp::writeDefault(const QString &dirname) const
|
|
{
|
|
XFile file(get_dump_filename(dirname, ".xca"));
|
|
file.open_write();
|
|
writeTemp(file);
|
|
}
|
|
|
|
bool pki_temp::pem(BioByteArray &b)
|
|
{
|
|
b += toExportData();
|
|
return true;
|
|
}
|
|
|
|
void pki_temp::fromExportData(QByteArray data)
|
|
{
|
|
int version;
|
|
|
|
if (data.size() < (int)sizeof(uint32_t))
|
|
my_error(tr("Template file content error (too small)"));
|
|
|
|
db::intFromData(data);
|
|
version = db::intFromData(data);
|
|
fromData((const unsigned char*)data.constData(),
|
|
data.size(), version);
|
|
}
|
|
|
|
void pki_temp::try_fload(XFile &file)
|
|
{
|
|
QByteArray ba = file.read(4096*1024);
|
|
try {
|
|
fromPEM_BIO(BioByteArray(ba).ro(), file.fileName());
|
|
} catch (errorEx &) {
|
|
fromExportData(ba);
|
|
}
|
|
pki_openssl_error();
|
|
}
|
|
|
|
void pki_temp::fload(const QString &fname)
|
|
{
|
|
try {
|
|
XFile file(fname);
|
|
file.open_read();
|
|
try_fload(file);
|
|
#if defined(Q_OS_WIN32)
|
|
} catch (errorEx &) {
|
|
/* Try again in ascii mode on Windows
|
|
* to support pre 1.1.0 template exports */
|
|
XFile file(fname);
|
|
file.open(QIODevice::ReadOnly | QIODevice::QIODevice::Text);
|
|
try_fload(file);
|
|
#else
|
|
} catch (errorEx &err) {
|
|
throw err;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void pki_temp::fromPEM_BIO(BIO *bio, const QString &name)
|
|
{
|
|
QByteArray ba;
|
|
QString msg;
|
|
char *nm = NULL, *header = NULL;
|
|
unsigned char *data = NULL;
|
|
long len;
|
|
|
|
PEM_read_bio(bio, &nm, &header, &data, &len);
|
|
|
|
if (ign_openssl_error())
|
|
throw errorEx(tr("Not a PEM encoded XCA Template"),
|
|
getClassName());
|
|
|
|
if (!strcmp(nm, PEM_STRING_XCA_TEMPLATE)) {
|
|
ba = QByteArray::fromRawData((char*)data, len);
|
|
fromExportData(ba);
|
|
setIntName(rmslashdot(name));
|
|
} else {
|
|
msg = tr("Not an XCA Template, but '%1'").arg(nm);
|
|
}
|
|
OPENSSL_free(nm);
|
|
OPENSSL_free(header);
|
|
OPENSSL_free(data);
|
|
if (!msg.isEmpty())
|
|
my_error(msg);
|
|
}
|
|
|
|
pki_temp::~pki_temp()
|
|
{
|
|
}
|
|
|
|
bool pki_temp::compare(const pki_base *) const
|
|
{
|
|
// we don't care if templates with identical contents
|
|
// are stored in the database ...
|
|
return false;
|
|
}
|
|
|
|
QVariant pki_temp::getIcon(const dbheader *hd) const
|
|
{
|
|
return hd->id == HD_internal_name ?
|
|
QVariant(QPixmap(":templateIco")) : QVariant();
|
|
}
|