diff --git a/src/dane.c b/src/dane.c index 11e699c..29062dd 100644 --- a/src/dane.c +++ b/src/dane.c @@ -328,17 +328,93 @@ static int compare_hashes(const unsigned char* expected, size_t expected_len, return result; } +// Check if a domain matches a pattern (supporting wildcards) +static int domain_matches_pattern(const char* domain, const char* pattern) { + // Exact match + if (strcmp(domain, pattern) == 0) { + return 1; + } + + // Wildcard match + if (pattern[0] == '*' && pattern[1] == '.') { + const char* domain_dot = strchr(domain, '.'); + if (domain_dot && strcmp(domain_dot, pattern + 1) == 0) { + return 1; + } + } + + return 0; +} + +// Validate that a certificate is valid for a given domain +static int validate_cert_domain(X509* cert, const char* hostname) { + int valid = 0; + + // First check the Common Name + X509_NAME* subject_name = X509_get_subject_name(cert); + if (subject_name) { + char common_name[256]; + X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name, sizeof(common_name)); + + if (domain_matches_pattern(hostname, common_name)) { + printf("Domain matches certificate Common Name: %s\n", common_name); + valid = 1; + } else { + printf("Domain does not match certificate Common Name: %s\n", common_name); + } + } + + // Then check Subject Alternative Names + STACK_OF(GENERAL_NAME)* san_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if (san_names) { + int san_count = sk_GENERAL_NAME_num(san_names); + printf("Certificate has %d Subject Alternative Names\n", san_count); + + for (int i = 0; i < san_count; i++) { + const GENERAL_NAME* current_name = sk_GENERAL_NAME_value(san_names, i); + + if (current_name->type == GEN_DNS) { + const char* dns_name = (const char*)ASN1_STRING_get0_data(current_name->d.dNSName); + + if (domain_matches_pattern(hostname, dns_name)) { + printf("Domain matches SAN: %s\n", dns_name); + valid = 1; + break; + } else { + printf("Domain does not match SAN: %s\n", dns_name); + } + } + } + + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + } else { + printf("Certificate has no Subject Alternative Names\n"); + } + + return valid; +} + // Verify a certificate against DANE TLSA records int verify_cert_against_dane(const char* hostname, X509* cert) { tlsa_record* records = NULL; int record_count = 0; - int verified = 0; + int dane_verified = 0; + int domain_verified = 0; if (!cert) { fprintf(stderr, "No certificate provided for DANE verification\n"); return -1; } + // Verify the certificate domain + domain_verified = validate_cert_domain(cert, hostname); + if (!domain_verified) { + fprintf(stderr, "Certificate is not valid for domain: %s\n", hostname); + return 0; + } + + printf("Certificate domain validation successful for: %s\n", hostname); + if (!get_tlsa_records(hostname, &records, &record_count)) { return -1; } @@ -389,7 +465,7 @@ int verify_cert_against_dane(const char* hostname, X509* cert) { case 3: // DANE-EE (3) - Domain-issued certificate // Verify the certificate directly against the TLSA record printf("Performing DANE-EE validation...\n"); - verified = compare_hashes(record->data, record->data_len, + dane_verified = compare_hashes(record->data, record->data_len, cert_hash, cert_hash_len); break; @@ -401,13 +477,16 @@ int verify_cert_against_dane(const char* hostname, X509* cert) { free(cert_hash); - if (verified) { + if (dane_verified) { printf("DANE verification succeeded for record #%d\n", i + 1); break; } } - printf("\nDANE verification result: %s\n", verified ? "SUCCESS" : "FAILED"); + int verified = domain_verified && dane_verified; + printf("\nDomain verification: %s\n", domain_verified ? "SUCCESS" : "FAILED"); + printf("DANE verification: %s\n", dane_verified ? "SUCCESS" : "FAILED"); + printf("Overall verification result: %s\n", verified ? "SUCCESS" : "FAILED"); // Clean up for (int i = 0; i < record_count; i++) {