xca/lib/func.cpp
Patrick Monnerat ad6c2baae5 Add support for OpenSSL 1.1.0
The API changed heavily. New functions arrived, old functions
disappeared and many structures became opaque.

This version of the patch implements pkcs11 signing as follows:
- openssl < 1.0.0: rsa & dsa without engine
- openssl 1.0.x: rsa, dsa & ec with engine
- openssl >= 1.1.0: rsa, dsa & ec without engine

In the operation, we therefore also gain implementation of dsa signing for openssl < 1.0.0 (ec disabled because EC_KEY_METHOD was not yet invented!).

I've given up trying to use a PKEY_ENGINE with openssl 1.1: seems not possible anymore.

I've succeeded compiling the patched xca with openssl 0.9.8n, 1.0.2j and 1.1.0e.
I've successfully tested pkcs11 signing using softhsm with openssl 1.0.2j and 1.1.0e.

The patch also removes gcc7 new warnings.
2017-06-19 08:32:39 +02:00

553 lines
14 KiB
C++

/* vi: set sw=4 ts=4:
*
* Copyright (C) 2001 - 2014 Christian Hohnstaedt.
*
* All rights reserved.
*/
#include "func.h"
#include "exception.h"
#include "lib/asn1time.h"
#include "widgets/validity.h"
#include <openssl/objects.h>
#include <openssl/asn1.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#if defined(Q_WS_MAC)
#include <QDesktopServices>
#endif
#include <QDir>
#include <QFile>
#include <QStringList>
#include <QLabel>
#include <QLineEdit>
#include <QComboBox>
#include <QMessageBox>
#include <QApplication>
#include <QPushButton>
#include <QProgressBar>
#include <QTextEdit>
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#else
/* for htons() */
#include <netinet/in.h>
#endif
QPixmap *loadImg(const char *name )
{
return new QPixmap(QString(":") + name);
}
QStringList getLibExtensions()
{
QStringList l;
#if defined(_WIN32) || defined(USE_CYGWIN)
l << QString("*.dll") << QString("*.DLL");
#elif defined(Q_WS_MAC)
l << QString("*.dylib") << QString("*.so");
#else
l << QString("*.so");
#endif
return l;
}
/* returns e.g. /usr/local/share/xca for unix systems
* or HKEY_LOCAL_MACHINE->Software->xca for WIN32
* (e.g. c:\Program Files\xca )
*/
QString getPrefix()
{
#ifdef WIN32
static char inst_dir[100] = "";
char *p;
ULONG dwLength = 100;
LONG lRc;
HKEY hKey;
if (inst_dir[0] != '\0') {
/* if we already once discovered the directory just return it */
return QString(inst_dir);
}
// fallback: directory of xca.exe
GetModuleFileName(0, inst_dir, dwLength - 1);
p = strrchr(inst_dir, '\\');
if (p) {
*p = '\0';
return QString(inst_dir);
}
p = inst_dir;
*p = '\0';
lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\xca", 0, KEY_READ, &hKey);
if (lRc != ERROR_SUCCESS) {
XCA_WARN("Registry Key: 'HKEY_LOCAL_MACHINE->Software->xca' not found");
return QString(inst_dir);
}
lRc = RegQueryValueEx(hKey, "Install_Dir", NULL, NULL,
(unsigned char *)inst_dir, &dwLength);
if (lRc != ERROR_SUCCESS){
XCA_WARN("Registry Key: 'HKEY_LOCAL_MACHINE->Software->xca->Install_Dir' not found");
}
lRc = RegCloseKey(hKey);
return QString(inst_dir);
#elif defined(Q_WS_MAC)
// since this is platform-specific anyway,
// this is a more robust way to get the bundle directory
QDir bundleDir(qApp->applicationDirPath());
bundleDir.cdUp();
return bundleDir.canonicalPath() + "/Resources";
#else
#ifndef XCA_PREFIX
#define XCA_PREFIX PREFIX "/share/xca"
#endif
return QString(XCA_PREFIX);
#endif
}
QString getHomeDir()
{
QString hd;
#ifdef WIN32
LPITEMIDLIST pidl = NULL;
TCHAR buf[255] = "";
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl))) {
SHGetPathFromIDList(pidl, buf);
}
hd = buf;
#else
hd = QDir::homePath();
#endif
return hd;
}
QString getLibDir()
{
QString hd;
#ifdef WIN32
LPITEMIDLIST pidl = NULL;
TCHAR buf[255] = "";
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_SYSTEM, &pidl))) {
SHGetPathFromIDList(pidl, buf);
}
hd = buf;
#else
hd = QString("/usr/lib");
#endif
return hd;
}
QString getDocDir()
{
#if defined(WIN32) || defined (Q_WS_MAC)
return getPrefix();
#else
return QString(DOCDIR);
#endif
}
// The intent of this function is to return the proper location for
// user-controlled settings on the current platform
// i.e. PROFILE\Application Data\xca on windows, HOME/.xca on UNIX,
// ~/Library/Preferences/xca on Mac OS X
QString getUserSettingsDir()
{
QString rv;
#ifdef WIN32
LPITEMIDLIST pidl = NULL;
TCHAR buf[255] = "";
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl))) {
SHGetPathFromIDList(pidl, buf);
}
rv = buf;
rv += QDir::separator();
rv += "xca";
#elif defined(Q_WS_MAC)
rv = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
rv.insert(rv.count() - QCoreApplication::applicationName().count(),
QCoreApplication::organizationName());
#else
rv = QDir::homePath();
rv += QDir::separator();
rv += ".xca";
#endif
return rv;
}
// Qt's open and save dialogs result in some undesirable quirks.
// This function makes sure that a filename has the user-selected extension.
QString getFullFilename(const QString & filename, const QString & selectedFilter)
{
QString rv = filename.trimmed(), ext;
QRegExp rx(".* \\( ?\\*(.[a-z]{1,3}) ?\\)");
rx.indexIn(selectedFilter);
ext = rx.cap(1);
if (!ext.isEmpty() && !rv.endsWith(ext)) {
rv += ext;
}
return rv;
}
QByteArray filename2bytearray(const QString &fname)
{
#ifdef WIN32
return fname.toLocal8Bit();
#else
return fname.toUtf8();
#endif
}
QString filename2QString(const char *fname)
{
#ifdef WIN32
return QString::fromLocal8Bit(fname);
#else
return QString::fromUtf8(fname);
#endif
}
QString compressFilename(QString filename, int maxlen)
{
if (filename.length() < maxlen)
return filename;
QString fn = filename.replace("\\", "/");
int len, lastslash = fn.lastIndexOf('/');
QString base = filename.mid(lastslash);
len = base.length();
len = maxlen - len -3;
if (len < 0)
return QString("...") + base.right(maxlen -3);
fn = fn.left(len);
lastslash = fn.lastIndexOf('/');
return filename.left(lastslash+1) + "..." + base;
}
QString asn1ToQString(const ASN1_STRING *str, bool quote)
{
QString qs;
unsigned short *bmp;
int i;
if (!str)
return qs;
switch (str->type) {
case V_ASN1_BMPSTRING:
bmp = (unsigned short*)str->data;
for (i = 0; i < str->length/2; i++) {
unsigned short s = ntohs(bmp[i]);
qs += QString::fromUtf16(&s, 1);
}
break;
case V_ASN1_UTF8STRING:
qs = QString::fromUtf8((const char*)str->data, str->length);
break;
case V_ASN1_T61STRING:
qs = QString::fromLocal8Bit((const char*)str->data, str->length);
break;
default:
qs = QString::fromLatin1((const char*)str->data, str->length);
}
#if 0
printf("Convert %s (%d %d) string to '%s' len %d:", ASN1_tag2str(str->type), str->type, V_ASN1_UTF8STRING, CCHAR(qs), str->length);
for (int i=0; i< str->length; i++)
printf(" %02x", str->data[i]);
printf("\n");
#endif
if (quote)
qs.replace('\n', "\\n\\");
return qs;
}
/* returns an encoded ASN1 string from QString for a special nid*/
ASN1_STRING *QStringToAsn1(const QString s, int nid)
{
QByteArray ba = s.toUtf8();
const unsigned char *utf8 = (const unsigned char *)ba.constData();
unsigned long global_mask = ASN1_STRING_get_default_mask();
unsigned long mask = DIRSTRING_TYPE & global_mask;
ASN1_STRING *out = NULL;
ASN1_STRING_TABLE *tbl;
tbl = ASN1_STRING_TABLE_get(nid);
if (tbl) {
mask = tbl->mask;
if (!(tbl->flags & STABLE_NO_MASK))
mask &= global_mask;
}
ASN1_mbstring_copy(&out, utf8, -1, MBSTRING_UTF8, mask);
openssl_error(QString("'%1' (%2)").arg(s).arg(OBJ_nid2ln(nid)));
return out;
}
const char *OBJ_ln2sn(const char *ln)
{
return OBJ_nid2sn(OBJ_ln2nid(ln));
}
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(const ASN1_OBJECT *a, int no_name)
{
char buf[512];
int len;
len = OBJ_obj2txt(buf, 256, a, no_name);
openssl_error();
return QString::fromLatin1(buf, len);
}
QByteArray i2d_bytearray(int(*i2d)(const void*, unsigned char **),
const void *data)
{
QByteArray ba;
ba.resize(i2d(data, NULL));
unsigned char *p = (unsigned char*)ba.data();
i2d(data, &p);
openssl_error();
return ba;
}
void *d2i_bytearray(void *(*d2i)(void *, unsigned char **, long),
QByteArray &ba)
{
unsigned char *p, *p1;
void *ret;
p = p1 = (unsigned char *)ba.constData();
ret = d2i(NULL, &p1, ba.count());
ba = ba.mid(p1-p);
openssl_error();
return ret;
}
void _openssl_error(const QString txt, const char *file, int line)
{
QString error;
while (int i = ERR_get_error() ) {
error += QString(ERR_error_string(i, NULL)) + "\n";
fputs(CCHAR(QString("OpenSSL error (%1:%2) : %3\n").
arg(file).arg(line).arg(ERR_error_string(i, NULL))),
stderr);
}
if (!error.isEmpty()) {
if (!txt.isEmpty())
error = txt + "\n" + error + "\n" +
QString("(%1:%2)").arg(file).arg(line);
throw errorEx(error);
}
}
#undef PRINT_IGNORED_ANYWAY
bool _ign_openssl_error(const QString txt, const char *file, int line)
{
// ignore openssl errors
QString errtxt;
#if PRINT_IGNORED_ANYWAY
if (!txt.isEmpty() && ERR_peek_error())
fprintf(stderr, "%s\n", CCHAR(txt));
#else
(void)txt;
(void)file;
(void)line;
#endif
while (int i = ERR_get_error() ) {
errtxt = ERR_error_string(i, NULL);
#if PRINT_IGNORED_ANYWAY
fprintf(stderr, CCHAR(QString("IGNORED (%1:%2) : %3\n").
arg(file).arg(line).arg(errtxt)));
#endif
}
return !errtxt.isEmpty();
}
void inc_progress_bar(int, int, void *p)
{
QProgressBar *bar = (QProgressBar *)p;
int value = bar->value();
if (value == bar->maximum()) {
bar->reset();
} else {
bar->setValue(value +1);
}
}
static long mem_ctrl(BIO *b, int cmd, long num, void *ptr)
{
BUF_MEM *bm;
int flags;
long (*ctrl)(BIO *, int, long, void *);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
BIO_get_mem_ptr(b, &bm);
flags = BIO_get_flags(b);
ctrl = BIO_meth_get_ctrl((BIO_METHOD *) BIO_s_mem());
#else
bm = (BUF_MEM *)b->ptr;
flags = b->flags;
ctrl = BIO_s_mem()->ctrl;
#endif
if (!bm->data || !(flags & BIO_FLAGS_MEM_RDONLY))
return ctrl(b, cmd, num, ptr);
switch (cmd) {
case BIO_C_FILE_SEEK:
if (num > (long)bm->max)
num = bm->max;
bm->data -= (bm->max - bm->length) - num;
bm->length = bm->max - num;
/* fallthrough */
case BIO_C_FILE_TELL:
return bm->max - bm->length;
}
return ctrl(b, cmd, num, ptr);
}
BIO_METHOD *BIO_METHOD_copy(const BIO_METHOD *src, BIO_METHOD *dst = NULL)
{
if (src)
return NULL;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (!dst) {
// The only way to get the type and name of the source method is
// to bind it to a BIO.
BIO *bio = BIO_new(src);
if (!bio)
return NULL;
if (!(dst = BIO_meth_new(BIO_method_type(bio), BIO_method_name(bio)))) {
BIO_free(bio);
return NULL;
}
BIO_free(bio);
}
BIO_meth_set_write(dst, BIO_meth_get_write((BIO_METHOD *) src));
BIO_meth_set_read(dst, BIO_meth_get_read((BIO_METHOD *) src));
BIO_meth_set_puts(dst, BIO_meth_get_puts((BIO_METHOD *) src));
BIO_meth_set_gets(dst, BIO_meth_get_gets((BIO_METHOD *) src));
BIO_meth_set_ctrl(dst, BIO_meth_get_ctrl((BIO_METHOD *) src));
BIO_meth_set_create(dst, BIO_meth_get_create((BIO_METHOD *) src));
BIO_meth_set_destroy(dst, BIO_meth_get_destroy((BIO_METHOD *) src));
BIO_meth_set_callback_ctrl(dst,
BIO_meth_get_callback_ctrl((BIO_METHOD *) src));
#else
if (!dst)
if (!(dst = (BIO_METHOD *) OPENSSL_malloc(sizeof *dst)))
return NULL;
*dst = *src;
#endif
return dst;
}
BIO_METHOD *BIO_METHOD_seekable_romem()
{
static BIO_METHOD *mymeth = NULL;
if (!mymeth) {
mymeth = BIO_METHOD_copy(BIO_s_mem());
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
BIO_meth_set_ctrl(mymeth, mem_ctrl);
#else
mymeth->ctrl = mem_ctrl;
#endif
}
return mymeth;
}
BIO *BIO_QBA_mem_buf(QByteArray &a)
{
BIO *bio;
BIO_METHOD *meth = BIO_METHOD_seekable_romem();
if (!meth)
return NULL;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
bio = BIO_new(meth);
if (bio) {
BUF_MEM bm;
bm.data = a.data();
bm.length = a.size();
bm.max = a.size();
BIO_set_mem_buf(bio, &bm, BIO_CLOSE);
BIO_set_flags(bio, BIO_get_flags(bio) | BIO_FLAGS_MEM_RDONLY);
}
#else
bio = BIO_new_mem_buf(a.data(), a.size());
if (bio)
bio->method = meth;
#endif
return bio;
}
bool translate_dn = false;
QMap<int, QString> dn_translations;
void dn_translations_setup()
{
dn_translations[NID_countryName] = QObject::tr("Country code");
dn_translations[NID_stateOrProvinceName] = QObject::tr("State or Province");
dn_translations[NID_localityName] = QObject::tr("Locality");
dn_translations[NID_organizationName] = QObject::tr("Organisation");
dn_translations[NID_organizationalUnitName] = QObject::tr("Organisational unit");
dn_translations[NID_commonName] = QObject::tr("Common name");
dn_translations[NID_pkcs9_emailAddress] = QObject::tr("E-Mail address");
dn_translations[NID_serialNumber] = QObject::tr("Serial number");
dn_translations[NID_givenName] = QObject::tr("Given name");
dn_translations[NID_surname] = QObject::tr("Surname");
dn_translations[NID_title] = QObject::tr("Title");
dn_translations[NID_initials] = QObject::tr("Initials");
dn_translations[NID_description] = QObject::tr("Description");
dn_translations[NID_role] = QObject::tr("Role");
dn_translations[NID_pseudonym] = QObject::tr("Pseudonym");
dn_translations[NID_generationQualifier] = QObject::tr("Generation Qualifier");
dn_translations[NID_x500UniqueIdentifier] = QObject::tr("x500 Unique Identifier");
dn_translations[NID_name] = QObject::tr("Name");
dn_translations[NID_dnQualifier] = QObject::tr("DN Qualifier");
dn_translations[NID_pkcs9_unstructuredName] = QObject::tr("Unstructured name");
dn_translations[NID_pkcs9_challengePassword] = QObject::tr("Challenge password");
dn_translations[NID_basic_constraints] = QObject::tr("Basic Constraints");
dn_translations[NID_subject_alt_name] = QObject::tr("Subject alternative name");
dn_translations[NID_issuer_alt_name] = QObject::tr("issuer alternative name");
dn_translations[NID_subject_key_identifier] = QObject::tr("Subject key identifier");
dn_translations[NID_authority_key_identifier] = QObject::tr("Authority key identifier");
dn_translations[NID_key_usage] = QObject::tr("Key usage");
dn_translations[NID_ext_key_usage] = QObject::tr("Extended key usage");
dn_translations[NID_crl_distribution_points] = QObject::tr("CRL distribution points");
dn_translations[NID_info_access] = QObject::tr("Authority information access");
dn_translations[NID_netscape_cert_type] = QObject::tr("Certificate type");
dn_translations[NID_netscape_base_url] = QObject::tr("Base URL");
dn_translations[NID_netscape_revocation_url] = QObject::tr("Revocation URL");
dn_translations[NID_netscape_ca_revocation_url] = QObject::tr("CA Revocation URL");
dn_translations[NID_netscape_renewal_url] = QObject::tr("Certificate renewal URL");
dn_translations[NID_netscape_ca_policy_url] = QObject::tr("CA policy URL");
dn_translations[NID_netscape_ssl_server_name] = QObject::tr("SSL server name");
dn_translations[NID_netscape_comment] = QObject::tr("Comment");
}