Merge branch 'refs/heads/t/clean_server_exit'
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 20 May 2018 09:26:26 +0000 (11:26 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 20 May 2018 09:33:27 +0000 (11:33 +0200)
This series removes many memory leaks of para_server by refactoring
the shutdown and signal handling code. Most of the leaks happen only
at shutdown and are hence harmless. But it is still good to plug
the leaks because this puts more focus on real memory leaks in the
valgrind output.

The merge conflicted rather badly due to the changes introduced with
the crypt branch that was merged last week. The resolution has been
thoroughly tested, though.

* refs/heads/t/clean_server_exit: (32 commits)
  command.c: Document return value of handle_connect().
  user_list: Make list head static.
  afs: Allow database switching on sighup.
  afs: Free current mood or playlist on exit.
  afs: Free status items on exit.
  afs: Shutdown signals on exit.
  server: Free parse result also in afs.
  afs: Deplete user list at startup.
  server: Free audio file header on exit.
  sender: Deplete ACLs on exit.
  Remove some unused includes from {dccp,http}_send.c.
  server: Make argument of user_list_init() constant.
  server: Deplete user list on exit.
  server: Combine user_list_init() and populate().
  server: Move para_fgets() to user_list.c.
  server: Initialize user list at compile time.
  server: Rename functions related to user lists.
  server: Constify return value of lookup_user().
  server: Let stat command handler perform cleanup on signals.
  server: Have afs process close the current mood on exit().
  ...

47 files changed:
Doxyfile
NEWS.md
afh_recv.c
afs.c
afs.h
aft.c
audiod.c
base64.h
client.c
client.h
client_common.c
command.c
configure.ac
crypt.c [deleted file]
crypt.h
crypt_backend.h
crypt_common.c
dccp_send.c
fd.c
gcrypt.c
gui.c
http_recv.c
http_send.c
m4/lls/audioc.suite.m4
m4/lls/filter.suite.m4
m4/lls/include/log-timing.m4
m4/lls/play.suite.m4
m4/lls/recv.suite.m4
m4/lls/recv_cmd.suite.m4
m4/lls/server.suite.m4
m4/lls/server_cmd.suite.m4
mixer.c
mp.c
net.c
net.h
openssl.c [new file with mode: 0644]
sched.c
send.h
send_common.c
server.c
server.h
sideband.h
udp_send.c
user_list.c
vss.c
web/download.in.html
web/manual.md

index d8960dd..b11683e 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -812,7 +812,7 @@ RECURSIVE              = NO
 # Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                =
+EXCLUDE                = config.h
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
diff --git a/NEWS.md b/NEWS.md
index d0bd658..d193a1a 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,23 @@
 NEWS
 ====
 
+-------------------------------------------
+0.6.2 (to be accounced) "elastic diversity"
+-------------------------------------------
+
+- para_gui no longer waits up to one second to update the screen when
+  the geometry of the terminal changes.
+- Minor documentation improvements.
+- Improvements to the crypto subsystem.
+- The server subcommand "task" has been deprecated. It still works,
+  but prints nothing. It will be removed in the next major release.
+- Server log output is now serialized, avoiding issues with partial
+  lines.
+- It is now possible to switch to a different afs database by changing
+  the server configuration and sending SIGHUP to the server process.
+
+Download: [tarball](./releases/paraslash-git.tar.xz)
+
 ----------------------------------------
 0.6.1 (2017-09-23) "segmented iteration"
 ----------------------------------------
index c3e6ddb..6525209 100644 (file)
@@ -68,7 +68,7 @@ static int afh_recv_open(struct receiver_node *rn)
        struct lls_parse_result *lpr = rn->lpr;
        struct private_afh_recv_data *pard;
        struct afh_info *afhi;
-       const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr);
+       const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr), *msg;
        int32_t bc = RECV_CMD_OPT_INT32_VAL(AFH, BEGIN_CHUNK, lpr);
        const struct lls_opt_result *r_e = RECV_CMD_OPT_RESULT(AFH, END_CHUNK, lpr);
        int ret;
@@ -87,8 +87,10 @@ static int afh_recv_open(struct receiver_node *rn)
                goto out_unmap;
        pard->audio_format_num = ret;
        ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+       msg = "no data chunks";
        if (afhi->chunks_total == 0)
                goto out_clear_afhi;
+       msg = "invalid begin chunk";
        if (PARA_ABS(bc) >= afhi->chunks_total)
                goto out_clear_afhi;
        if (bc >= 0)
@@ -100,6 +102,7 @@ static int afh_recv_open(struct receiver_node *rn)
        if (lls_opt_given(r_e)) {
                int32_t ec = lls_int32_val(0, r_e);
                ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+               msg = "invalid end chunk";
                if (PARA_ABS(ec) > afhi->chunks_total)
                        goto out_clear_afhi;
                if (ec >= 0)
@@ -109,12 +112,14 @@ static int afh_recv_open(struct receiver_node *rn)
        } else
                pard->last_chunk = afhi->chunks_total - 1;
        ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+       msg = "begin chunk >= end chunk!?";
        if (pard->first_chunk >= pard->last_chunk)
                goto out_clear_afhi;
        pard->current_chunk = pard->first_chunk;
        return pard->audio_format_num;
 out_clear_afhi:
        clear_afhi(afhi);
+       PARA_ERROR_LOG("%s: %s\n", fn, msg);
 out_unmap:
        para_munmap(pard->map, pard->map_size);
        close(pard->fd);
diff --git a/afs.c b/afs.c
index 0014379..4fe2140 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -22,8 +22,8 @@
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
-#include "server.h"
 #include "net.h"
+#include "server.h"
 #include "ipc.h"
 #include "list.h"
 #include "sched.h"
@@ -687,8 +687,7 @@ static int open_afs_tables(void)
                ret = afs_tables[i].open(database_dir);
                if (ret >= 0)
                        continue;
-               PARA_ERROR_LOG("%s open: %s\n", afs_tables[i].name,
-                       para_strerror(-ret));
+               PARA_ERROR_LOG("could not open %s\n", afs_tables[i].name);
                break;
        }
        if (ret >= 0)
diff --git a/afs.h b/afs.h
index 0bdd76d..b0d283f 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -161,6 +161,14 @@ struct afs_callback_arg {
        struct osl_object query;
        /** Will be written on band SBD_OUTPUT, fully buffered. */
        struct para_buffer pbout;
+       /**
+        * Convenience pointer for the deserialized parse result.
+        *
+        * Most afs command handlers call \ref send_lls_callback_request() to
+        * serialize the parse result of the subcommand and pass it to the
+        * callback. In afs context a pointer to the deserialized parse result
+        * is stored here.
+        */
        struct lls_parse_result *lpr;
 };
 
diff --git a/aft.c b/aft.c
index f9512c7..15769d5 100644 (file)
--- a/aft.c
+++ b/aft.c
 #include "sideband.h"
 #include "command.h"
 
-static struct osl_table *audio_file_table;
+/* Data about one audio file. Needed for ls and stat output. */
+struct ls_data {
+       /* Usual audio format handler information. */
+       struct afh_info afhi;
+       /* Audio file selector information. */
+       struct afs_info afsi;
+       /* The full path of the audio file. */
+       char *path;
+       /* The score value (if -a was given). */
+       long score;
+       /* The hash value of the audio file data. */
+       unsigned char *hash;
+};
+
+/*
+ * The internal state of the audio file table is described by the following
+ * variables which are private to aft.c.
+ */
+static struct osl_table *audio_file_table; /* NULL if table not open */
+static struct osl_row *current_aft_row; /* NULL if no audio file open */
+
 static char *status_items;
 static char *parser_friendly_status_items;
+static struct ls_data status_item_ls_data;
 
 /** The different sorting methods of the ls command. */
 enum ls_sorting_method {
@@ -70,20 +91,6 @@ enum ls_listing_mode {
        LS_MODE_PARSER,
 };
 
-/* Data about one audio file. Needed for ls and stat output. */
-struct ls_data {
-       /* Usual audio format handler information. */
-       struct afh_info afhi;
-       /* Audio file selector information. */
-       struct afs_info afsi;
-       /* The full path of the audio file. */
-       char *path;
-       /* The score value (if -a was given). */
-       long score;
-       /* The hash value of the audio file data. */
-       unsigned char *hash;
-};
-
 /**
  * The size of the individual output fields of the ls command.
  *
@@ -954,9 +961,6 @@ out:
        return ret;
 }
 
-static struct ls_data status_item_ls_data;
-static struct osl_row *current_aft_row;
-
 static void make_inode_status_items(struct para_buffer *pb)
 {
        struct stat statbuf = {.st_size = 0};
@@ -1594,7 +1598,7 @@ ACTION:   Table modifications to be done by the callback.
 +----+----+---+------+---------------------------------------------------+
 | N  |  N | Y |  Y   | (new file) create new entry (force has no effect)
 +----+----+---+------+---------------------------------------------------+
-|  N |  N | N |  Y   | (new file) create new entry
+|  |  N | N |  Y   | (new file) create new entry
 +----+----+---+------+---------------------------------------------------+
 
 Notes:
index 8d152fa..083c2a7 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -1493,7 +1493,7 @@ int main(int argc, char *argv[])
        version_handle_flag("audiod", OPT_GIVEN(VERSION));
        handle_help_flags();
        parse_config_or_die();
-       init_random_seed_or_die();
+       crypt_init();
        daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
        recv_init();
        if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
@@ -1542,7 +1542,7 @@ int main(int argc, char *argv[])
        audiod_cleanup();
        sched_shutdown(&sched);
        signal_shutdown(signal_task);
-
+       crypt_shutdown();
 out:
        lls_free_parse_result(lpr, CMD_PTR);
        if (errctx)
index 4bfaa99..ea62d98 100644 (file)
--- a/base64.h
+++ b/base64.h
@@ -1,3 +1,7 @@
+/* Copyright (C) 2016 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+/** \file base64.h uudecode/base64 API. */
+
 int uudecode(char const *src, size_t encoded_size, char **result,
                size_t *decoded_size);
 int base64_decode(char const *src, size_t encoded_size, char **result,
index 3e9219f..c45826a 100644 (file)
--- a/client.c
+++ b/client.c
@@ -618,7 +618,7 @@ int main(int argc, char *argv[])
 {
        int ret;
 
-       init_random_seed_or_die();
+       crypt_init();
        sched.default_timeout.tv_sec = 1;
 
        ret = client_parse_config(argc, argv, &ct, &client_loglevel);
@@ -664,6 +664,7 @@ int main(int argc, char *argv[])
                }
        }
        sched_shutdown(&sched);
+       crypt_shutdown();
 out:
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
index f8cc5d6..7ba56b9 100644 (file)
--- a/client.h
+++ b/client.h
@@ -36,8 +36,6 @@ struct client_task {
        unsigned char *challenge_hash;
        /** The parsed command line (including the command). */
        struct lls_parse_result *lpr;
-       /** The config file for client options. */
-       char *config_file;
        /** The RSA private key. */
        char *key_file;
        /** Paraslash user name. */
index 97d29f8..b28e895 100644 (file)
@@ -40,7 +40,6 @@ void client_close(struct client_task *ct)
        if (!ct)
                return;
        free(ct->user);
-       free(ct->config_file);
        free(ct->key_file);
        lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
        free(ct->challenge_hash);
@@ -318,15 +317,15 @@ static int client_post_select(struct sched *s, void *context)
                }
                n = sbb.iov.iov_len;
                PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
-               ret = priv_decrypt(ct->key_file, crypt_buf,
+               ret = apc_priv_decrypt(ct->key_file, crypt_buf,
                        sbb.iov.iov_base, n);
                free(sbb.iov.iov_base);
                if (ret < 0)
                        goto out;
                ct->challenge_hash = para_malloc(HASH_SIZE);
-               hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
-               ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
-               ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
+               hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+               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);
@@ -536,7 +535,6 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
        ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
        if (ret < 0)
                goto out;
-       ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr);
        version_handle_flag("client", CLIENT_OPT_GIVEN(VERSION, lpr));
        handle_help_flag(lpr);
 
