diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2019-06-30 10:51:58 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2019-06-30 10:51:58 +0200 |
commit | dd1d4b33c086fb13a27ee30b253adee88614f780 (patch) | |
tree | 397ab0e98ecbd4ecd28472e4380a7a24902e377a | |
parent | 76ad717ca068d9af838c346aca792e9d3afd6026 (diff) |
preparations for use of multiple hash algos for challenge response
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r-- | .gitlab-ci.yml | 4 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/challenge.c | 51 | ||||
-rw-r--r-- | src/challenge.h | 41 | ||||
-rw-r--r-- | src/options.c | 41 | ||||
-rw-r--r-- | src/options.h | 10 | ||||
-rw-r--r-- | src/pconfig.h | 2 | ||||
-rw-r--r-- | src/pkt.c | 22 | ||||
-rw-r--r-- | src/ptunnel.c | 3 | ||||
-rw-r--r-- | src/utils.c | 2 |
11 files changed, 134 insertions, 46 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94ef12a..77484de 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,7 +63,7 @@ build-clang: script: - autoreconf -fi - CC=clang ./configure --enable-option-checking=fatal --prefix=/ - - make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy/clang)" V=s + - make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/clang)" V=s stage: build artifacts: paths: @@ -83,7 +83,7 @@ build-android: - cd .. - autoreconf -fi - CC=aarch64-linux-android28-clang PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" ./configure --enable-option-checking=fatal --host=aarch64-linux-android - - PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy/android28-clang)" V=s + - PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/android28-clang)" V=s stage: build artifacts: paths: @@ -123,7 +123,7 @@ installed. TODOs ----- -- challenge response: switch from md5 to sha-512 +- challenge response: switch from md5 to sha-512 (WiP) - packet obfuscation - encryption (metadata + payload) @@ -141,7 +141,7 @@ installed. ## TODOs ``` -- challenge response: switch from md5 to sha-512 +- challenge response: switch from md5 to sha-512 (WiP) - packet obfuscation - encryption (metadata + payload) ``` diff --git a/src/challenge.c b/src/challenge.c index f269313..f0b02ad 100644 --- a/src/challenge.c +++ b/src/challenge.c @@ -46,6 +46,7 @@ #include <stdlib.h> #include <string.h> #include <sys/time.h> +#include <assert.h> #include "challenge.h" #include "options.h" @@ -55,48 +56,66 @@ /* generate_challenge: Generates a random challenge, incorporating the current * local timestamp to avoid replay attacks. */ -challenge_t* generate_challenge(void) { +challenge_t *generate_challenge(void) { struct timeval tt; challenge_t *c; int i; c = (challenge_t *) calloc(1, sizeof(challenge_t)); + assert(c != NULL); gettimeofday(&tt, 0); - c->sec = tt.tv_sec; - c->usec_rnd = tt.tv_usec + pt_random(); + c->plain.sec = tt.tv_sec; + c->plain.usec_rnd = tt.tv_usec + pt_random(); for (i=0;i<6;i++) - c->random[i] = pt_random(); + c->plain.random[i] = pt_random(); return c; } -/* generate_response: Generates a response to the given challenge. The response +/* generate_response_md5: Generates a response to the given challenge. The response * is generated by combining the concatenating the challenge data with the * md5 digest of the password, and then calculating the MD5 digest of the * entire buffer. The result is stored in the passed-in challenge, overwriting * the challenge data. */ -void generate_response(challenge_t *challenge) { - md5_byte_t buf[sizeof(challenge_t)+kMD5_digest_size]; +void generate_response_md5(challenge_plain_t *plain, challenge_digest_t *digest) { + md5_byte_t buf[sizeof(*plain) + kMD5_digest_size]; md5_state_t state; - memcpy(buf, challenge, sizeof(challenge_t)); - memcpy(&buf[sizeof(challenge_t)], opts.password_digest, kMD5_digest_size); - memset(challenge, 0, sizeof(challenge_t)); + digest->hash_type = HT_MD5; + memcpy(buf, plain, sizeof(*plain)); + memcpy(&buf[sizeof(*plain)], opts.md5_password_digest, kMD5_digest_size); + memset(plain, 0, sizeof(*plain)); + md5_init(&state); - md5_append(&state, buf, sizeof(challenge_t)+kMD5_digest_size); - md5_finish(&state, (md5_byte_t*)challenge); + md5_append(&state, buf, sizeof(*plain) + kMD5_digest_size); + md5_finish(&state, (md5_byte_t *) &digest->md5[0]); } -/* validate_challenge: Checks whether a given response matches the expected +/* validate_challenge_md5: Checks whether a given response matches the expected * response, returning 1 if validation succeeded, and 0 otherwise. Note that * overwriting the local challenge with the challenge result is not a problem, * as the data will not be used again anyway (authentication either succeeds, * or the connection is closed down). */ -int validate_challenge(challenge_t *local, challenge_t *remote) { - generate_response(local); - if (memcmp(local, remote, sizeof(challenge_t)) == 0) +int validate_challenge_md5(challenge_t *local, challenge_digest_t *remote) { + generate_response_md5(&local->plain, &local->digest); + if (remote->hash_type == HT_MD5 && + memcmp(&local->digest.md5[0], &remote->md5[0], sizeof(local->digest.md5)) == 0) + { return 1; + } return 0; } + +#ifdef ENABLE_SHA512 +void generate_response_sha512(challenge_t *challenge) +{ + /* TODO: Implement me! */ +} + +int validate_challenge_sha512(challenge_t *local, challenge_t *remote) +{ + /* TODO: Implement me! */ +} +#endif /* ENABLE_SHA512 */ diff --git a/src/challenge.h b/src/challenge.h index 18495cf..203e420 100644 --- a/src/challenge.h +++ b/src/challenge.h @@ -46,23 +46,50 @@ #ifndef CHALLENGE_H #define CHALLENGE_H 1 +#include "pconfig.h" + #include <stdint.h> +#ifdef ENABLE_SHA512 +#include <openssl/sha.h> +#endif -/** challenge_t: This structure contains the pseudo-random challenge used for - * authentication. - */ -typedef struct challenge_t { +#define HT_MD5 0x1 +#define HT_SHA512 0x2 + + +typedef struct challenge_plain_t { /** tv_sec as returned by gettimeofday */ uint32_t sec; /** tv_usec as returned by gettimeofday + random value */ uint32_t usec_rnd; /** random values */ uint32_t random[6]; +} __attribute__ ((packed)) challenge_plain_t; + +typedef struct challenge_digest_t { + uint8_t hash_type; + union { + unsigned char md5[kMD5_digest_size]; + unsigned char sha512[kSHA512_digest_size]; + }; +} __attribute__ ((packed)) challenge_digest_t; + +/** challenge_t: This structure contains the pseudo-random challenge used for + * authentication. If OpenSSL is available SHA512 will be used per default. + */ +typedef struct challenge_t { + challenge_plain_t plain; + challenge_digest_t digest; } __attribute__ ((packed)) challenge_t; +challenge_t *generate_challenge(void); + +void generate_response_md5(challenge_plain_t *plain, challenge_digest_t *digest); +int validate_challenge_md5(challenge_t *local, challenge_digest_t *remote); -challenge_t* generate_challenge(void); -void generate_response(challenge_t *challenge); -int validate_challenge(challenge_t *local, challenge_t *remote); +#ifdef ENABLE_SHA512 +void generate_response_sha512(challenge_plain_t *plain, challenge_digest_t *digest); +int validate_challenge_sha512(challenge_t *local, challenge_digest_t *remote); +#endif #endif diff --git a/src/options.c b/src/options.c index 79c11ec..10838ad 100644 --- a/src/options.c +++ b/src/options.c @@ -41,6 +41,9 @@ #ifdef WIN32 #include <ws2tcpip.h> #endif +#ifdef ENABLE_SHA512 +#include <openssl/sha.h> +#endif #ifdef HAVE_CONFIG_H #include "config.h" @@ -169,6 +172,22 @@ static const struct option_usage usage[] = { "Tune the number of empty pings to send with each explicit acknowledgement.\n" "Empty pings can compensate for ICMP sequence number inspection.\n" }, + /** --force-md5 */ + {"force-md5", 0, OPT_BOOL, {.num = 0}, + "Force MD5 as challenge response checksum generator.\n" +#ifndef ENABLE_SHA512 + "This is the default for this configuration.\n" +#endif + }, + /** --force-sha512 */ + {"force-sha512", 0, OPT_BOOL, {.num = 0}, + "Force SHA512 as challenge response checksum generator.\n" +#ifdef ENABLE_SHA512 + "This is the default for this configuration.\n" +#else + "SHA512 is not available for this configuration.\n" +#endif + }, /** --daemon */ {"pidfile", 0, OPT_STR, {.str = "/run/ptunnel.pid"}, #ifdef WIN32 @@ -237,6 +256,8 @@ static struct option long_options[] = { {"resend-interval", required_argument, 0, 't'}, {"payload-size", required_argument, 0, 'y'}, {"empty-pings", required_argument, 0, 'E'}, + {"force-md5", no_argument, &opts.force_md5, 1}, + {"force-sha512", no_argument, &opts.force_sha512, 1}, {"daemon", optional_argument, 0, 'd'}, {"syslog", no_argument, 0, 'S'}, {"user", optional_argument, 0, 'u'}, @@ -498,13 +519,17 @@ int parse_options(int argc, char **argv) { if (opts.password) free(opts.password); opts.password = strdup(optarg); - pt_log(kLog_debug, "Password set - unauthenicated connections will be refused.\n"); - // Compute the password digest + pt_log(kLog_debug, "%s\n", "Password set - unauthenicated connections will be refused."); + /* Compute the md5 password digest */ md5_init(&state); md5_append(&state, (md5_byte_t*)optarg, strnlen(opts.password, BUFSIZ /* not optimal */)); - md5_finish(&state, &opts.password_digest[0]); + md5_finish(&state, &opts.md5_password_digest[0]); // Hide the password in process listing memset(optarg, '*', strnlen(optarg, BUFSIZ /* not optimal */)); +#ifdef ENABLE_SHA512 + pt_log(kLog_debug, "%s\n", "Password set - sha512 authentication enabled."); + SHA512(optarg, strnlen(opts.password, BUFSIZ /* not optimal */), &opts.sha512_password_digest[0]); +#endif break; #ifndef WIN32 case 'd': @@ -609,6 +634,16 @@ int parse_options(int argc, char **argv) { exit(1); } +#if ENABLE_SHA512 + if (opts.force_md5) { + pt_log(kLog_error, "%s\n", "You are forcing md5 but sha512 is available."); + } +#else + if (opts.force_sha512) { + pt_log(kLog_error, "%s\n", "You are forcing sha512 but it isn't available."); + } +#endif + if (opts.given_proxy_hostname) { if ((ret = host_to_addr(opts.given_proxy_hostname, &opts.given_proxy_ip)) != 0) { pt_log(kLog_error, "Failed to look up %s as destination address: %s\n", diff --git a/src/options.h b/src/options.h index 9c2ca3f..5551426 100644 --- a/src/options.h +++ b/src/options.h @@ -75,6 +75,10 @@ struct options { /** Device to capture packets from */ char *pcap_device; #endif + /** Force MD5 based challenge response. */ + int force_md5; + /** Force SHA512 based challenge response. */ + int force_sha512; /** List all available pcap devices and exit */ int list_pcap_devices; /** Usually stdout, but can be altered by the user */ @@ -84,8 +88,10 @@ struct options { int print_stats; /** Password (must be the same on proxy and client for authentica tion to succeed) */ char *password; - /** MD5 digest of challenge+password */ - md5_byte_t password_digest[kMD5_digest_size]; + /** MD5 digest of password */ + md5_byte_t md5_password_digest[kMD5_digest_size]; + /** SHA512 digest of password */ + unsigned char sha512_password_digest[kSHA512_digest_size]; /** use UDP instead of ICMP */ int udp; /** unpriviledged mode */ diff --git a/src/pconfig.h b/src/pconfig.h index e13f7ee..5c642ee 100644 --- a/src/pconfig.h +++ b/src/pconfig.h @@ -88,6 +88,8 @@ enum { kAutomatic_close_timeout = 60, // Seconds! /** size of md5 digest in bytes */ kMD5_digest_size = 16, + /** size of sha512 digest in bytes */ + kSHA512_digest_size = 64, /** These constants are used to indicate the protocol state. The protocol * works as follows: * - The identifier is used by both the proxy and the forwarder @@ -236,7 +236,7 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a return; } pt_log(kLog_debug, "Got authentication challenge - sending response\n"); - generate_response(challenge); + generate_response_md5(&challenge->plain, &challenge->digest); queue_packet(icmp_sock, cur->pkt_type, (char*)challenge, sizeof(challenge_t), cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, @@ -254,8 +254,8 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a /* If proxy: Handle client's response to challenge */ else if (type_flag == proxy_flag) { pt_log(kLog_debug, "Received remote challenge response.\n"); - if (validate_challenge(cur->challenge, challenge) || - cur->authenticated) + if (validate_challenge_md5(cur->challenge, &challenge->digest) || + cur->authenticated) { pt_log(kLog_verbose, "Remote end authenticated successfully.\n"); handle_extended_options(cur); @@ -438,8 +438,8 @@ void handle_extended_options(void *vcur) if (cur->extended_options[0] > 0) { if (cur->extended_options[0] > cur->window_size) { size_t extend = cur->extended_options[0] - cur->window_size; - cur->send_ring = realloc(cur->send_ring, cur->extended_options[0] * sizeof(icmp_desc_t)); - cur->recv_ring = realloc(cur->recv_ring, cur->extended_options[0] * sizeof(forward_desc_t *)); + cur->send_ring = (icmp_desc_t *) realloc(cur->send_ring, cur->extended_options[0] * sizeof(icmp_desc_t)); + cur->recv_ring = (forward_desc_t **) realloc(cur->recv_ring, cur->extended_options[0] * sizeof(forward_desc_t *)); memset(cur->send_ring + cur->window_size, 0, extend * sizeof(icmp_desc_t)); memset(cur->recv_ring + cur->window_size, 0, extend * sizeof(forward_desc_t *)); } @@ -474,14 +474,14 @@ void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, pt_log(kLog_debug, "Received ack for only seq %d\n", seq_no); pt_pkt = (ping_tunnel_pkt_t*)ring[i].pkt->data; /* WARNING: We make the dangerous assumption here that packets arrive in order! */ - *remote_ack = (uint16_t)ntohl(pt_pkt->ack); + *remote_ack = (uint16_t) ntohl(pt_pkt->ack); free(ring[i].pkt); ring[i].pkt = 0; ring[i].pkt_len = 0; (*packets_awaiting_ack)--; if (i == *first_ack) { - for (j=1;j<window_size;j++) { - k = (i+j)%window_size; + for (j = 1; j < window_size; j += 2) { + k = (i + j) % window_size; if (ring[k].pkt) { *first_ack = k; break; @@ -489,7 +489,6 @@ void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, /* we have looped through everything */ if (k == i) *first_ack = insert_idx; - j++; } } return; @@ -525,7 +524,6 @@ void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, } } } -/* else - * pt_log(kLog_verbose, "Dropping superfluous acknowledgement (no outstanding packets needing ack.)\n"); - */ + else + pt_log(kLog_verbose, "Dropping superfluous acknowledgement (no outstanding packets needing ack.)\n"); } diff --git a/src/ptunnel.c b/src/ptunnel.c index bdf0a6f..c63c57a 100644 --- a/src/ptunnel.c +++ b/src/ptunnel.c @@ -175,7 +175,8 @@ int main(int argc, char *argv[]) { return -1; #endif - memset(opts.password_digest, 0, kMD5_digest_size); + memset(opts.md5_password_digest, 0, kMD5_digest_size); + memset(opts.sha512_password_digest, 0, kSHA512_digest_size); /* The seq_expiry_tbl is used to prevent the remote ends from prematurely * re-using a sequence number. diff --git a/src/utils.c b/src/utils.c index a65f947..8e340d4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -160,7 +160,7 @@ int pt_random(void) { #ifdef HAVE_ARC4RANDOM return arc4random(); #else -#if defined(USE_CUSTOMRNG) && !defined(_WIN32) +#if defined(RNGDEV) && !defined(_WIN32) static int rng_fd = -1; ssize_t bytes_read; int rnd_val; |