]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - command.c
Use sideband also for challenge response.
[paraslash.git] / command.c
index cdc42fa1a220662ac7413b52fc40aba0f56e14d8..5e13df66bce15a7e759ce1dd85347b9a176a5417 100644 (file)
--- a/command.c
+++ b/command.c
@@ -15,6 +15,7 @@
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
+#include "sideband.h"
 #include "command.h"
 #include "server.cmdline.h"
 #include "string.h"
@@ -191,6 +192,118 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman
        return 1;
 }
 
+/**
+ * Send a sideband packet through a blocking file descriptor.
+ *
+ * \param scc fd and crypto keys.
+ * \param buf The buffer to send.
+ * \param numbytes The size of \a buf.
+ * \param band The sideband designator of this packet.
+ * \param dont_free If true, never deallocate \a buf.
+ *
+ * The nonblock flag must be disabled for the file descriptor given by \a scc.
+ *
+ * Stream cipher encryption is automatically activated if neccessary via the
+ * sideband transformation, depending on the value of \a band.
+ *
+ * \return Standard.
+ *
+ * \sa \ref send_sb_va().
+ */
+int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
+               int band, bool dont_free)
+{
+       int ret;
+       struct sb_context *sbc;
+       struct iovec iov[2];
+       struct sb_buffer sbb = SBB_INIT(band, buf, numbytes);
+       sb_transformation trafo = band < SBD_PROCEED? NULL : sc_trafo;
+
+       sbc = sb_new_send(&sbb, dont_free, trafo, scc->send);
+       do {
+               ret = sb_get_send_buffers(sbc, iov);
+               ret = xwritev(scc->fd, iov, ret);
+               if (ret < 0)
+                       goto fail;
+       } while (sb_sent(sbc, ret) == false);
+       return 1;
+fail:
+       sb_free(sbc);
+       return ret;
+}
+
+/**
+ * Create a variable sized buffer and send it as a sideband packet.
+ *
+ * \param scc Passed to \ref send_sb.
+ * \param band See \ref send_sb.
+ * \param fmt The format string.
+ *
+ * \return The return value of the underlying call to \ref send_sb.
+ */
+__printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
+               const char *fmt, ...)
+{
+       va_list ap;
+       char *msg;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = xvasprintf(&msg, fmt, ap);
+       va_end(ap);
+       return send_sb(scc, msg, ret, band, false);
+}
+
+/**
+ * Send a sideband packet through a blocking file descriptor.
+ *
+ * \param scc fd and crypto keys.
+ * \param expected_band The expected band designator.
+ * \param max_size Passed to \ref sb_new_recv().
+ * \param result Body of the sideband packet is returned here.
+ *
+ * If \a expected_band is not \p SBD_ANY, the band designator of the received
+ * sideband packet is compared to \a expected_band and a mismatch is considered
+ * an error.
+ *
+ * \return Standard.
+ */
+int recv_sb(struct stream_cipher_context *scc,
+               enum sb_designator expected_band,
+               size_t max_size, struct iovec *result)
+{
+       int ret;
+       struct sb_context *sbc;
+       struct iovec iov;
+       struct sb_buffer sbb;
+       sb_transformation trafo;
+
+       trafo = expected_band != SBD_ANY && expected_band < SBD_PROCEED?
+               NULL : sc_trafo;
+       sbc = sb_new_recv(max_size, trafo, scc->recv);
+       for (;;) {
+               sb_get_recv_buffer(sbc, &iov);
+               ret = recv_bin_buffer(scc->fd, iov.iov_base, iov.iov_len);
+               if (ret == 0)
+                       ret = -E_EOF;
+               if (ret < 0)
+                       goto fail;
+               ret = sb_received(sbc, ret, &sbb);
+               if (ret < 0)
+                       goto fail;
+               if (ret > 0)
+                       break;
+       }
+       ret = -E_BAD_BAND;
+       if (expected_band != SBD_ANY && sbb.band != expected_band)
+               goto fail;
+       *result = sbb.iov;
+       return 1;
+fail:
+       sb_free(sbc);
+       return ret;
+}
+
 int com_sender(struct command_context *cc)
 {
        int i, ret;
@@ -741,6 +854,8 @@ out:
        return ret;
 }
 
+#define HANDSHAKE_BUFSIZE 4096
+
 /**
  * Perform user authentication and execute a command.
  *
@@ -771,10 +886,9 @@ out:
 __noreturn void handle_connect(int fd, const char *peername)
 {
        int ret;
-       char buf[4096];
        unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH_SIZE];
-       char *p, *command = NULL;
+       char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
        struct command_context cc_struct = {.peer = peername}, *cc = &cc_struct;
 
@@ -792,7 +906,7 @@ __noreturn void handle_connect(int fd, const char *peername)
        if (ret < 0)
                goto net_err;
        /* recv auth request line */
-       ret = recv_buffer(fd, buf, sizeof(buf));
+       ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
        if (ret < 0)
                goto net_err;
        ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband);
@@ -819,15 +933,29 @@ __noreturn void handle_connect(int fd, const char *peername)
        }
        PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n",
                CHALLENGE_SIZE, numbytes);
-       ret = write_all(fd, buf, numbytes);
-       if (ret < 0)
-               goto net_err;
-       /* recv challenge response */
-       ret = recv_bin_buffer(fd, buf, HASH_SIZE);
-       if (ret < 0)
-               goto net_err;
-       numbytes = ret;
-       PARA_DEBUG_LOG("received %d bytes challenge response\n", ret);
+       if (cc->use_sideband) {
+               struct iovec iov;
+               ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
+               buf = NULL;
+               if (ret < 0)
+                       goto net_err;
+               ret = recv_sb(&cc->scc, SBD_CHALLENGE_RESPONSE,
+                       HANDSHAKE_BUFSIZE, &iov);
+               if (ret < 0)
+                       goto net_err;
+               buf = iov.iov_base;
+               numbytes = iov.iov_len;
+       } else {
+               ret = write_all(fd, buf, numbytes);
+               if (ret < 0)
+                       goto net_err;
+               /* recv challenge response */
+               ret = recv_bin_buffer(fd, buf, HASH_SIZE);
+               if (ret < 0)
+                       goto net_err;
+               numbytes = ret;
+       }
+       PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes);
        ret = -E_BAD_USER;
        if (!cc->u)
                goto net_err;
@@ -882,6 +1010,7 @@ err_out:
 net_err:
        PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
 out:
+       free(buf);
        free(command);
        sc_free(cc->scc.recv);
        sc_free(cc->scc.send);