X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=command.c;h=2ef9c5a87b54760ed38fc41e4f3b83391a4aa3f5;hp=2e733c5c6151b87eff0a9ac1c16b65ab64cbf5ad;hb=f167629b3191c57a6b691cd2a6af04a45a74ccb0;hpb=a092ce3940929f594d1f55d945cf43081706fb4b diff --git a/command.c b/command.c index 2e733c5c..2ef9c5a8 100644 --- a/command.c +++ b/command.c @@ -1,15 +1,20 @@ /* - * Copyright (C) 1997-2013 Andre Noll + * Copyright (C) 1997 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file command.c Client authentication and server commands. */ +#include +#include #include #include #include #include +#include +#include +#include #include "para.h" #include "error.h" @@ -30,13 +35,13 @@ #include "fd.h" #include "ipc.h" #include "user_list.h" -#include "server_command_list.h" -#include "afs_command_list.h" +#include "server.command_list.h" +#include "afs.command_list.h" #include "signal.h" #include "version.h" -struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY}; -struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY}; +static struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY}; +static struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY}; /** Commands including options must be shorter than this. */ #define MAX_COMMAND_LEN 32768 @@ -110,7 +115,6 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly, char mtime[30] = ""; char *status, *flags; /* vss status info */ /* nobody updates our version of "now" */ - char *ut = get_server_uptime_str(NULL); long offset = (nmmd->offset + 500) / 1000; struct timeval current_time; struct tm mtime_tm; @@ -144,7 +148,6 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly, (long unsigned)current_time.tv_usec); free(flags); free(status); - free(ut); *result = b.buf; return b.offset; } @@ -207,7 +210,7 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman * * The nonblock flag must be disabled for the file descriptor given by \a scc. * - * Stream cipher encryption is automatically activated if neccessary via the + * Stream cipher encryption is automatically activated if necessary via the * sideband transformation, depending on the value of \a band. * * \return Standard. @@ -220,8 +223,8 @@ int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes, 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; + struct sb_buffer sbb = SBB_INIT(band, buf, numbytes); sbc = sb_new_send(&sbb, dont_free, trafo, scc->send); do { @@ -268,10 +271,7 @@ __printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band, */ int send_strerror(struct command_context *cc, int err) { - return cc->use_sideband? - send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err)) - : - sc_send_va_buffer(&cc->scc, "%s\n", para_strerror(err)); + return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err)); } /** @@ -338,23 +338,17 @@ static int com_sender(struct command_context *cc) free(msg); msg = tmp; } - if (cc->use_sideband) - return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); } ret = check_sender_args(cc->argc, cc->argv, &scd); if (ret < 0) { if (scd.sender_num < 0) return ret; - msg = senders[scd.sender_num].help(); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, - false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + if (strcmp(cc->argv[2], "status") == 0) + msg = senders[scd.sender_num].status(); + else + msg = senders[scd.sender_num].help(); + return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); } switch (scd.cmd_num) { @@ -369,11 +363,13 @@ static int com_sender(struct command_context *cc) for (i = 0; i < 10; i++) { mutex_lock(mmd_mutex); if (mmd->sender_cmd_data.cmd_num >= 0) { + /* another sender command is active, retry in 100ms */ + struct timespec ts = {.tv_nsec = 100 * 1000 * 1000}; mutex_unlock(mmd_mutex); - usleep(100 * 1000); + nanosleep(&ts, NULL); continue; } - memcpy(&mmd->sender_cmd_data, &scd, sizeof(scd)); + mmd->sender_cmd_data = scd; mutex_unlock(mmd_mutex); break; } @@ -383,28 +379,20 @@ static int com_sender(struct command_context *cc) /* server info */ static int com_si(struct command_context *cc) { - int i, ret; - char *msg, *ut, *sender_info = NULL; + int ret; + char *msg, *ut; if (cc->argc != 1) return -E_COMMAND_SYNTAX; mutex_lock(mmd_mutex); - for (i = 0; senders[i].name; i++) { - char *info = senders[i].info(); - sender_info = para_strcat(sender_info, info); - free(info); - } - ut = get_server_uptime_str(now); + ut = daemon_get_uptime_str(now); ret = xasprintf(&msg, - "version: %s\n" "up: %s\nplayed: %u\n" "server_pid: %d\n" "afs_pid: %d\n" "connections (active/accepted/total): %u/%u/%u\n" "current loglevel: %s\n" - "supported audio formats: %s\n" - "%s", - version_git(), + "supported audio formats: %s\n", ut, mmd->num_played, (int)getppid(), (int)mmd->afs_pid, @@ -412,17 +400,11 @@ static int com_si(struct command_context *cc) mmd->num_commands, mmd->num_connects, conf.loglevel_arg, - AUDIO_FORMAT_HANDLERS, - sender_info + AUDIO_FORMAT_HANDLERS ); mutex_unlock(mmd_mutex); free(ut); - free(sender_info); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); - ret = sc_send_bin_buffer(&cc->scc, msg, ret); - free(msg); - return ret; + return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false); } /* version */ @@ -434,9 +416,7 @@ static int com_version(struct command_context *cc) if (cc->argc != 1) return -E_COMMAND_SYNTAX; len = xasprintf(&msg, "%s", version_text("server")); - if (cc->use_sideband) - return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false); - return sc_send_bin_buffer(&cc->scc, msg, len); + return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false); } /** These status items are cleared if no audio file is currently open. */ @@ -541,24 +521,13 @@ static int com_stat(struct command_context *cc) for (;;) { mmd_dup(nmmd); ret = get_status(nmmd, parser_friendly, &s); - if (cc->use_sideband) - ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false); - else { - ret = sc_send_bin_buffer(&cc->scc, s, ret); - free(s); - } + ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false); if (ret < 0) goto out; if (nmmd->vss_status_flags & VSS_NEXT) { char *esi; ret = empty_status_items(parser_friendly, &esi); - if (cc->use_sideband) - ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, - false); - else { - ret = sc_send_bin_buffer(&cc->scc, esi, ret); - free(esi); - } + ret = send_sb(&cc->scc, esi, ret, SBD_OUTPUT, false); if (ret < 0) goto out; } else @@ -578,7 +547,6 @@ out: static int send_list_of_commands(struct command_context *cc, struct server_command *cmd, const char *handler) { - int ret; char *msg = NULL; for (; cmd->name; cmd++) { @@ -589,11 +557,8 @@ static int send_list_of_commands(struct command_context *cc, struct server_comma msg = para_strcat(msg, tmp); free(tmp); } - if (cc->use_sideband) - return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, msg); - free(msg); - return ret; + assert(msg); + return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false); } /* returns string that must be freed by the caller */ @@ -649,11 +614,7 @@ static int com_help(struct command_context *cc) ); free(perms); free(handler); - if (cc->use_sideband) - return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false); - ret = sc_send_buffer(&cc->scc, buf); - free(buf); - return ret; + return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false); } /* hup */ @@ -762,7 +723,7 @@ static int com_ff(struct command_context *cc) promille += 1000 * i / mmd->afd.afhi.seconds_total; if (promille < 0) promille = 0; - if (promille > 1000) { + if (promille > 1000) { mmd->new_vss_status_flags |= VSS_NEXT; goto out; } @@ -793,8 +754,8 @@ static int com_jmp(struct command_context *cc) if (i > 100) i = 100; PARA_INFO_LOG("jumping to %lu%%\n", i); - mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50)/ 100; - PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n", + mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50) / 100; + PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n", mmd->chunks_sent, mmd->offset); mmd->new_vss_status_flags |= VSS_REPOS; mmd->new_vss_status_flags &= ~VSS_NEXT; @@ -805,6 +766,16 @@ out: return ret; } +static int com_tasks(struct command_context *cc) +{ + char *tl = server_get_tasks(); + int ret = 1; + + if (tl) + ret = send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false); + return ret; +} + /* * check if perms are sufficient to exec a command having perms cmd_perms. * Returns 0 if perms are sufficient, -E_PERM otherwise. @@ -815,58 +786,6 @@ static int check_perms(unsigned int perms, struct server_command *cmd_ptr) return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0; } -/* - * Parse first string from *cmd and lookup in table of valid commands. - * On error, NULL is returned. - */ -static struct server_command *parse_cmd(const char *cmdstr) -{ - char buf[255]; - int n = 0; - - sscanf(cmdstr, "%200s%n", buf, &n); - if (!n) - return NULL; - buf[n] = '\0'; - return get_cmd_ptr(buf, NULL); -} - -static int read_command(struct stream_cipher_context *scc, char **result) -{ - int ret; - char buf[4096]; - char *command = NULL; - - for (;;) { - size_t numbytes; - char *p; - - ret = sc_recv_buffer(scc, buf, sizeof(buf)); - if (ret < 0) - goto out; - if (!ret) - break; - numbytes = ret; - ret = -E_COMMAND_SYNTAX; - if (command && numbytes + strlen(command) > MAX_COMMAND_LEN) /* DOS */ - goto out; - command = para_strcat(command, buf); - p = strstr(command, EOC_MSG); - if (p) { - *p = '\0'; - break; - } - } - ret = command? 1 : -E_COMMAND_SYNTAX; -out: - if (ret < 0) - free(command); - else - *result = command; - return ret; - -} - static void reset_signals(void) { para_sigaction(SIGCHLD, SIG_IGN); @@ -875,15 +794,20 @@ static void reset_signals(void) para_sigaction(SIGHUP, SIG_DFL); } +struct connection_features { + bool sideband_requested; + bool aes_ctr128_requested; +}; + static int parse_auth_request(char *buf, int len, struct user **u, - bool *use_sideband) + struct connection_features *cf) { int ret; char *p, *username, **features = NULL; size_t auth_rq_len = strlen(AUTH_REQUEST_MSG); *u = NULL; - *use_sideband = false; + memset(cf, 0, sizeof(*cf)); if (len < auth_rq_len + 2) return -E_AUTH_REQUEST; if (strncmp(buf, AUTH_REQUEST_MSG, auth_rq_len) != 0) @@ -899,15 +823,16 @@ static int parse_auth_request(char *buf, int len, struct user **u, create_argv(p, ",", &features); for (i = 0; features[i]; i++) { if (strcmp(features[i], "sideband") == 0) - *use_sideband = true; + cf->sideband_requested = true; + else if (strcmp(features[i], "aes_ctr128") == 0) + cf->aes_ctr128_requested = 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"); + PARA_DEBUG_LOG("received auth request for user %s\n", username); *u = lookup_user(username); ret = 1; out: @@ -934,7 +859,7 @@ static int parse_sb_command(struct command_context *cc, struct iovec *iov) if (ret < 0) goto out; end = iov->iov_base + iov->iov_len; - for (i = 0, p = iov->iov_base; p < end; i++) + for (i = 0; p < end; i++) p += strlen(p) + 1; cc->argc = i; cc->argv = para_malloc((cc->argc + 1) * sizeof(char *)); @@ -955,24 +880,22 @@ out: * \param fd The file descriptor to send output to. * \param peername Identifies the connecting peer. * - * Whenever para_server accepts an incoming tcp connection on - * the port it listens on, it forks and the resulting child - * calls this function. + * Whenever para_server accepts an incoming tcp connection on the port it + * listens on, it forks and the resulting child calls this function. * - * An RSA-based challenge/response is used to authenticate - * the peer. It that authentication succeeds, a random - * session key is generated and sent back to the peer, - * encrypted with its RSA public key. From this point on, - * all transfers are crypted with this session key. + * An RSA-based challenge/response is used to authenticate the peer. It that + * authentication succeeds, a random session key is generated and sent back to + * the peer, encrypted with its RSA public key. From this point on, all + * transfers are crypted with this session key. * - * Next it is checked if the peer supplied a valid server command or a command - * for the audio file selector. If yes, and if the user has sufficient + * Next it is checked if the peer supplied a valid server command or a command + * for the audio file selector. If yes, and if the user has sufficient * permissions to execute that command, the function calls the corresponding * command handler which does argument checking and further processing. * - * In order to cope with a DOS attacks, a timeout is set up - * which terminates the function if the connection was not - * authenticated when the timeout expires. + * In order to cope with a DOS attacks, a timeout is set up which terminates + * the function if the connection was not authenticated when the timeout + * expires. * * \sa alarm(2), crypt.c, crypt.h */ @@ -981,9 +904,11 @@ __noreturn void handle_connect(int fd, const char *peername) int ret; unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN]; unsigned char challenge_hash[HASH_SIZE]; - char *p, *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */; + char *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; + struct iovec iov; + struct connection_features cf; cc->scc.fd = fd; reset_signals(); @@ -993,8 +918,8 @@ __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" - "Features: sideband\n" + PACKAGE_VERSION ".\n" + "Features: sideband,aes_ctr128\n" ); if (ret < 0) goto net_err; @@ -1002,12 +927,14 @@ __noreturn void handle_connect(int fd, const char *peername) ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE); if (ret < 0) goto net_err; - ret = parse_auth_request(buf, ret, &cc->u, &cc->use_sideband); + ret = parse_auth_request(buf, ret, &cc->u, &cf); if (ret < 0) goto net_err; - p = buf + strlen(AUTH_REQUEST_MSG); - PARA_DEBUG_LOG("received auth request for user %s\n", p); - cc->u = lookup_user(p); + if (!cf.sideband_requested) { /* sideband is mandatory */ + PARA_ERROR_LOG("client did not request sideband\n"); + ret = -E_BAD_FEATURE; + goto net_err; + } if (cc->u) { get_random_bytes_or_die(rand_buf, sizeof(rand_buf)); ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf), @@ -1024,30 +951,18 @@ __noreturn void handle_connect(int fd, const char *peername) numbytes = 256; get_random_bytes_or_die((unsigned char *)buf, numbytes); } - PARA_DEBUG_LOG("sending %u byte challenge + rc4 keys (%zu bytes)\n", + PARA_DEBUG_LOG("sending %u byte challenge + session key (%zu bytes)\n", CHALLENGE_SIZE, numbytes); - 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; - } + 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; PARA_DEBUG_LOG("received %zu bytes challenge response\n", numbytes); ret = -E_BAD_USER; if (!cc->u) @@ -1066,43 +981,20 @@ __noreturn void handle_connect(int fd, const char *peername) alarm(0); PARA_INFO_LOG("good auth for %s\n", cc->u->name); /* 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); - if (cc->use_sideband) - ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false); - else - ret = sc_send_buffer(&cc->scc, PROCEED_MSG); + cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN, + cf.aes_ctr128_requested); + cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN, + SESSION_KEY_LEN, cf.aes_ctr128_requested); + ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false); if (ret < 0) goto net_err; - if (cc->use_sideband) { - struct iovec iov; - ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov); - if (ret < 0) - goto net_err; - ret = parse_sb_command(cc, &iov); - if (ret < 0) - goto err_out; - cc->argc = ret; - } else { - ret = read_command(&cc->scc, &command); - if (ret == -E_COMMAND_SYNTAX) - goto err_out; - if (ret < 0) - goto net_err; - ret = -E_BAD_CMD; - cc->cmd = parse_cmd(command); - if (!cc->cmd) - goto err_out; - /* valid command, check permissions */ - ret = check_perms(cc->u->perms, cc->cmd); - if (ret < 0) - goto err_out; - /* valid command and sufficient perms */ - ret = create_argv(command, "\n", &cc->argv); - if (ret < 0) - goto err_out; - cc->argc = ret; - } + ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov); + if (ret < 0) + goto net_err; + ret = parse_sb_command(cc, &iov); + if (ret < 0) + goto err_out; + cc->argc = ret; PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name, cc->u->name, peername); ret = cc->cmd->handler(cc); @@ -1113,7 +1005,7 @@ __noreturn void handle_connect(int fd, const char *peername) if (ret >= 0) goto out; err_out: - if (send_strerror(cc, -ret) >= 0 && cc->use_sideband) + if (send_strerror(cc, -ret) >= 0) send_sb(&cc->scc, NULL, 0, SBD_EXIT__FAILURE, true); net_err: PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); @@ -1125,7 +1017,7 @@ out: mmd->events++; mmd->active_connections--; mutex_unlock(mmd_mutex); - if (ret >= 0 && cc->use_sideband) { + if (ret >= 0) { ret = send_sb(&cc->scc, NULL, 0, SBD_EXIT__SUCCESS, true); if (ret < 0) PARA_NOTICE_LOG("%s\n", para_strerror(-ret));