xca/lib/pki_temp.cpp
2018-03-05 07:46:10 +01:00

527 lines
12 KiB
C++

/* vi: set sw=4 ts=4:
*
* Copyright (C) 2001 - 2015 Christian Hohnstaedt.
*
* All rights reserved.
*/
#include "pki_temp.h"
#include "func.h"
#include "db.h"
#include "exception.h"
#include "widgets/MainWindow.h"
#include <QDir>
#include <QBuffer>
#include <QDataStream>
#define TEMPLATE_DS_VERSION (QDataStream::Qt_4_2)
QList<QString> pki_temp::tmpl_keys = QList<QString>()
<< "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";
QPixmap *pki_temp::icon = NULL;
pki_temp::pki_temp(const pki_temp *pk)
:pki_x509name(pk->desc)
{
pkiType = pk->pkiType;
pre_defined = false;
xname = pk->xname;
settings = pk->settings;
}
pki_temp::pki_temp(const QString d)
:pki_x509name(d)
{
pkiType = tmpl;
pre_defined = false;
foreach(QString key, tmpl_keys) {
settings[key] = QString();
}
settings["nsComment"] = "xca certificate";
settings["validN"] = "365";
}
const char *pki_temp::getClassName() const
{
return "pki_temp";
}
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(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)
{
/*
* 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, MainWindow::explicit_dn) {
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";
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 keyUseCritical;
settings["keyUse"] = QString::number(
bitsToInt(el, NID_key_usage, &keyUseCritical));
settings["keyUseCritical"] = keyUseCritical ? "1" : "0";
fromExtList(&el, NID_ext_key_usage, "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;
}
void pki_temp::fromData(const unsigned char *p, db_header_t *head )
{
int version, size;
size = head->len - sizeof(db_header_t);
version = head->version;
fromData(p, size, version);
}
static QString old_eKeyUse2QString(int old)
{
QStringList sl;
NIDlist eku_nid = *MainWindow::eku_nid;
for (int i=0; i<eku_nid.count(); i++) {
if (old & (1<<i)) {
sl << OBJ_nid2sn(eku_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["keyUseCritical"] = QString::number(db::boolFromData(ba));
settings["eKyUseCritical"] = 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.count() > 0) {
my_error(tr("Wrong Size %1").arg(ba.count()));
}
}
QByteArray pki_temp::toData()
{
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;
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()
{
QByteArray data, header;
data = toData();
header = db::intToData(data.count());
header += db::intToData(TMPL_VERSION);
header += data;
return header;
}
void pki_temp::writeTemp(QString fname)
{
FILE *fp = fopen_write(fname);
if (fp == NULL) {
fopen_error(fname);
return;
}
fwrite_ba(fp, toExportData(), fname);
fclose(fp);
}
void pki_temp::writeDefault(const QString fname)
{
writeTemp(fname + QDir::separator() + getIntName() + ".xca");
}
BIO *pki_temp::pem(BIO *b, int format)
{
(void)format;
QByteArray ba = toExportData();
if (!b)
b = BIO_new(BIO_s_mem());
#if OPENSSL_VERSION_NUMBER < 0x10002000L
PEM_write_bio(b, PEM_STRING_XCA_TEMPLATE, (char*)"",
(unsigned char*)(ba.data()), ba.size());
#else
PEM_write_bio(b, PEM_STRING_XCA_TEMPLATE, "",
(const unsigned char*)(ba.constData()), ba.size());
#endif
pki_openssl_error();
return b;
}
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(QString fname, const char *mode)
{
FILE *fp = fopen(QString2filename(fname), mode);
char buf[4096];
QByteArray ba;
BIO *b;
if (fp == NULL) {
fopen_error(fname);
return;
}
b = BIO_new(BIO_s_file());
pki_openssl_error();
BIO_set_fp(b,fp,BIO_NOCLOSE);
try {
fromPEM_BIO(b, fname);
BIO_free(b);
return;
} catch (errorEx &err) {
BIO_free(b);
fseek(fp, 0, SEEK_SET);
}
while (1) {
size_t ret = fread(buf, 1, sizeof buf, fp);
ba.append(buf, ret);
if (ret < sizeof buf)
break;
}
int err = ferror(fp);
fclose(fp);
if (err) {
my_error(tr("Template file content error (too small): %1").
arg(fname));
}
fromExportData(ba);
setIntName(rmslashdot(fname));
}
void pki_temp::fload(QString fname)
{
try {
try_fload(fname, "rb");
} catch (errorEx &err) {
#if defined(_WIN32)
/* Try again in ascii mode on Windows
* to support pre 1.1.0 template exports */
try_fload(fname, "r");
#else
throw err;
#endif
}
}
void pki_temp::fromPEM_BIO(BIO *bio, 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(pki_base *)
{
// we don't care if templates with identical contents
// are stored in the database ...
return false;
}
QVariant pki_temp::getIcon(dbheader *hd)
{
return hd->id == HD_internal_name ? QVariant(*icon) : QVariant();
}