feat: Get started on DANE
This commit is contained in:
270
src/proxy.c
270
src/proxy.c
@@ -1,5 +1,6 @@
|
||||
#include "proxy.h"
|
||||
#include "doh.h"
|
||||
#include "dane.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -11,6 +12,10 @@
|
||||
#include <netdb.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#define MAX_REQUEST_SIZE 8192
|
||||
#define MAX_URL_LENGTH 2048
|
||||
@@ -91,8 +96,186 @@ int extract_port(const char* request) {
|
||||
return default_port;
|
||||
}
|
||||
|
||||
// Handle HTTPS CONNECT tunneling
|
||||
void handle_https_tunnel(int client_sock, int server_sock) {
|
||||
// Initialize SSL context for intercepting HTTPS
|
||||
ssl_context_t* init_ssl_context(const char* cert_path, const char* key_path) {
|
||||
ssl_context_t* ssl_ctx = malloc(sizeof(ssl_context_t));
|
||||
if (!ssl_ctx) {
|
||||
fprintf(stderr, "Failed to allocate memory for SSL context\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Initialize SSL context
|
||||
ssl_ctx->ctx = SSL_CTX_new(TLS_server_method());
|
||||
if (!ssl_ctx->ctx) {
|
||||
fprintf(stderr, "Failed to create SSL context\n");
|
||||
free(ssl_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Configure SSL context
|
||||
SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
|
||||
|
||||
// Load certificate and private key
|
||||
if (SSL_CTX_use_certificate_file(ssl_ctx->ctx, cert_path, SSL_FILETYPE_PEM) <= 0) {
|
||||
fprintf(stderr, "Failed to load certificate: %s\n", cert_path);
|
||||
SSL_CTX_free(ssl_ctx->ctx);
|
||||
free(ssl_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ssl_ctx->ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
|
||||
fprintf(stderr, "Failed to load private key: %s\n", key_path);
|
||||
SSL_CTX_free(ssl_ctx->ctx);
|
||||
free(ssl_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Verify private key
|
||||
if (!SSL_CTX_check_private_key(ssl_ctx->ctx)) {
|
||||
fprintf(stderr, "Private key does not match the certificate\n");
|
||||
SSL_CTX_free(ssl_ctx->ctx);
|
||||
free(ssl_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
// Handle HTTPS CONNECT tunneling with DANE verification
|
||||
void handle_https_tunnel(int client_sock, int server_sock, const char* hostname, const char* ip_addr) {
|
||||
// Unused parameter
|
||||
(void)ip_addr;
|
||||
|
||||
// Check if we have DANE records for this domain
|
||||
int has_dane = is_dane_available(hostname);
|
||||
|
||||
if (has_dane) {
|
||||
printf("DANE records found for %s, will verify certificate\n", hostname);
|
||||
|
||||
char cert_path[256];
|
||||
char key_path[256];
|
||||
|
||||
snprintf(cert_path, sizeof(cert_path), "certs/%s.crt", hostname);
|
||||
snprintf(key_path, sizeof(key_path), "certs/%s.key", hostname);
|
||||
|
||||
// Generate a trusted certificate for this domain
|
||||
if (!generate_trusted_cert(hostname, cert_path, key_path)) {
|
||||
fprintf(stderr, "Failed to generate trusted certificate for %s\n", hostname);
|
||||
// Fall back to regular tunneling
|
||||
handle_regular_https_tunnel(client_sock, server_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize SSL context with our certificate
|
||||
ssl_context_t* client_ctx = init_ssl_context(cert_path, key_path);
|
||||
if (!client_ctx) {
|
||||
fprintf(stderr, "Failed to initialize SSL context\n");
|
||||
handle_regular_https_tunnel(client_sock, server_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect to the server with SSL to verify DANE
|
||||
SSL_CTX* server_ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!server_ctx) {
|
||||
fprintf(stderr, "Failed to create server SSL context\n");
|
||||
SSL_CTX_free(client_ctx->ctx);
|
||||
free(client_ctx);
|
||||
handle_regular_https_tunnel(client_sock, server_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
SSL* server_ssl = SSL_new(server_ctx);
|
||||
SSL_set_fd(server_ssl, server_sock);
|
||||
|
||||
// Set SNI hostname
|
||||
SSL_set_tlsext_host_name(server_ssl, hostname);
|
||||
|
||||
// Connect to the server with SSL
|
||||
if (SSL_connect(server_ssl) <= 0) {
|
||||
fprintf(stderr, "SSL connection to server failed\n");
|
||||
SSL_free(server_ssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx->ctx);
|
||||
free(client_ctx);
|
||||
handle_regular_https_tunnel(client_sock, server_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the server's certificate
|
||||
X509* server_cert = SSL_get_peer_certificate(server_ssl);
|
||||
if (!server_cert) {
|
||||
fprintf(stderr, "Failed to get server certificate\n");
|
||||
SSL_free(server_ssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx->ctx);
|
||||
free(client_ctx);
|
||||
handle_regular_https_tunnel(client_sock, server_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the certificate against DANE
|
||||
int dane_verified = verify_cert_against_dane(hostname, server_cert);
|
||||
if (dane_verified <= 0) {
|
||||
fprintf(stderr, "DANE verification failed for %s\n", hostname);
|
||||
X509_free(server_cert);
|
||||
SSL_free(server_ssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx->ctx);
|
||||
free(client_ctx);
|
||||
handle_regular_https_tunnel(client_sock, server_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("DANE verification successful for %s\n", hostname);
|
||||
|
||||
// Send 200 Connection Established to the client
|
||||
const char* success_response = "HTTP/1.1 200 Connection Established\r\n\r\n";
|
||||
if (send(client_sock, success_response, strlen(success_response), 0) < 0) {
|
||||
perror("Failed to send connection established response");
|
||||
X509_free(server_cert);
|
||||
SSL_free(server_ssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx->ctx);
|
||||
free(client_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize SSL connection with the client
|
||||
client_ctx->ssl = SSL_new(client_ctx->ctx);
|
||||
SSL_set_fd(client_ctx->ssl, client_sock);
|
||||
|
||||
if (SSL_accept(client_ctx->ssl) <= 0) {
|
||||
fprintf(stderr, "SSL accept failed\n");
|
||||
SSL_free(client_ctx->ssl);
|
||||
X509_free(server_cert);
|
||||
SSL_free(server_ssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx->ctx);
|
||||
free(client_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("SSL connection established with client for %s\n", hostname);
|
||||
|
||||
// Now we have SSL connections to both client and server
|
||||
// We can forward data between them
|
||||
ssl_tunnel_data(client_ctx->ssl, server_ssl);
|
||||
|
||||
// Clean up
|
||||
SSL_free(client_ctx->ssl);
|
||||
X509_free(server_cert);
|
||||
SSL_free(server_ssl);
|
||||
SSL_CTX_free(server_ctx);
|
||||
SSL_CTX_free(client_ctx->ctx);
|
||||
free(client_ctx);
|
||||
} else {
|
||||
// No DANE records, use regular tunneling
|
||||
handle_regular_https_tunnel(client_sock, server_sock);
|
||||
}
|
||||
}
|
||||
|
||||
// Regular HTTPS tunneling without interception
|
||||
void handle_regular_https_tunnel(int client_sock, int server_sock) {
|
||||
fd_set read_fds;
|
||||
char buffer[MAX_REQUEST_SIZE];
|
||||
int max_fd = (client_sock > server_sock) ? client_sock : server_sock;
|
||||
@@ -129,6 +312,62 @@ void handle_https_tunnel(int client_sock, int server_sock) {
|
||||
}
|
||||
}
|
||||
|
||||
// Forward data between SSL connections
|
||||
void ssl_tunnel_data(SSL* client_ssl, SSL* server_ssl) {
|
||||
fd_set read_fds;
|
||||
char buffer[MAX_REQUEST_SIZE];
|
||||
int client_fd = SSL_get_fd(client_ssl);
|
||||
int server_fd = SSL_get_fd(server_ssl);
|
||||
int max_fd = (client_fd > server_fd) ? client_fd : server_fd;
|
||||
|
||||
while (1) {
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(client_fd, &read_fds);
|
||||
FD_SET(server_fd, &read_fds);
|
||||
|
||||
if (select(max_fd + 1, &read_fds, NULL, NULL, NULL) < 0) {
|
||||
perror("Select failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(client_fd, &read_fds)) {
|
||||
int bytes_received = SSL_read(client_ssl, buffer, sizeof(buffer));
|
||||
if (bytes_received <= 0) {
|
||||
int err = SSL_get_error(client_ssl, bytes_received);
|
||||
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int bytes_sent = SSL_write(server_ssl, buffer, bytes_received);
|
||||
if (bytes_sent <= 0) {
|
||||
int err = SSL_get_error(server_ssl, bytes_sent);
|
||||
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(server_fd, &read_fds)) {
|
||||
int bytes_received = SSL_read(server_ssl, buffer, sizeof(buffer));
|
||||
if (bytes_received <= 0) {
|
||||
int err = SSL_get_error(server_ssl, bytes_received);
|
||||
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int bytes_sent = SSL_write(client_ssl, buffer, bytes_received);
|
||||
if (bytes_sent <= 0) {
|
||||
int err = SSL_get_error(client_ssl, bytes_sent);
|
||||
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modify HTTP request for direct server communication
|
||||
char* rewrite_http_request(const char* original_request, size_t* new_length) {
|
||||
// Find the first line ending
|
||||
@@ -261,8 +500,8 @@ void* handle_client(void* arg) {
|
||||
}
|
||||
|
||||
if (is_connect) {
|
||||
// HTTPS: Handle CONNECT tunnel
|
||||
handle_https_tunnel(client_sock, server_sock);
|
||||
// HTTPS: Handle CONNECT tunnel with DANE support
|
||||
handle_https_tunnel(client_sock, server_sock, host, ip_addr);
|
||||
} else {
|
||||
// HTTP: Rewrite the request and forward it
|
||||
printf("HTTP request received, rewriting for server...\n");
|
||||
@@ -341,6 +580,12 @@ int start_proxy_server(int port) {
|
||||
struct sockaddr_in server_addr, client_addr;
|
||||
socklen_t client_len = sizeof(client_addr);
|
||||
|
||||
// Initialize proxy components
|
||||
if (proxy_init() != 0) {
|
||||
fprintf(stderr, "Failed to initialize proxy server\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create socket
|
||||
server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server_sock < 0) {
|
||||
@@ -419,10 +664,27 @@ int start_proxy_server(int port) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize the proxy server
|
||||
int proxy_init() {
|
||||
// Initialize DANE support
|
||||
if (!dane_init()) {
|
||||
fprintf(stderr, "Failed to initialize DANE support\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Clean up proxy resources
|
||||
void proxy_cleanup() {
|
||||
dane_cleanup();
|
||||
}
|
||||
|
||||
// Signal handler for graceful termination
|
||||
void handle_signal(int sig) {
|
||||
if (sig == SIGINT) {
|
||||
printf("\nShutting down proxy server...\n");
|
||||
proxy_cleanup();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user