@@ -573,6 +571,9 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
                lpr = merged_lpr;
        }
        /* success */
+       ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr);
+       if (loglevel)
+               *loglevel = ll;
        user = CLIENT_OPT_GIVEN(USER, lpr)?
                para_strdup(CLIENT_OPT_STRING_VAL(USER, lpr)) : para_logname();
 
@@ -593,21 +594,17 @@ int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
        ct->scc.fd = -1;
        ct->lpr = lpr;
        ct->key_file = kf;
-       ct->config_file = cf;
        ct->user = user;
        *ct_ptr = ct;
-       if (loglevel)
-               *loglevel = ll;
        ret = lls_num_inputs(lpr);
 out:
        free(home);
+       free(cf);
        if (ret < 0) {
                if (errctx)
                        PARA_ERROR_LOG("%s\n", errctx);
                free(errctx);
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
                lls_free_parse_result(lpr, cmd);
-               free(cf);
                free(kf);
                *ct_ptr = NULL;
        }
index 6b9f91d..25b9271 100644 (file)
--- a/command.c
+++ b/command.c
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
+#include "net.h"
 #include "server.h"
 #include "list.h"
 #include "send.h"
 #include "sched.h"
 #include "vss.h"
-#include "net.h"
 #include "daemon.h"
 #include "fd.h"
 #include "ipc.h"
@@ -687,8 +687,7 @@ static int com_nomore(__a_unused struct command_context *cc,
 }
 EXPORT_SERVER_CMD_HANDLER(nomore);
 
-static int com_ff(__a_unused struct command_context *cc,
-               struct lls_parse_result *lpr)
+static int com_ff(struct command_context *cc, struct lls_parse_result *lpr)
 {
        long promille;
        int ret, backwards = 0;
@@ -708,6 +707,7 @@ static int com_ff(__a_unused struct command_context *cc,
        ret = -E_NO_AUDIO_FILE;
        if (!mmd->afd.afhi.chunks_total || !mmd->afd.afhi.seconds_total)
                goto out;
+       ret = 1;
        promille = (1000 * mmd->current_chunk) / mmd->afd.afhi.chunks_total;
        if (backwards)
                promille -= 1000 * i / mmd->afd.afhi.seconds_total;
@@ -723,15 +723,13 @@ static int com_ff(__a_unused struct command_context *cc,
        mmd->new_vss_status_flags |= VSS_REPOS;
        mmd->new_vss_status_flags &= ~VSS_NEXT;
        mmd->events++;
-       ret = 1;
 out:
        mutex_unlock(mmd_mutex);
        return ret;
 }
 EXPORT_SERVER_CMD_HANDLER(ff);
 
-static int com_jmp(__a_unused struct command_context *cc,
-               struct lls_parse_result *lpr)
+static int com_jmp(struct command_context *cc, struct lls_parse_result *lpr)
 {
        long unsigned int i;
        int ret;
@@ -902,12 +900,12 @@ static int run_command(struct command_context *cc, struct iovec *iov)
  *
  * \return Standard.
  *
- * \sa alarm(2), \ref crypt.c, \ref crypt.h.
+ * \sa alarm(2), \ref openssl.c, \ref crypt.h.
  */
 int handle_connect(int fd)
 {
        int ret;
-       unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
+       unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
        unsigned char challenge_hash[HASH_SIZE];
        char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
        size_t numbytes;
@@ -937,7 +935,7 @@ int handle_connect(int fd)
                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),
+               ret = apc_pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
                        (unsigned char *)buf);
                if (ret < 0)
                        goto net_err;
@@ -952,7 +950,7 @@ int handle_connect(int fd)
                get_random_bytes_or_die((unsigned char *)buf, numbytes);
        }
        PARA_DEBUG_LOG("sending %d byte challenge + session key (%zu bytes)\n",
-               CHALLENGE_SIZE, numbytes);
+               APC_CHALLENGE_SIZE, numbytes);
        ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
        buf = NULL;
        if (ret < 0)
@@ -968,21 +966,21 @@ int handle_connect(int fd)
        if (!cc->u)
                goto net_err;
        /*
-        * The correct response is the hash of the first CHALLENGE_SIZE bytes
+        * The correct response is the hash of the first APC_CHALLENGE_SIZE bytes
         * of the random data.
         */
        ret = -E_BAD_AUTH;
        if (numbytes != HASH_SIZE)
                goto net_err;
