Пример #1
0
static gboolean
ssl_verify_certificate (LmSSL *ssl, const gchar *server)
{
    LmSSLBase *base;
    unsigned int        status;
    int rc;

    base = LM_SSL_BASE (ssl);

    /* This verification function uses the trusted CAs in the credentials
     * structure. So you must have installed one or more CA certificates.
     */
    rc = gnutls_certificate_verify_peers2 (ssl->gnutls_session, &status);

    if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) {
        if (base->func (ssl,
                        LM_SSL_STATUS_NO_CERT_FOUND,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            return FALSE;
        }
    }

    if (rc != 0) {
        if (base->func (ssl,
                        LM_SSL_STATUS_GENERIC_ERROR,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            return FALSE;
        }
    }

    if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) {
        if (base->func (ssl,
                        LM_SSL_STATUS_NO_CERT_FOUND,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            return FALSE;
        }
    }

    if (status & GNUTLS_CERT_INVALID
        || status & GNUTLS_CERT_REVOKED) {
        if (base->func (ssl, LM_SSL_STATUS_UNTRUSTED_CERT,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            return FALSE;
        }
    }

    if (gnutls_certificate_expiration_time_peers (ssl->gnutls_session) < time (0)) {
        if (base->func (ssl, LM_SSL_STATUS_CERT_EXPIRED,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            return FALSE;
        }
    }

    if (gnutls_certificate_activation_time_peers (ssl->gnutls_session) > time (0)) {
        if (base->func (ssl, LM_SSL_STATUS_CERT_NOT_ACTIVATED,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            return FALSE;
        }
    }

    if (gnutls_certificate_type_get (ssl->gnutls_session) == GNUTLS_CRT_X509) {
        const gnutls_datum_t* cert_list;
        guint cert_list_size;
        gnutls_digest_algorithm_t digest = GNUTLS_DIG_SHA256;
        guchar digest_bin[LM_FINGERPRINT_LENGTH];
        size_t digest_size;
        gnutls_x509_crt_t cert;

        cert_list = gnutls_certificate_get_peers (ssl->gnutls_session, &cert_list_size);
        if (cert_list == NULL) {
            if (base->func (ssl, LM_SSL_STATUS_NO_CERT_FOUND,
                            base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                return FALSE;
            }
        }

        gnutls_x509_crt_init (&cert);

        if (gnutls_x509_crt_import (cert, &cert_list[0],
                                    GNUTLS_X509_FMT_DER) != 0) {
            if (base->func (ssl, LM_SSL_STATUS_NO_CERT_FOUND,
                            base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                return FALSE;
            }
        }

        if (!gnutls_x509_crt_check_hostname (cert, server)) {
            if (base->func (ssl, LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
                            base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                return FALSE;
            }
        }

        gnutls_x509_crt_deinit (cert);

        digest_size = gnutls_hash_get_len(digest);
        g_assert(digest_size < sizeof(digest_bin));

        if (gnutls_fingerprint (digest,
                                &cert_list[0],
                                digest_bin,
                                &digest_size) >= 0) {
            _lm_ssl_base_set_fingerprint(base, digest_bin, digest_size);
            if (_lm_ssl_base_check_fingerprint(base) != 0 &&
                base->func (ssl,
                            LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
                            base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                return FALSE;
            }
        }
        else if (base->func (ssl, LM_SSL_STATUS_GENERIC_ERROR,
                             base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            return FALSE;
        }
    }

    return TRUE;
}
Пример #2
0
/* side effect: fills the ssl->fingerprint buffer */
static gboolean
ssl_verify_certificate (LmSSL *ssl, const gchar *server)
{
    gboolean retval = TRUE;
    gboolean match_result = FALSE;
    LmSSLBase *base;
    long verify_res;
    int rc;
    const EVP_MD *digest = EVP_sha256();
    unsigned int digest_len;
    guchar digest_bin[EVP_MD_size(digest)];
    X509 *srv_crt;
    gchar *cn;
    X509_NAME *crt_subj;

    base = LM_SSL_BASE(ssl);

    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s: Cipher: %s/%s/%i\n",
           __FILE__,
           SSL_get_cipher_version(ssl->ssl),
           SSL_get_cipher_name(ssl->ssl),
           SSL_get_cipher_bits(ssl->ssl, NULL));

    verify_res = SSL_get_verify_result(ssl->ssl);
    srv_crt = SSL_get_peer_certificate(ssl->ssl);
    rc = X509_digest(srv_crt, digest, digest_bin, &digest_len);
    if ((rc != 0) && (digest_len == EVP_MD_size(digest))) {
        _lm_ssl_base_set_fingerprint(base, digest_bin, digest_len);
        if (_lm_ssl_base_check_fingerprint(base) != 0) {
            if (base->func(ssl,
                           LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH,
                           base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
                return FALSE;
            }
        }
    } else {
      if (base->func(ssl,
                     LM_SSL_STATUS_GENERIC_ERROR,
                     base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
          return FALSE;
      }
    }
    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s: SSL_get_verify_result() = %ld\n",
           __FILE__,
           verify_res);
    switch (verify_res) {
    case X509_V_OK:
        break;
    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
    case X509_V_ERR_UNABLE_TO_GET_CRL:
        if (base->func(ssl,
                       LM_SSL_STATUS_NO_CERT_FOUND,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
        /* special case for self signed certificates? */
    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
    case X509_V_ERR_INVALID_CA:
    case X509_V_ERR_CERT_UNTRUSTED:
    case X509_V_ERR_CERT_REVOKED:
        if (base->func(ssl,
                       LM_SSL_STATUS_UNTRUSTED_CERT,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_CERT_NOT_YET_VALID:
    case X509_V_ERR_CRL_NOT_YET_VALID:
        if (base->func(ssl,
                       LM_SSL_STATUS_CERT_NOT_ACTIVATED,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    case X509_V_ERR_CERT_HAS_EXPIRED:
    case X509_V_ERR_CRL_HAS_EXPIRED:
        if (base->func(ssl,
                       LM_SSL_STATUS_CERT_EXPIRED,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
        break;
    default:
        if (base->func(ssl, LM_SSL_STATUS_GENERIC_ERROR,
                       base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
    }
    /*if (retval == FALSE) {
      g_set_error (error, LM_ERROR, LM_ERROR_CONNECTION_OPEN,
      ssl_get_x509_err(verify_res), NULL);
      }*/
    crt_subj = X509_get_subject_name(srv_crt);
    cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1);

    /* FWB: deprecated call, can only get first entry */
    if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) {

        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
               "%s: server = '%s', cn = '%s'\n",
               __FILE__, server, cn);

        if (cn != NULL && ssl_match_domain_name(server, cn)) {
            match_result = TRUE;
        } else {
            /* g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: CN does not match server name\n", __FILE__); */
        }
    } else {
        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
               "X509_NAME_get_text_by_NID() failed");
    }

    /* RFC6125: "...However, it is perfectly acceptable for the subject field to be empty,
     * as long as the certificate contains a subject alternative name ("subjectAltName")
     * extension that includes at least one subjectAltName entry"
     */
    if (!match_result) {
        /* FWB: CN doesn't match, try SANs */
        int subject_alt_names_nb = -1;
        int san_counter;
        STACK_OF(GENERAL_NAME) *subject_alt_names = NULL;

        // Try to extract the names within the SAN extension from the certificate
        subject_alt_names = X509_get_ext_d2i((X509 *) srv_crt, NID_subject_alt_name, NULL, NULL);
        if (subject_alt_names != NULL) {

            // Check each name within the extension
            subject_alt_names_nb = sk_GENERAL_NAME_num(subject_alt_names);
            for (san_counter=0; san_counter<subject_alt_names_nb; san_counter++) {
                const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(subject_alt_names, san_counter);
                if (current_name->type == GEN_DNS) {
                    // Current name is a DNS name, let's check it, it's ASCII
                    if (ssl_match_domain_name(server, (const char *)current_name->d.dNSName->data)) {
                        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s' - MATCH\n", __FILE__, current_name->d.dNSName->data);
                        match_result = TRUE; /* break; */
                    } else {
                        g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s'\n", __FILE__, current_name->d.dNSName->data);
                    }
                }
            }

        }

        sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free);
    }

    if (!match_result) {
        if (base->func (ssl,
                        LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
                        base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
            retval = FALSE;
        }
    }

    g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
           "%s:\n\tIssuer: %s\n\tSubject: %s\n\tFor: %s\n",
           __FILE__,
           X509_NAME_oneline(X509_get_issuer_name(srv_crt), NULL, 0),
           X509_NAME_oneline(X509_get_subject_name(srv_crt), NULL, 0),
           cn);

    g_free(cn);

    return retval;
}