From b92a1de60b4988ba966b16948e9a331a0ac35e60 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Thu, 12 Mar 2020 23:13:17 +0100 Subject: [PATCH] Use sha256 for the challenge response. sha1 is broken and should no longer be used. This commit introduces the new server feature "sha256". It is announced during the handshake with the client. The client code is patched to detect whether the server supports the feature and uses sha256 if it does. This change is backwards compatible. That is, old clients can still connect to a new server (using sha1). Also new clients can connect to an old server (and also use sha1 in this case). --- client_common.c | 37 +++++++++++++++++++++++++++++++------ command.c | 30 ++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/client_common.c b/client_common.c index 16568b9c..94a6e865 100644 --- a/client_common.c +++ b/client_common.c @@ -258,6 +258,11 @@ static int send_sb_command(struct client_task *ct) return send_sb(ct, 0, command, len, SBD_COMMAND, false); } +static bool has_feature(const char *feature, struct client_task *ct) +{ + return find_arg(feature, ct->features) >= 0? true : false; +} + /* * This function reads or writes to the socket file descriptor which * corresponds to an established connection between the client and the server. @@ -290,15 +295,25 @@ static int client_post_select(struct sched *s, void *context) ct->status = CL_RECEIVED_WELCOME; return 0; case CL_RECEIVED_WELCOME: /* send auth command */ + { + /* + * Use sha256 iff the server announced the feature. After 0.7.0 + * we may request and use the feature unconditionally. After + * 0.8.0 we no longer need to request the feature. + */ + bool has_sha256; if (!FD_ISSET(ct->scc.fd, &s->wfds)) return 0; - sprintf(buf, AUTH_REQUEST_MSG "%s", ct->user); + has_sha256 = has_feature("sha256", ct); + sprintf(buf, AUTH_REQUEST_MSG "%s%s", ct->user, has_sha256? + " sha256" : ""); PARA_INFO_LOG("--> %s\n", buf); ret = write_buffer(ct->scc.fd, buf); if (ret < 0) goto out; ct->status = CL_SENT_AUTH; return 0; + } case CL_SENT_AUTH: /* * Receive challenge and session keys, decrypt the challenge and @@ -324,19 +339,29 @@ static int client_post_select(struct sched *s, void *context) free(sbb.iov.iov_base); if (ret < 0) goto out; - ct->challenge_hash = para_malloc(HASH_SIZE); - hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash); + ct->challenge_hash = para_malloc(HASH2_SIZE); + + if (has_feature("sha256", ct)) { + hash2_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash); + hash2_to_asc(ct->challenge_hash, buf); + } else { + hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash); + hash_to_asc(ct->challenge_hash, buf); + } ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN); ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN); - hash_to_asc(ct->challenge_hash, buf); PARA_INFO_LOG("--> %s\n", buf); ct->status = CL_RECEIVED_CHALLENGE; return 0; } case CL_RECEIVED_CHALLENGE: - ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE, - SBD_CHALLENGE_RESPONSE, false); + if (has_feature("sha256", ct)) + ret = send_sb(ct, 0, ct->challenge_hash, HASH2_SIZE, + SBD_CHALLENGE_RESPONSE, false); + else + ret = send_sb(ct, 0, ct->challenge_hash, HASH_SIZE, + SBD_CHALLENGE_RESPONSE, false); if (ret != 0) ct->challenge_hash = NULL; if (ret <= 0) diff --git a/command.c b/command.c index f4eeb524..21714571 100644 --- a/command.c +++ b/command.c @@ -762,7 +762,7 @@ static void reset_signals(void) } struct connection_features { - int dummy; /* none at the moment */ + bool sha256_requested; /* can be removed after 0.7.0 */ }; static int parse_auth_request(char *buf, int len, const struct user **u, @@ -797,6 +797,12 @@ static int parse_auth_request(char *buf, int len, const struct user **u, continue; if (strcmp(features[i], "aes_ctr128") == 0) continue; + /* + * ->sha256_requested can go away after 0.7.0 but the + * check has to stay until 0.9.0. + */ + if (strcmp(features[i], "sha256") == 0) + cf->sha256_requested = true; else { ret = -E_BAD_FEATURE; goto out; @@ -894,7 +900,7 @@ int handle_connect(int fd) { int ret; unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN]; - unsigned char challenge_hash[HASH_SIZE]; + unsigned char challenge_hash[HASH2_SIZE]; char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */; size_t numbytes; struct command_context cc_struct = {.u = NULL}, *cc = &cc_struct; @@ -910,7 +916,7 @@ int handle_connect(int fd) /* send Welcome message */ ret = write_va_buffer(fd, "This is para_server, version " PACKAGE_VERSION ".\n" - "Features:\n" + "Features: sha256\n" /* no longer announce this after 0.8.0 */ ); if (ret < 0) goto net_err; @@ -958,11 +964,19 @@ int handle_connect(int fd) * of the random data. */ ret = -E_BAD_AUTH; - if (numbytes != HASH_SIZE) - goto net_err; - hash_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash); - if (memcmp(challenge_hash, buf, HASH_SIZE)) - goto net_err; + if (cf.sha256_requested) { + if (numbytes != HASH2_SIZE) + goto net_err; + hash2_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash); + if (memcmp(challenge_hash, buf, HASH2_SIZE)) + goto net_err; + } else { /* old client. This can be removed after 0.7.0 */ + if (numbytes != HASH_SIZE) + goto net_err; + hash_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash); + if (memcmp(challenge_hash, buf, HASH_SIZE)) + goto net_err; + } /* auth successful */ alarm(0); PARA_INFO_LOG("good auth for %s\n", cc->u->name); -- 2.39.5