-       hash_function((char *)rand_buf, CHALLENGE_SIZE, challenge_hash);
+       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);
        /* 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,
+       cc->scc.recv = sc_new(rand_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
+       cc->scc.send = sc_new(rand_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
                SESSION_KEY_LEN);
        ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
        if (ret < 0)
index 499571a..9493f8e 100644 (file)
@@ -394,7 +394,7 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                version
        "
        if test "$CRYPTOLIB" = openssl; then
-               server_errlist_objs="$server_errlist_objs crypt"
+               server_errlist_objs="$server_errlist_objs openssl"
        else
                server_errlist_objs="$server_errlist_objs gcrypt"
        fi
@@ -432,7 +432,7 @@ if test -n "$CRYPTOLIB"; then
                version
        "
        if test "$CRYPTOLIB" = openssl; then
-               client_errlist_objs="$client_errlist_objs crypt"
+               client_errlist_objs="$client_errlist_objs openssl"
        else
                client_errlist_objs="$client_errlist_objs gcrypt"
        fi
@@ -488,7 +488,7 @@ if test -n "$CRYPTOLIB"; then
                sync_filter
        "
        if test "$CRYPTOLIB" = openssl; then
-               audiod_errlist_objs="$audiod_errlist_objs crypt"
+               audiod_errlist_objs="$audiod_errlist_objs openssl"
        else
                audiod_errlist_objs="$audiod_errlist_objs gcrypt"
        fi
diff --git a/crypt.c b/crypt.c
deleted file mode 100644 (file)
index b8a587c..0000000
--- a/crypt.c
+++ /dev/null
@@ -1,296 +0,0 @@
-/* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file crypt.c Openssl-based encryption/decryption routines. */
-
-#include <regex.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <openssl/rand.h>
-#include <openssl/err.h>
-#include <openssl/pem.h>
-#include <openssl/sha.h>
-#include <openssl/bn.h>
-#include <openssl/aes.h>
-
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "crypt.h"
-#include "fd.h"
-#include "crypt_backend.h"
-#include "base64.h"
-#include "portable_io.h"
-
-struct asymmetric_key {
-       RSA *rsa;
-};
-
-void get_random_bytes_or_die(unsigned char *buf, int num)
-{
-       unsigned long err;
-
-       /* RAND_bytes() returns 1 on success, 0 otherwise. */
-       if (RAND_bytes(buf, num) == 1)
-               return;
-       err = ERR_get_error();
-       PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err));
-       exit(EXIT_FAILURE);
-}
-
-/*
- * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Seed the PRNG
- * used by random(3) with a random seed obtained from SSL. If /dev/urandom is
- * not readable, the function calls exit().
- *
- * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3),
- * random(3), \ref para_random().
- */
-void init_random_seed_or_die(void)
-{
-       int seed, ret = RAND_load_file("/dev/urandom", 64);
-
-       if (ret != 64) {
-               PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret);
-               exit(EXIT_FAILURE);
-       }
-       get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
-       srandom(seed);
-}
-
-static int get_private_key(const char *path, RSA **rsa)
-{
-       EVP_PKEY *pkey;
-       BIO *bio = BIO_new(BIO_s_file());
-
-       *rsa = NULL;
-       if (!bio)
-               return -E_PRIVATE_KEY;
-       if (BIO_read_filename(bio, path) <= 0)
-               goto bio_free;
-       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
-       if (!pkey)
-               goto bio_free;
-       *rsa = EVP_PKEY_get1_RSA(pkey);
-       EVP_PKEY_free(pkey);
-bio_free:
-       BIO_free(bio);
-       return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
-}
-
-/*
- * The public key loading functions below were inspired by corresponding code
- * of openssh-5.2p1, Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo,
- * Finland. However, not much of the original code remains.
- */
-
-static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
-{
-       const unsigned char *p = buf, *end = buf + len;
-       uint32_t bnsize;
-       BIGNUM *bn;
-
-       if (p + 4 < p)
-               return -E_BIGNUM;
-       if (p + 4 > end)
-               return -E_BIGNUM;
-       bnsize = read_u32_be(p);
-       PARA_DEBUG_LOG("bnsize: %u\n", bnsize);
-       p += 4;
-       if (p + bnsize < p)
-               return -E_BIGNUM;
-       if (p + bnsize > end)
-               return -E_BIGNUM;
-       if (bnsize > 8192)
-               return -E_BIGNUM;
-       bn = BN_bin2bn(p, bnsize, NULL);
-       if (!bn)
-               return -E_BIGNUM;
-       *result = bn;
-       return bnsize + 4;
-}
-
-static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
-{
-       int ret;
-       RSA *rsa;
-       BIGNUM *n, *e;
-       const unsigned char *p = blob, *end = blob + blen;
-
-       rsa = RSA_new();
-       if (!rsa)
-               return -E_BIGNUM;
-       ret = read_bignum(p, end - p, &e);
-       if (ret < 0)
-               goto fail;
-       p += ret;
-       ret = read_bignum(p, end - p, &n);
-       if (ret < 0)
-               goto fail;
-#ifdef HAVE_RSA_SET0_KEY
-       RSA_set0_key(rsa, n, e, NULL);
-#else
-       rsa->n = n;
-       rsa->e = e;
-#endif
-       *result = rsa;
-       return 1;
-fail:
-       RSA_free(rsa);
-       return ret;
-}
-
-int get_public_key(const char *key_file, struct asymmetric_key **result)
-{
-       struct asymmetric_key *key = NULL;
-       void *map = NULL;
-       unsigned char *blob = NULL;
-       size_t map_size, encoded_size, decoded_size;
-       int ret, ret2;
-       char *cp;
-
-       key = para_malloc(sizeof(*key));
-       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
-       if (ret < 0)
-               goto out;
-       ret = is_ssh_rsa_key(map, map_size);
-       if (!ret) {
-               ret = -E_SSH_PARSE;
-               goto out_unmap;
-       }
-       cp = map + ret;
-       encoded_size = map_size - ret;
-       PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file);
-       ret = uudecode(cp, encoded_size, (char **)&blob, &decoded_size);
-       if (ret < 0)
-               goto out_unmap;
-       ret = check_ssh_key_header(blob, decoded_size);
-       if (ret < 0)
-               goto out_unmap;
-       ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
-       if (ret < 0)
-               goto out_unmap;
-       ret = RSA_size(key->rsa);
-out_unmap:
-       ret2 = para_munmap(map, map_size);
-       if (ret >= 0 && ret2 < 0)
-               ret = ret2;
-out:
-       if (ret < 0) {
-               free(key);
-               *result = NULL;
-               PARA_ERROR_LOG("key %s: %s\n", key_file, para_strerror(-ret));
-       } else
-               *result = key;
-       free(blob);
-       return ret;
-}
-
-void free_public_key(struct asymmetric_key *key)
-{
-       if (!key)
-               return;
-       RSA_free(key->rsa);
-       free(key);
-}
-
-int priv_decrypt(const char *key_file, unsigned char *outbuf,
-               unsigned char *inbuf, int inlen)
-{
-       struct asymmetric_key *priv;
-       int ret;
-
-       ret = check_private_key_file(key_file);
-       if (ret < 0)
-               return ret;
-       if (inlen < 0)
-               return -E_RSA;
-       priv = para_malloc(sizeof(*priv));
-       ret = get_private_key(key_file, &priv->rsa);
-       if (ret < 0) {
-               free(priv);
-               return ret;
-       }
-       /*
-        * RSA is vulnerable to timing attacks. Generate a random blinding
-        * factor to protect against this kind of attack.
-        */
-       ret = -E_BLINDING;
-       if (RSA_blinding_on(priv->rsa, NULL) == 0)
-               goto out;
-       ret = RSA_private_decrypt(inlen, inbuf, outbuf, priv->rsa,
-               RSA_PKCS1_OAEP_PADDING);
-       RSA_blinding_off(priv->rsa);
-       if (ret <= 0)
-               ret = -E_DECRYPT;
-out:
-       RSA_free(priv->rsa);
-       free(priv);
-       return ret;
-}
-
-int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
-               unsigned len, unsigned char *outbuf)
-{
-       int ret, flen = len; /* RSA_public_encrypt expects a signed int */
-
-       if (flen < 0)
-               return -E_ENCRYPT;
-       ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa,
-               RSA_PKCS1_OAEP_PADDING);
-       return ret < 0? -E_ENCRYPT : ret;
-}
-
-struct stream_cipher {
-       EVP_CIPHER_CTX *aes;
-};
-
-struct stream_cipher *sc_new(const unsigned char *data, int len)
-{
-       struct stream_cipher *sc = para_malloc(sizeof(*sc));
-
-       assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
-       sc->aes = EVP_CIPHER_CTX_new();
-       EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
-               data + AES_CRT128_BLOCK_SIZE);
-       return sc;
-}
-
-void sc_free(struct stream_cipher *sc)
-{
-       if (!sc)
-               return;
-       EVP_CIPHER_CTX_free(sc->aes);
-       free(sc);
-}
-
-static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
-               struct iovec *dst)
-{
-       int ret, inlen = src->iov_len, outlen, tmplen;
-
-       *dst = (typeof(*dst)) {
-               /* Add one for the terminating zero byte. */
-               .iov_base = para_malloc(inlen + 1),
-               .iov_len = inlen
-       };
-       ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
-       assert(ret != 0);
-       ret = EVP_EncryptFinal_ex(ctx, dst->iov_base + outlen, &tmplen);
-       assert(ret != 0);
-       outlen += tmplen;
-       ((char *)dst->iov_base)[outlen] = '\0';
-       dst->iov_len = outlen;
-}
-
-void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
-{
-       return aes_ctr128_crypt(sc->aes, src, dst);
-}
-
-void hash_function(const char *data, unsigned long len, unsigned char *hash)
-{
-       SHA_CTX c;
-       SHA1_Init(&c);
-       SHA1_Update(&c, data, len);
-       SHA1_Final(hash, &c);
-}
diff --git a/crypt.h b/crypt.h
index 85623fb..8562959 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -2,15 +2,14 @@
 
 /** \file crypt.h Public crypto interface. */
 
+/*
+ * Asymmetric pubkey cryptosystem (apc).
+ *
+ * This is just RSA, but this fact is a hidden implementation detail.
+ */
 
-/* These are used to distinguish between loading of private/public key. */
-
-/** The key to load is a public key. */
-#define LOAD_PUBLIC_KEY 0
-/** The key to load is a private key. */
-#define LOAD_PRIVATE_KEY 1
 /** The size of the challenge sent to the client. */
-#define CHALLENGE_SIZE 64
+#define APC_CHALLENGE_SIZE 64
 
 /** Opaque structure for public and private keys. */
 struct asymmetric_key;
@@ -25,7 +24,7 @@ struct asymmetric_key;
  *
  * \return The size of the encrypted data on success, negative on errors.
  */
-int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
+int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
                unsigned len, unsigned char *outbuf);
 
 /**
@@ -40,7 +39,7 @@ int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
  *
  * \return The size of the recovered plaintext on success, negative on errors.
  */
-int priv_decrypt(const char *key_file, unsigned char *outbuf,
+int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
                unsigned char *inbuf, int inlen);
 
 /**
@@ -51,17 +50,17 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
  *
  * \return The size of the key on success, negative on errors.
  */
-int get_public_key(const char *key_file, struct asymmetric_key **result);
+int apc_get_pubkey(const char *key_file, struct asymmetric_key **result);
 
 /**
  * Deallocate a public key.
  *
  * \param key Pointer to the key structure to free.
  *
- * This should be called for keys obtained by get_public_key() if the key is no
+ * This should be called for keys obtained by \ref apc_get_pubkey() if the key is no
  * longer needed.
  */
-void free_public_key(struct asymmetric_key *key);
+void apc_free_pubkey(struct asymmetric_key *key);
 
 
 /**
@@ -78,17 +77,20 @@ void free_public_key(struct asymmetric_key *key);
 void get_random_bytes_or_die(unsigned char *buf, int num);
 
 /**
- * Seed pseudo random number generators.
+ * Initialize the crypto backend.
  *
- * This function seeds the PRNG used by random() with a random seed obtained
- * from the crypto implementation. On errors, an error message is logged and
- * the function calls exit().
+ * This function initializes the crypto library and seeds the pseudo random
+ * number generator used by random() with a random seed obtained from the
+ * crypto implementation. On errors, an error message is logged and the
+ * function calls exit().
  *
  * \sa \ref get_random_bytes_or_die(), srandom(3), random(3), \ref
  * para_random().
  */
-void init_random_seed_or_die(void);
+void crypt_init(void);
 
+/** Allocate all resources of the crypto backend. */
+void crypt_shutdown(void);
 
 /** Opaque structure for stream ciphers. */
 struct stream_cipher;
index ff956ce..175a688 100644 (file)
@@ -7,6 +7,6 @@
 /** AES block size in bytes. */
 #define AES_CRT128_BLOCK_SIZE 16
 
-size_t is_ssh_rsa_key(char *data, size_t size);
-int check_ssh_key_header(const unsigned char *blob, int blen);
+int decode_ssh_key(const char *filename, unsigned char **blob,
+               size_t *decoded_size);
 int check_private_key_file(const char *file);
index 08361b2..235b8b8 100644 (file)
 #include "crypt.h"
 #include "crypt_backend.h"
 #include "portable_io.h"
+#include "fd.h"
+#include "base64.h"
 
 /** If the key begins with this text, we treat it as an ssh key. */
 #define KEY_TYPE_TXT "ssh-rsa"
 
