X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=command.c;h=8deb69ce2f233ee7d0c691a62cebd2d8ded43ce1;hp=4a464ebf88c56ab4ed0257e39ae5767709819811;hb=9c2a265397821d91ec628f549516b25757f5c801;hpb=69b3a249fb8f0f4a01e26f7a414dbe79f480a78c diff --git a/command.c b/command.c index 4a464ebf..8deb69ce 100644 --- 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" @@ -43,8 +44,6 @@ extern struct misc_meta_data *mmd; extern struct sender senders[]; int send_afs_status(struct command_context *cc, int parser_friendly); -const char *status_item_list[] = {STATUS_ITEM_ARRAY}; - static void dummy(__a_unused int s) { } @@ -193,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; @@ -703,6 +814,48 @@ static void reset_signals(void) para_sigaction(SIGHUP, SIG_DFL); } +static int parse_auth_request(char *buf, int len, struct user **u, + bool *use_sideband) +{ + int ret; + char *p, *username, **features = NULL; + size_t auth_rq_len = strlen(AUTH_REQUEST_MSG); + + *u = NULL; + *use_sideband = false; + if (len < auth_rq_len + 2) + return -E_AUTH_REQUEST; + if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0) + return -E_AUTH_REQUEST; + username = buf + auth_rq_len; + p = strchr(username, ' '); + if (p) { + int i; + if (p == username) + return -E_AUTH_REQUEST; + *p = '\0'; + p++; + create_argv(p, ",", &features); + for (i = 0; features[i]; i++) { + if (strcmp(features[i], "sideband") == 0) + *use_sideband = true; + else { + ret = -E_BAD_FEATURE; + goto out; + } + } + } + PARA_DEBUG_LOG("received auth request for user %s (sideband = %s)\n", + username, *use_sideband? "true" : "false"); + *u = lookup_user(username); + ret = 1; +out: + free_argv(features); + return ret; +} + +#define HANDSHAKE_BUFSIZE 4096 + /** * Perform user authentication and execute a command. * @@ -733,10 +886,9 @@ static void reset_signals(void) __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; @@ -748,19 +900,17 @@ __noreturn void handle_connect(int fd, const char *peername) goto net_err; /* send Welcome message */ ret = write_va_buffer(fd, "This is para_server, version " - PACKAGE_VERSION ".\n" ); + PACKAGE_VERSION ".\n" + "Features: sideband,foo\n" + ); 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; - if (ret < 10) { - ret = -E_AUTH_REQUEST; - goto net_err; - } - ret = -E_AUTH_REQUEST; - if (strncmp(buf, AUTH_REQUEST_MSG, strlen(AUTH_REQUEST_MSG))) + ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband); + if (ret < 0) goto net_err; p = buf + strlen(AUTH_REQUEST_MSG); PARA_DEBUG_LOG("received auth request for user %s\n", p); @@ -783,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; @@ -811,7 +975,10 @@ __noreturn void handle_connect(int fd, const char *peername) /* init stream cipher keys with the second part of the random buffer */ cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN); cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, SESSION_KEY_LEN); - ret = sc_send_buffer(&cc->scc, PROCEED_MSG); + if (cc->use_sideband) + ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false); + else + ret = sc_send_buffer(&cc->scc, PROCEED_MSG); if (ret < 0) goto net_err; ret = read_command(&cc->scc, &command); @@ -846,6 +1013,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);