diff options
Diffstat (limited to 'kio/kssl/ksslcertificate.cc')
-rw-r--r-- | kio/kssl/ksslcertificate.cc | 1141 |
1 files changed, 1141 insertions, 0 deletions
diff --git a/kio/kssl/ksslcertificate.cc b/kio/kssl/ksslcertificate.cc new file mode 100644 index 000000000..aca7a7ea7 --- /dev/null +++ b/kio/kssl/ksslcertificate.cc @@ -0,0 +1,1141 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2000-2003 George Staikos <staikos@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + + +#include <unistd.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qfile.h> + +#include "kssldefs.h" +#include "ksslcertificate.h" +#include "ksslcertchain.h" +#include "ksslutils.h" + +#include <kstandarddirs.h> +#include <kmdcodec.h> +#include <klocale.h> +#include <qdatetime.h> +#include <ktempfile.h> + +#include <sys/types.h> + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +// this hack provided by Malte Starostik to avoid glibc/openssl bug +// on some systems +#ifdef KSSL_HAVE_SSL +#define crypt _openssl_crypt +#include <openssl/ssl.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/x509_vfy.h> +#include <openssl/pem.h> +#undef crypt +#endif + +#include <kopenssl.h> +#include <qcstring.h> +#include <kdebug.h> +#include "ksslx509v3.h" + + + +static char hv[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + + +class KSSLCertificatePrivate { +public: + KSSLCertificatePrivate() { + kossl = KOSSL::self(); + _lastPurpose = KSSLCertificate::None; + } + + ~KSSLCertificatePrivate() { + } + + KSSLCertificate::KSSLValidation m_stateCache; + bool m_stateCached; + #ifdef KSSL_HAVE_SSL + X509 *m_cert; + #endif + KOSSL *kossl; + KSSLCertChain _chain; + KSSLX509V3 _extensions; + KSSLCertificate::KSSLPurpose _lastPurpose; +}; + +KSSLCertificate::KSSLCertificate() { + d = new KSSLCertificatePrivate; + d->m_stateCached = false; + KGlobal::dirs()->addResourceType("kssl", KStandardDirs::kde_default("data") + "kssl"); + #ifdef KSSL_HAVE_SSL + d->m_cert = NULL; + #endif +} + + +KSSLCertificate::KSSLCertificate(const KSSLCertificate& x) { + d = new KSSLCertificatePrivate; + d->m_stateCached = false; + KGlobal::dirs()->addResourceType("kssl", KStandardDirs::kde_default("data") + "kssl"); + #ifdef KSSL_HAVE_SSL + d->m_cert = NULL; + setCert(KOSSL::self()->X509_dup(const_cast<KSSLCertificate&>(x).getCert())); + KSSLCertChain *c = x.d->_chain.replicate(); + setChain(c->rawChain()); + delete c; + #endif +} + + + +KSSLCertificate::~KSSLCertificate() { +#ifdef KSSL_HAVE_SSL + if (d->m_cert) + d->kossl->X509_free(d->m_cert); +#endif + delete d; +} + + +KSSLCertChain& KSSLCertificate::chain() { + return d->_chain; +} + + +KSSLCertificate *KSSLCertificate::fromX509(X509 *x5) { +KSSLCertificate *n = NULL; +#ifdef KSSL_HAVE_SSL + if (x5) { + n = new KSSLCertificate; + n->setCert(KOSSL::self()->X509_dup(x5)); + } +#endif +return n; +} + + +KSSLCertificate *KSSLCertificate::fromString(QCString cert) { +KSSLCertificate *n = NULL; +#ifdef KSSL_HAVE_SSL + if (cert.length() == 0) + return NULL; + + QByteArray qba, qbb = cert.copy(); + KCodecs::base64Decode(qbb, qba); + unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data()); + X509 *x5c = KOSSL::self()->d2i_X509(NULL, &qbap, qba.size()); + if (!x5c) { + return NULL; + } + + n = new KSSLCertificate; + n->setCert(x5c); +#endif +return n; +} + + + +QString KSSLCertificate::getSubject() const { +QString rc = ""; + +#ifdef KSSL_HAVE_SSL + char *t = d->kossl->X509_NAME_oneline(d->kossl->X509_get_subject_name(d->m_cert), 0, 0); + if (!t) + return rc; + rc = t; + d->kossl->OPENSSL_free(t); +#endif +return rc; +} + + +QString KSSLCertificate::getSerialNumber() const { +QString rc = ""; + +#ifdef KSSL_HAVE_SSL + ASN1_INTEGER *aint = d->kossl->X509_get_serialNumber(d->m_cert); + if (aint) { + rc = ASN1_INTEGER_QString(aint); + // d->kossl->ASN1_INTEGER_free(aint); this makes the sig test fail + } +#endif +return rc; +} + + +QString KSSLCertificate::getSignatureText() const { +QString rc = ""; + +#ifdef KSSL_HAVE_SSL +char *s; +int n, i; + + i = d->kossl->OBJ_obj2nid(d->m_cert->sig_alg->algorithm); + rc = i18n("Signature Algorithm: "); + rc += (i == NID_undef)?i18n("Unknown"):QString(d->kossl->OBJ_nid2ln(i)); + + rc += "\n"; + rc += i18n("Signature Contents:"); + n = d->m_cert->signature->length; + s = (char *)d->m_cert->signature->data; + for (i = 0; i < n; i++) { + if (i%20 != 0) rc += ":"; + else rc += "\n"; + rc.append(hv[(s[i]&0xf0)>>4]); + rc.append(hv[s[i]&0x0f]); + } + +#endif + +return rc; +} + + +void KSSLCertificate::getEmails(QStringList &to) const { + to.clear(); +#ifdef KSSL_HAVE_SSL + if (!d->m_cert) + return; + + STACK *s = d->kossl->X509_get1_email(d->m_cert); + if (s) { + for(int n=0; n < s->num; n++) { + to.append(d->kossl->sk_value(s,n)); + } + d->kossl->X509_email_free(s); + } +#endif +} + + +QString KSSLCertificate::getKDEKey() const { + return getSubject() + " (" + getMD5DigestText() + ")"; +} + + +QString KSSLCertificate::getMD5DigestFromKDEKey(const QString &k) { + QString rc; + int pos = k.findRev('('); + if (pos != -1) { + unsigned int len = k.length(); + if (k.at(len-1) == ')') { + rc = k.mid(pos+1, len-pos-2); + } + } + return rc; +} + + +QString KSSLCertificate::getMD5DigestText() const { +QString rc = ""; + +#ifdef KSSL_HAVE_SSL + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; + + if (!d->kossl->X509_digest(d->m_cert, d->kossl->EVP_md5(), md, &n)) { + return rc; + } + + for (unsigned int j = 0; j < n; j++) { + if (j > 0) + rc += ":"; + rc.append(hv[(md[j]&0xf0)>>4]); + rc.append(hv[md[j]&0x0f]); + } + +#endif + +return rc; +} + + + +QString KSSLCertificate::getMD5Digest() const { +QString rc = ""; + +#ifdef KSSL_HAVE_SSL + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; + + if (!d->kossl->X509_digest(d->m_cert, d->kossl->EVP_md5(), md, &n)) { + return rc; + } + + for (unsigned int j = 0; j < n; j++) { + rc.append(hv[(md[j]&0xf0)>>4]); + rc.append(hv[md[j]&0x0f]); + } + +#endif + +return rc; +} + + + +QString KSSLCertificate::getKeyType() const { +QString rc = ""; + +#ifdef KSSL_HAVE_SSL + EVP_PKEY *pkey = d->kossl->X509_get_pubkey(d->m_cert); + if (pkey) { + #ifndef NO_RSA + if (pkey->type == EVP_PKEY_RSA) + rc = "RSA"; + else + #endif + #ifndef NO_DSA + if (pkey->type == EVP_PKEY_DSA) + rc = "DSA"; + else + #endif + rc = "Unknown"; + d->kossl->EVP_PKEY_free(pkey); + } +#endif + +return rc; +} + + + +QString KSSLCertificate::getPublicKeyText() const { +QString rc = ""; +char *x = NULL; + +#ifdef KSSL_HAVE_SSL + EVP_PKEY *pkey = d->kossl->X509_get_pubkey(d->m_cert); + if (pkey) { + rc = i18n("Unknown", "Unknown key algorithm"); + #ifndef NO_RSA + if (pkey->type == EVP_PKEY_RSA) { + rc = i18n("Key type: RSA (%1 bit)") + "\n"; + + x = d->kossl->BN_bn2hex(pkey->pkey.rsa->n); + rc += i18n("Modulus: "); + rc = rc.arg(strlen(x)*4); + for (unsigned int i = 0; i < strlen(x); i++) { + if (i%40 != 0 && i%2 == 0) + rc += ":"; + else if (i%40 == 0) + rc += "\n"; + rc += x[i]; + } + rc += "\n"; + d->kossl->OPENSSL_free(x); + + x = d->kossl->BN_bn2hex(pkey->pkey.rsa->e); + rc += i18n("Exponent: 0x") + x + "\n"; + d->kossl->OPENSSL_free(x); + } + #endif + #ifndef NO_DSA + if (pkey->type == EVP_PKEY_DSA) { + rc = i18n("Key type: DSA (%1 bit)") + "\n"; + + x = d->kossl->BN_bn2hex(pkey->pkey.dsa->p); + rc += i18n("Prime: "); + // hack - this may not be always accurate + rc = rc.arg(strlen(x)*4) ; + for (unsigned int i = 0; i < strlen(x); i++) { + if (i%40 != 0 && i%2 == 0) + rc += ":"; + else if (i%40 == 0) + rc += "\n"; + rc += x[i]; + } + rc += "\n"; + d->kossl->OPENSSL_free(x); + + x = d->kossl->BN_bn2hex(pkey->pkey.dsa->q); + rc += i18n("160 bit prime factor: "); + for (unsigned int i = 0; i < strlen(x); i++) { + if (i%40 != 0 && i%2 == 0) + rc += ":"; + else if (i%40 == 0) + rc += "\n"; + rc += x[i]; + } + rc += "\n"; + d->kossl->OPENSSL_free(x); + + x = d->kossl->BN_bn2hex(pkey->pkey.dsa->g); + rc += QString("g: "); + for (unsigned int i = 0; i < strlen(x); i++) { + if (i%40 != 0 && i%2 == 0) + rc += ":"; + else if (i%40 == 0) + rc += "\n"; + rc += x[i]; + } + rc += "\n"; + d->kossl->OPENSSL_free(x); + + x = d->kossl->BN_bn2hex(pkey->pkey.dsa->pub_key); + rc += i18n("Public key: "); + for (unsigned int i = 0; i < strlen(x); i++) { + if (i%40 != 0 && i%2 == 0) + rc += ":"; + else if (i%40 == 0) + rc += "\n"; + rc += x[i]; + } + rc += "\n"; + d->kossl->OPENSSL_free(x); + } + #endif + d->kossl->EVP_PKEY_free(pkey); + } +#endif + +return rc; +} + + + +QString KSSLCertificate::getIssuer() const { +QString rc = ""; + +#ifdef KSSL_HAVE_SSL + char *t = d->kossl->X509_NAME_oneline(d->kossl->X509_get_issuer_name(d->m_cert), 0, 0); + + if (!t) + return rc; + + rc = t; + d->kossl->OPENSSL_free(t); +#endif + +return rc; +} + +void KSSLCertificate::setChain(void *c) { +#ifdef KSSL_HAVE_SSL + d->_chain.setChain(c); +#endif + d->m_stateCached = false; + d->m_stateCache = KSSLCertificate::Unknown; +} + +void KSSLCertificate::setCert(X509 *c) { +#ifdef KSSL_HAVE_SSL +d->m_cert = c; +if (c) { + d->_extensions.flags = 0; + d->kossl->X509_check_purpose(c, -1, 0); // setup the fields (!!) + +#if 0 + kdDebug(7029) << "---------------- Certificate ------------------" + << endl; + kdDebug(7029) << getSubject() << endl; +#endif + + for (int j = 0; j < d->kossl->X509_PURPOSE_get_count(); j++) { + X509_PURPOSE *ptmp = d->kossl->X509_PURPOSE_get0(j); + int id = d->kossl->X509_PURPOSE_get_id(ptmp); + for (int ca = 0; ca < 2; ca++) { + int idret = d->kossl->X509_check_purpose(c, id, ca); + if (idret == 1 || idret == 2) { // have it +// kdDebug() << "PURPOSE: " << id << (ca?" CA":"") << endl; + if (!ca) + d->_extensions.flags |= (1L <<(id-1)); + else d->_extensions.flags |= (1L <<(16+id-1)); + } else { + if (!ca) + d->_extensions.flags &= ~(1L <<(id-1)); + else d->_extensions.flags &= ~(1L <<(16+id-1)); + } + } + } + +#if 0 + kdDebug(7029) << "flags: " << QString::number(c->ex_flags, 2) + << "\nkeyusage: " << QString::number(c->ex_kusage, 2) + << "\nxkeyusage: " << QString::number(c->ex_xkusage, 2) + << "\nnscert: " << QString::number(c->ex_nscert, 2) + << endl; + if (c->ex_flags & EXFLAG_KUSAGE) + kdDebug(7029) << " --- Key Usage extensions found" << endl; + else kdDebug(7029) << " --- Key Usage extensions NOT found" << endl; + + if (c->ex_flags & EXFLAG_XKUSAGE) + kdDebug(7029) << " --- Extended key usage extensions found" << endl; + else kdDebug(7029) << " --- Extended key usage extensions NOT found" << endl; + + if (c->ex_flags & EXFLAG_NSCERT) + kdDebug(7029) << " --- NS extensions found" << endl; + else kdDebug(7029) << " --- NS extensions NOT found" << endl; + + if (d->_extensions.certTypeSSLCA()) + kdDebug(7029) << "NOTE: this is an SSL CA file." << endl; + else kdDebug(7029) << "NOTE: this is NOT an SSL CA file." << endl; + + if (d->_extensions.certTypeEmailCA()) + kdDebug(7029) << "NOTE: this is an EMAIL CA file." << endl; + else kdDebug(7029) << "NOTE: this is NOT an EMAIL CA file." << endl; + + if (d->_extensions.certTypeCodeCA()) + kdDebug(7029) << "NOTE: this is a CODE CA file." << endl; + else kdDebug(7029) << "NOTE: this is NOT a CODE CA file." << endl; + + if (d->_extensions.certTypeSSLClient()) + kdDebug(7029) << "NOTE: this is an SSL client." << endl; + else kdDebug(7029) << "NOTE: this is NOT an SSL client." << endl; + + if (d->_extensions.certTypeSSLServer()) + kdDebug(7029) << "NOTE: this is an SSL server." << endl; + else kdDebug(7029) << "NOTE: this is NOT an SSL server." << endl; + + if (d->_extensions.certTypeNSSSLServer()) + kdDebug(7029) << "NOTE: this is a NETSCAPE SSL server." << endl; + else kdDebug(7029) << "NOTE: this is NOT a NETSCAPE SSL server." << endl; + + if (d->_extensions.certTypeSMIME()) + kdDebug(7029) << "NOTE: this is an SMIME certificate." << endl; + else kdDebug(7029) << "NOTE: this is NOT an SMIME certificate." << endl; + + if (d->_extensions.certTypeSMIMEEncrypt()) + kdDebug(7029) << "NOTE: this is an SMIME encrypt cert." << endl; + else kdDebug(7029) << "NOTE: this is NOT an SMIME encrypt cert." << endl; + + if (d->_extensions.certTypeSMIMESign()) + kdDebug(7029) << "NOTE: this is an SMIME sign cert." << endl; + else kdDebug(7029) << "NOTE: this is NOT an SMIME sign cert." << endl; + + if (d->_extensions.certTypeCRLSign()) + kdDebug(7029) << "NOTE: this is a CRL signer." << endl; + else kdDebug(7029) << "NOTE: this is NOT a CRL signer." << endl; + + kdDebug(7029) << "-----------------------------------------------" + << endl; +#endif +} +#endif +d->m_stateCached = false; +d->m_stateCache = KSSLCertificate::Unknown; +} + +X509 *KSSLCertificate::getCert() { +#ifdef KSSL_HAVE_SSL + return d->m_cert; +#endif +return 0; +} + +// pull in the callback. It's common across multiple files but we want +// it to be hidden. + +#include "ksslcallback.c" + + +bool KSSLCertificate::isValid(KSSLCertificate::KSSLPurpose p) { + return (validate(p) == KSSLCertificate::Ok); +} + + +bool KSSLCertificate::isValid() { + return isValid(KSSLCertificate::SSLServer); +} + + +int KSSLCertificate::purposeToOpenSSL(KSSLCertificate::KSSLPurpose p) const { +int rc = 0; +#ifdef KSSL_HAVE_SSL + if (p == KSSLCertificate::SSLServer) { + rc = X509_PURPOSE_SSL_SERVER; + } else if (p == KSSLCertificate::SSLClient) { + rc = X509_PURPOSE_SSL_CLIENT; + } else if (p == KSSLCertificate::SMIMEEncrypt) { + rc = X509_PURPOSE_SMIME_ENCRYPT; + } else if (p == KSSLCertificate::SMIMESign) { + rc = X509_PURPOSE_SMIME_SIGN; + } else if (p == KSSLCertificate::Any) { + rc = X509_PURPOSE_ANY; + } +#endif +return rc; +} + + +// For backward compatibility +KSSLCertificate::KSSLValidation KSSLCertificate::validate() { + return validate(KSSLCertificate::SSLServer); +} + +KSSLCertificate::KSSLValidation KSSLCertificate::validate(KSSLCertificate::KSSLPurpose purpose) +{ + KSSLValidationList result = validateVerbose(purpose); + if (result.isEmpty()) + return KSSLCertificate::Ok; + else + return result.first(); +} + +// +// See apps/verify.c in OpenSSL for the source of most of this logic. +// + +// CRL files? we don't do that yet +KSSLCertificate::KSSLValidationList KSSLCertificate::validateVerbose(KSSLCertificate::KSSLPurpose purpose) +{ + return validateVerbose(purpose, 0); +} + +KSSLCertificate::KSSLValidationList KSSLCertificate::validateVerbose(KSSLCertificate::KSSLPurpose purpose, KSSLCertificate *ca) +{ + KSSLValidationList errors; + if (ca || (d->_lastPurpose != purpose)) { + d->m_stateCached = false; + } + + if (!d->m_stateCached) + d->_lastPurpose = purpose; + +#ifdef KSSL_HAVE_SSL + X509_STORE *certStore; + X509_LOOKUP *certLookup; + X509_STORE_CTX *certStoreCTX; + int rc = 0; + + if (!d->m_cert) + { + errors << KSSLCertificate::Unknown; + return errors; + } + + if (d->m_stateCached) { + errors << d->m_stateCache; + return errors; + } + + QStringList qsl = KGlobal::dirs()->resourceDirs("kssl"); + + if (qsl.isEmpty()) { + errors << KSSLCertificate::NoCARoot; + return errors; + } + + KSSLCertificate::KSSLValidation ksslv = Unknown; + + for (QStringList::Iterator j = qsl.begin(); j != qsl.end(); ++j) { + struct stat sb; + QString _j = (*j) + "ca-bundle.crt"; + if (-1 == stat(_j.ascii(), &sb)) { + continue; + } + + certStore = d->kossl->X509_STORE_new(); + if (!certStore) { + errors << KSSLCertificate::Unknown; + return errors; + } + + X509_STORE_set_verify_cb_func(certStore, X509Callback); + + certLookup = d->kossl->X509_STORE_add_lookup(certStore, d->kossl->X509_LOOKUP_file()); + if (!certLookup) { + ksslv = KSSLCertificate::Unknown; + d->kossl->X509_STORE_free(certStore); + continue; + } + + if (!d->kossl->X509_LOOKUP_load_file(certLookup, _j.ascii(), X509_FILETYPE_PEM)) { + // error accessing directory and loading pems + kdDebug(7029) << "KSSL couldn't read CA root: " + << _j << endl; + ksslv = KSSLCertificate::ErrorReadingRoot; + d->kossl->X509_STORE_free(certStore); + continue; + } + + // This is the checking code + certStoreCTX = d->kossl->X509_STORE_CTX_new(); + + // this is a bad error - could mean no free memory. + // This may be the wrong thing to do here + if (!certStoreCTX) { + kdDebug(7029) << "KSSL couldn't create an X509 store context." << endl; + d->kossl->X509_STORE_free(certStore); + continue; + } + + d->kossl->X509_STORE_CTX_init(certStoreCTX, certStore, d->m_cert, NULL); + if (d->_chain.isValid()) { + d->kossl->X509_STORE_CTX_set_chain(certStoreCTX, (STACK_OF(X509)*)d->_chain.rawChain()); + } + + //kdDebug(7029) << "KSSL setting CRL.............." << endl; + // int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x); + + d->kossl->X509_STORE_CTX_set_purpose(certStoreCTX, purposeToOpenSSL(purpose)); + + KSSL_X509CallBack_ca = ca ? ca->d->m_cert : 0; + KSSL_X509CallBack_ca_found = false; + + certStoreCTX->error = X509_V_OK; + rc = d->kossl->X509_verify_cert(certStoreCTX); + int errcode = certStoreCTX->error; + if (ca && !KSSL_X509CallBack_ca_found) { + ksslv = KSSLCertificate::Irrelevant; + } else { + ksslv = processError(errcode); + } + // For servers, we can try NS_SSL_SERVER too + if ( (ksslv != KSSLCertificate::Ok) && + (ksslv != KSSLCertificate::Irrelevant) && + purpose == KSSLCertificate::SSLServer) { + d->kossl->X509_STORE_CTX_set_purpose(certStoreCTX, + X509_PURPOSE_NS_SSL_SERVER); + + certStoreCTX->error = X509_V_OK; + rc = d->kossl->X509_verify_cert(certStoreCTX); + errcode = certStoreCTX->error; + ksslv = processError(errcode); + } + d->kossl->X509_STORE_CTX_free(certStoreCTX); + d->kossl->X509_STORE_free(certStore); + // end of checking code + // + + //kdDebug(7029) << "KSSL Validation procedure RC: " + // << rc << endl; + //kdDebug(7029) << "KSSL Validation procedure errcode: " + // << errcode << endl; + //kdDebug(7029) << "KSSL Validation procedure RESULTS: " + // << ksslv << endl; + + if (ksslv != NoCARoot && ksslv != InvalidCA) { + d->m_stateCached = true; + d->m_stateCache = ksslv; + } + break; + } + + if (ksslv != KSSLCertificate::Ok) + errors << ksslv; +#else + errors << KSSLCertificate::NoSSL; +#endif + return errors; +} + + + +KSSLCertificate::KSSLValidation KSSLCertificate::revalidate() { + return revalidate(KSSLCertificate::SSLServer); +} + + +KSSLCertificate::KSSLValidation KSSLCertificate::revalidate(KSSLCertificate::KSSLPurpose p) { + d->m_stateCached = false; + return validate(p); +} + + +KSSLCertificate::KSSLValidation KSSLCertificate::processError(int ec) { +KSSLCertificate::KSSLValidation rc; + +rc = KSSLCertificate::Unknown; +#ifdef KSSL_HAVE_SSL + switch (ec) { + case X509_V_OK: // OK + rc = KSSLCertificate::Ok; + break; + + + case X509_V_ERR_CERT_REJECTED: + rc = KSSLCertificate::Rejected; + break; + + + case X509_V_ERR_CERT_UNTRUSTED: + rc = KSSLCertificate::Untrusted; + break; + + + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + rc = KSSLCertificate::SignatureFailed; + break; + + case X509_V_ERR_INVALID_CA: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + rc = KSSLCertificate::InvalidCA; + break; + + + case X509_V_ERR_INVALID_PURPOSE: + rc = KSSLCertificate::InvalidPurpose; + break; + + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + rc = KSSLCertificate::SelfSigned; + break; + + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + rc = KSSLCertificate::SelfSignedChain; + break; + + case X509_V_ERR_CERT_REVOKED: + rc = KSSLCertificate::Revoked; + break; + + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + rc = KSSLCertificate::PathLengthExceeded; + break; + + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_CRL_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + rc = KSSLCertificate::Expired; + kdDebug(7029) << "KSSL apparently this is expired. Not after: " + << getNotAfter() << endl; + break; + + //case 1: + case X509_V_ERR_APPLICATION_VERIFICATION: + case X509_V_ERR_OUT_OF_MEM: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + default: + rc = KSSLCertificate::Unknown; + break; +} + +d->m_stateCache = rc; +d->m_stateCached = true; +#endif +return rc; +} + + +QString KSSLCertificate::getNotBefore() const { +#ifdef KSSL_HAVE_SSL +return ASN1_UTCTIME_QString(X509_get_notBefore(d->m_cert)); +#else +return QString::null; +#endif +} + + +QString KSSLCertificate::getNotAfter() const { +#ifdef KSSL_HAVE_SSL +return ASN1_UTCTIME_QString(X509_get_notAfter(d->m_cert)); +#else +return QString::null; +#endif +} + + +QDateTime KSSLCertificate::getQDTNotBefore() const { +#ifdef KSSL_HAVE_SSL +return ASN1_UTCTIME_QDateTime(X509_get_notBefore(d->m_cert), NULL); +#else +return QDateTime::currentDateTime(); +#endif +} + + +QDateTime KSSLCertificate::getQDTNotAfter() const { +#ifdef KSSL_HAVE_SSL +return ASN1_UTCTIME_QDateTime(X509_get_notAfter(d->m_cert), NULL); +#else +return QDateTime::currentDateTime(); +#endif +} + + +int operator==(KSSLCertificate &x, KSSLCertificate &y) { +#ifndef KSSL_HAVE_SSL + return 1; +#else + if (!KOSSL::self()->X509_cmp(x.getCert(), y.getCert())) return 1; + return 0; +#endif +} + + +KSSLCertificate *KSSLCertificate::replicate() { +// The new certificate doesn't have the cached value. It's probably +// better this way. We can't anticipate every reason for doing this. +KSSLCertificate *newOne = new KSSLCertificate(); +#ifdef KSSL_HAVE_SSL + newOne->setCert(d->kossl->X509_dup(getCert())); + KSSLCertChain *c = d->_chain.replicate(); + newOne->setChain(c->rawChain()); + delete c; +#endif +return newOne; +} + + +QString KSSLCertificate::toString() { +return KCodecs::base64Encode(toDer()); +} + + +QString KSSLCertificate::verifyText(KSSLValidation x) { +switch (x) { +case KSSLCertificate::Ok: + return i18n("The certificate is valid."); +case KSSLCertificate::PathLengthExceeded: +case KSSLCertificate::ErrorReadingRoot: +case KSSLCertificate::NoCARoot: + return i18n("Certificate signing authority root files could not be found so the certificate is not verified."); +case KSSLCertificate::SelfSignedChain: +case KSSLCertificate::InvalidCA: + return i18n("Certificate signing authority is unknown or invalid."); +case KSSLCertificate::SelfSigned: + return i18n("Certificate is self-signed and thus may not be trustworthy."); +case KSSLCertificate::Expired: + return i18n("Certificate has expired."); +case KSSLCertificate::Revoked: + return i18n("Certificate has been revoked."); +case KSSLCertificate::NoSSL: + return i18n("SSL support was not found."); +case KSSLCertificate::Untrusted: + return i18n("Signature is untrusted."); +case KSSLCertificate::SignatureFailed: + return i18n("Signature test failed."); +case KSSLCertificate::Rejected: +case KSSLCertificate::InvalidPurpose: + return i18n("Rejected, possibly due to an invalid purpose."); +case KSSLCertificate::PrivateKeyFailed: + return i18n("Private key test failed."); +case KSSLCertificate::InvalidHost: + return i18n("The certificate has not been issued for this host."); +case KSSLCertificate::Irrelevant: + return i18n("This certificate is not relevant."); +default: +break; +} + +return i18n("The certificate is invalid."); +} + + +QByteArray KSSLCertificate::toDer() { +QByteArray qba; +#ifdef KSSL_HAVE_SSL +unsigned int certlen = d->kossl->i2d_X509(getCert(), NULL); +unsigned char *cert = new unsigned char[certlen]; +unsigned char *p = cert; + // FIXME: return code! + d->kossl->i2d_X509(getCert(), &p); + + // encode it into a QString + qba.duplicate((const char*)cert, certlen); + delete[] cert; +#endif +return qba; +} + + + +QByteArray KSSLCertificate::toPem() { +QByteArray qba; +QString thecert = toString(); +const char *header = "-----BEGIN CERTIFICATE-----\n"; +const char *footer = "-----END CERTIFICATE-----\n"; + + // We just do base64 on the ASN1 + // 64 character lines (unpadded) + unsigned int xx = thecert.length() - 1; + for (unsigned int i = 0; i < xx/64; i++) { + thecert.insert(64*(i+1)+i, '\n'); + } + + thecert.prepend(header); + + if (thecert[thecert.length()-1] != '\n') + thecert += "\n"; + + thecert.append(footer); + + qba.duplicate(thecert.local8Bit(), thecert.length()); +return qba; +} + + +#define NETSCAPE_CERT_HDR "certificate" + +// what a piece of crap this is +QByteArray KSSLCertificate::toNetscape() { +QByteArray qba; +#ifdef KSSL_HAVE_SSL +ASN1_HEADER ah; +ASN1_OCTET_STRING os; +KTempFile ktf; + + os.data = (unsigned char *)NETSCAPE_CERT_HDR; + os.length = strlen(NETSCAPE_CERT_HDR); + ah.header = &os; + ah.data = (char *)getCert(); + ah.meth = d->kossl->X509_asn1_meth(); + + d->kossl->ASN1_i2d_fp(ktf.fstream(),(unsigned char *)&ah); + + ktf.close(); + + QFile qf(ktf.name()); + qf.open(IO_ReadOnly); + char *buf = new char[qf.size()]; + qf.readBlock(buf, qf.size()); + qba.duplicate(buf, qf.size()); + qf.close(); + delete[] buf; + + ktf.unlink(); + +#endif +return qba; +} + + + +QString KSSLCertificate::toText() { +QString text; +#ifdef KSSL_HAVE_SSL +KTempFile ktf; + + d->kossl->X509_print(ktf.fstream(), getCert()); + ktf.close(); + + QFile qf(ktf.name()); + qf.open(IO_ReadOnly); + char *buf = new char[qf.size()+1]; + qf.readBlock(buf, qf.size()); + buf[qf.size()] = 0; + text = buf; + delete[] buf; + qf.close(); + ktf.unlink(); +#endif +return text; +} + +// KDE 4: Make it const QString & +bool KSSLCertificate::setCert(QString& cert) { +#ifdef KSSL_HAVE_SSL +QByteArray qba, qbb = cert.local8Bit().copy(); + KCodecs::base64Decode(qbb, qba); + unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data()); + X509 *x5c = KOSSL::self()->d2i_X509(NULL, &qbap, qba.size()); + if (x5c) { + setCert(x5c); + return true; + } +#endif +return false; +} + + +KSSLX509V3& KSSLCertificate::x509V3Extensions() { +return d->_extensions; +} + + +bool KSSLCertificate::isSigner() { +return d->_extensions.certTypeCA(); +} + + +QStringList KSSLCertificate::subjAltNames() const { + QStringList rc; +#ifdef KSSL_HAVE_SSL + STACK_OF(GENERAL_NAME) *names; + names = (STACK_OF(GENERAL_NAME)*)d->kossl->X509_get_ext_d2i(d->m_cert, NID_subject_alt_name, 0, 0); + + if (!names) { + return rc; + } + + int cnt = d->kossl->sk_GENERAL_NAME_num(names); + + for (int i = 0; i < cnt; i++) { + const GENERAL_NAME *val = (const GENERAL_NAME *)d->kossl->sk_value(names, i); + if (val->type != GEN_DNS) { + continue; + } + + QString s = (const char *)d->kossl->ASN1_STRING_data(val->d.ia5); + if (!s.isEmpty()) { + rc += s; + } + } + d->kossl->sk_free(names); +#endif + return rc; +} + + +QDataStream& operator<<(QDataStream& s, const KSSLCertificate& r) { +QStringList qsl; +QPtrList<KSSLCertificate> cl = const_cast<KSSLCertificate&>(r).chain().getChain(); + + for (KSSLCertificate *c = cl.first(); c != 0; c = cl.next()) { + qsl << c->toString(); + } + + cl.setAutoDelete(true); + + s << const_cast<KSSLCertificate&>(r).toString() << qsl; + +return s; +} + + +QDataStream& operator>>(QDataStream& s, KSSLCertificate& r) { +QStringList qsl; +QString cert; + +s >> cert >> qsl; + + if (r.setCert(cert) && !qsl.isEmpty()) + r.chain().setCertChain(qsl); + +return s; +} + + + |