-/**
- * Check if given buffer starts with a ssh rsa key signature.
- *
- * \param data The buffer.
- * \param size Number of data bytes.
+/*
+ * Check if the given buffer starts with an ssh rsa key signature.
  *
- * \return Number of header bytes to be skipped on success, zero if
- * ssh rsa signature was not found.
+ * Returns number of header bytes to be skipped on success, zero if no ssh rsa
+ * signature was found.
  */
-size_t is_ssh_rsa_key(char *data, size_t size)
+static size_t is_ssh_rsa_key(char *data, size_t size)
 {
        char *cp;
 
@@ -42,20 +41,13 @@ size_t is_ssh_rsa_key(char *data, size_t size)
        return cp - data;
 }
 
-/**
- * Sanity checks for the header of an ssh key.
- *
- * \param blob The buffer.
- * \param blen The number of bytes of \a blob.
+/*
+ * Perform some sanity checks on the decoded ssh key.
  *
- * This performs some checks to make sure we really have an ssh key. It also
- * computes the offset in bytes of the start of the key values (modulus,
- * exponent..).
- *
- * \return The number of bytes to skip until the start of the first encoded
- * number (usually 11).
+ * This function returns the size of the header. Usually, the header is 11
+ * bytes long: four bytes for the length field, and the string "ssh-rsa".
  */
-int check_ssh_key_header(const unsigned char *blob, int blen)
+static int check_ssh_key_header(const unsigned char *blob, int blen)
 {
        const unsigned char *p = blob, *end = blob + blen;
        uint32_t rlen;
@@ -76,6 +68,51 @@ int check_ssh_key_header(const unsigned char *blob, int blen)
        return 4 + rlen;
 }
 
+/**
+ * Perform sanity checks and base64-decode an ssh-rsa key.
+ *
+ * \param filename The public key file (usually id_rsa.pub).
+ * \param blob Pointer to base64-decoded blob is returned here.
+ * \param decoded_size The size of the decoded blob.
+ *
+ * The memory pointed at by the returned blob pointer has to be freed by the
+ * caller.
+ *
+ * \return On success, the offset in bytes of the start of the key values
+ * (modulus, exponent..). This is the number of bytes to skip from the blob
+ * until the start of the first encoded number. On failure, a negative error
+ * code is returned.
+ *
+ * \sa \ref uudecode().
+ */
+int decode_ssh_key(const char *filename, unsigned char **blob,
+               size_t *decoded_size)
+{
+       int ret, ret2;
+       void *map;
+       size_t map_size;
+
+       ret = mmap_full_file(filename, O_RDONLY, &map, &map_size, NULL);
+       if (ret < 0)
+               return ret;
+       ret = is_ssh_rsa_key(map, map_size);
+       if (ret == 0) {
+               ret = -E_SSH_PARSE;
+               goto unmap;
+       }
+       ret = uudecode(map + ret, map_size - ret, (char **)blob, decoded_size);
+       if (ret < 0)
+               goto unmap;
+       ret = check_ssh_key_header(*blob, *decoded_size);
+       if (ret < 0)
+               goto unmap;
+unmap:
+       ret2 = para_munmap(map, map_size);
+       if (ret >= 0 && ret2 < 0)
+               ret = ret2;
+       return ret;
+}
+
 /**
  * Check existence and permissions of a private key file.
  *
index 0b454e7..770ac60 100644 (file)
@@ -21,8 +21,8 @@
 #include "error.h"
 #include "string.h"
 #include "afh.h"
-#include "server.h"
 #include "net.h"
+#include "server.h"
 #include "list.h"
 #include "send.h"
 #include "sched.h"
@@ -164,7 +164,8 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
 
 static int dccp_com_on(__a_unused struct sender_command_data *scd)
 {
-       return generic_com_on(dss, IPPROTO_DCCP);
+       generic_com_on(dss, IPPROTO_DCCP);
+       return 1;
 }
 
 static int dccp_com_off(__a_unused struct sender_command_data *scd)
@@ -223,14 +224,10 @@ static char *dccp_status(void)
  */
 static void dccp_send_init(void)
 {
-       int ret;
-
        init_sender_status(dss, OPT_RESULT(DCCP_ACCESS),
                OPT_UINT32_VAL(DCCP_PORT), OPT_UINT32_VAL(DCCP_MAX_CLIENTS),
                OPT_GIVEN(DCCP_DEFAULT_DENY));
-       ret = generic_com_on(dss, IPPROTO_DCCP);
-       if (ret < 0)
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       generic_com_on(dss, IPPROTO_DCCP);
 }
 
 /**
diff --git a/fd.c b/fd.c
index ae5ef41..33891d2 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -179,15 +179,15 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...)
  * \param rfds An optional fd set pointer.
  * \param num_bytes Result pointer. Contains the number of bytes read from \a fd.
  *
- * If \a rfds is not \p NULL and the (non-blocking) file descriptor \a fd is
- * not set in \a rfds, this function returns early without doing anything.
- * Otherwise The function tries to read up to \a sz bytes from \a fd, where \a
- * sz is the sum of the lengths of all vectors in \a iov. As for xwrite(),
- * \p EAGAIN is not considered an error condition. However, \p EOF is.
+ * If rfds is not NULL and the (non-blocking) file descriptor fd is not set in
+ * rfds, this function returns early without doing anything. Otherwise it tries
+ * to read up to sz bytes from fd, where sz is the sum of the lengths of all
+ * vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are not considered
+ * error conditions. However, EOF is.
  *
  * \return Zero or a negative error code. If the underlying call to readv(2)
  * returned zero (indicating an end of file condition) or failed for some
- * reason other than \p EAGAIN, a negative error code is returned.
+ * reason other than EAGAIN or EINTR, a negative error code is returned.
  *
  * In any case, \a num_bytes contains the number of bytes that have been
  * successfully read from \a fd (zero if the first readv() call failed with
@@ -226,7 +226,7 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds,
                if (ret == 0)
                        return -E_EOF;
                if (ret < 0) {
-                       if (errno == EAGAIN)
+                       if (errno == EAGAIN || errno == EINTR)
                                return 0;
                        return -ERRNO_TO_PARA_ERROR(errno);
                }
index 052546d..ff4dab3 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -56,7 +56,7 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
  * call to gcry_check_version() initializes the gcrypt library and checks that
  * we have at least the minimal required version.
  */
-void init_random_seed_or_die(void)
+void crypt_init(void)
 {
        const char *req_ver = "1.5.0";
        int seed;
@@ -66,10 +66,29 @@ void init_random_seed_or_die(void)
                        req_ver, gcry_check_version(NULL));
                exit(EXIT_FAILURE);
        }
+
+       /*
+        * Allocate a pool of secure memory. This also drops privileges where
+        * needed.
+        */
+       gcry_control(GCRYCTL_INIT_SECMEM, 65536, 0);
+
+       /* Tell Libgcrypt that initialization has completed. */
+       gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
        get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
        srandom(seed);
 }
 
+void crypt_shutdown(void)
+{
+       /*
+        * WK does not see a way to apply a patch for the sake of Valgrind, so
+        * as of 2018 libgrypt has no deinitialization routine to free the
+        * resources on exit.
+        */
+}
+
 /** S-expression for the public part of an RSA key. */
 #define RSA_PUBKEY_SEXP "(public-key (rsa (n %m) (e %m)))"
 /** S-expression for a private RSA key. */
@@ -366,27 +385,21 @@ free_blob:
        return ret;
 }
 
-static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result)
+int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
 {
+       unsigned char *blob, *p, *end;
        int ret;
        gcry_error_t gret;
-       unsigned char *blob = NULL, *p, *end;
        size_t nr_scanned, erroff, decoded_size;
-       gcry_mpi_t e = NULL, n = NULL;
+       gcry_mpi_t e, n;
+       gcry_sexp_t sexp;
+       struct asymmetric_key *key;
 
-       PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size);
-       ret = uudecode((char *)data, size, (char **)&blob, &decoded_size);
+       ret = decode_ssh_key(key_file, &blob, &decoded_size);
        if (ret < 0)
-               goto free_blob;
-       end = blob + decoded_size;
-       dump_buffer("decoded key", blob, decoded_size);
-       ret = check_ssh_key_header(blob, decoded_size);
-       if (ret < 0)
-               goto free_blob;
+               return ret;
        p = blob + ret;
-       ret = -E_SSH_PARSE;
-       if (p >= end)
-               goto free_blob;
+       end = blob + decoded_size;
        PARA_DEBUG_LOG("scanning modulus and public exponent\n");
        gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
        if (gret) {
@@ -395,8 +408,6 @@ static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result
                goto free_blob;
        }
        PARA_DEBUG_LOG("scanned e (%zu bytes)\n", nr_scanned);
-//     gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, rsa_e);
-//     PARA_CRIT_LOG("e: %s\n", buf);
        p += nr_scanned;
        if (p >= end)
                goto release_e;
@@ -407,17 +418,19 @@ static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result
                goto release_e;
        }
        PARA_DEBUG_LOG("scanned n (%zu bytes)\n", nr_scanned);
-//     gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, rsa_n);
-//     PARA_CRIT_LOG("n: %s\n", buf);
-       gret = gcry_sexp_build(result, &erroff, RSA_PUBKEY_SEXP, n, e);
+       gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e);
        if (gret) {
                PARA_ERROR_LOG("offset %zu: %s\n", erroff,
                        gcry_strerror(gcry_err_code(gret)));
                ret = -E_SEXP_BUILD;
                goto release_n;
        }
-       ret = nr_scanned / 32 * 32;
+       ret = ROUND_DOWN(nr_scanned, 32);
        PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8);
+       key = para_malloc(sizeof(*key));
+       key->num_bytes = ret;
+       key->sexp = sexp;
+       *result = key;
 release_n:
        gcry_mpi_release(n);
 release_e:
@@ -427,43 +440,7 @@ free_blob:
        return ret;
 }
 
