Esempio n. 1
0
int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t *domain,
                                    const char *certificate_db)
{
  if (!domain) return -1;

  // certificates can be either a file or a directory, which determines how it is passed
  // to SSL_CTX_load_verify_locations()
  struct stat sbuf;
  if (stat( certificate_db, &sbuf ) != 0) {
    pn_transport_logf(NULL, "stat(%s) failed: %s", certificate_db, strerror(errno));
    return -1;
  }

  const char *file;
  const char *dir;
  if (S_ISDIR(sbuf.st_mode)) {
    dir = certificate_db;
    file = NULL;
  } else {
    dir = NULL;
    file = certificate_db;
  }

  if (SSL_CTX_load_verify_locations( domain->ctx, file, dir ) != 1) {
    ssl_log_error("SSL_CTX_load_verify_locations( %s ) failed", certificate_db);
    return -1;
  }

  domain->has_ca_db = true;

  return 0;
}
Esempio n. 2
0
int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
                               const char *certificate_file,
                               const char *private_key_file,
                               const char *password)
{
  if (!domain || !domain->ctx) return -1;

  if (SSL_CTX_use_certificate_chain_file(domain->ctx, certificate_file) != 1) {
    ssl_log_error("SSL_CTX_use_certificate_chain_file( %s ) failed", certificate_file);
    return -3;
  }

  if (password) {
    domain->keyfile_pw = pn_strdup(password);  // @todo: obfuscate me!!!
    SSL_CTX_set_default_passwd_cb(domain->ctx, keyfile_pw_cb);
    SSL_CTX_set_default_passwd_cb_userdata(domain->ctx, domain->keyfile_pw);
  }

  if (SSL_CTX_use_PrivateKey_file(domain->ctx, private_key_file, SSL_FILETYPE_PEM) != 1) {
    ssl_log_error("SSL_CTX_use_PrivateKey_file( %s ) failed", private_key_file);
    return -4;
  }

  if (SSL_CTX_check_private_key(domain->ctx) != 1) {
    ssl_log_error("The key file %s is not consistent with the certificate %s",
                   private_key_file, certificate_file);
    return -5;
  }

  domain->has_certificate = true;

  // bug in older versions of OpenSSL: servers may request client cert even if anonymous
  // cipher was negotiated.  TLSv1 will reject such a request.  Hack: once a cert is
  // configured, allow only authenticated ciphers.
  if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_AUTHENTICATE )) {
      ssl_log_error("Failed to set cipher list to %s", CIPHERS_AUTHENTICATE);
      return -6;
  }

  return 0;
}
Esempio n. 3
0
const char* pn_ssl_get_remote_subject_subfield(pn_ssl_t *ssl0, pn_ssl_cert_subject_subfield field)
{
    int openssl_field = 0;

    // Assign openssl internal representations of field values to openssl_field
    switch (field) {
        case PN_SSL_CERT_SUBJECT_COUNTRY_NAME :
            openssl_field = NID_countryName;
            break;
        case PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE :
            openssl_field = NID_stateOrProvinceName;
            break;
        case PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY :
            openssl_field = NID_localityName;
            break;
        case PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME :
            openssl_field = NID_organizationName;
            break;
        case PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT :
            openssl_field = NID_organizationalUnitName;
            break;
        case PN_SSL_CERT_SUBJECT_COMMON_NAME :
            openssl_field = NID_commonName;
            break;
        default:
            ssl_log_error("Unknown or unhandled certificate subject subfield %i \n", field);
            return NULL;
    }

    pni_ssl_t *ssl = get_ssl_internal(ssl0);
    X509 *cert = get_peer_certificate(ssl);

    X509_NAME *subject_name = X509_get_subject_name(cert);

    // TODO (gmurthy) - A server side cert subject field can have more than one common name like this - Subject: CN=www.domain1.com, CN=www.domain2.com, see https://bugzilla.mozilla.org/show_bug.cgi?id=380656
    // For now, we will only return the first common name if there is more than one common name in the cert
    int index = X509_NAME_get_index_by_NID(subject_name, openssl_field, -1);

    if (index > -1) {
        X509_NAME_ENTRY *ne = X509_NAME_get_entry(subject_name, index);
        if(ne) {
            ASN1_STRING *name_asn1 = X509_NAME_ENTRY_get_data(ne);
            return (char *) name_asn1->data;
        }
    }

    return NULL;
}
Esempio n. 4
0
pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode )
{
  if (!ssl_initialized) {
    ssl_initialized = 1;
    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();
    ssl_ex_data_index = SSL_get_ex_new_index( 0, (void *) "org.apache.qpid.proton.ssl",
                                              NULL, NULL, NULL);
  }

  pn_ssl_domain_t *domain = (pn_ssl_domain_t *) calloc(1, sizeof(pn_ssl_domain_t));
  if (!domain) return NULL;

  domain->ref_count = 1;
  domain->mode = mode;

  // enable all supported protocol versions, then explicitly disable the
  // known vulnerable ones.  This should allow us to use the latest version
  // of the TLS standard that the installed library supports.
  switch(mode) {
  case PN_SSL_MODE_CLIENT:
    domain->ctx = SSL_CTX_new(SSLv23_client_method()); // and TLSv1+
    if (!domain->ctx) {
      ssl_log_error("Unable to initialize OpenSSL context.");
      free(domain);
      return NULL;
    }
    break;

  case PN_SSL_MODE_SERVER:
    domain->ctx = SSL_CTX_new(SSLv23_server_method()); // and TLSv1+
    if (!domain->ctx) {
      ssl_log_error("Unable to initialize OpenSSL context.");
      free(domain);
      return NULL;
    }
    break;

  default:
    pn_transport_logf(NULL, "Invalid value for pn_ssl_mode_t: %d", mode);
    free(domain);
    return NULL;
  }
  const long reject_insecure = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
  SSL_CTX_set_options(domain->ctx, reject_insecure);
#ifdef SSL_OP_NO_COMPRESSION
  // Mitigate the CRIME vulnerability
  SSL_CTX_set_options(domain->ctx, SSL_OP_NO_COMPRESSION);
#endif

  // by default, allow anonymous ciphers so certificates are not required 'out of the box'
  if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_ANONYMOUS )) {
    ssl_log_error("Failed to set cipher list to %s", CIPHERS_ANONYMOUS);
    pn_ssl_domain_free(domain);
    return NULL;
  }

  // ditto: by default do not authenticate the peer (can be done by SASL).
  if (pn_ssl_domain_set_peer_authentication( domain, PN_SSL_ANONYMOUS_PEER, NULL )) {
    pn_ssl_domain_free(domain);
    return NULL;
  }

  DH *dh = get_dh2048();
  if (dh) {
    SSL_CTX_set_tmp_dh(domain->ctx, dh);
    DH_free(dh);
    SSL_CTX_set_options(domain->ctx, SSL_OP_SINGLE_DH_USE);
  }

  return domain;
}
Esempio n. 5
0
int pn_ssl_get_cert_fingerprint(pn_ssl_t *ssl0, char *fingerprint, size_t fingerprint_length, pn_ssl_hash_alg hash_alg)
{
    const char *digest_name = NULL;
    size_t min_required_length;

    // old versions of python expect fingerprint to contain a valid string on
    // return from this function
    fingerprint[0] = 0;

    // Assign the correct digest_name value based on the enum values.
    switch (hash_alg) {
        case PN_SSL_SHA1 :
            min_required_length = 41; // 40 hex characters + 1 '\0' character
            digest_name = "sha1";
            break;
        case PN_SSL_SHA256 :
            min_required_length = 65; // 64 hex characters + 1 '\0' character
            digest_name = "sha256";
            break;
        case PN_SSL_SHA512 :
            min_required_length = 129; // 128 hex characters + 1 '\0' character
            digest_name = "sha512";
            break;
        case PN_SSL_MD5 :
            min_required_length = 33; // 32 hex characters + 1 '\0' character
            digest_name = "md5";
            break;
        default:
            ssl_log_error("Unknown or unhandled hash algorithm %i \n", hash_alg);
            return PN_ERR;

    }

    if(fingerprint_length < min_required_length) {
        ssl_log_error("Insufficient fingerprint_length %i. fingerprint_length must be %i or above for %s digest\n",
            fingerprint_length, min_required_length, digest_name);
        return PN_ERR;
    }

    const EVP_MD  *digest = EVP_get_digestbyname(digest_name);

    pni_ssl_t *ssl = get_ssl_internal(ssl0);

    X509 *cert = get_peer_certificate(ssl);

    if(cert) {
        unsigned int len;

        unsigned char bytes[64]; // sha512 uses 64 octets, we will use that as the maximum.

        if (X509_digest(cert, digest, bytes, &len) != 1) {
            ssl_log_error("Failed to extract X509 digest\n");
            return PN_ERR;
       }

        char *cursor = fingerprint;

        for (size_t i=0; i<len ; i++) {
            cursor +=  snprintf((char *)cursor, fingerprint_length, "%02x", bytes[i]);
            fingerprint_length = fingerprint_length - 2;
        }

        return PN_OK;
    }
    else {
        ssl_log_error("No certificate is available yet \n");
        return PN_ERR;
    }

    return 0;
}