Compare commits

...

3 Commits

Author SHA1 Message Date
80abb9e4dd feat: Add some improvments 2025-04-23 18:26:51 +10:00
49592ed4c0 fix: Update cert details 2025-04-23 18:12:13 +10:00
4bde414964 feat: Fix a few other issues with certs 2025-04-23 18:06:06 +10:00
2 changed files with 150 additions and 15 deletions

View File

@@ -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;

View File

@@ -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);
}
}