Compare commits
3 Commits
eb4b9c9f7e
...
80abb9e4dd
| Author | SHA1 | Date | |
|---|---|---|---|
|
80abb9e4dd
|
|||
|
49592ed4c0
|
|||
|
4bde414964
|
161
src/dane.c
161
src/dane.c
@@ -13,6 +13,8 @@
|
||||
#include <openssl/sha.h> // For SHA256 and SHA512 functions
|
||||
#include <sys/stat.h> // For mkdir
|
||||
#include <sys/types.h> // For mkdir
|
||||
#include <dirent.h> // For directory operations
|
||||
#include <limits.h> // For PATH_MAX
|
||||
#include <errno.h> // For errno and EEXIST
|
||||
|
||||
#define CA_CERT_FILE "ca/ca_cert.pem"
|
||||
@@ -103,8 +105,6 @@ static int get_tlsa_records(const char* hostname, tlsa_record** records, int* re
|
||||
char dns_query[256];
|
||||
sprintf(dns_query, "_443._tcp.%s", hostname);
|
||||
|
||||
printf("Looking up TLSA records for %s\n", dns_query);
|
||||
|
||||
// Initialize the record count and records array
|
||||
*record_count = 0;
|
||||
*records = NULL;
|
||||
@@ -114,12 +114,12 @@ static int get_tlsa_records(const char* hostname, tlsa_record** records, int* re
|
||||
int raw_record_count = 0;
|
||||
|
||||
if (query_tlsa_records_doh(hostname, &raw_records, &raw_record_count) != 0 || raw_record_count == 0) {
|
||||
// If no records found, return that DANE is not available
|
||||
printf("No TLSA records found for %s\n", hostname);
|
||||
// If no records found, return silently
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Found %d real TLSA records\n", raw_record_count);
|
||||
// Only log if TLSA records are found
|
||||
printf("Found %d TLSA records for %s\n", raw_record_count, hostname);
|
||||
|
||||
// Allocate memory for TLSA records
|
||||
*records = malloc(raw_record_count * sizeof(tlsa_record));
|
||||
@@ -328,25 +328,114 @@ 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 verbose) {
|
||||
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)) {
|
||||
if (verbose) {
|
||||
printf("Domain matches certificate Common Name: %s\n", common_name);
|
||||
}
|
||||
valid = 1;
|
||||
} else if (verbose) {
|
||||
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);
|
||||
if (verbose) {
|
||||
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)) {
|
||||
if (verbose) {
|
||||
printf("Domain matches SAN: %s\n", dns_name);
|
||||
}
|
||||
valid = 1;
|
||||
break;
|
||||
} else if (verbose) {
|
||||
printf("Domain does not match SAN: %s\n", dns_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
|
||||
} else if (verbose) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (!get_tlsa_records(hostname, &records, &record_count)) {
|
||||
return -1;
|
||||
}
|
||||
// Get TLSA records first to determine if we should do verbose logging
|
||||
int has_tlsa = get_tlsa_records(hostname, &records, &record_count);
|
||||
|
||||
if (record_count == 0) {
|
||||
if (!has_tlsa || record_count == 0) {
|
||||
// No TLSA records, return silently
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now that we know the domain has TLSA records, do verbose domain validation
|
||||
domain_verified = validate_cert_domain(cert, hostname, 1);
|
||||
if (!domain_verified) {
|
||||
fprintf(stderr, "Certificate is not valid for domain: %s\n", hostname);
|
||||
// Clean up records
|
||||
for (int i = 0; i < record_count; i++) {
|
||||
if (records[i].data) {
|
||||
free(records[i].data);
|
||||
}
|
||||
}
|
||||
free(records);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Certificate domain validation successful for: %s\n", hostname);
|
||||
|
||||
// Print records that will be used for verification
|
||||
printf("\n=== Starting DANE verification for %s ===\n", hostname);
|
||||
for (int i = 0; i < record_count; i++) {
|
||||
@@ -389,7 +478,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 +490,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++) {
|
||||
@@ -449,6 +541,7 @@ static int generate_ca_cert() {
|
||||
// Check if CA certificate already exists
|
||||
if (access(CA_CERT_FILE, F_OK) == 0 && access(CA_KEY_FILE, F_OK) == 0) {
|
||||
printf("CA certificate already exists\n");
|
||||
|
||||
// Load the existing CA certificate and key
|
||||
FILE* ca_cert_file = fopen(CA_CERT_FILE, "r");
|
||||
if (!ca_cert_file) {
|
||||
@@ -543,7 +636,7 @@ static int generate_ca_cert() {
|
||||
X509_NAME* name = X509_get_subject_name(ca_cert);
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)"FireProxy CA", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)"FireProxy", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*)"US", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*)"AU", -1, -1, 0);
|
||||
|
||||
// Self-sign the certificate
|
||||
X509_set_issuer_name(ca_cert, name);
|
||||
@@ -681,7 +774,7 @@ int generate_trusted_cert(const char* hostname, const char* cert_path, const cha
|
||||
// Set certificate subject
|
||||
X509_NAME* name = X509_get_subject_name(cert);
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)hostname, -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)"FireProxy Secured", -1, -1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)"FireProxy", -1, -1, 0);
|
||||
|
||||
// Set certificate issuer (our CA)
|
||||
X509_set_issuer_name(cert, X509_get_subject_name(ca_cert));
|
||||
@@ -746,12 +839,50 @@ int generate_trusted_cert(const char* hostname, const char* cert_path, const cha
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Function to delete all generated certificates (but not the CA certs)
|
||||
static void delete_all_generated_certs() {
|
||||
DIR* dir;
|
||||
struct dirent* entry;
|
||||
char path[PATH_MAX];
|
||||
|
||||
// Open the certificates directory
|
||||
dir = opendir(CERT_DIR);
|
||||
if (!dir) {
|
||||
perror("Failed to open certificates directory for cleanup");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read directory entries
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
// Skip "." and ".." directories
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
// Create full path
|
||||
snprintf(path, PATH_MAX, "%s/%s", CERT_DIR, entry->d_name);
|
||||
|
||||
// Delete the file and log the action
|
||||
if (unlink(path) == 0) {
|
||||
printf("Deleted temporary certificate: %s\n", path);
|
||||
} else {
|
||||
perror("Failed to delete certificate");
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
printf("Cleaned up all generated certificates\n");
|
||||
}
|
||||
|
||||
int dane_init() {
|
||||
init_openssl();
|
||||
return setup_local_ca();
|
||||
}
|
||||
|
||||
void dane_cleanup() {
|
||||
// Delete all generated certificates first
|
||||
delete_all_generated_certs();
|
||||
|
||||
// Then clean up CA resources
|
||||
if (ca_cert) {
|
||||
X509_free(ca_cert);
|
||||
ca_cert = NULL;
|
||||
|
||||
@@ -710,7 +710,11 @@ void proxy_cleanup() {
|
||||
void handle_signal(int sig) {
|
||||
if (sig == SIGINT) {
|
||||
printf("\nShutting down proxy server...\n");
|
||||
printf("Cleaning up temporary certificates...\n");
|
||||
|
||||
// Clean up DANE resources (which will delete all generated certificates)
|
||||
proxy_cleanup();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user