-int get_public_key(const char *key_file, struct asymmetric_key **result)
-{
-       int ret, ret2;
-       void *map;
-       size_t map_size;
-       unsigned char *start, *end;
-       gcry_sexp_t sexp;
-       struct asymmetric_key *key;
-
-       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
-       if (ret < 0)
-               return ret;
-       ret = is_ssh_rsa_key(map, map_size);
-       if (!ret) {
-               para_munmap(map, map_size);
-               return -E_SSH_PARSE;
-       }
-       start = map + ret;
-       end = map + map_size;
-       ret = -E_SSH_PARSE;
-       if (start >= end)
-               goto unmap;
-       ret = get_ssh_public_key(start, end - start, &sexp);
-       if (ret < 0)
-               goto unmap;
-       key = para_malloc(sizeof(*key));
-       key->num_bytes = ret;
-       key->sexp = sexp;
-       *result = key;
-unmap:
-       ret2 = para_munmap(map, map_size);
-       if (ret >= 0 && ret2 < 0)
-               ret = ret2;
-       return ret;
-}
-
-void free_public_key(struct asymmetric_key *key)
+void apc_free_pubkey(struct asymmetric_key *key)
 {
        if (!key)
                return;
@@ -481,7 +458,7 @@ static int decode_rsa(gcry_sexp_t sexp, unsigned char *outbuf, size_t *nbytes)
        return 1;
 }
 
-int priv_decrypt(const char *key_file, unsigned char *outbuf,
+int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
                unsigned char *inbuf, int inlen)
 {
        gcry_error_t gret;
@@ -548,7 +525,7 @@ free_key:
        return ret;
 }
 
-int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
+int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
                unsigned len, unsigned char *outbuf)
 {
        gcry_error_t gret;
diff --git a/gui.c b/gui.c
index 6882a31..69a9243 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -941,6 +941,14 @@ static int signal_post_select(struct sched *s, __a_unused void *context)
        switch (ret) {
        case SIGTERM:
                die(EXIT_FAILURE, "only the good die young (caught SIGTERM)\n");
+       case SIGWINCH:
+               PARA_NOTICE_LOG("got SIGWINCH\n");
+               if (curses_active()) {
+                       shutdown_curses();
+                       init_curses();
+                       redraw_bot_win();
+               }
+               return 1;
        case SIGINT:
                return 1;
        case SIGUSR1:
@@ -1146,14 +1154,8 @@ static int input_post_select(__a_unused struct sched *s,
        ret = wgetch(top.win);
        if (ret == ERR)
                return 0;
-       if (ret == KEY_RESIZE) {
-               if (curses_active()) {
-                       shutdown_curses();
-                       init_curses();
-                       redraw_bot_win();
-               }
+       if (ret == KEY_RESIZE) /* already handled in signal_post_select() */
                return 0;
-       }
        if (exs == EXEC_IDLE)
                handle_command(ret);
        else if (exec_pid > 0)
@@ -1456,6 +1458,7 @@ static int setup_tasks_and_schedule(void)
        para_install_sighandler(SIGTERM);
        para_install_sighandler(SIGCHLD);
        para_install_sighandler(SIGUSR1);
+       para_install_sighandler(SIGWINCH);
        signal_task->task = task_register(&(struct task_info) {
                .name = "signal",
                .pre_select = signal_pre_select,
index c581ebd..3a72329 100644 (file)
@@ -106,8 +106,10 @@ static int http_recv_post_select(struct sched *s, void *context)
        }
        if (phd->status == HTTP_SENT_GET_REQUEST) {
                ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG), &s->rfds);
-               if (ret < 0)
+               if (ret < 0) {
+                       PARA_ERROR_LOG("did not receive HTTP OK message\n");
                        goto out;
+               }
                if (ret == 0)
                        return 0;
                PARA_INFO_LOG("received ok msg, streaming\n");
@@ -128,8 +130,10 @@ static int http_recv_post_select(struct sched *s, void *context)
                btr_add_output_pool(rn->btrp, num_bytes - iov[0].iov_len, btrn);
        }
 out:
-       if (ret < 0)
+       if (ret < 0) {
+               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
                btr_remove_node(&rn->btrn);
+       }
        return ret;
 }
 
index 9a35fc9..4d61228 100644 (file)
 #include "error.h"
 #include "string.h"
 #include "afh.h"
+#include "net.h"
 #include "server.h"
 #include "http.h"
 #include "list.h"
 #include "send.h"
 #include "sched.h"
 #include "vss.h"
-#include "net.h"
+#include "close_on_fork.h"
 #include "fd.h"
 #include "chunk_queue.h"
 
@@ -213,7 +214,8 @@ static void http_pre_select(int *max_fileno, fd_set *rfds, fd_set *wfds)
 
 static int http_com_on(__a_unused struct sender_command_data *scd)
 {
-       return generic_com_on(hss, IPPROTO_TCP);
+       generic_com_on(hss, IPPROTO_TCP);
+       return 1;
 }
 
 static int http_com_off(__a_unused struct sender_command_data *scd)
@@ -245,16 +247,12 @@ static char *http_status(void)
  */
 static void http_send_init(void)
 {
-       int ret;
-
        init_sender_status(hss, OPT_RESULT(HTTP_ACCESS),
                OPT_UINT32_VAL(HTTP_PORT), OPT_UINT32_VAL(HTTP_MAX_CLIENTS),
                OPT_GIVEN(HTTP_DEFAULT_DENY));
        if (OPT_GIVEN(HTTP_NO_AUTOSTART))
                return;
-       ret = generic_com_on(hss, IPPROTO_TCP);
-       if (ret < 0)
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       generic_com_on(hss, IPPROTO_TCP);
 }
 
 /**
index 1c037bc..6ad8c71 100644 (file)
@@ -6,9 +6,9 @@ version-string = GIT_VERSION()
        purpose = communicate with para_audiod through a local socket
        non-opts-name = [command [options]]
        [description]
-               The client program to control para_audiod at runtime. It allows to
-               enable/disable streaming, to receive status info, or to grab the
-               audio stream at any point of the decoding process.
+               The client program to control para_audiod at runtime. It can
+               enable/disable streaming, receive status info, or grab the audio
+               stream at any point of the decoding process.
 
                If no command is given, para_audioc enters interactive mode.
        [/description]
index 0cd69eb..376e875 100644 (file)
@@ -4,9 +4,9 @@ version-string = GIT_VERSION()
 [supercommand para_filter]
        purpose = decode or process audio data from STDIN to STDOUT
        [description]
-               This program allows to specify a chain of filters which transform the
-               audio stream read from STDIN. A common mode of operation is to decode
-               an mp3 file with the mp3dec filter, but many other filters exist which
+               This program transforms the audio stream read from STDIN by chaining
+               one or more filters. A common mode of operation is to decode an
+               mp3 file with the mp3dec filter, but many other filters exist which
                transform the audio stream in different ways.
        [/description]
        m4_include(common-option-section.m4)
@@ -35,3 +35,15 @@ version-string = GIT_VERSION()
                        through all given filters (in a single thread without copying the
                        data). The same filter may appear more than once, and order matters.
                [/help]
+[section Examples]
+       .IP \(bu 4
+       Decode a wma file to wav format:
+       .EX
+       \       para_filter -f wmadec -f wav < file.wma > file.wav
+       .EE
+       .IP \(bu 4
+       Amplify a raw audio file by a factor of 1.5:
+       .EX
+       \       para_filter -f amp --amp 32 < foo.raw > bar.raw
+       .EE
+[/section]
index a7364e1..ba19be9 100644 (file)
@@ -3,7 +3,7 @@
        summary = show milliseconds in log messages
        [help]
                Selecting this option causes milliseconds to be included in
-               the log message output. This allows to measure the interval
+               the log message output. This allows measuring of the interval
                between log messages in milliseconds which is useful for
                identifying timing problems.
        [/help]
index 0fbba0c..4af2a05 100644 (file)
@@ -7,8 +7,8 @@ version-string = GIT_VERSION()
        purpose = command line audio player
        non-opts-name = <audio_file>...
        [description]
-               para_play operates either in command mode or in insert mode. In
-               insert mode it presents a prompt and allows to enter commands like
+               para_play operates either in command mode or in insert mode. In insert
+               mode it presents a prompt and allows the user to enter commands like
                stop, play, pause etc. In command mode the current audio file and the
                playback position are shown and the program reads single key strokes
                from stdin. Keys may be mapped to commands so that the configured
index 793a2f5..993a449 100644 (file)
@@ -2,7 +2,16 @@ m4_define(PROGRAM, para_recv)
 [suite recv]
 version-string = GIT_VERSION()
 [supercommand para_recv]
-       purpose = a command line HTTP/DCCP/UDP stream grabber
+       purpose = receive an audio stream
+       [description]
+               para_recv starts one paraslash receiver (http, dccp, udp or afh)
+               to produce an audio stream in the same way para_audiod would download
+               the stream from para_server (http, dccp or udp) or para_server makes a
+               stream out of an audio file (afh). This is mostly useful for debugging.
+
+               Regardless of which receiver was started, the audio stream is written
+               to stdout.
+       [/description]
        m4_include(common-option-section.m4)
        m4_include(help.m4)
        m4_include(detailed-help.m4)
@@ -19,4 +28,6 @@ version-string = GIT_VERSION()
                        Any options for the selected receiver must be quoted. Example:
 
                                -r 'http -i www.paraslash.org -p 8009'
+
+                       If no receiver is given, http is assumed.
                [/help]
index 674a448..de6c5a6 100644 (file)
@@ -79,7 +79,7 @@ caption = receivers
 
                        The request is reconciled with the CCIDs on the server through the
                        'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs
-                       can be listed by calling 'para_client si'.
+                       can be listed by calling 'para_client sender dccp status'.
                [/help]
 [subcommand udp]
        purpose = receive an audio stream over UDP
index 5bba85d..6ffdc00 100644 (file)
@@ -159,10 +159,10 @@ version-string = GIT_VERSION()
                summary = make the http access control list a whitelist
                [help]
                        The default is to use blacklists, i.e. connections to the http sender
-                       are allowed unless the connecting host matches a pattern given by a
-                       http-access option. This allows to use access control the other way
-                       round: Connections are denied from hosts which are not explicitly
-                       allowed by one or more http-access options.
+                       are allowed unless the connecting host matches a pattern given by
+                       a http-access option. This option allows using access control lists
+                       the other way round: Connections are denied from hosts which are not
+                       explicitly allowed by one or more http-access options.
                [/help]
        [option http-access]
                summary = add an entry to the http access control list
@@ -247,9 +247,9 @@ version-string = GIT_VERSION()
                        (path MTU) of an incoming connection, i.e. on the largest packet size
                        that can be transmitted without causing fragmentation.
 
-                       This option allows to use a value less than the MPS in order to
-                       fine-tune application performance. Values greater than the MPS of an
-                       incoming connection can not be set.
+                       This option allows values less than the MPS in order to fine-tune
+                       application performance. Values greater than the MPS of an incoming
+                       connection can not be set.
                [/help]
        [option dccp-data-slices-per-group]
                summary = the number of non-redundant slices per FEC group
index 9378b7c..96ce530 100644 (file)
@@ -407,20 +407,45 @@ aux_info_prefix = Permissions:
 
 [subcommand sender]
        purpose = control paraslash senders
-       synopsis = [sender cmd [arguments]]
+       synopsis = [sender subcmd [arguments]]
        aux_info = VSS_READ | VSS_WRITE
        [description]
-               Send a command to a specific sender. The following commands are
-               available, but not all senders support every command.
+               This command executes a subcommand for the given sender, which is
+               one of "http", "dccp" or "udp". Various subcommands exist to print
+               information about the sender, to activate and deactivate the sender,
+               and to change the access permissions and targets. The following
+               subcommands are available:
 
-                      help, on, off, add, delete, allow, deny, status.
+                      help, status, on, off, allow, deny, add, delete.
 
-               The help command prints the help text of the given sender. If no
-               command is given the list of available senders is shown.
+               All senders support the first four commands. The "allow" and "deny"
+               commands are supported by the http and the dccp senders while "add"
+               and "delete" are only supported by the udp sender. If no sender is
+               given, the list of available senders is shown.
 
-               Example:
+               Examples:
+
+               Get help for the udp sender (contains further examples):
+
+                       sender udp help
+
+               Show the access control list and the number of connected clients of
+               the http sender:
+
+                       sender http status
+
+               Senders may be activated and deactivated independently of each
+               other. The following command switches off the dccp sender:
+
+                       sender dccp off
+
+               Add an UDP unicast for a client to the target list of the UDP sender:
+
+                       sender udp add client.foo.org
+
+               Start UDP multicast, using the default multicast address:
 
-                       para_client sender http help
+                       sender udp add 224.0.1.38
 
        [/description]
 
diff --git a/mixer.c b/mixer.c
index 00af08a..52af25f 100644 (file)
--- a/mixer.c
+++ b/mixer.c
@@ -192,9 +192,6 @@ static void change_afs_mode(const char *afs_mode)
 {
        char *cmd;
 
-       client_cmd("stop");
-       if (!afs_mode)
-               return;
        cmd = make_message("select %s", afs_mode);
        client_cmd(cmd);
        free(cmd);
@@ -290,7 +287,7 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h)
        PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
        client_cmd("stop");
        sleep(1);
-       if (fot) {
+       if (fot && fo_mood) {
                ret = set_initial_volume(m, h);
                if (ret < 0)
                        return ret;
@@ -307,12 +304,13 @@ static int com_sleep(const struct mixer *m, struct mixer_handle *h)
                if (ret < 0)
                        return ret;
        }
-       if (OPT_GIVEN(SLEEP, SLEEP_MOOD)) {
+       if (sleep_mood) {
                change_afs_mode(sleep_mood);
-               client_cmd("play");
-       } else
+               if (!fot || !fo_mood) /* currently stopped */
+                       client_cmd("play");
+       } else if (fot && fo_mood) /* currently playing */
                client_cmd("stop");
