Use sha256 for the challenge response.
authorAndre Noll <maan@tuebingen.mpg.de>
Thu, 12 Mar 2020 22:13:17 +0000 (23:13 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Thu, 21 Oct 2021 17:52:40 +0000 (19:52 +0200)
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
command.c

index 16568b9cc42362381e4d57f1f3305340a406e720..94a6e86544e63824664b114cc400161d4e223684 100644 (file)
@@ -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)
index f4eeb524deaf87c303fec3ff78d4a56c278a8cff..2171457168c294861ece3c644efe34709f74f5cf 100644 (file)
--- 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);