From a425fa20d8b6ffdbe7889e476e3760c5fc5abf3a Mon Sep 17 00:00:00 2001 From: Sven Geuer <68420948@users.noreply.github.com> Date: Mon, 11 Dec 2023 18:31:42 +0100 Subject: [PATCH] On SSL connections, introduce authentication by client certificate --- cmdline.c | 42 ++++++++++++++++++++++++++++++++++++++--- cmdline.h | 4 ++++ docs/proxytunnel.1.adoc | 22 +++++++++++++++++++-- ptstream.c | 12 ++++++++++++ 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/cmdline.c b/cmdline.c index 529e3b7..ab96d96 100644 --- a/cmdline.c +++ b/cmdline.c @@ -78,6 +78,10 @@ void cmdline_parser_print_help (void) { " -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" +#ifdef USE_SSL +" -c, --cert=FILENAME client SSL certificate (chain)\n" +" -k, --key=FILENAME client SSL key\n" +#endif // " -u, --user=STRING Username for proxy authentication\n" // " -s, --pass=STRING Password for proxy authentication\n" // " -U, --uservar=STRING Environment variable that holds username\n" @@ -145,6 +149,8 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar args_info->encrypt_given = 0; args_info->encryptproxy_given = 0; args_info->encryptremproxy_given = 0; + args_info->clientcert_given = 0; + args_info->clientkey_given = 0; args_info->wa_bug_29744_given = 0; args_info->proctitle_given = 0; args_info->enforcetls1_given = 0; @@ -174,6 +180,8 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar args_info->encrypt_flag = 0; \ args_info->encryptproxy_flag = 0; \ args_info->encryptremproxy_flag = 0; \ + args_info->clientcert_arg = NULL; \ + args_info->clientkey_arg = NULL; \ args_info->wa_bug_29744_flag = 0; \ args_info->no_ssl3_flag = 0; \ args_info->proctitle_arg = NULL; \ @@ -226,6 +234,8 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar { "encrypt", 0, NULL, 'e' }, { "encrypt-proxy", 0, NULL, 'E' }, { "encrypt-remproxy",0,NULL, 'X' }, + { "cert", 1, NULL, 'c' }, + { "key", 1, NULL, 'k' }, { "wa-bug-29744", 0, NULL, 'W' }, { "buggy-encrypt-proxy", 0, NULL, 'B' }, { "no-ssl3", 0, NULL, 'T' }, @@ -236,9 +246,9 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar { NULL, 0, NULL, 0 } }; - c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXWBqLo:TzC:46", long_options, &option_index); + c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:c:k:vNeEXWBqLo:TzC:46", long_options, &option_index); #else - c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXWBqLo:TzC:46" ); + c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:c:k:vNeEXWBqLo:TzC:46" ); #endif if (c == -1) @@ -263,6 +273,26 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar message("SSL client to proxy enabled\n"); break; + case 'c': /* client SSL certificate (chain) */ + if (args_info->clientcert_given) { + fprintf (stderr, "%s: '--cert' ('-c') option given more than once\n", PACKAGE); + clear_args (); + exit(1); + } + args_info->clientcert_given = 1; + args_info->clientcert_arg = gengetopt_strdup (optarg); + break; + + case 'k': /* client SSL key */ + if (args_info->clientkey_given) { + fprintf (stderr, "%s: '--key' ('-k') option given more than once\n", PACKAGE); + clear_args (); + exit(1); + } + args_info->clientkey_given = 1; + args_info->clientkey_arg = gengetopt_strdup (optarg); + break; + case 'W': /* if SSL is active stop it after CONNECT */ args_info->wa_bug_29744_flag = !(args_info->wa_bug_29744_flag); if( args_info->verbose_flag ) @@ -318,7 +348,7 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar case 'o': args_info->host_given = 1; - message("Host-header override enabled\n"); + message("Host-header/SNI override enabled\n"); args_info->host_arg = gengetopt_strdup (optarg); break; @@ -585,6 +615,12 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar exit(1); } + if ( args_info->clientcert_given ^ args_info->clientkey_given ) { + clear_args (); + message( "Both of '--cert' ('-c') and '--key' ('-k') must be specified\n" ); + exit(1); + } + if (args_info->proxy_given ) { char proxy_arg_fmt[32]; size_t proxy_arg_len; diff --git a/cmdline.h b/cmdline.h index d0fe282..498a5e6 100644 --- a/cmdline.h +++ b/cmdline.h @@ -47,6 +47,8 @@ struct gengetopt_args_info { int encrypt_flag; /* Turn on SSL encryption (default=off). */ int encryptproxy_flag; /* Turn on client to proxy SSL encryption (def=off).*/ int encryptremproxy_flag; /* Turn on local to remote proxy SSL encryption (def=off).*/ + char *clientcert_arg; /* client SSL certificate */ + char *clientkey_arg; /* client SSL key */ int wa_bug_29744_flag; /* Use SSL encryption only until CONNECT, if at all (def=off).*/ int no_ssl3_flag; /* Turn off SSLv3 (default=on) */ char *proctitle_arg; /* Override process title (default=off). */ @@ -78,6 +80,8 @@ struct gengetopt_args_info { int encrypt_given; /* Whether encrypt was given */ int encryptproxy_given; /* Whether encrypt was given */ int encryptremproxy_given; /* Whether encrypt was given */ + int clientcert_given; /* Whether client SSL certificate was given */ + int clientkey_given; /* Whether client SSL key was given */ int wa_bug_29744_given; /* Whether work around was given */ int proctitle_given; /* Whether to override process title */ int enforcetls1_given; /* Wheter to enforce TLSv1 */ diff --git a/docs/proxytunnel.1.adoc b/docs/proxytunnel.1.adoc index cf2846e..e45b766 100644 --- a/docs/proxytunnel.1.adoc +++ b/docs/proxytunnel.1.adoc @@ -101,6 +101,24 @@ also be used for other proxy-traversing purposes like proxy bouncing. If the _password_ is omitted and no *REMPROXYPASS* environment variable is set, proxytunnel will prompt for a password +*-c*, *--cert*=_filename_:: + Provide the name of the file containing the client SSL certificate to + authenticate by client certificate against a local proxy, remote proxy or + the destination. The file must be in PEM format. + On top of this it may contain one or more intermediary certificates missing + at the servers's end, effectively forming a certificate chain. + Requires specification of *-k*, *--key* in addition. + Ignored if neither *-e*, *--encrypt** nor *-E*, *--encrypt-proxy* nor + *-X*, *--encrypt-remproxy* is given. + +*-k*, *--key*=_filename_:: + Provide the name of the file containing the client SSL key to authenticate + by client certificate against a local proxy, remote proxy or the + destination. The file must be in PEM format. + Requires specification of *-c*, *--cert* in addition. + Ignored if neither *-e*, *--encrypt** nor *-E*, *--encrypt-proxy* nor + *-X*, *--encrypt-remproxy* is given. + *-N*, *--ntlm*:: Use NTLM based authentication @@ -110,8 +128,8 @@ also be used for other proxy-traversing purposes like proxy bouncing. *-H*, *--header*=_STRING_:: Add additional HTTP headers to send to proxy -*-o*, *--host*=_STRING_:: - Send a custom Host Header +*-o*, *--host*=_fully_qualified_domain_name_:: + Send a custom Host Header. Also used as SNI with SSL connections. *-x*, *--proctitle*=_STRING_:: Use a different process title diff --git a/ptstream.c b/ptstream.c index 33010a1..64a4782 100644 --- a/ptstream.c +++ b/ptstream.c @@ -317,6 +317,18 @@ int stream_enable_ssl(PTSTREAM *pts, const char *proxy_arg) { } } + /* If given, load client certificate (chain) and key */ + if ( args_info.clientcert_given && args_info.clientkey_given ) { + if ( 1 != SSL_CTX_use_certificate_chain_file(ctx, args_info.clientcert_arg) ) { + message("Error loading client certificate (chain) from %s\n", args_info.clientcert_arg); + goto fail; + } + if ( 1 != SSL_CTX_use_PrivateKey_file(ctx, args_info.clientkey_arg, SSL_FILETYPE_PEM) ) { + message("Error loading client key from %s, or key does not match certificate\n", args_info.clientkey_arg); + goto fail; + } + } + ssl = SSL_new (ctx); if ( ssl == NULL ) { message("SSL_new failed\n");