-       if (!fit)
+       if (!fit || !fi_mood) /* nothing to do */
                return 1;
        change_afs_mode(fi_mood);
        for (;;) {
diff --git a/mp.c b/mp.c
index c5702c6..bade05b 100644 (file)
--- a/mp.c
+++ b/mp.c
@@ -6,7 +6,7 @@
  * This file contains the public and the private API of the flex/bison based
  * mood parser.
  *
- * The public API (at the bottom of the file) allows to parse the same mood
+ * The public API (at the bottom of the file) allows parsing the same mood
  * definition many times in an efficient manner.
  *
  * The first function to call is \ref mp_init(), which analyzes the given mood
@@ -61,8 +61,8 @@ struct mp_context {
  *
  * This function turns a generalized C99 string literal like "xyz\n" into a C
  * string (containing the three characters 'x', 'y' and 'z', followed by a
- * newline character and the terminating zero byte). The function allows to
- * specify different quote characters so that, for example, regular expression
+ * newline character and the terminating zero byte). The function receives
+ * quote characters as an argument so that, for example, regular expression
  * patterns enclosed in '/' can be parsed as well. To parse a proper string
  * literal, one has to pass two double quotes as the second argument.
  *
diff --git a/net.c b/net.c
index 3f76d21..1fece04 100644 (file)
--- a/net.c
+++ b/net.c
@@ -425,15 +425,20 @@ int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
        for (; ai; ai = ai->ai_next) {
                int fd;
                ret = socket(ai->ai_family, sock_type(l4type), l4type);
-               if (ret < 0)
+               if (ret < 0) {
+                       PARA_NOTICE_LOG("socket(): %s\n", strerror(errno));
                        continue;
+               }
                fd = ret;
                flowopt_setopts(fd, fo);
                if (!passive) {
-                       if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
-                               return fd;
-                       close(fd);
-                       continue;
+                       if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+                               PARA_NOTICE_LOG("connect(): %s\n",
+                                       strerror(errno));
+                               close(fd);
+                               continue;
+                       }
+                       return fd;
                }
                /*
                 * Reuse the address on passive sockets to avoid failure on
@@ -442,10 +447,12 @@ int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai,
                 */
                if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on,
                                sizeof(on)) == -1) {
+                       PARA_NOTICE_LOG("setsockopt(): %s\n", strerror(errno));
                        close(fd);
                        continue;
                }
                if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+                       PARA_NOTICE_LOG("bind(): %s\n", strerror(errno));
                        close(fd);
                        continue;
                }
@@ -496,16 +503,15 @@ int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_numb
  *
  * \param l4type The transport-layer type (\p IPPROTO_xxx).
  * \param port The decimal port number to listen on.
- * \param fo Flowopts (if any) to set before starting to listen.
  *
  * \return Positive integer (socket descriptor) on success, negative value
  * otherwise.
  *
  * \sa \ref makesock(), ip(7), ipv6(7), bind(2), listen(2).
  */
