Mimic Key Usage and Basic Constraints There are cases where the generated certificates do not mimic enough properties and secure connection with the client fails. For example, Squid does not mimic Key Usage extensions. Clients using GnuTLS (or similar libraries that validate server certificate using those extensions) fail to secure the connection with Squid. This patch add mimicking for the following extensions, which are considered as safe to mimic: * X509v3 Key Usage * X509v3 Extended Key Usage, * X509v3 Basic Constraints CA. We would be happy to add more "safe to mimic" extensions if users request (and vouch for) them. This is a Measurement Factory project === modified file 'src/ssl/gadgets.cc' --- src/ssl/gadgets.cc 2012-10-10 04:47:29 +0000 +++ src/ssl/gadgets.cc 2013-01-23 22:03:25 +0000 @@ -232,40 +232,65 @@ if (setValidAfter) certKey.append("+SetValidAfter=on", 17); if (setValidBefore) certKey.append("+SetValidBefore=on", 18); if (setCommonName) { certKey.append("+SetCommonName=", 15); certKey.append(commonName); } if (signAlgorithm != Ssl::algSignEnd) { certKey.append("+Sign=", 6); certKey.append(certSignAlgorithm(signAlgorithm)); } return certKey; } +// Copy certificate extensions from cert to mimicCert. +// Currently only extensions which are reported by the users that required are +// mimicked. More safe to mimic extensions would be added here if users request +// them. +static void +mimicExtensions(Ssl::X509_Pointer & cert, Ssl::X509_Pointer const & mimicCert) +{ + static int extensions[]= { + NID_key_usage, + NID_ext_key_usage, + NID_basic_constraints, + 0 + }; + + int nid; + for (int i = 0; (nid = extensions[i]) != 0; ++i) { + const int pos = X509_get_ext_by_NID(mimicCert.get(), nid, -1); + if (X509_EXTENSION *ext = X509_get_ext(mimicCert.get(), pos)) + X509_add_ext(cert.get(), ext, -1); + } + + // We could also restrict mimicking of the CA extension to CA:FALSE + // because Squid does not generate valid fake CA certificates. +} + static bool buildCertificate(Ssl::X509_Pointer & cert, Ssl::CertificateProperties const &properties) { // not an Ssl::X509_NAME_Pointer because X509_REQ_get_subject_name() // returns a pointer to the existing subject name. Nothing to clean here. if (properties.mimicCert.get()) { // Leave subject empty if we cannot extract it from true cert. if (X509_NAME *name = X509_get_subject_name(properties.mimicCert.get())) { // X509_set_subject_name will call X509_dup for name X509_set_subject_name(cert.get(), name); } } if (properties.setCommonName || !properties.mimicCert.get()) { // In this case the CN of the certificate given by the user // Ignore errors: it is better to make a certificate with no CN // than to quit ssl_crtd because we cannot make a certificate. // Most errors are caused by user input such as huge domain names. (void)replaceCommonName(cert, properties.commonName); } @@ -303,40 +328,42 @@ int alLen; alStr = X509_alias_get0(properties.mimicCert.get(), &alLen); if (alStr) { X509_alias_set1(cert.get(), alStr, alLen); } // Mimic subjectAltName unless we used a configured CN: browsers reject // certificates with CN unrelated to subjectAltNames. if (!properties.setCommonName) { int pos=X509_get_ext_by_NID (properties.mimicCert.get(), OBJ_sn2nid("subjectAltName"), -1); X509_EXTENSION *ext=X509_get_ext(properties.mimicCert.get(), pos); if (ext) { X509_add_ext(cert.get(), ext, -1); /* According the RFC 5280 using extensions requires version 3 certificate. Set version value to 2 for version 3 certificates. */ X509_set_version(cert.get(), 2); } } + + mimicExtensions(cert, properties.mimicCert); } return true; } static bool generateFakeSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties, Ssl::BIGNUM_Pointer const &serial) { Ssl::EVP_PKEY_Pointer pkey; // Use signing certificates private key as generated certificate private key if (properties.signWithPkey.get()) pkey.resetAndLock(properties.signWithPkey.get()); else // if not exist generate one pkey.reset(Ssl::createSslPrivateKey()); if (!pkey) return false; Ssl::X509_Pointer cert(X509_new()); if (!cert) return false;