/*
- * Copyright (C) 1997-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1997-2014 Andre Noll <maan@tuebingen.mpg.de>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/** \file command.c Client authentication and server commands. */
+#include <netinet/in.h>
+#include <sys/socket.h>
#include <regex.h>
#include <signal.h>
#include <sys/types.h>
#include <osl.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
#include "para.h"
#include "error.h"
#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
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;
(long unsigned)current_time.tv_usec);
free(flags);
free(status);
- free(ut);
*result = b.buf;
return b.offset;
}
*
* 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.
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 {
if (ret < 0) {
if (scd.sender_num < 0)
return ret;
- msg = senders[scd.sender_num].help();
+ 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);
}
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;
}
/* 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,
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);
return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
}
msg = para_strcat(msg, tmp);
free(tmp);
}
+ assert(msg);
return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
}
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;
}
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;
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.
para_sigaction(SIGHUP, SIG_DFL);
}
-static int parse_auth_request(char *buf, int len, struct user **u)
+struct connection_features {
+ bool sideband_requested;
+ bool aes_ctr128_requested;
+};
+
+static int parse_auth_request(char *buf, int len, struct user **u,
+ struct connection_features *cf)
{
int ret;
char *p, *username, **features = NULL;
size_t auth_rq_len = strlen(AUTH_REQUEST_MSG);
- bool sideband_requested = false;
*u = NULL;
+ 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)
create_argv(p, ",", &features);
for (i = 0; features[i]; i++) {
if (strcmp(features[i], "sideband") == 0)
- sideband_requested = 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;
}
}
}
- if (sideband_requested == false) { /* sideband is mandatory */
- PARA_ERROR_LOG("client did not request sideband\n");
- ret = -E_BAD_FEATURE;
- goto out;
- }
PARA_DEBUG_LOG("received auth request for user %s\n", username);
*u = lookup_user(username);
ret = 1;
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 *));
* \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
*/
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();
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;
ret = recv_buffer(fd, buf, HANDSHAKE_BUFSIZE);
if (ret < 0)
goto net_err;
- ret = parse_auth_request(buf, ret, &cc->u);
+ 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),
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);
ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
buf = NULL;
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);
+ 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;