-int para_listen(unsigned l4type, uint16_t port, struct flowopts *fo)
+int para_listen_simple(unsigned l4type, uint16_t port)
 {
-       int ret, fd = makesock(l4type, 1, NULL, port, fo);
+       int ret, fd = makesock(l4type, 1, NULL, port, NULL);
 
        if (fd > 0) {
                ret = listen(fd, BACKLOG);
diff --git a/net.h b/net.h
index f172131..143fb81 100644 (file)
--- a/net.h
+++ b/net.h
@@ -48,6 +48,9 @@
 #define DCCP_SOCKOPT_TX_CCID 14 /**< Set/get the TX CCID. */
 #endif
 
+/** The maximum length of the host component in an URL. */
+#define MAX_HOSTLEN 256
+
 /**
  * Flowopts: Transport-layer independent encapsulation of socket options
  *           that need to be registered prior to setting up a connection.
@@ -124,12 +127,8 @@ bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2);
  */
 /** How many pending connections queue of a listening server will hold. */
 #define BACKLOG        10
-extern int para_listen(unsigned l4type, uint16_t port, struct flowopts *fo);
 
-static inline int para_listen_simple(unsigned l4type, uint16_t port)
-{
-       return para_listen(l4type, port, NULL);
-}
+int para_listen_simple(unsigned l4type, uint16_t port);
 
 /** Pretty-printing of IPv4/6 socket addresses */
 extern char *remote_name(int sockfd);
diff --git a/openssl.c b/openssl.c
new file mode 100644 (file)
index 0000000..7d5bb25
--- /dev/null
+++ b/openssl.c
@@ -0,0 +1,280 @@
+/* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+/** \file openssl.c Openssl-based encryption/decryption routines. */
+
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/sha.h>
+#include <openssl/bn.h>
+#include <openssl/aes.h>
+
+#include "para.h"
+#include "error.h"
+#include "string.h"
+#include "crypt.h"
+#include "crypt_backend.h"
+#include "portable_io.h"
+
+struct asymmetric_key {
+       RSA *rsa;
+};
+
+void get_random_bytes_or_die(unsigned char *buf, int num)
+{
+       unsigned long err;
+
+       /* RAND_bytes() returns 1 on success, 0 otherwise. */
+       if (RAND_bytes(buf, num) == 1)
+               return;
+       err = ERR_get_error();
+       PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err));
+       exit(EXIT_FAILURE);
+}
+
+/*
+ * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Seed the PRNG
+ * used by random(3) with a random seed obtained from SSL. If /dev/urandom is
+ * not readable, the function calls exit().
+ *
+ * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3),
+ * random(3), \ref para_random().
+ */
+void crypt_init(void)
+{
+       int seed, ret = RAND_load_file("/dev/urandom", 64);
+
+       if (ret != 64) {
+               PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret);
+               exit(EXIT_FAILURE);
+       }
+       get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
+       srandom(seed);
+}
+
+void crypt_shutdown(void)
+{
+       CRYPTO_cleanup_all_ex_data();
+}
+
+static int get_private_key(const char *path, RSA **rsa)
+{
+       EVP_PKEY *pkey;
+       BIO *bio = BIO_new(BIO_s_file());
+
+       *rsa = NULL;
+       if (!bio)
+               return -E_PRIVATE_KEY;
+       if (BIO_read_filename(bio, path) <= 0)
+               goto bio_free;
+       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+       if (!pkey)
+               goto bio_free;
+       *rsa = EVP_PKEY_get1_RSA(pkey);
+       EVP_PKEY_free(pkey);
+bio_free:
+       BIO_free(bio);
+       return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
+}
+
+/*
+ * The public key loading functions below were inspired by corresponding code
+ * of openssh-5.2p1, Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo,
+ * Finland. However, not much of the original code remains.
+ */
+
+static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
+{
+       const unsigned char *p = buf, *end = buf + len;
+       uint32_t bnsize;
+       BIGNUM *bn;
+
+       if (p + 4 < p)
+               return -E_BIGNUM;
+       if (p + 4 > end)
+               return -E_BIGNUM;
+       bnsize = read_u32_be(p);
+       PARA_DEBUG_LOG("bnsize: %u\n", bnsize);
+       p += 4;
+       if (p + bnsize < p)
+               return -E_BIGNUM;
+       if (p + bnsize > end)
+               return -E_BIGNUM;
+       if (bnsize > 8192)
+               return -E_BIGNUM;
+       bn = BN_bin2bn(p, bnsize, NULL);
+       if (!bn)
+               return -E_BIGNUM;
+       *result = bn;
+       return bnsize + 4;
+}
+
+static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
+{
+       int ret;
+       RSA *rsa;
+       BIGNUM *n, *e;
+       const unsigned char *p = blob, *end = blob + blen;
+
+       rsa = RSA_new();
+       if (!rsa)
+               return -E_BIGNUM;
+       ret = read_bignum(p, end - p, &e);
+       if (ret < 0)
+               goto fail;
+       p += ret;
+       ret = read_bignum(p, end - p, &n);
+       if (ret < 0)
+               goto fail;
+#ifdef HAVE_RSA_SET0_KEY
+       RSA_set0_key(rsa, n, e, NULL);
+#else
+       rsa->n = n;
+       rsa->e = e;
+#endif
+       *result = rsa;
+       return 1;
+fail:
+       RSA_free(rsa);
+       return ret;
+}
+
+int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
+{
+       unsigned char *blob;
+       size_t decoded_size;
+       int ret;
+       struct asymmetric_key *key = para_malloc(sizeof(*key));
+
+       ret = decode_ssh_key(key_file, &blob, &decoded_size);
+       if (ret < 0)
+               goto out;
+       ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
+       if (ret < 0)
+               goto free_blob;
+       ret = RSA_size(key->rsa);
+       assert(ret > 0);
+       *result = key;
+free_blob:
+       free(blob);
+out:
+       if (ret < 0) {
+               free(key);
+               *result = NULL;
+               PARA_ERROR_LOG("can not load key %s\n", key_file);
+       }
+       return ret;
+}
+
+void apc_free_pubkey(struct asymmetric_key *key)
+{
+       if (!key)
+               return;
+       RSA_free(key->rsa);
+       free(key);
+}
+
+int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
+               unsigned char *inbuf, int inlen)
+{
+       struct asymmetric_key *priv;
+       int ret;
+
+       ret = check_private_key_file(key_file);
+       if (ret < 0)
+               return ret;
+       if (inlen < 0)
+               return -E_RSA;
+       priv = para_malloc(sizeof(*priv));
+       ret = get_private_key(key_file, &priv->rsa);
+       if (ret < 0) {
+               free(priv);
+               return ret;
+       }
+       /*
+        * RSA is vulnerable to timing attacks. Generate a random blinding
+        * factor to protect against this kind of attack.
+        */
+       ret = -E_BLINDING;
+       if (RSA_blinding_on(priv->rsa, NULL) == 0)
+               goto out;
+       ret = RSA_private_decrypt(inlen, inbuf, outbuf, priv->rsa,
+               RSA_PKCS1_OAEP_PADDING);
+       RSA_blinding_off(priv->rsa);
+       if (ret <= 0)
+               ret = -E_DECRYPT;
+out:
+       RSA_free(priv->rsa);
+       free(priv);
+       return ret;
+}
+
+int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
+               unsigned len, unsigned char *outbuf)
+{
+       int ret, flen = len; /* RSA_public_encrypt expects a signed int */
+
+       if (flen < 0)
+               return -E_ENCRYPT;
+       ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa,
+               RSA_PKCS1_OAEP_PADDING);
+       return ret < 0? -E_ENCRYPT : ret;
+}
+
+struct stream_cipher {
+       EVP_CIPHER_CTX *aes;
+};
+
+struct stream_cipher *sc_new(const unsigned char *data, int len)
+{
+       struct stream_cipher *sc = para_malloc(sizeof(*sc));
+
+       assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
+       sc->aes = EVP_CIPHER_CTX_new();
+       EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
+               data + AES_CRT128_BLOCK_SIZE);
+       return sc;
+}
+
+void sc_free(struct stream_cipher *sc)
+{
+       if (!sc)
+               return;
+       EVP_CIPHER_CTX_free(sc->aes);
+       free(sc);
+}
+
+static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
+               struct iovec *dst)
+{
+       int ret, inlen = src->iov_len, outlen, tmplen;
+
+       *dst = (typeof(*dst)) {
+               /* Add one for the terminating zero byte. */
+               .iov_base = para_malloc(inlen + 1),
+               .iov_len = inlen
+       };
+       ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
+       assert(ret != 0);
+       ret = EVP_EncryptFinal_ex(ctx, dst->iov_base + outlen, &tmplen);
+       assert(ret != 0);
+       outlen += tmplen;
+       ((char *)dst->iov_base)[outlen] = '\0';
+       dst->iov_len = outlen;
+}
+
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+{
+       return aes_ctr128_crypt(sc->aes, src, dst);
+}
+
+void hash_function(const char *data, unsigned long len, unsigned char *hash)
+{
+       SHA_CTX c;
+       SHA1_Init(&c);
+       SHA1_Update(&c, data, len);
+       SHA1_Final(hash, &c);
+}
diff --git a/sched.c b/sched.c
index 4fc65b4..a290394 100644 (file)
--- a/sched.c
+++ b/sched.c
@@ -168,7 +168,7 @@ again:
  * \param tptr Identifies the task to reap.
  *
  * This function is similar to wait(2) in that it returns information about a
- * terminated task and allows to release the resources associated with the
+ * terminated task which allows releasing the resources associated with the
  * task. Until this function is called, the terminated task remains in a zombie
  * state.
  *
diff --git a/send.h b/send.h
index 212fb4a..7a4c01b 100644 (file)
--- a/send.h
+++ b/send.h
@@ -190,8 +190,8 @@ void generic_com_allow(struct sender_command_data *scd,
                struct sender_status *ss);
 void generic_com_deny(struct sender_command_data *scd,
                struct sender_status *ss);
+void generic_com_on(struct sender_status *ss, unsigned protocol);
 void generic_acl_deplete(struct list_head *acl);
-int generic_com_on(struct sender_status *ss, unsigned protocol);
 void generic_com_off(struct sender_status *ss);
 char *generic_sender_help(void);
 struct sender_client *accept_sender_client(struct sender_status *ss, fd_set *rfds);
index 3e8a7c0..61a12c8 100644 (file)
 /** Clients will be kicked if there are more than that many bytes pending. */
 #define MAX_CQ_BYTES 40000
 
-/**
- * Open a passive socket of given layer4 type.
- *
- * Set the resulting file descriptor to nonblocking mode and add it to the list
- * of fds that are being closed in the child process when the server calls
- * fork().
- *
- * \param l4type The transport-layer protocol.
- * \param port The port number.
- *
- * \return The listening fd on success, negative on errors.
- */
-static int open_sender(unsigned l4type, int port)
-{
-       int fd, ret = para_listen_simple(l4type, port);
-
-       if (ret < 0)
-               return ret;
-       fd = ret;
-       ret = mark_fd_nonblocking(fd);
-       if (ret < 0) {
-               close(fd);
-               return ret;
-       }
-       add_close_on_fork_list(fd);
-       return fd;
-}
-
 /**
  * Shut down a client connected to a paraslash sender.
  *
@@ -252,21 +224,38 @@ void generic_com_deny(struct sender_command_data *scd,
  * Activate a paraslash sender.
  *
  * \param ss The sender to activate.
- * \param protocol The symbolic name of the transport-layer protocol.
+ * \param protocol layer4 type (IPPROTO_TCP or IPPROTO_DCCP).
  *
- * \return Standard.
+ * This opens a passive socket of given layer4 type, sets the resulting file
+ * descriptor to nonblocking mode and adds it to the close on fork list.
+ *
+ * Errors are logged but otherwise ignored.
  */
-int generic_com_on(struct sender_status *ss, unsigned protocol)
+void generic_com_on(struct sender_status *ss, unsigned protocol)
 {
-       int ret;
+       int fd, ret;
 
        if (ss->listen_fd >= 0)
-               return 1;
-       ret = open_sender(protocol, ss->port);
-       if (ret < 0)
-               return ret;
-       ss->listen_fd = ret;
-       return 1;
+               return;
+       ret = para_listen_simple(protocol, ss->port);
+       if (ret < 0) {
+               PARA_ERROR_LOG("could not listen on port %d: %s\n", ss->port,
+                       para_strerror(-ret));
+               return;
+       }
+       fd = ret;
+       ret = mark_fd_nonblocking(fd);
+       if (ret < 0) {
+               PARA_ERROR_LOG("could not set %s socket fd for port %d to "
+                       "nonblocking mode: %s\n",
+                       protocol == IPPROTO_TCP? "TCP" : "DCCP", ss->port,
+                       para_strerror(-ret));
+               close(fd);
+               return;
+       }
+       add_close_on_fork_list(fd);
+       ss->listen_fd = fd;
+       return;
 }
 
 /**
@@ -295,9 +284,8 @@ void generic_com_off(struct sender_status *ss)
  * \param ss The sender whose listening fd is ready for reading.
  * \param rfds Passed to para_accept(),
  *
- * This must be called only if the socket fd of \a ss is ready for reading.  It
- * calls para_accept() to accept the connection and performs the following
- * actions on the resulting file descriptor \a fd:
+ * This calls para_accept() and performs the following actions on the resulting
+ * file descriptor fd:
  *
  *     - Checks whether the maximal number of connections are exceeded.
  *     - Sets \a fd to nonblocking mode.
index 076bf4f..f19fc99 100644 (file)
--- a/server.c
+++ b/server.c
@@ -45,6 +45,7 @@
 #include "afh.h"
 #include "string.h"
 #include "afs.h"
+#include "net.h"
 #include "server.h"
 #include "list.h"
 #include "send.h"
@@ -52,7 +53,6 @@
 #include "vss.h"
 #include "config.h"
 #include "close_on_fork.h"
-#include "net.h"
 #include "daemon.h"
 #include "ipc.h"
 #include "fd.h"
@@ -505,6 +505,7 @@ static int init_afs(int argc, char **argv)
                int i;
 
                afs_pid = getpid();
+               crypt_shutdown();
                user_list_deplete();
                for (i = argc - 1; i >= 0; i--)
                        memset(argv[i], 0, strlen(argv[i]));
@@ -564,7 +565,7 @@ static void server_init(int argc, char **argv, struct server_command_task *sct)
        if (OPT_GIVEN(DAEMON))
                daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
        server_pid = getpid();
-       init_random_seed_or_die();
+       crypt_init();
        daemon_log_welcome("server");
        init_ipc_or_die(); /* init mmd struct, mmd and log mutex */
        daemon_set_start_time();
@@ -684,12 +685,12 @@ int main(int argc, char *argv[])
         */
        mutex_unlock(mmd_mutex);
        sched_shutdown(&sched);
+       crypt_shutdown();
        signal_shutdown(signal_task);
        if (!process_is_command_handler()) { /* parent (server) */
                mutex_destroy(mmd_mutex);
                daemon_set_hooks(NULL, NULL); /* only one process remaining */
                mutex_destroy(log_mutex);
-               shm_detach(mmd);
                deplete_close_on_fork_list();
                if (ret < 0)
                        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
index d56da7e..da75d86 100644 (file)
--- a/server.h
+++ b/server.h
@@ -5,10 +5,6 @@
 /** Size of the selector_info and audio_file info strings of struct misc_meta_data. */
 #define MMD_INFO_SIZE 16384
 
-/** The maximum length of the host component in an URL */
-#define MAX_HOSTLEN 256
-
-
 /** Arguments for the sender command. */
 struct sender_command_data {
        /** Greater than zero indicates that a sender cmd is already queued. */
index 3023c7b..6973b84 100644 (file)
@@ -97,7 +97,7 @@ struct sb_context;
 /**
  * The type of a sideband transformation.
  *
- * The sideband API allows to filter all data through an arbitrary
+ * The sideband API allows the filtering of data through an arbitrary
  * transformation, which is useful for crypto purposes. The transformation may
  * either transform the data in place, or return a pointer to a new buffer
  * which contains the transformed source buffer. The internal sideband
index 52947b2..04e2982 100644 (file)
 #include "error.h"
 #include "string.h"
 #include "afh.h"
+#include "net.h"
 #include "server.h"
 #include "list.h"
 #include "send.h"
 #include "sched.h"
 #include "vss.h"
 #include "portable_io.h"
-#include "net.h"
 #include "fd.h"
 #include "close_on_fork.h"
 
index d54b619..32a4309 100644 (file)
@@ -52,7 +52,7 @@ void user_list_deplete(void)
        list_for_each_entry_safe(u, tmpu, &user_list, node) {
                list_del(&u->node);
                free(u->name);
-               free_public_key(u->pubkey);
+               apc_free_pubkey(u->pubkey);
                free(u);
        }
 }
@@ -92,22 +92,22 @@ void user_list_init(const char *user_list_file)
                if (strcmp(w, "user"))
                        continue;
                PARA_DEBUG_LOG("found entry for user %s\n", n);
-               ret = get_public_key(k, &pubkey);
+               ret = apc_get_pubkey(k, &pubkey);
                if (ret < 0) {
                        PARA_NOTICE_LOG("skipping entry for user %s: %s\n", n,
                                para_strerror(-ret));
                        continue;
                }
                /*
-                * In order to encrypt len := CHALLENGE_SIZE + 2 * SESSION_KEY_LEN
+                * In order to encrypt len := APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN
                 * bytes using RSA_public_encrypt() with EME-OAEP padding mode,
                 * RSA_size(rsa) must be greater than len + 41. So ignore keys
                 * which are too short. For details see RSA_public_encrypt(3).
                 */
-               if (ret <= CHALLENGE_SIZE + 2 * SESSION_KEY_LEN + 41) {
+               if (ret <= APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN + 41) {
                        PARA_WARNING_LOG("public key %s too short (%d)\n",
                                k, ret);
-                       free_public_key(pubkey);
+                       apc_free_pubkey(pubkey);
                        continue;
                }
                u = para_malloc(sizeof(*u));
diff --git a/vss.c b/vss.c
index 0a1ac52..2cd0a16 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -25,8 +25,8 @@
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
-#include "server.h"
 #include "net.h"
+#include "server.h"
 #include "list.h"
 #include "send.h"
 #include "sched.h"
@@ -283,7 +283,7 @@ static int initialize_fec_client(struct fec_client *fc, struct vss_task *vsst)
        if (fcp->init_fec) {
                /*
                 * Set the maximum slice size to the Maximum Packet Size if the
-                * transport protocol allows to determine this value. The user
+                * transport protocol allows determination of this value. The user
                 * can specify a slice size up to this value.
                 */
                ret = fcp->init_fec(fc->sc);
index 1ca8a4d..9ef92b7 100644 (file)
@@ -15,8 +15,8 @@ provided at this point. There are several ways to download the source:
 
                <p> The repository contains the full history of the
                project since 2006, all work in progress and the source
-               code for the web pages. Choosing this option allows to
-               check out any of the four integration branches maint,
+               code for the web pages. Choosing this option allows the
+               checkout of any of the four integration branches maint,
                master, next, pu (see the
 
                <a href="manual.html#Git.branches">Git branches</a>
@@ -68,7 +68,7 @@ provided at this point. There are several ways to download the source:
                        <a href="http://git.tuebingen.mpg.de/paraslash.git">gitweb</a>
 
                page contains a snapshot link for each revision. This
-               allows to get a specific revision without downloading
+               allows getting a specific revision without downloading
                the full history.
 
        </li>
index fb3a05f..04c716a 100644 (file)
@@ -230,9 +230,9 @@ compatible with arbitrary HTTP streaming sources (e.g. icecast).
 In addition to the three network streaming modes, para_recv can also
 operate in local (afh) mode. In this mode it writes the content of
 an audio file on the local file system in complete chunks to stdout,
-optionally 'just in time'. This allows to cut an audio file without
-first decoding it, and it enables third-party software which is unaware
-of the particular audio format to send complete frames in real time.
+optionally 'just in time'. This allows cutting audio files without
+decoding, and it enables third-party software which is unaware of
+the particular audio format to send complete frames in real time.
 
 <h3> para_filter </h3>
 
@@ -1541,27 +1541,6 @@ currently running server process.
 
        para_client si
 
-The sender command of para_server prints information about senders,
-like the various access control lists, and it allows to (de-)activate
-senders and to change the access permissions at runtime.
-
--> List all senders
-
-       para_client sender
-
--> Obtain general help for the sender command:
-
-       para_client help sender
-
--> Get help for a specific sender (contains further examples):
-
-       s=http # or dccp or udp
-       para_client sender $s help
-
--> Show status of the http sender
-
-       para_client sender http status
-
 By default para_server activates both the HTTP and th DCCP sender on
 startup. This can be changed via command line options or para_server's
 config file.
@@ -1570,13 +1549,6 @@ config file.
 
        para_server -h
 
-All senders share the "on" and "off" commands, so senders may be
-activated and deactivated independently of each other.
-
--> Switch off the http sender:
-
-       para_client sender http off
-
 -> Receive a DCCP stream using CCID2 and write the output into a file:
 
        host=foo.org; ccid=2; filename=bar
@@ -1587,20 +1559,11 @@ receiver has its own set of command line options and its own command
 line parser, so arguments for the dccp receiver must be protected
 from being interpreted by para_recv.
 
--> Start UDP multicast, using the default multicast address:
-
-       para_client sender udp add 224.0.1.38
-
 -> Receive FEC-encoded multicast stream and write the output into a file:
 
        filename=foo
        para_recv -r udp > $filename
 
--> Add an UDP unicast for a client to the target list of the UDP sender:
-
-       t=client.foo.org
-       para_client sender udp add $t
-
 -> Receive this (FEC-encoded) unicast stream:
 
        filename=foo
@@ -1778,7 +1741,7 @@ These filters are rather simple and do not modify the audio stream at
 all. The wav filter is only useful with para_filter and in connection
 with a decoder. It asks the decoder for the number of channels and the
 sample rate of the stream and adds a Microsoft wave header containing
-this information at the beginning. This allows to write wav files
+this information at the beginning. This allows writing wav files
 rather than raw PCM files (which do not contain any information about
 the number of channels and the sample rate).
 
@@ -1792,17 +1755,6 @@ Both filters require almost no additional computing time, even when
 operating on uncompressed audio streams, since data buffers are simply
 "pushed down" rather than copied.
 
-Examples
---------
-
--> Decode an mp3 file to wav format:
-
-       para_filter -f mp3dec -f wav < file.mp3 > file.wav
-
--> Amplify a raw audio file by a factor of 1.5:
-
-       para_filter -f amp --amp 32 < foo.raw > bar.raw
-
 ======
 Output
 ======
@@ -1852,8 +1804,8 @@ emulation for backwards compatibility. This API is rather simple but
 also limited. For example only one application can open the device
 at any time. The OSS writer is activated by default on BSD Systems.
 
-- *FILE*. The file writer allows to capture the audio stream and
-write the PCM data to a file on the file system rather than playing
+- *FILE*. The file writer allows capturing the audio stream and
+writing the PCM data to a file on the file system rather than playing
 it through a sound device. It is supported on all platforms and is
 always compiled in.