mirror of
https://github.com/proxytunnel/proxytunnel.git
synced 2026-01-23 02:34:59 +00:00
Add SSL certificate verification support and enable it by default
Disable SSL certificate verification with -z/--no-check-certificate
This commit is contained in:
parent
4554d4b801
commit
5a7692f28f
7 changed files with 179 additions and 10 deletions
15
cmdline.c
15
cmdline.c
|
|
@ -64,6 +64,9 @@ void cmdline_parser_print_help (void) {
|
|||
#endif
|
||||
"\n"
|
||||
"Additional options for specific features:\n"
|
||||
#ifdef USE_SSL
|
||||
" -z, --no-check-certficate Don't verify server SSL certificate\n"
|
||||
#endif
|
||||
" -F, --passfile=STRING File with credentials for proxy authentication\n"
|
||||
" -P, --proxyauth=STRING Proxy auth credentials user:pass combination\n"
|
||||
" -R, --remproxyauth=STRING Remote proxy auth credentials user:pass combination\n"
|
||||
|
|
@ -165,6 +168,7 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar
|
|||
args_info->proctitle_arg = NULL; \
|
||||
args_info->enforcetls1_flag = 0; \
|
||||
args_info->host_arg = NULL; \
|
||||
args_info->no_check_cert_flag = 0; \
|
||||
}
|
||||
|
||||
clear_args();
|
||||
|
|
@ -209,12 +213,13 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar
|
|||
{ "encrypt-proxy", 0, NULL, 'E' },
|
||||
{ "encrypt-remproxy",0,NULL, 'X' },
|
||||
{ "no-ssl3", 0, NULL, 'T' },
|
||||
{ "no-check-certificate",0,NULL,'z' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXqLo:T", long_options, &option_index);
|
||||
c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXqLo:Tz", long_options, &option_index);
|
||||
#else
|
||||
c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXqLo:T" );
|
||||
c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXqLo:Tz" );
|
||||
#endif
|
||||
|
||||
if (c == -1)
|
||||
|
|
@ -431,6 +436,12 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar
|
|||
args_info->quiet_flag = !(args_info->quiet_flag);
|
||||
break;
|
||||
|
||||
case 'z': /* Don't verify server SSL certificate */
|
||||
args_info->no_check_cert_flag = 1;
|
||||
if( args_info->verbose_flag )
|
||||
message("Server SSL certificate verification disabled\n");
|
||||
break;
|
||||
|
||||
case 0: /* Long option with no short option */
|
||||
|
||||
case '?': /* Invalid option. */
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ struct gengetopt_args_info {
|
|||
char *proctitle_arg; /* Override process title (default=off). */
|
||||
int enforcetls1_flag; /* Override default and enforce TLSv1 */
|
||||
char *host_arg; /* Optional Host Header */
|
||||
int no_check_cert_flag; /* Turn off server SSL certificate verification (default=on) */
|
||||
int help_given; /* Whether help was given. */
|
||||
int version_given; /* Whether version was given. */
|
||||
int user_given; /* Whether user was given. */
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ also be used for other proxy-traversing purposes like proxy bouncing.
|
|||
*-T*, *--no-ssl3*::
|
||||
Prevent the use of SSLv3 in encrypted connections (default: enabled)
|
||||
|
||||
*-z*, *--no-check-certificate*::
|
||||
Do not verify server SSL certificate when establishing an SSL connection.
|
||||
By default, the server SSL certficate is verified and the target host name
|
||||
is checked against the server certificate's subject alternative names if
|
||||
any are present, or common name if there are no subject alternative names.
|
||||
|
||||
*-F*, *--passfile*=_filename_::
|
||||
Use _filename_ for reading username and password for HTTPS proxy
|
||||
authentication, the file uses the same format as .wgetrc and can be shared
|
||||
|
|
|
|||
2
http.c
2
http.c
|
|
@ -159,7 +159,7 @@ void proxy_protocol(PTSTREAM *pts) {
|
|||
|
||||
/* If --encrypt-remproxy is specified, connect to the remote proxy using SSL */
|
||||
if ( args_info.encryptremproxy_flag )
|
||||
stream_enable_ssl(stunnel);
|
||||
stream_enable_ssl(stunnel, args_info.remproxy_arg);
|
||||
|
||||
if( args_info.verbose_flag )
|
||||
message( "\nTunneling to %s (destination)\n", args_info.dest_arg );
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ void do_daemon()
|
|||
#ifdef USE_SSL
|
||||
/* If --encrypt-proxy is specified, connect to the proxy using SSL */
|
||||
if ( args_info.encryptproxy_flag )
|
||||
stream_enable_ssl(stunnel);
|
||||
stream_enable_ssl(stunnel, args_info.proxy_arg);
|
||||
#endif /* USE_SSL */
|
||||
|
||||
/* Open the tunnel */
|
||||
|
|
@ -274,7 +274,7 @@ void do_daemon()
|
|||
#ifdef USE_SSL
|
||||
/* If --encrypt is specified, wrap all traffic after the proxy handoff in SSL */
|
||||
if( args_info.encrypt_flag )
|
||||
stream_enable_ssl(stunnel);
|
||||
stream_enable_ssl(stunnel, args_info.dest_arg);
|
||||
#endif /* USE_SSL */
|
||||
|
||||
#ifdef SETPROCTITLE
|
||||
|
|
@ -387,7 +387,7 @@ int main( int argc, char *argv[] ) {
|
|||
/* If --encrypt-proxy is specified, connect to the proxy using SSL */
|
||||
#ifdef USE_SSL
|
||||
if ( args_info.encryptproxy_flag )
|
||||
stream_enable_ssl(stunnel);
|
||||
stream_enable_ssl(stunnel, args_info.proxy_arg);
|
||||
#endif /* USE_SSL */
|
||||
|
||||
/* Open the tunnel */
|
||||
|
|
@ -396,7 +396,7 @@ int main( int argc, char *argv[] ) {
|
|||
/* If --encrypt is specified, wrap all traffic after the proxy handoff in SSL */
|
||||
#ifdef USE_SSL
|
||||
if( args_info.encrypt_flag )
|
||||
stream_enable_ssl(stunnel);
|
||||
stream_enable_ssl(stunnel, args_info.dest_arg);
|
||||
#endif /* USE_SSL */
|
||||
|
||||
#ifdef SETPROCTITLE
|
||||
|
|
|
|||
155
ptstream.c
155
ptstream.c
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
/* ptstream.c */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -142,15 +144,109 @@ int stream_copy(PTSTREAM *pts_from, PTSTREAM *pts_to) {
|
|||
}
|
||||
|
||||
|
||||
/* Check the certificate host name against the expected host name */
|
||||
/* Return 1 if peer hostname is valid, any other value indicates failure */
|
||||
int check_cert_valid_host(const char *cert_host, const char *peer_host) {
|
||||
if (cert_host == NULL || peer_host == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (cert_host[0] == '*') {
|
||||
if (strncmp(cert_host, "*.", 2) != 0) {
|
||||
/* Invalid wildcard hostname */
|
||||
return 0;
|
||||
}
|
||||
/* Skip "*." */
|
||||
cert_host += 2;
|
||||
/* Wildcards can only match the first subdomain component */
|
||||
while (*peer_host++ != '.' && *peer_host != '\0')
|
||||
;;
|
||||
}
|
||||
if (strlen(cert_host) == 0 || strlen(peer_host) == 0) {
|
||||
return 0;
|
||||
}
|
||||
return strcmp(cert_host, peer_host) == 0;
|
||||
}
|
||||
|
||||
|
||||
int check_cert_valid_ip6(const unsigned char *cert_ip_data, const int cert_ip_len, const struct in6_addr *addr6) {
|
||||
int i;
|
||||
for (i = 0; i < cert_ip_len; i++) {
|
||||
if (cert_ip_data[i] != addr6->s6_addr[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int check_cert_valid_ip(const unsigned char *cert_ip_data, const int cert_ip_len, const struct in_addr *addr) {
|
||||
int i;
|
||||
for (i = 0; i < cert_ip_len; i++) {
|
||||
if (cert_ip_data[i] != ((addr->s_addr >> (i * 8)) & 0xFF)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int check_cert_names(X509 *cert, char *peer_host) {
|
||||
char peer_cn[256];
|
||||
const GENERAL_NAME *gn;
|
||||
STACK_OF(GENERAL_NAME) *gen_names;
|
||||
struct in_addr addr;
|
||||
struct in6_addr addr6;
|
||||
int peer_host_is_ipv4 = 0, peer_host_is_ipv6 = 0;
|
||||
int i, san_count;
|
||||
|
||||
gen_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
|
||||
san_count = sk_GENERAL_NAME_num(gen_names);
|
||||
if (san_count > 0) {
|
||||
peer_host_is_ipv4 = (inet_pton(AF_INET, peer_host, &addr) == 1);
|
||||
peer_host_is_ipv6 = (peer_host_is_ipv4 ? 0 : inet_pton(AF_INET6, peer_host, &addr6) == 1);
|
||||
for (i = 0; i < san_count; i++) {
|
||||
gn = sk_GENERAL_NAME_value(gen_names, i);
|
||||
if (gn->type == GEN_DNS && !(peer_host_is_ipv4 || peer_host_is_ipv6)) {
|
||||
if (check_cert_valid_host((char*)ASN1_STRING_data(gn->d.ia5), peer_host)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (gn->type == GEN_IPADD) {
|
||||
if (gn->d.ip->length == 4 && peer_host_is_ipv4) {
|
||||
if (check_cert_valid_ip(gn->d.ip->data, gn->d.ip->length, &addr)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (gn->d.ip->length == 16 && peer_host_is_ipv6) {
|
||||
if (check_cert_valid_ip6(gn->d.ip->data, gn->d.ip->length, &addr6)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message("Host name %s does not match certificate subject alternative names\n", peer_host);
|
||||
} else {
|
||||
X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, peer_cn, sizeof(peer_cn));
|
||||
message("Host name %s does not match certificate common name %s or any subject alternative names\n", peer_host, peer_cn);
|
||||
return check_cert_valid_host(peer_cn, peer_host);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initiate an SSL handshake on this stream and encrypt all subsequent data */
|
||||
int stream_enable_ssl(PTSTREAM *pts) {
|
||||
int stream_enable_ssl(PTSTREAM *pts, const char *proxy_arg) {
|
||||
#ifdef USE_SSL
|
||||
const SSL_METHOD *meth;
|
||||
SSL *ssl;
|
||||
SSL_CTX *ctx;
|
||||
long res = 1;
|
||||
long ssl_options = 0;
|
||||
|
||||
|
||||
X509* cert = NULL;
|
||||
const char *ca_dir = "/etc/ssl/certs/"; /* Default cert directory if none given */
|
||||
long vresult;
|
||||
char *peer_host = NULL;
|
||||
char proxy_arg_fmt[32];
|
||||
size_t proxy_arg_len;
|
||||
|
||||
/* Initialise the connection */
|
||||
SSLeay_add_ssl_algorithms();
|
||||
if (args_info.enforcetls1_flag) {
|
||||
|
|
@ -165,6 +261,14 @@ int stream_enable_ssl(PTSTREAM *pts) {
|
|||
ssl_options |= SSL_OP_NO_SSLv3;
|
||||
}
|
||||
SSL_CTX_set_options (ctx, ssl_options);
|
||||
|
||||
if ( !args_info.no_check_cert_flag ) {
|
||||
if (!SSL_CTX_load_verify_locations(ctx, NULL, ca_dir)) {
|
||||
message("Error loading certificates from %s\n", ca_dir);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ssl = SSL_new (ctx);
|
||||
|
||||
SSL_set_rfd (ssl, stream_get_incoming_fd(pts));
|
||||
|
|
@ -182,6 +286,42 @@ int stream_enable_ssl(PTSTREAM *pts) {
|
|||
|
||||
SSL_connect (ssl);
|
||||
|
||||
if ( !args_info.no_check_cert_flag ) {
|
||||
/* Make sure peer presented a certificate */
|
||||
cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert == NULL) {
|
||||
message("No certificate presented\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check that the certificate is valid */
|
||||
vresult = SSL_get_verify_result(ssl);
|
||||
if (vresult != X509_V_OK) {
|
||||
message("Certificate verification failed (%s)\n",
|
||||
X509_verify_cert_error_string(vresult));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Determine the host name we are connecting to */
|
||||
proxy_arg_len = strlen(proxy_arg);
|
||||
if ((peer_host = malloc(proxy_arg_len + 1)) == NULL) {
|
||||
message("Out of memory\n");
|
||||
goto fail;
|
||||
}
|
||||
snprintf( proxy_arg_fmt, sizeof(proxy_arg_fmt), proxy_arg[0] == '[' ? "[%%%zu[^]]]" : "%%%zu[^:]", proxy_arg_len - 1 );
|
||||
if ( sscanf( proxy_arg, proxy_arg_fmt, peer_host ) != 1 ) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Verify the certificate name matches the host we are connecting to */
|
||||
if (!check_cert_names(cert, peer_host)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(peer_host);
|
||||
X509_free(cert);
|
||||
}
|
||||
|
||||
/* Store ssl and ctx parameters */
|
||||
pts->ssl = ssl;
|
||||
pts->ctx = ctx;
|
||||
|
|
@ -190,6 +330,17 @@ int stream_enable_ssl(PTSTREAM *pts) {
|
|||
#endif /* USE_SSL */
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
#ifdef USE_SSL
|
||||
if (cert != NULL) {
|
||||
X509_free(cert);
|
||||
}
|
||||
if (peer_host != NULL) {
|
||||
free(peer_host);
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ int stream_close(PTSTREAM *pts);
|
|||
int stream_read(PTSTREAM *pts, void *buf, size_t len);
|
||||
int stream_write(PTSTREAM *pts, void *buf, size_t len);
|
||||
int stream_copy(PTSTREAM *pts_from, PTSTREAM *pts_to);
|
||||
int stream_enable_ssl(PTSTREAM *pts);
|
||||
int stream_enable_ssl(PTSTREAM *pts, const char *proxy_arg);
|
||||
int stream_get_incoming_fd(PTSTREAM *pts);
|
||||
int stream_get_outgoing_fd(PTSTREAM *pts);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue