Merge branch 't/sched_improvements'
authorAndre Noll <maan@systemlinux.org>
Sat, 13 Aug 2011 10:15:10 +0000 (12:15 +0200)
committerAndre Noll <maan@systemlinux.org>
Sat, 13 Aug 2011 10:16:09 +0000 (12:16 +0200)
22 files changed:
NEWS
afh.h
afh_common.c
aft.c
audiod.c
audiod_command.c
client.h
configure.ac
crypt.c
crypt.h
crypt_backend.h [new file with mode: 0644]
crypt_common.c [new file with mode: 0644]
error.h
filter_common.c
gcrypt.c [new file with mode: 0644]
gui.c
mp3dec_filter.c
net.c
osx_write.c
server.c
web/manual.m4
wma_common.c

diff --git a/NEWS b/NEWS
index c6c2e4d..f76a6e9 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,11 +2,23 @@
 0.4.8 (to be announced) "nested assignment"
 -------------------------------------------
 
+Gcrypt support, the overhauled osx writer and regex format specifiers
+are the highlights of this release.
+
+       - support for libgcrypt as a drop-in replacement for openssl.
+         Run configure --enable-cryptolib=gcrypt to link against
+         libgcrypt. The two crypto implementations are compatible to
+         each other, i.e. a para_client executable linked against
+         gcrypt can connect to para_server linked against libssl
+         and vice versa.
+       - Rewrite of the osx writer (output plugin for Mac OS).
        - audiod: The format specifier for receivers, filters and
          writers is now treated as a regular expression. This allows
          to replace 5 lines in the config file (one for each audio
          format) by one single line. See the manual for details.
        - Compiles cleanly also with llvm/clang.
+       - Corrupt mp3 files are handled more gracefully.
+       - sched: Optimized zero timeouts.
 
 --------------------------------------
 0.4.7 (2011-06-01) "infinite rollback"
diff --git a/afh.h b/afh.h
index 8b747f8..0486bd7 100644 (file)
--- a/afh.h
+++ b/afh.h
@@ -80,7 +80,7 @@ struct audio_format_handler {
        /**
         * Check if this audio format handler can handle the file.
         *
-        * This is a  pointer to a function returning whether a given file is
+        * This is a pointer to a function returning whether a given file is
         * valid for this audio format. A negative return value indicates that
         * this audio format handler is unable to decode the given file. On
         * success, the function must return a positive value and fill in the
@@ -90,7 +90,7 @@ struct audio_format_handler {
         */
        int (*get_file_info)(char *map, size_t numbytes, int fd,
                struct afh_info *afi);
-
+       /** Optional, used for header-rewriting. See \ref afh_get_header(). */
        void (*get_header)(void *map, size_t mapsize, char **buf, size_t *len);
 };
 
@@ -101,7 +101,6 @@ int compute_afhi(const char *path, char *data, size_t size,
 const char *audio_format_name(int);
 void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
                void *map, const char **buf, size_t *len);
-uint32_t afh_get_largest_chunk_size(struct afh_info *afhi);
 void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
                void *map, size_t mapsize, char **buf, size_t *len);
 void afh_free_header(char *header_buf, uint8_t audio_format_id);
index 7696741..44a6fd6 100644 (file)
@@ -213,7 +213,7 @@ success:
  *
  * \param i The audio format number.
  *
- * This returns a pointer to statically allocated memory so it
+ * \return This returns a pointer to statically allocated memory so it
  * must not be freed by the caller.
  */
 const char *audio_format_name(int i)
diff --git a/aft.c b/aft.c
index a27e67e..f408023 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -1823,7 +1823,7 @@ static void hash_sister_callback(int fd, const struct osl_object *query)
 static int get_row_pointer_from_result(struct osl_object *result, void *private)
 {
        struct osl_row **row = private;
-       *row = result->data;
+       *row = *(struct osl_row **)(result->data);
        return 1;
 }
 
index 6e5efd6..c7998cd 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -289,7 +289,7 @@ out:
                length / 60,
                length % 60
        );
-       PARA_DEBUG_LOG("slot %d: %s\n", slot_num, msg);
+       //PARA_DEBUG_LOG("slot %d: %s\n", slot_num, msg);
        return msg;
 empty:
        return para_strdup(NULL);
index 0f7aa7e..1c22f58 100644 (file)
@@ -194,7 +194,7 @@ static int get_play_time_slot_num(void)
        FOR_EACH_SLOT(i) {
                struct slot_info *s = &slot[i];
                struct timeval wstime;
-               if (!s->wns)
+               if (!s->wns || !s->wns[0].btrn)
                        continue;
                btr_get_node_start(s->wns[0].btrn, &wstime);
                if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
index 2d63c41..28b786a 100644 (file)
--- a/client.h
+++ b/client.h
@@ -42,6 +42,7 @@ struct client_task {
        char *user;
        /** The client task structure. */
        struct task task;
+       /** The buffer tree node of the client task. */
        struct btr_node *btrn;
 };
 
index 8904281..79aaee6 100644 (file)
@@ -85,14 +85,14 @@ AC_DEFUN([add_cmdline],[$(for i in $@; do printf "${i}.cmdline "; done)])
 
 
 all_errlist_objs="server mp3_afh afh_common vss command net string signal time
-daemon stat crypt http_send close_on_fork ipc acl afh fade amp_filter
+daemon stat http_send close_on_fork ipc acl afh fade amp_filter
 dccp_send fd user_list chunk_queue afs aft mood score attribute blob ringbuffer
 playlist sched audiod grab_client filter_common wav_filter compress_filter
 http_recv dccp_recv recv_common write_common file_write audiod_command
 client_common recv stdout filter stdin audioc write client exec send_common ggo
 udp_recv udp_send color fec fecdec_filter prebuffer_filter mm
 server_command_list afs_command_list audiod_command_list bitstream imdct wma_afh
-wma_common wmadec_filter buffer_tree
+wma_common wmadec_filter buffer_tree crypt_common
 "
 
 executables="recv filter audioc write client afh audiod"
@@ -115,9 +115,9 @@ audioc_errlist_objs="audioc string net fd"
 audioc_ldflags=""
 
 audiod_cmdline_objs="add_cmdline(audiod compress_filter http_recv dccp_recv file_write client amp_filter udp_recv prebuffer_filter)"
-audiod_errlist_objs="audiod signal string daemon stat net
+audiod_errlist_objs="audiod signal string daemon stat net crypt_common
        time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
-       recv_common fd sched write_common file_write audiod_command crypt fecdec_filter
+       recv_common fd sched write_common file_write audiod_command fecdec_filter
        client_common ggo udp_recv color fec prebuffer_filter audiod_command_list
        bitstream imdct wma_common wmadec_filter buffer_tree"
 audiod_ldflags="-lm"
@@ -129,7 +129,7 @@ afh_ldflags=""
 
 server_cmdline_objs="add_cmdline(server)"
 server_errlist_objs="server afh_common mp3_afh vss command net string signal
-       time daemon crypt http_send close_on_fork mm
+       time daemon http_send close_on_fork mm crypt_common
        ipc dccp_send fd user_list chunk_queue afs aft mood score attribute
        blob playlist sched acl send_common udp_send color fec
        server_command_list afs_command_list wma_afh wma_common"
@@ -144,8 +144,8 @@ writers=" file"
 default_writer="FILE_WRITE"
 
 client_cmdline_objs="add_cmdline(client)"
-client_errlist_objs="client net string crypt fd sched stdin stdout time
-       client_common buffer_tree"
+client_errlist_objs="client net string fd sched stdin stdout time
+       client_common buffer_tree crypt_common"
 client_ldflags=""
 
 gui_cmdline_objs="add_cmdline(gui)"
@@ -273,52 +273,122 @@ fi
 CPPFLAGS="$OLD_CPPFLAGS"
 LDFLAGS="$OLD_LDFLAGS"
 LIBS="$OLD_LIBS"
+########################################################################### crypto
+AC_ARG_ENABLE(cryptolib, [AS_HELP_STRING(--enable-cryptolib=lib, [
+       Force using crypto library "lib". This package requires either
+       openssl or libgcrypt being installed. Possible values for "lib"
+       are thus "openssl" and "gcrypt". If this option is not given,
+       openssl is tried first. If openssl was not found, gcrypt is
+       tried next.])])
+
+case "$enable_cryptolib" in
+       "openssl") check_openssl="yes"; check_gcrypt="no";;
+       "gcrypt") check_openssl="no"; check_gcrypt="yes";;
+       "") check_openssl="yes"; check_gcrypt="yes";;
+       *) AC_MSG_ERROR([invalid value "$enable_cryptolib" for --enable-cryptolib]);;
+esac
 ###################################################################### openssl
-OLD_CPPFLAGS="$CPPFLAGS"
-OLD_LD_FLAGS="$LDFLAGS"
-OLD_LIBS="$LIBS"
-have_openssl="yes"
-AC_ARG_WITH(openssl_headers, [AC_HELP_STRING(--with-openssl-headers=dir,
-       [look for openssl headers also in dir])])
-if test -n "$with_openssl_headers"; then
-       openssl_cppflags="-I$with_openssl_headers"
-       CPPFLAGS="$CPPFLAGS $openssl_cppflags"
-fi
-AC_ARG_WITH(openssl_libs, [AC_HELP_STRING(--with-openssl-libs=dir,
-       [look for openssl libraries also in dir])])
-if test -n "$with_openssl_libs"; then
-       openssl_libs="-L$with_openssl_libs"
-       LDFLAGS="$LDFLAGS $openssl_libs"
-fi
-AC_CHECK_HEADER(openssl/ssl.h, [], [have_openssl="no"])
-AC_CHECK_LIB([crypto], [RAND_bytes], [], [have_openssl="no"])
-if test "$have_openssl" = "no" -a -z "$with_openssl_headers$with_openssl_libs"; then
-       # try harder: openssl is sometimes installed in /usr/local/ssl
-       openssl_cppflags="-I/usr/local/ssl/include"
-       CPPFLAGS="$CPPFLAGS $openssl_cppflags"
-       openssl_libs="-L/usr/local/ssl/lib"
-       LDFLAGS="$LDFLAGS $openssl_libs"
-       # clear cache
-       unset ac_cv_header_openssl_ssl_h 2> /dev/null
-       unset ac_cv_lib_crypto_RAND_bytes 2> /dev/null
-       AC_CHECK_HEADER(openssl/ssl.h, [have_openssl="yes"], [])
+if test "$check_openssl" = "yes"; then
+       OLD_CPPFLAGS="$CPPFLAGS"
+       OLD_LD_FLAGS="$LDFLAGS"
+       OLD_LIBS="$LIBS"
+       have_openssl="yes"
+       AC_ARG_WITH(openssl_headers, [AC_HELP_STRING(--with-openssl-headers=dir,
+               [look for openssl headers also in dir])])
+       if test -n "$with_openssl_headers"; then
+               openssl_cppflags="-I$with_openssl_headers"
+               CPPFLAGS="$CPPFLAGS $openssl_cppflags"
+       fi
+       AC_ARG_WITH(openssl_libs, [AC_HELP_STRING(--with-openssl-libs=dir,
+               [look for openssl libraries also in dir])])
+       if test -n "$with_openssl_libs"; then
+               openssl_libs="-L$with_openssl_libs"
+               LDFLAGS="$LDFLAGS $openssl_libs"
+       fi
+       AC_CHECK_HEADER(openssl/ssl.h, [], [have_openssl="no"])
+       AC_CHECK_LIB([crypto], [RAND_bytes], [], [have_openssl="no"])
+       if test "$have_openssl" = "no" -a -z "$with_openssl_headers$with_openssl_libs"; then
+               # try harder: openssl is sometimes installed in /usr/local/ssl
+               openssl_cppflags="-I/usr/local/ssl/include"
+               CPPFLAGS="$CPPFLAGS $openssl_cppflags"
+               openssl_libs="-L/usr/local/ssl/lib"
+               LDFLAGS="$LDFLAGS $openssl_libs"
+               # clear cache
+               unset ac_cv_header_openssl_ssl_h 2> /dev/null
+               unset ac_cv_lib_crypto_RAND_bytes 2> /dev/null
+               AC_CHECK_HEADER(openssl/ssl.h, [have_openssl="yes"], [])
+               if test "$have_openssl" = "yes"; then
+                       AC_CHECK_LIB([crypto], [RAND_bytes], [], [have_openssl="no"])
+               fi
+       fi
        if test "$have_openssl" = "yes"; then
-               AC_CHECK_LIB([crypto], [RAND_bytes], [], [have_openssl="no"])
+               AC_DEFINE(HAVE_OPENSSL, 1, [define to 1 to turn on openssl support])
+               AC_SUBST(openssl_cppflags)
+               openssl_libs="$openssl_libs -lssl -lcrypto"
+               server_ldflags="$server_ldflags $openssl_libs"
+               client_ldflags="$client_ldflags $openssl_libs"
+               audiod_ldflags="$audiod_ldflags $openssl_libs"
+
+               all_errlist_objs="$all_errlist_objs crypt"
+               server_errlist_objs="$server_errlist_objs crypt"
+               client_errlist_objs="$client_errlist_objs crypt"
+               audiod_errlist_objs="$audiod_errlist_objs crypt"
+
+               check_gcrypt="no"
+       else
+               AC_MSG_WARN([openssl libraries not found])
        fi
-fi
-if test "$have_openssl" = "yes"; then
-       AC_DEFINE(HAVE_OPENSSL, 1, [define to 1 to turn on openssl support])
-       AC_SUBST(openssl_cppflags)
-       openssl_libs="$openssl_libs -lssl -lcrypto"
-       server_ldflags="$server_ldflags $openssl_libs"
-       client_ldflags="$client_ldflags $openssl_libs"
-       audiod_ldflags="$audiod_ldflags $openssl_libs"
+       CPPFLAGS="$OLD_CPPFLAGS"
+       LDFLAGS="$OLD_LDFLAGS"
+       LIBS="$OLD_LIBS"
 else
-       AC_MSG_ERROR([openssl libraries not found])
+       have_openssl="no"
+fi
+########################################################################### gcrypt
+if test "$check_gcrypt" = "yes"; then
+       OLD_CPPFLAGS="$CPPFLAGS"
+       OLD_LD_FLAGS="$LDFLAGS"
+       OLD_LIBS="$LIBS"
+       have_gcrypt="yes"
+       AC_ARG_WITH(gcrypt_headers, [AC_HELP_STRING(--with-gcrypt-headers=dir,
+               [look for gcrypt headers also in dir])])
+       if test -n "$with_gcrypt_headers"; then
+               gcrypt_cppflags="-I$with_gcrypt_headers"
+               CPPFLAGS="$CPPFLAGS $gcrypt_cppflags"
+       fi
+       AC_ARG_WITH(gcrypt_libs, [AC_HELP_STRING(--with-gcrypt-libs=dir,
+               [look for libgcrypt also in dir])])
+       if test -n "$with_gcrypt_libs"; then
+               gcrypt_libs="-L$with_gcrypt_libs"
+               LDFLAGS="$LDFLAGS $gcrypt_libs"
+       fi
+       AC_CHECK_HEADER(gcrypt.h, [], [have_gcrypt="no"])
+       AC_CHECK_LIB([gcrypt], [gcry_randomize], [], [have_gcrypt="no"])
+       if test "$have_gcrypt" = "yes"; then
+               AC_DEFINE(HAVE_GCRYPT, 1, [define to 1 to turn on gcrypt support])
+               AC_SUBST(gcrypt_cppflags)
+               gcrypt_libs="$gcrypt_libs -lgcrypt"
+               server_ldflags="$server_ldflags $gcrypt_libs"
+               client_ldflags="$client_ldflags $gcrypt_libs"
+               audiod_ldflags="$audiod_ldflags $gcrypt_libs"
+
+               all_errlist_objs="$all_errlist_objs gcrypt"
+               server_errlist_objs="$server_errlist_objs gcrypt"
+               client_errlist_objs="$client_errlist_objs gcrypt"
+               audiod_errlist_objs="$audiod_errlist_objs gcrypt"
+       else
+               AC_MSG_WARN([gcrypt library not found])
+       fi
+       CPPFLAGS="$OLD_CPPFLAGS"
+       LDFLAGS="$OLD_LDFLAGS"
+       LIBS="$OLD_LIBS"
+else
+       have_gcrypt="no"
+fi
+###########################################################################
+if test "$have_openssl" = "no" -a "$have_gcrypt" = "no"; then
+       AC_MSG_ERROR([neither openssl nor gcrypt usable])
 fi
-CPPFLAGS="$OLD_CPPFLAGS"
-LDFLAGS="$OLD_LDFLAGS"
-LIBS="$OLD_LIBS"
 ########################################################################### libsocket
 AC_CHECK_LIB([c], [socket],
        [socket_lib=],
@@ -423,11 +493,11 @@ if test ${have_core_audio} = yes; then
        f="$f1 $f2 $f3 $f4"
 
        all_errlist_objs="$all_errlist_objs osx_write"
-       audiod_errlist_objs="$audiod_errlist_objs osx_write"
+       audiod_errlist_objs="$audiod_errlist_objs osx_write ipc"
        audiod_cmdline_objs="$audiod_cmdline_objs osx_write.cmdline"
        audiod_ldflags="$audiod_ldflags $f"
 
-       write_errlist_objs="$write_errlist_objs osx_write"
+       write_errlist_objs="$write_errlist_objs osx_write ipc"
        write_cmdline_objs="$write_cmdline_objs osx_write.cmdline"
        write_ldflags="$write_ldflags $f"
        writers="$writers osx"
@@ -807,7 +877,7 @@ for obj in $all_errlist_objs; do
 done
 AC_DEFINE_UNQUOTED(DEFINE_ERRLIST_OBJECT_ENUM,
        [enum {$SS NUM_SS}],
-       [list of all objects that use paraslash's error facility]
+       [list of all objects that use the paraslash error facility]
 )
 
 ################################################################## status items
diff --git a/crypt.c b/crypt.c
index 6f7e611..f064fb3 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -7,6 +7,7 @@
 /** \file crypt.c Openssl-based encryption/decryption routines. */
 
 #include <regex.h>
+#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <openssl/rand.h>
 #include "string.h"
 #include "crypt.h"
 #include "fd.h"
+#include "crypt_backend.h"
 
 struct asymmetric_key {
        RSA *rsa;
 };
 
-/**
- * Fill a buffer with random content.
- *
- * \param buf The buffer to fill.
- * \param num The size of \a buf in bytes.
- *
- * This function puts \a num cryptographically strong pseudo-random bytes into
- * buf. If libssl can not guarantee an unpredictable byte sequence (for example
- * because the PRNG has not been seeded with enough randomness) the function
- * logs an error message and calls exit().
- */
 void get_random_bytes_or_die(unsigned char *buf, int num)
 {
        unsigned long err;
@@ -49,13 +40,10 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
        exit(EXIT_FAILURE);
 }
 
-/**
- * Seed pseudo random number generators.
- *
- * This function reads 64 bytes from /dev/urandom and adds them to the SSL
- * PRNG. It also seeds the PRNG used by random() with a random seed obtained
- * from SSL. If /dev/random could not be read, an error message is logged and
- * the function calls exit().
+/*
+ * Read 64 bytes from /dev/urandom and adds them to the SSL PRNG. Seed the PRNG
+ * used by random() with a random seed obtained from SSL. If /dev/random 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().
@@ -72,19 +60,6 @@ void init_random_seed_or_die(void)
        srandom(seed);
 }
 
-static int check_key_file(const char *file, int private)
-{
-       struct stat st;
-
-       if (stat(file, &st) != 0)
-               return -ERRNO_TO_PARA_ERROR(errno);
-       if (private != LOAD_PRIVATE_KEY)
-               return 0;
-       if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0)
-               return -E_KEY_PERM;
-       return 1;
-}
-
 static EVP_PKEY *load_key(const char *file, int private)
 {
        BIO *key;
@@ -122,203 +97,12 @@ static int get_openssl_key(const char *key_file, RSA **rsa, int private)
        return RSA_size(*rsa);
 }
 
-#define KEY_TYPE_TXT "ssh-rsa"
-
-/* check if it is an ssh rsa key */
-static size_t is_ssh_rsa_key(char *data, size_t size)
-{
-       char *cp;
-
-       if (size < strlen(KEY_TYPE_TXT) + 2)
-               return 0;
-       cp = memchr(data, ' ', size);
-       if (cp == NULL)
-               return 0;
-       if (strncmp(KEY_TYPE_TXT, data, strlen(KEY_TYPE_TXT)))
-               return 0;
-       cp++;
-       if (cp >= data + size)
-               return 0;
-       if (*cp == '\0')
-               return 0;
-       return cp - data;
-}
-
-/*
- * This base64/uudecode stuff below is taken from openssh-5.2p1, Copyright (c)
- * 1996 by Internet Software Consortium.  Portions Copyright (c) 1995 by
- * International Business Machines, Inc.
- */
-
-static const char Base64[] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const char Pad64 = '=';
-/*
- * Skips all whitespace anywhere. Converts characters, four at a time, starting
- * at (or after) src from base - 64 numbers into three 8 bit bytes in the
- * target area. it returns the number of data bytes stored at the target, or -1
- * on error.
- */
-static int base64_decode(char const *src, unsigned char *target, size_t targsize)
-{
-       unsigned int tarindex, state;
-       int ch;
-       char *pos;
-
-       state = 0;
-       tarindex = 0;
-
-       while ((ch = *src++) != '\0') {
-               if (para_isspace(ch)) /* Skip whitespace anywhere. */
-                       continue;
-
-               if (ch == Pad64)
-                       break;
-
-               pos = strchr(Base64, ch);
-               if (pos == 0) /* A non-base64 character. */
-                       return -1;
-
-               switch (state) {
-               case 0:
-                       if (target) {
-                               if (tarindex >= targsize)
-                                       return (-1);
-                               target[tarindex] = (pos - Base64) << 2;
-                       }
-                       state = 1;
-                       break;
-               case 1:
-                       if (target) {
-                               if (tarindex + 1 >= targsize)
-                                       return (-1);
-                               target[tarindex]   |=  (pos - Base64) >> 4;
-                               target[tarindex+1]  = ((pos - Base64) & 0x0f)
-                                                       << 4 ;
-                       }
-                       tarindex++;
-                       state = 2;
-                       break;
-               case 2:
-                       if (target) {
-                               if (tarindex + 1 >= targsize)
-                                       return (-1);
-                               target[tarindex]   |=  (pos - Base64) >> 2;
-                               target[tarindex+1]  = ((pos - Base64) & 0x03)
-                                                       << 6;
-                       }
-                       tarindex++;
-                       state = 3;
-                       break;
-               case 3:
-                       if (target) {
-                               if (tarindex >= targsize)
-                                       return (-1);
-                               target[tarindex] |= (pos - Base64);
-                       }
-                       tarindex++;
-                       state = 0;
-                       break;
-               }
-       }
-
-       /*
-        * We are done decoding Base-64 chars.  Let's see if we ended
-        * on a byte boundary, and/or with erroneous trailing characters.
-        */
-
-       if (ch == Pad64) {              /* We got a pad char. */
-               ch = *src++;            /* Skip it, get next. */
-               switch (state) {
-               case 0:         /* Invalid = in first position */
-               case 1:         /* Invalid = in second position */
-                       return (-1);
-
-               case 2:         /* Valid, means one byte of info */
-                       /* Skip any number of spaces. */
-                       for (; ch != '\0'; ch = *src++)
-                               if (!isspace(ch))
-                                       break;
-                       /* Make sure there is another trailing = sign. */
-                       if (ch != Pad64)
-                               return (-1);
-                       ch = *src++;            /* Skip the = */
-                       /* Fall through to "single trailing =" case. */
-                       /* FALLTHROUGH */
-
-               case 3:         /* Valid, means two bytes of info */
-                       /*
-                        * We know this char is an =.  Is there anything but
-                        * whitespace after it?
-                        */
-                       for (; ch != '\0'; ch = *src++)
-                               if (!isspace(ch))
-                                       return (-1);
-
-                       /*
-                        * Now make sure for cases 2 and 3 that the "extra"
-                        * bits that slopped past the last full byte were
-                        * zeros.  If we don't check them, they become a
-                        * subliminal channel.
-                        */
-                       if (target && target[tarindex] != 0)
-                               return (-1);
-               }
-       } else {
-               /*
-                * We ended by seeing the end of the string.  Make sure we
-                * have no partial bytes lying around.
-                */
-               if (state != 0)
-                       return (-1);
-       }
-
-       return (tarindex);
-}
-
-static int uudecode(const char *src, unsigned char *target, size_t targsize)
-{
-       int len;
-       char *encoded, *p;
-
-       /* copy the 'readonly' source */
-       encoded = para_strdup(src);
-       /* skip whitespace and data */
-       for (p = encoded; *p == ' ' || *p == '\t'; p++)
-               ;
-       for (; *p != '\0' && *p != ' ' && *p != '\t'; p++)
-               ;
-       /* and remove trailing whitespace because base64_decode needs this */
-       *p = '\0';
-       len = base64_decode(encoded, target, targsize);
-       free(encoded);
-       return len >= 0? len : -E_BASE64;
-}
-
 /*
  * 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.
  */
 
-
-/*
- * Can not use the inline functions of portable_io.h here because the byte
- * order is different.
- */
-static uint32_t read_ssh_u32(const void *vp)
-{
-       const unsigned char *p = (const unsigned char *)vp;
-       uint32_t v;
-
-       v  = (uint32_t)p[0] << 24;
-       v |= (uint32_t)p[1] << 16;
-       v |= (uint32_t)p[2] << 8;
-       v |= (uint32_t)p[3];
-
-       return v;
-}
-
 static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
 {
        const unsigned char *p = buf, *end = buf + len;
@@ -350,23 +134,6 @@ static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
        int ret;
        RSA *rsa;
        const unsigned char *p = blob, *end = blob + blen;
-       uint32_t rlen;
-
-       *result = NULL;
-       if (p + 4 > end)
-               return -E_BIGNUM;
-       rlen = read_ssh_u32(p);
-       p += 4;
-       if (p + rlen < p)
-               return -E_BIGNUM;
-       if (p + rlen > end)
-               return -E_BIGNUM;
-       if (rlen < strlen(KEY_TYPE_TXT))
-               return -E_BIGNUM;
-       PARA_DEBUG_LOG("type: %s, rlen: %d\n", p, rlen);
-       if (strncmp((char *)p, KEY_TYPE_TXT, strlen(KEY_TYPE_TXT)))
-               return -E_BIGNUM;
-       p += rlen;
 
        rsa = RSA_new();
        if (!rsa)
@@ -386,24 +153,13 @@ fail:
        return ret;
 }
 
-/**
- * Read an asymmetric key from a file.
- *
- * \param key_file The file containing the key.
- * \param private if non-zero, read the private key, otherwise the public key.
- * \param result The key structure is returned here.
- *
- * \return The size of the key on success, negative on errors.
- *
- * \sa openssl(1), rsa(1).
- */
 int get_asymmetric_key(const char *key_file, int private,
                struct asymmetric_key **result)
 {
        struct asymmetric_key *key = NULL;
        void *map = NULL;
        unsigned char *blob = NULL;
-       size_t map_size, blob_size;
+       size_t map_size, blob_size, decoded_size;
        int ret, ret2;
        char *cp;
 
@@ -434,7 +190,11 @@ int get_asymmetric_key(const char *key_file, int private,
        ret = uudecode(cp, blob, blob_size);
        if (ret < 0)
                goto out;
-       ret = read_rsa_bignums(blob, ret, &key->rsa);
+       decoded_size = ret;
+       ret = check_ssh_key_header(blob, decoded_size);
+       if (ret < 0)
+               goto out;
+       ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
        if (ret < 0)
                goto out;
        ret = RSA_size(key->rsa);
@@ -452,13 +212,6 @@ out:
        return ret;
 }
 
-/**
- * Deallocate an asymmetric key structure.
- *
- * \param key Pointer to the key structure to free.
- *
- * This must be called for any key obtained by get_asymmetric_key().
- */
 void free_asymmetric_key(struct asymmetric_key *key)
 {
        if (!key)
@@ -467,20 +220,6 @@ void free_asymmetric_key(struct asymmetric_key *key)
        free(key);
 }
 
-/**
- * Decrypt a buffer using a private key.
- *
- * \param key_file Full path of the key.
- * \param outbuf The output buffer.
- * \param inbuf The encrypted input buffer.
- * \param inlen The length of \a inbuf in bytes.
- *
- * The \a outbuf must be large enough to hold at least \a rsa_inlen bytes.
- *
- * \return The size of the recovered plaintext on success, negative on errors.
- *
- * \sa RSA_private_decrypt(3)
- **/
 int priv_decrypt(const char *key_file, unsigned char *outbuf,
                unsigned char *inbuf, int inlen)
 {
@@ -509,18 +248,6 @@ out:
        return ret;
 }
 
-/**
- * Encrypt a buffer using an RSA key
- *
- * \param pub: The public key.
- * \param inbuf The input buffer.
- * \param len The length of \a inbuf.
- * \param outbuf The output buffer.
- *
- * \return The size of the encrypted data on success, negative on errors.
- *
- * \sa RSA_public_encrypt(3)
- */
 int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
                unsigned len, unsigned char *outbuf)
 {
@@ -537,14 +264,6 @@ struct stream_cipher {
        RC4_KEY key;
 };
 
-/**
- * Allocate and initialize a stream cipher structure.
- *
- * \param data The key.
- * \param len The size of the key.
- *
- * \return A new stream cipher structure.
- */
 struct stream_cipher *sc_new(const unsigned char *data, int len)
 {
        struct stream_cipher *sc = para_malloc(sizeof(*sc));
@@ -552,11 +271,6 @@ struct stream_cipher *sc_new(const unsigned char *data, int len)
        return sc;
 }
 
-/**
- * Deallocate a stream cipher structure.
- *
- * \param sc A stream cipher previously obtained by sc_new().
- */
 void sc_free(struct stream_cipher *sc)
 {
        free(sc);
@@ -569,18 +283,7 @@ void sc_free(struct stream_cipher *sc)
  */
 #define RC4_ALIGN 8
 
-/**
- * Encrypt and send a buffer.
- *
- * \param scc The context.
- * \param buf The buffer to send.
- * \param len The size of \a buf in bytes.
- *
- * \return The return value of the underyling call to write_all().
- *
- * \sa \ref write_all(), RC4(3).
- */
-int sc_send_bin_buffer(struct stream_cipher_context *scc, const char *buf,
+int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf,
                size_t len)
 {
        int ret;
@@ -600,51 +303,6 @@ int sc_send_bin_buffer(struct stream_cipher_context *scc, const char *buf,
        return ret;
 }
 
-/**
- * Encrypt and send a \p NULL-terminated buffer.
- *
- * \param scc The context.
- * \param buf The buffer to send.
- *
- * \return The return value of the underyling call to sc_send_bin_buffer().
- */
-int sc_send_buffer(struct stream_cipher_context *scc, const char *buf)
-{
-       return sc_send_bin_buffer(scc, buf, strlen(buf));
-}
-
-/**
- * Format, encrypt and send a buffer.
- *
- * \param scc The context.
- * \param fmt A format string.
- *
- * \return The return value of the underyling call to sc_send_buffer().
- */
-__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc,
-               const char *fmt, ...)
-{
-       char *msg;
-       int ret;
-
-       PARA_VSPRINTF(fmt, msg);
-       ret = sc_send_buffer(scc, msg);
-       free(msg);
-       return ret;
-}
-
-/**
- * Receive a buffer and decrypt it.
- *
- * \param scc The context.
- * \param buf The buffer to write the decrypted data to.
- * \param size The size of \a buf.
- *
- * \return The number of bytes received on success, negative on errors, zero if
- * the peer has performed an orderly shutdown.
- *
- * \sa recv(2), RC4(3).
- */
 int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
                size_t size)
 {
@@ -659,43 +317,6 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
        return ret;
 }
 
-/**
- * Receive a buffer, decrypt it and write terminating NULL byte.
- *
- * \param scc The context.
- * \param buf The buffer to write the decrypted data to.
- * \param size The size of \a buf.
- *
- * Read at most \a size - 1 bytes from file descriptor given by \a scc, decrypt
- * the received data and write a NULL byte at the end of the decrypted data.
- *
- * \return The return value of the underlying call to \ref
- * sc_recv_bin_buffer().
- */
-int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size)
-{
-       int n;
-
-       assert(size);
-       n = sc_recv_bin_buffer(scc, buf, size - 1);
-       if (n >= 0)
-               buf[n] = '\0';
-       else
-               *buf = '\0';
-       return n;
-}
-
-/**
- * Compute the hash of the given input data.
- *
- * \param data Pointer to the data to compute the hash value from.
- * \param len The length of \a data in bytes.
- * \param hash Result pointer.
- *
- * \a hash must point to an area at least \p HASH_SIZE bytes large.
- *
- * \sa sha(3), openssl(1).
- * */
 void hash_function(const char *data, unsigned long len, unsigned char *hash)
 {
        SHA_CTX c;
diff --git a/crypt.h b/crypt.h
index 21abe41..4696ee4 100644 (file)
--- a/crypt.h
+++ b/crypt.h
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file crypt.h Prototypes for paraslash crypto functions. */
+/** \file crypt.h Public crypto interface. */
+
+
+/** \cond used to distinguish between loading of private/public key */
+#define LOAD_PUBLIC_KEY 0
+#define LOAD_PRIVATE_KEY 1
+#define CHALLENGE_SIZE 64
+/** \endcond **/
+
+/* asymetric (public key) crypto */
 
 /** Opaque structure for public and private keys. */
 struct asymmetric_key;
 
+/**
+ * Encrypt a buffer using asymmetric keys.
+ *
+ * \param pub: The public key.
+ * \param inbuf The input buffer.
+ * \param len The length of \a inbuf.
+ * \param outbuf The output buffer.
+ *
+ * \return The size of the encrypted data on success, negative on errors.
+ */
 int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
                unsigned len, unsigned char *outbuf);
+
+/**
+ * Decrypt a buffer using a private key.
+ *
+ * \param key_file Full path of the key.
+ * \param outbuf The output buffer.
+ * \param inbuf The encrypted input buffer.
+ * \param inlen The length of \a inbuf.
+ *
+ * The \a outbuf must be large enough to hold at least 512 bytes.
+ *
+ * \return The size of the recovered plaintext on success, negative on errors.
+ */
 int priv_decrypt(const char *key_file, unsigned char *outbuf,
                unsigned char *inbuf, int inlen);
+
+/**
+ * Read an asymmetric key from a file.
+ *
+ * \param key_file The file containing the key.
+ * \param private if non-zero, read the private key, otherwise the public key.
+ * \param result The key structure is returned here.
+ *
+ * \return The size of the key on success, negative on errors.
+ */
 int get_asymmetric_key(const char *key_file, int private,
                struct asymmetric_key **result);
+
+/**
+ * Deallocate an asymmetric key structure.
+ *
+ * \param key Pointer to the key structure to free.
+ *
+ * This must be called for any key obtained by get_asymmetric_key().
+ */
 void free_asymmetric_key(struct asymmetric_key *key);
 
+
+/**
+ * Fill a buffer with random content.
+ *
+ * \param buf The buffer to fill.
+ * \param num The size of \a buf in bytes.
+ *
+ * This function puts \a num cryptographically strong pseudo-random bytes into
+ * buf. If it can not guarantee an unpredictable byte sequence (for example
+ * because the PRNG has not been seeded with enough randomness) the function
+ * logs an error message and calls exit().
+ */
 void get_random_bytes_or_die(unsigned char *buf, int num);
+
+/**
+ * Seed pseudo random number generators.
+ *
+ * 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().
+ *
+ * \sa \ref get_random_bytes_or_die(), srandom(3), random(3), \ref
+ * para_random().
+ */
 void init_random_seed_or_die(void);
 
-/** Opaque structure for stream cipher crypto. */
+
+/** Opaque structure for stream ciphers. */
 struct stream_cipher;
 
-/** Number of bytes of the session key. */
+/** Number of bytes of the session key for stream ciphers. */
 #define SESSION_KEY_LEN 32
 
 /**
- * Used on the server-side for client-server communication encryption.
+ * Used for client-server communication encryption.
  *
  * The traffic between (the forked child of) para_server and the remote client
  * process is crypted by a symmetric session key. This structure contains the
@@ -43,50 +117,105 @@ struct stream_cipher_context {
        struct stream_cipher *send;
 };
 
+/**
+ * Allocate and initialize a stream cipher structure.
+ *
+ * \param data The key.
+ * \param len The size of the key.
+ *
+ * \return A new stream cipher structure.
+ */
 struct stream_cipher *sc_new(const unsigned char *data, int len);
+
+/**
+ * Deallocate a stream cipher structure.
+ *
+ * \param sc A stream cipher previously obtained by sc_new().
+ */
 void sc_free(struct stream_cipher *sc);
 
-int sc_send_bin_buffer(struct stream_cipher_context *scc, const char *buf,
+/**
+ * Encrypt and send a buffer.
+ *
+ * \param scc The context.
+ * \param buf The buffer to send.
+ * \param len The size of \a buf in bytes.
+ *
+ * \return The return value of the underyling call to write_all().
+ *
+ * \sa \ref write_all(), RC4(3).
+ */
+int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf,
                size_t len);
-int sc_send_buffer(struct stream_cipher_context *scc, const char *buf);
+
+/**
+ * Encrypt and send a \p NULL-terminated buffer.
+ *
+ * \param scc The context.
+ * \param buf The buffer to send.
+ *
+ * \return The return value of the underyling call to sc_send_bin_buffer().
+ */
+int sc_send_buffer(struct stream_cipher_context *scc, char *buf);
+
+/**
+ * Format, encrypt and send a buffer.
+ *
+ * \param scc The context.
+ * \param fmt A format string.
+ *
+ * \return The return value of the underyling call to sc_send_buffer().
+ */
 __printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc,
                const char *fmt, ...);
+
+/**
+ * Receive a buffer and decrypt it.
+ *
+ * \param scc The context.
+ * \param buf The buffer to write the decrypted data to.
+ * \param size The size of \a buf.
+ *
+ * \return The number of bytes received on success, negative on errors, zero if
+ * the peer has performed an orderly shutdown.
+ *
+ * \sa recv(2), RC4(3).
+ */
 int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
                size_t size);
+
+/**
+ * Receive a buffer, decrypt it and write terminating NULL byte.
+ *
+ * \param scc The context.
+ * \param buf The buffer to write the decrypted data to.
+ * \param size The size of \a buf.
+ *
+ * Read at most \a size - 1 bytes from file descriptor given by \a scc, decrypt
+ * the received data and write a NULL byte at the end of the decrypted data.
+ *
+ * \return The return value of the underlying call to \ref
+ * sc_recv_bin_buffer().
+ */
 int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size);
 
-/** \cond used to distinguish between loading of private/public key */
-#define LOAD_PUBLIC_KEY 0
-#define LOAD_PRIVATE_KEY 1
-#define CHALLENGE_SIZE 64
-/** \endcond **/
 
 /** Size of the hash value in bytes. */
 #define HASH_SIZE 20
 
-void hash_function(const char *data, unsigned long len, unsigned char *hash);
 
 /**
- * Compare two hashes.
+ * Compute the hash of the given input data.
  *
- * \param h1 Pointer to the first hash value.
- * \param h2 Pointer to the second hash value.
+ * \param data Pointer to the data to compute the hash value from.
+ * \param len The length of \a data in bytes.
+ * \param hash Result pointer.
  *
- * \return 1, -1, or zero, depending on whether \a h1 is greater than,
- * less than or equal to h2, respectively.
- */
-_static_inline_ int hash_compare(unsigned char *h1, unsigned char *h2)
-{
-       int i;
-
-       for (i = 0; i < HASH_SIZE; i++) {
-               if (h1[i] < h2[i])
-                       return -1;
-               if (h1[i] > h2[i])
-                       return 1;
-       }
-       return 0;
-}
+ * \a hash must point to an area at least \p HASH_SIZE bytes large.
+ *
+ * \sa sha(3), openssl(1).
+ * */
+void hash_function(const char *data, unsigned long len, unsigned char *hash);
 
 /**
  * Convert a hash value to ascii format.
@@ -98,14 +227,15 @@ _static_inline_ int hash_compare(unsigned char *h1, unsigned char *h2)
  * will be filled by the function with the ascii representation of the hash
  * value given by \a hash, and a terminating \p NULL byte.
  */
-_static_inline_ void hash_to_asc(unsigned char *hash, char *asc)
-{
-       int i;
-       const char hexchar[] = "0123456789abcdef";
-
-       for (i = 0; i < HASH_SIZE; i++) {
-               asc[2 * i] = hexchar[hash[i] >> 4];
-               asc[2 * i + 1] = hexchar[hash[i] & 0xf];
-       }
-       asc[2 * HASH_SIZE] = '\0';
-}
+void hash_to_asc(unsigned char *hash, char *asc);
+
+/**
+ * Compare two hashes.
+ *
+ * \param h1 Pointer to the first hash value.
+ * \param h2 Pointer to the second hash value.
+ *
+ * \return 1, -1, or zero, depending on whether \a h1 is greater than,
+ * less than or equal to h2, respectively.
+ */
+int hash_compare(unsigned char *h1, unsigned char *h2);
diff --git a/crypt_backend.h b/crypt_backend.h
new file mode 100644 (file)
index 0000000..3ea32c2
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2011 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file crypt_backend.h Non-public crypto interface. */
+
+/* This should only be incuded from files which provide crypto functions. */
+
+size_t is_ssh_rsa_key(char *data, size_t size);
+uint32_t read_ssh_u32(const void *vp);
+int uudecode(const char *src, unsigned char *target, size_t targsize);
+int check_ssh_key_header(const unsigned char *blob, int blen);
+int check_key_file(const char *file, bool private_key);
+int base64_decode(char const *src, unsigned char *target, size_t targsize);
diff --git a/crypt_common.c b/crypt_common.c
new file mode 100644 (file)
index 0000000..8de346c
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2005-2011 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file crypt_common.c Crypto functions independent of openssl/libgcrypt. */
+
+#include <regex.h>
+#include <stdbool.h>
+
+#include "para.h"
+#include "error.h"
+#include "string.h"
+#include "crypt_backend.h"
+#include "crypt.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.
+ *
+ * \return Number of header bytes to be skipped on success, zero if
+ * ssh rsa signature was not found.
+ */
+size_t is_ssh_rsa_key(char *data, size_t size)
+{
+       char *cp;
+
+       if (size < strlen(KEY_TYPE_TXT) + 2)
+               return 0;
+       cp = memchr(data, ' ', size);
+       if (cp == NULL)
+               return 0;
+       if (strncmp(KEY_TYPE_TXT, data, strlen(KEY_TYPE_TXT)))
+               return 0;
+       cp++;
+       if (cp >= data + size)
+               return 0;
+       if (*cp == '\0')
+               return 0;
+       return cp - data;
+}
+
+/*
+ * This base64/uudecode stuff below is taken from openssh-5.2p1, Copyright (c)
+ * 1996 by Internet Software Consortium.  Portions Copyright (c) 1995 by
+ * International Business Machines, Inc.
+ */
+
+static const char Base64[] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/**
+ * base64-decode a buffer.
+ *
+ * \param src The buffer to decode.
+ * \param target Result is stored here.
+ * \param targsize Number of bytes of \a target.
+ *
+ * Skips all whitespace anywhere. Converts characters, four at a time, starting
+ * at (or after) src from base - 64 numbers into three 8 bit bytes in the
+ * target area.
+ *
+ * \return The number of data bytes stored at the target, -E_BASE64 on errors.
+ */
+int base64_decode(char const *src, unsigned char *target, size_t targsize)
+{
+       unsigned int tarindex, state;
+       int ch;
+       char *pos;
+
+       state = 0;
+       tarindex = 0;
+
+       while ((ch = *src++) != '\0') {
+               if (para_isspace(ch)) /* Skip whitespace anywhere. */
+                       continue;
+
+               if (ch == Pad64)
+                       break;
+
+               pos = strchr(Base64, ch);
+               if (pos == NULL) /* A non-base64 character. */
+                       return -E_BASE64;
+
+               switch (state) {
+               case 0:
+                       if (target) {
+                               if (tarindex >= targsize)
+                                       return -E_BASE64;
+                               target[tarindex] = (pos - Base64) << 2;
+                       }
+                       state = 1;
+                       break;
+               case 1:
+                       if (target) {
+                               if (tarindex + 1 >= targsize)
+                                       return -E_BASE64;
+                               target[tarindex]   |=  (pos - Base64) >> 4;
+                               target[tarindex+1]  = ((pos - Base64) & 0x0f)
+                                                       << 4 ;
+                       }
+                       tarindex++;
+                       state = 2;
+                       break;
+               case 2:
+                       if (target) {
+                               if (tarindex + 1 >= targsize)
+                                       return -E_BASE64;
+                               target[tarindex]   |=  (pos - Base64) >> 2;
+                               target[tarindex+1]  = ((pos - Base64) & 0x03)
+                                                       << 6;
+                       }
+                       tarindex++;
+                       state = 3;
+                       break;
+               case 3:
+                       if (target) {
+                               if (tarindex >= targsize)
+                                       return -E_BASE64;
+                               target[tarindex] |= (pos - Base64);
+                       }
+                       tarindex++;
+                       state = 0;
+                       break;
+               }
+       }
+
+       /*
+        * We are done decoding Base-64 chars.  Let's see if we ended
+        * on a byte boundary, and/or with erroneous trailing characters.
+        */
+
+       if (ch == Pad64) {              /* We got a pad char. */
+               ch = *src++;            /* Skip it, get next. */
+               switch (state) {
+               case 0:         /* Invalid = in first position */
+               case 1:         /* Invalid = in second position */
+                       return -E_BASE64;
+
+               case 2:         /* Valid, means one byte of info */
+                       /* Skip any number of spaces. */
+                       for (; ch != '\0'; ch = *src++)
+                               if (!isspace(ch))
+                                       break;
+                       /* Make sure there is another trailing = sign. */
+                       if (ch != Pad64)
+                               return -E_BASE64;
+                       ch = *src++;            /* Skip the = */
+                       /* Fall through to "single trailing =" case. */
+                       /* FALLTHROUGH */
+
+               case 3:         /* Valid, means two bytes of info */
+                       /*
+                        * We know this char is an =.  Is there anything but
+                        * whitespace after it?
+                        */
+                       for (; ch != '\0'; ch = *src++)
+                               if (!isspace(ch))
+                                       return -E_BASE64;
+
+                       /*
+                        * Now make sure for cases 2 and 3 that the "extra"
+                        * bits that slopped past the last full byte were
+                        * zeros.  If we don't check them, they become a
+                        * subliminal channel.
+                        */
+                       if (target && target[tarindex] != 0)
+                               return -E_BASE64;
+               }
+       } else {
+               /*
+                * We ended by seeing the end of the string.  Make sure we
+                * have no partial bytes lying around.
+                */
+               if (state != 0)
+                       return -E_BASE64;
+       }
+
+       return tarindex;
+}
+
+/**
+ * uudecode a buffer.
+ *
+ * \param src The buffer to decode.
+ * \param target Result buffer.
+ * \param targsize The length of \a target in bytes.
+ *
+ * This is just a simple wrapper for base64_decode() which strips whitespace.
+ *
+ * \return The return value of the underlying call to base64_decode().
+ */
+int uudecode(const char *src, unsigned char *target, size_t targsize)
+{
+       int len;
+       char *encoded, *p;
+
+       /* copy the 'readonly' source */
+       encoded = para_strdup(src);
+       /* skip whitespace and data */
+       for (p = encoded; *p == ' ' || *p == '\t'; p++)
+               ;
+       for (; *p != '\0' && *p != ' ' && *p != '\t'; p++)
+               ;
+       /* and remove trailing whitespace because base64_decode needs this */
+       *p = '\0';
+       len = base64_decode(encoded, target, targsize);
+       free(encoded);
+       return len;
+}
+
+/**
+ * Read a 4-byte number from a buffer in big-endian format.
+ *
+ * \param vp The buffer.
+ *
+ * The byte-order of the buffer is expected to be big-endian, unlike read_u32()
+ * of portable_io.h which expects little endian.
+ *
+ * \return The 32 bit number given by \a vp.
+ */
+uint32_t read_ssh_u32(const void *vp)
+{
+       const unsigned char *p = (const unsigned char *)vp;
+       uint32_t v;
+
+       v  = (uint32_t)p[0] << 24;
+       v |= (uint32_t)p[1] << 16;
+       v |= (uint32_t)p[2] << 8;
+       v |= (uint32_t)p[3];
+
+       return v;
+}
+
+/**
+ * Sanity checks for the header of an ssh key.
+ *
+ * \param blob The buffer.
+ * \param blen The number of bytes of \a blob.
+ *
+ * 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).
+ */
+int check_ssh_key_header(const unsigned char *blob, int blen)
+{
+       const unsigned char *p = blob, *end = blob + blen;
+       uint32_t rlen;
+
+       if (p + 4 > end)
+               return -E_SSH_KEY_HEADER;
+       rlen = read_ssh_u32(p);
+       p += 4;
+       if (p + rlen < p)
+               return -E_SSH_KEY_HEADER;
+       if (p + rlen > end)
+               return -E_SSH_KEY_HEADER;
+       if (rlen < strlen(KEY_TYPE_TXT))
+               return -E_SSH_KEY_HEADER;
+       PARA_DEBUG_LOG("type: %s, rlen: %d\n", p, rlen);
+       if (strncmp((char *)p, KEY_TYPE_TXT, strlen(KEY_TYPE_TXT)))
+               return -E_SSH_KEY_HEADER;
+       return 4 + rlen;
+}
+
+/**
+ * Check existence and permissions of a key file.
+ *
+ * \param file The path of the key file.
+ * \param private_key Whether this is a private key.
+ *
+ * This checks whether the file exists. If it is a private key, we additionally
+ * check that the permissions are restrictive enough. It is considered an error
+ * if we own the file and it is readable for others.
+ *
+ * \return Standard.
+ */
+int check_key_file(const char *file, bool private_key)
+{
+       struct stat st;
+
+       if (stat(file, &st) != 0)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       if (!private_key)
+               return 0;
+       if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0)
+               return -E_KEY_PERM;
+       return 1;
+}
+
+void hash_to_asc(unsigned char *hash, char *asc)
+{
+       int i;
+       const char hexchar[] = "0123456789abcdef";
+
+       for (i = 0; i < HASH_SIZE; i++) {
+               asc[2 * i] = hexchar[hash[i] >> 4];
+               asc[2 * i + 1] = hexchar[hash[i] & 0xf];
+       }
+       asc[2 * HASH_SIZE] = '\0';
+}
+
+int hash_compare(unsigned char *h1, unsigned char *h2)
+{
+       int i;
+
+       for (i = 0; i < HASH_SIZE; i++) {
+               if (h1[i] < h2[i])
+                       return -1;
+               if (h1[i] > h2[i])
+                       return 1;
+       }
+       return 0;
+}
+
+int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size)
+{
+       int n;
+
+       assert(size);
+       n = sc_recv_bin_buffer(scc, buf, size - 1);
+       if (n >= 0)
+               buf[n] = '\0';
+       else
+               *buf = '\0';
+       return n;
+}
+
+int sc_send_buffer(struct stream_cipher_context *scc, char *buf)
+{
+       return sc_send_bin_buffer(scc, buf, strlen(buf));
+}
+
+__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc,
+               const char *fmt, ...)
+{
+       char *msg;
+       int ret;
+
+       PARA_VSPRINTF(fmt, msg);
+       ret = sc_send_buffer(scc, msg);
+       free(msg);
+       return ret;
+}
diff --git a/error.h b/error.h
index 3f5ff73..b807877 100644 (file)
--- a/error.h
+++ b/error.h
@@ -368,6 +368,12 @@ extern const char **para_errlist[];
        PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \
 
 
+#define CRYPT_COMMON_ERRORS \
+       PARA_ERROR(SSH_KEY_HEADER, "ssh key header not found"), \
+       PARA_ERROR(BASE64, "failed to base64-decode ssh public key"), \
+       PARA_ERROR(KEY_PERM, "unprotected private key"), \
+
+
 #define CRYPT_ERRORS \
        PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
        PARA_ERROR(PUBLIC_KEY, "can not read public key"), \
@@ -375,10 +381,23 @@ extern const char **para_errlist[];
        PARA_ERROR(ENCRYPT, "encrypt error"), \
        PARA_ERROR(DECRYPT, "decrypt error"), \
        PARA_ERROR(BLINDING, "failed to activate key blinding"), \
-       PARA_ERROR(KEY_PERM, "unprotected private key"), \
-       PARA_ERROR(BASE64, "failed to base64-decode ssh private key"), \
        PARA_ERROR(BIGNUM, "bignum error"), \
 
+#define GCRYPT_ERRORS \
+       PARA_ERROR(MPI_SCAN, "could not scan multi-precision integer"), \
+       PARA_ERROR(MPI_PRINT, "could not convert multi-precision integer"), \
+       PARA_ERROR(SEXP_FIND, "could not find sublist in S-expression"), \
+       PARA_ERROR(SEXP_BUILD, "could not build S-expression"), \
+       PARA_ERROR(SEXP_ENCRYPT, "could not encrypt S-expression"), \
+       PARA_ERROR(SEXP_DECRYPT, "could not decrypt S-expression"), \
+       PARA_ERROR(MD_OPEN, "could not open message digest object"), \
+       PARA_ERROR(CIPHER_OPEN, "could not create stream cipher handle"), \
+       PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
+       PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
+       PARA_ERROR(ASN1_PARSE, "could not parse ASN.1 key"), \
+       PARA_ERROR(SSH_PARSE, "could not parse ssh public key"), \
+       PARA_ERROR(OEAP, "error during oeap (un)padding"), \
+
 
 #define COMMAND_ERRORS \
        PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
index 4b1a45d..1233c9a 100644 (file)
@@ -169,6 +169,8 @@ void generic_filter_pre_select(struct sched *s, struct task *t)
  * \param sample_rate Known to the decoder.
  * \param channels Known to the decoder.
  * \param result Ascii representation on the answer is stored here.
+ *
+ * \return Standard.
  */
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result)
diff --git a/gcrypt.c b/gcrypt.c
new file mode 100644 (file)
index 0000000..f825f6c
--- /dev/null
+++ b/gcrypt.c
@@ -0,0 +1,975 @@
+/*
+ * Copyright (C) 2011 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file gcrypt.c Libgrcypt-based encryption/decryption routines. */
+
+#include <regex.h>
+#include <stdbool.h>
+#include <gcrypt.h>
+
+#include "para.h"
+#include "error.h"
+#include "string.h"
+#include "crypt.h"
+#include "crypt_backend.h"
+#include "fd.h"
+
+//#define GCRYPT_DEBUG 1
+
+static bool libgcrypt_has_oaep;
+static const char *rsa_decrypt_sexp;
+
+#ifdef GCRYPT_DEBUG
+static void dump_buffer(const char *msg, unsigned char *buf, int len)
+{
+       int i;
+
+       fprintf(stderr, "%s (%u bytes): ", msg, len);
+       for (i = 0; i < len; i++)
+               fprintf(stderr, "%02x ", buf[i]);
+       fprintf(stderr, "\n");
+}
+#else
+/** Empty. Define GCRYPT_DEBUG to dump buffers. */
+#define dump_buffer(a, b, c)
+#endif
+
+void hash_function(const char *data, unsigned long len, unsigned char *hash)
+{
+       gcry_error_t gret;
+       gcry_md_hd_t handle;
+       unsigned char *md;
+
+       gret = gcry_md_open(&handle, GCRY_MD_SHA1, 0);
+       assert(gret == 0);
+       gcry_md_write(handle, data, (size_t)len);
+       gcry_md_final(handle);
+       md = gcry_md_read(handle, GCRY_MD_SHA1);
+       assert(md);
+       memcpy(hash, md, HASH_SIZE);
+       gcry_md_close(handle);
+}
+
+void get_random_bytes_or_die(unsigned char *buf, int num)
+{
+       gcry_randomize(buf, (size_t)num, GCRY_STRONG_RANDOM);
+}
+
+/*
+ * This is called at the beginning of every program that uses libgcrypt. We
+ * don't have to initialize any random seed here, but we must initialize the
+ * gcrypt library. This task is performed by gcry_check_version() which can
+ * also check that the gcrypt library version is at least the minimal required
+ * version. This function also tells us whether we have to use our own OAEP
+ * padding code.
+ */
+void init_random_seed_or_die(void)
+{
+       const char *ver, *req_ver;
+
+       ver = gcry_check_version(NULL);
+       req_ver = "1.4.0";
+       if (!gcry_check_version(req_ver)) {
+               PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n",
+                       req_ver, ver);
+               exit(EXIT_FAILURE);
+       }
+       req_ver = "1.5.0";
+       if (gcry_check_version(req_ver)) {
+               libgcrypt_has_oaep = true;
+               rsa_decrypt_sexp = "(enc-val(flags oaep)(rsa(a %m)))";
+       } else {
+               libgcrypt_has_oaep = false;
+               rsa_decrypt_sexp = "(enc-val(rsa(a %m)))";
+       }
+}
+
+/** 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. */
+#define RSA_PRIVKEY_SEXP "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))"
+
+/* rfc 3447, appendix B.2 */
+static void mgf1(unsigned char *seed, size_t seed_len, unsigned result_len,
+               unsigned char *result)
+{
+       gcry_error_t gret;
+       gcry_md_hd_t handle;
+       size_t n;;
+       unsigned char *md;
+       unsigned char octet_string[4], *rp = result, *end = rp + result_len;
+
+       assert(result_len / HASH_SIZE < 1ULL << 31);
+       gret = gcry_md_open(&handle, GCRY_MD_SHA1, 0);
+       assert(gret == 0);
+       for (n = 0; rp < end; n++) {
+               gcry_md_write(handle, seed, seed_len);
+               octet_string[0] = (unsigned char)((n >> 24) & 255);
+               octet_string[1] = (unsigned char)((n >> 16) & 255);
+               octet_string[2] = (unsigned char)((n >> 8)) & 255;
+               octet_string[3] = (unsigned char)(n & 255);
+               gcry_md_write(handle, octet_string, 4);
+               gcry_md_final(handle);
+               md = gcry_md_read(handle, GCRY_MD_SHA1);
+               memcpy(rp, md, PARA_MIN(HASH_SIZE, (int)(end - rp)));
+               rp += HASH_SIZE;
+               gcry_md_reset(handle);
+       }
+       gcry_md_close(handle);
+}
+
+/** The sha1 hash of an empty file. */
+static const unsigned char empty_hash[HASH_SIZE] =
+       "\xda" "\x39" "\xa3" "\xee" "\x5e"
+       "\x6b" "\x4b" "\x0d" "\x32" "\x55"
+       "\xbf" "\xef" "\x95" "\x60" "\x18"
+       "\x90" "\xaf" "\xd8" "\x07" "\x09";
+
+/* rfc3447, section 7.1.1 */
+static void pad_oaep(unsigned char *in, size_t in_len, unsigned char *out,
+               size_t out_len)
+{
+       size_t ps_len = out_len - in_len - 2 * HASH_SIZE - 2;
+       size_t n, mask_len = out_len - HASH_SIZE - 1;
+       unsigned char *seed = out + 1, *db = seed + HASH_SIZE,
+               *ps = db + HASH_SIZE, *one = ps + ps_len;
+       unsigned char *db_mask, seed_mask[HASH_SIZE];
+
+       assert(in_len <= out_len - 2 - 2 * HASH_SIZE);
+       assert(out_len > 2 * HASH_SIZE + 2);
+       PARA_DEBUG_LOG("padding %zu byte input -> %zu byte output\n",
+               in_len, out_len);
+       dump_buffer("unpadded buffer", in, in_len);
+
+       out[0] = '\0';
+       get_random_bytes_or_die(seed, HASH_SIZE);
+       memcpy(db, empty_hash, HASH_SIZE);
+       memset(ps, 0, ps_len);
+       *one = 0x01;
+       memcpy(one + 1, in, in_len);
+       db_mask = para_malloc(mask_len);
+       mgf1(seed, HASH_SIZE, mask_len, db_mask);
+       for (n = 0; n < mask_len; n++)
+               db[n] ^= db_mask[n];
+       mgf1(db, mask_len, HASH_SIZE, seed_mask);
+       for (n = 0; n < HASH_SIZE; n++)
+               seed[n] ^= seed_mask[n];
+       free(db_mask);
+       dump_buffer("padded buffer", out, out_len);
+}
+
+/* rfc 3447, section 7.1.2 */
+static int unpad_oaep(unsigned char *in, size_t in_len, unsigned char *out,
+               size_t *out_len)
+{
+       unsigned char *masked_seed = in + 1;
+       unsigned char *db = in + 1 + HASH_SIZE;
+       unsigned char seed[HASH_SIZE], seed_mask[HASH_SIZE];
+       unsigned char *db_mask, *p;
+       size_t n, mask_len = in_len - HASH_SIZE - 1;
+
+       mgf1(db, mask_len, HASH_SIZE, seed_mask);
+       for (n = 0; n < HASH_SIZE; n++)
+               seed[n] = masked_seed[n] ^ seed_mask[n];
+       db_mask = para_malloc(mask_len);
+       mgf1(seed, HASH_SIZE, mask_len, db_mask);
+       for (n = 0; n < mask_len; n++)
+               db[n] ^= db_mask[n];
+       free(db_mask);
+       if (memcmp(db, empty_hash, HASH_SIZE))
+               return -E_OEAP;
+       for (p = db + HASH_SIZE; p < in + in_len - 1; p++)
+               if (*p != '\0')
+                       break;
+       if (p >= in + in_len - 1)
+               return -E_OEAP;
+       p++;
+       *out_len = in + in_len - p;
+       memcpy(out, p, *out_len);
+       return 1;
+}
+
+struct asymmetric_key {
+       gcry_sexp_t sexp;
+       int num_bytes;
+};
+
+static const char *gcrypt_strerror(gcry_error_t gret)
+{
+       return gcry_strerror(gcry_err_code(gret));
+}
+
+static int decode_key(const char *key_file, const char *header_str,
+               const char *footer_str, unsigned char **result)
+{
+       int ret, ret2, i, j;
+       void *map;
+       size_t map_size, key_size, blob_size;
+       unsigned char *blob = NULL;
+       char *begin, *footer, *key;
+
+       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
+       if (ret < 0)
+               return ret;
+       ret = -E_KEY_MARKER;
+       if (strncmp(map, header_str, strlen(header_str)))
+               goto unmap;
+       footer = strstr(map, footer_str);
+       ret = -E_KEY_MARKER;
+       if (!footer)
+               goto unmap;
+       begin = map + strlen(header_str);
+       /* skip whitespace at the beginning */
+       for (; begin < footer; begin++) {
+               if (para_isspace(*begin))
+                       continue;
+               break;
+       }
+       ret = -E_KEY_MARKER;
+       if (begin >= footer)
+               goto unmap;
+
+       key_size = footer - begin;
+       key = para_malloc(key_size + 1);
+       for (i = 0, j = 0; begin + i < footer; i++) {
+               if (para_isspace(begin[i]))
+                       continue;
+               key[j++] = begin[i];
+       }
+       key[j] = '\0';
+       //PARA_CRIT_LOG("key: %s\n", key);
+       blob_size = key_size * 2;
+       blob = para_malloc(blob_size);
+       ret = base64_decode(key, blob, blob_size);
+       free(key);
+       if (ret < 0)
+               goto free_unmap;
+       goto unmap;
+free_unmap:
+       free(blob);
+       blob = NULL;
+unmap:
+       ret2 = para_munmap(map, map_size);
+       if (ret >= 0 && ret2 < 0)
+               ret = ret2;
+       if (ret < 0) {
+               free(blob);
+               blob = NULL;
+       }
+       *result = blob;
+       return ret;
+}
+
+/** ASN Types and their code. */
+enum asn1_types {
+       /** The next object is an integer. */
+       ASN1_TYPE_INTEGER = 0x2,
+       /** Bit string object. */
+       ASN1_TYPE_BIT_STRING = 0x03,
+       /** Keys start with one big type sequence. */
+       ASN1_TYPE_SEQUENCE = 0x30,
+};
+
+/* bit 6 has value 0 */
+static inline bool is_primitive(unsigned char c)
+{
+       return ((c & (1<<6)) == 0);
+}
+
+static inline bool is_primitive_integer(unsigned char c)
+{
+       if (!is_primitive(c))
+               return false;
+       return ((c & 0x1f) == ASN1_TYPE_INTEGER);
+}
+
+/* Bit 8 is zero (and bits 7-1 give the length) */
+static inline bool is_short_form(unsigned char c)
+{
+       return (c & 0x80) == 0;
+}
+
+static inline int get_short_form_length(unsigned char c)
+{
+       return c & 0x7f;
+}
+
+static inline int get_long_form_num_length_bytes(unsigned char c)
+{
+       return c & 0x7f;
+}
+
+static int find_pubkey_bignum_offset(const unsigned char *data, int len)
+{
+       const unsigned char *p = data, *end = data + len;
+
+       /* the whole thing istarts with one sequence */
+       if (*p != ASN1_TYPE_SEQUENCE)
+               return -E_ASN1_PARSE;
+       p++;
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       if (is_short_form(*p))
+               p++;
+       else
+               p += 1 + get_long_form_num_length_bytes(*p);
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       /* another sequence containing the object id, skip it */
+       if (*p != ASN1_TYPE_SEQUENCE)
+               return -E_ASN1_PARSE;
+       p++;
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       if (!is_short_form(*p))
+               return -E_ASN1_PARSE;
+       p += 1 + get_short_form_length(*p);
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       /* all numbers are wrapped in a bit string object that follows */
+       if (*p != ASN1_TYPE_BIT_STRING)
+               return -E_ASN1_PARSE;
+       p++;
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       if (is_short_form(*p))
+               p++;
+       else
+               p += 1 + get_long_form_num_length_bytes(*p);
+       p++; /* skip number of unused bits in the bit string */
+       if (p >= end)
+               return -E_ASN1_PARSE;
+
+       /* next, we have a sequence of two integers (n and e) */
+       if (*p != ASN1_TYPE_SEQUENCE)
+               return -E_ASN1_PARSE;
+       p++;
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       if (is_short_form(*p))
+               p++;
+       else
+               p += 1 + get_long_form_num_length_bytes(*p);
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       if (*p != ASN1_TYPE_INTEGER)
+               return -E_ASN1_PARSE;
+       return p - data;
+}
+
+/*
+ * Returns: Number of bytes scanned. This may differ from the value returned via
+ * bn_bytes because the latter does not include the ASN.1 prefix and a leading
+ * zero is not considered as an additional byte for bn_bytes.
+ */
+static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
+               int *bn_bytes)
+{
+       int i, bn_size;
+       gcry_error_t gret;
+       unsigned char *cp = start;
+
+       if (!is_primitive_integer(*cp))
+               return -E_BAD_PRIVATE_KEY;
+       cp++;
+       if (is_short_form(*cp)) {
+               bn_size = get_short_form_length(*cp);
+               cp++;
+       } else {
+               int num_bytes = get_long_form_num_length_bytes(*cp);
+               if (cp + num_bytes > end)
+                       return -E_BAD_PRIVATE_KEY;
+               if (num_bytes > 4) /* nobody has such a large modulus */
+                       return -E_BAD_PRIVATE_KEY;
+               cp++;
+               bn_size = 0;
+               for (i = 0; i < num_bytes; i++, cp++)
+                       bn_size = (bn_size << 8) + *cp;
+       }
+       PARA_DEBUG_LOG("bn_size %d (0x%x)\n", bn_size, bn_size);
+       gret = gcry_mpi_scan(bn, GCRYMPI_FMT_STD, cp, bn_size, NULL);
+       if (gret) {
+               PARA_ERROR_LOG("%s while scanning n\n",
+                       gcry_strerror(gcry_err_code(gret)));
+               return-E_MPI_SCAN;
+       }
+       /*
+        * Don't take the first leading zero into account for the size of the
+        * bignum.
+        */
+       if (*cp == '\0') {
+               cp++;
+               bn_size--;
+       }
+       if (bn_bytes)
+               *bn_bytes = bn_size;
+       cp += bn_size;
+//     unsigned char *buf;
+//     gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, *bn);
+//     PARA_CRIT_LOG("bn: %s\n", buf);
+       return cp - start;
+}
+
+static int find_privkey_bignum_offset(const unsigned char *data, int len)
+{
+       const unsigned char *p = data, *end = data + len;
+
+       /* like the public key, the whole thing is contained in a sequence */
+       if (*p != ASN1_TYPE_SEQUENCE)
+               return -E_ASN1_PARSE;
+       p++;
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       if (is_short_form(*p))
+               p++;
+       else
+               p += 1 + get_long_form_num_length_bytes(*p);
+       if (p >= end)
+               return -E_ASN1_PARSE;
+
+       /* Skip next integer  */
+       if (*p != ASN1_TYPE_INTEGER)
+               return -E_ASN1_PARSE;
+       p++;
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       if (is_short_form(*p))
+               p += 1 + get_short_form_length(*p);
+       else
+               p += 1 + get_long_form_num_length_bytes(*p);
+       if (p >= end)
+               return -E_ASN1_PARSE;
+       return p - data;
+}
+
+/** Private keys start with this header. */
+#define PRIVATE_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
+/** Private keys end with this footer. */
+#define PRIVATE_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
+
+static int get_private_key(const char *key_file, struct asymmetric_key **result)
+{
+       gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL,
+               u = NULL;
+       unsigned char *blob, *cp, *end;
+       int blob_size, ret, n_size;
+       gcry_error_t gret;
+       size_t erroff;
+       gcry_sexp_t sexp;
+       struct asymmetric_key *key;
+
+       ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER,
+               &blob);
+       if (ret < 0)
+               return ret;
+       blob_size = ret;
+       end = blob + blob_size;
+       ret = find_privkey_bignum_offset(blob, blob_size);
+       if (ret < 0)
+               goto free_blob;
+       PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
+       cp = blob + ret;
+
+       ret = read_bignum(cp, end, &n, &n_size);
+       if (ret < 0)
+               goto free_blob;
+       cp += ret;
+
+       ret = read_bignum(cp, end, &e, NULL);
+       if (ret < 0)
+               goto release_n;
+       cp += ret;
+
+       ret = read_bignum(cp, end, &d, NULL);
+       if (ret < 0)
+               goto release_e;
+       cp += ret;
+
+       ret = read_bignum(cp, end, &p, NULL);
+       if (ret < 0)
+               goto release_d;
+       cp += ret;
+
+       ret = read_bignum(cp, end, &q, NULL);
+       if (ret < 0)
+               goto release_p;
+       cp += ret;
+       ret = read_bignum(cp, end, &u, NULL);
+       if (ret < 0)
+               goto release_q;
+       cp += ret;
+       /*
+        * OpenSSL uses slightly different parameters than gcrypt. To use these
+        * parameters we need to swap the values of p and q and recompute u.
+        */
+       if (gcry_mpi_cmp(p, q) > 0) {
+               gcry_mpi_swap(p, q);
+               gcry_mpi_invm(u, p, q);
+       }
+       gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP,
+               n, e, d, p, q, u);
+
+       if (gret) {
+               PARA_ERROR_LOG("offset %zu: %s\n", erroff,
+                       gcry_strerror(gcry_err_code(gret)));
+               ret = -E_SEXP_BUILD;
+               goto release_u;
+       }
+       key = para_malloc(sizeof(*key));
+       key->sexp = sexp;
+       *result = key;
+       ret = n_size * 8;
+       PARA_INFO_LOG("succesfully read %d bit private key\n", ret);
+release_u:
+       gcry_mpi_release(u);
+release_q:
+       gcry_mpi_release(q);
+release_p:
+       gcry_mpi_release(p);
+release_d:
+       gcry_mpi_release(d);
+release_e:
+       gcry_mpi_release(e);
+release_n:
+       gcry_mpi_release(n);
+free_blob:
+       free(blob);
+       return ret;
+}
+
+/** Public keys start with this header. */
+#define PUBLIC_KEY_HEADER "-----BEGIN PUBLIC KEY-----"
+/** Public keys end with this footer. */
+#define PUBLIC_KEY_FOOTER "-----END PUBLIC KEY-----"
+
+static int get_asn_public_key(const char *key_file, struct asymmetric_key **result)
+{
+       gcry_mpi_t n = NULL, e = NULL;
+       unsigned char *blob, *cp, *end;
+       int blob_size, ret, n_size;
+       gcry_error_t gret;
+       size_t erroff;
+       gcry_sexp_t sexp;
+       struct asymmetric_key *key;
+
+       ret = decode_key(key_file, PUBLIC_KEY_HEADER, PUBLIC_KEY_FOOTER,
+               &blob);
+       if (ret < 0)
+               return ret;
+       blob_size = ret;
+       end = blob + blob_size;
+       ret = find_pubkey_bignum_offset(blob, blob_size);
+       if (ret < 0)
+               goto free_blob;
+       PARA_DEBUG_LOG("decoding public RSA params at offset %d\n", ret);
+       cp = blob + ret;
+
+       ret = read_bignum(cp, end, &n, &n_size);
+       if (ret < 0)
+               goto free_blob;
+       cp += ret;
+
+       ret = read_bignum(cp, end, &e, NULL);
+       if (ret < 0)
+               goto release_n;
+       cp += ret;
+
+       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_e;
+       }
+       key = para_malloc(sizeof(*key));
+       key->sexp = sexp;
+       *result = key;
+       ret = n_size;
+       PARA_INFO_LOG("successfully read %u bit asn public key\n", n_size * 8);
+
+release_e:
+       gcry_mpi_release(e);
+release_n:
+       gcry_mpi_release(n);
+free_blob:
+       free(blob);
+       return ret;
+}
+
+static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result)
+{
+       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;
+
+       PARA_DEBUG_LOG("decoding %d byte  public rsa-ssh key\n", size);
+       if (size > INT_MAX / 4)
+               return -ERRNO_TO_PARA_ERROR(EOVERFLOW);
+       blob = para_malloc(2 * size);
+       ret = uudecode((char *)data, blob, 2 * size);
+       if (ret < 0)
+               goto free_blob;
+       decoded_size = ret;
+       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;
+       p = blob + ret;
+       ret = -E_SSH_PARSE;
+       if (p >= end)
+               goto free_blob;
+       PARA_DEBUG_LOG("scanning modulus and public exponent\n");
+       gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
+       if (gret) {
+               ret = -E_MPI_SCAN;
+               PARA_CRIT_LOG("%s\n", gcry_strerror(gcry_err_code(gret)));
+               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;
+       gret = gcry_mpi_scan(&n, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
+       if (gret) {
+               ret = -E_MPI_SCAN;
+               PARA_ERROR_LOG("%s\n", gcry_strerror(gcry_err_code(gret)));
+               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);
+       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;
+       PARA_INFO_LOG("successfully read %u bit ssh public key\n", ret * 8);
+release_n:
+       gcry_mpi_release(n);
+release_e:
+       gcry_mpi_release(e);
+free_blob:
+       free(blob);
+       return ret;
+}
+
+int get_asymmetric_key(const char *key_file, int private,
+               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;
+
+       if (private)
+               return get_private_key(key_file, result);
+       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) {
+               ret = para_munmap(map, map_size);
+               if (ret < 0)
+                       return ret;
+               return get_asn_public_key(key_file, result);
+       }
+       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;
+       ret = key->num_bytes;
+unmap:
+       ret2 = para_munmap(map, map_size);
+       if (ret >= 0 && ret2 < 0)
+               ret = ret2;
+       return ret;
+}
+
+void free_asymmetric_key(struct asymmetric_key *key)
+{
+       if (!key)
+               return;
+       gcry_sexp_release(key->sexp);
+       free(key);
+}
+
+static int decode_rsa(gcry_sexp_t sexp, int key_size, unsigned char *outbuf,
+               size_t *nbytes)
+{
+       int ret;
+       gcry_error_t gret;
+       unsigned char oaep_buf[512];
+       gcry_mpi_t out_mpi;
+
+       if (libgcrypt_has_oaep) {
+               const char *p = gcry_sexp_nth_data(sexp, 1, nbytes);
+
+               if (!p) {
+                       PARA_ERROR_LOG("could not get data from list\n");
+                       return -E_OEAP;
+               }
+               memcpy(outbuf, p, *nbytes);
+               return 1;
+       }
+       out_mpi = gcry_sexp_nth_mpi(sexp, 0, GCRYMPI_FMT_USG);
+       if (!out_mpi)
+               return -E_SEXP_FIND;
+       gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf),
+               nbytes, out_mpi);
+       if (gret) {
+               PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret));
+               ret = -E_MPI_PRINT;
+               goto out_mpi_release;
+       }
+       /*
+        * An oaep-encoded buffer always starts with at least one zero byte.
+        * However, leading zeroes in an mpi are omitted in the output of
+        * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The
+        * alternative, GCRYMPI_FMT_STD, does not work either because here the
+        * leading zero(es) might also be omitted, depending on the value of
+        * the second byte.
+        *
+        * To circumvent this, we shift the oaep buffer to the right. But first
+        * we check that the buffer actually started with a zero byte, i.e. that
+        * nbytes < key_size. Otherwise a decoding error occurred.
+        */
+       ret = -E_SEXP_DECRYPT;
+       if (*nbytes >= key_size)
+               goto out_mpi_release;
+       memmove(oaep_buf + key_size - *nbytes, oaep_buf, *nbytes);
+       memset(oaep_buf, 0, key_size - *nbytes);
+
+       PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n",
+               key_size);
+       dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size);;
+       ret = unpad_oaep(oaep_buf, key_size, outbuf, nbytes);
+       if (ret < 0)
+               goto out_mpi_release;
+       PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n",
+               *nbytes);
+       dump_buffer("unpadded decrypted buffer", outbuf, *nbytes);;
+       ret = 1;
+out_mpi_release:
+       gcry_mpi_release(out_mpi);
+       return ret;
+}
+
+int priv_decrypt(const char *key_file, unsigned char *outbuf,
+               unsigned char *inbuf, int inlen)
+{
+       gcry_error_t gret;
+       int ret, key_size;
+       struct asymmetric_key *priv;
+       gcry_mpi_t in_mpi = NULL;
+       gcry_sexp_t in, out, priv_key;
+       size_t nbytes;
+
+       PARA_INFO_LOG("decrypting %d byte input\n", inlen);
+       /* key_file -> asymmetric key priv */
+       ret = get_private_key(key_file, &priv);
+       if (ret < 0)
+               return ret;
+       key_size = ret / 8;
+
+       /* asymmetric key priv -> sexp priv_key */
+       ret = -E_SEXP_FIND;
+       priv_key = gcry_sexp_find_token(priv->sexp, "private-key", 0);
+       if (!priv_key)
+               goto free_key;
+
+       /* inbuf -> in_mpi */
+       gret = gcry_mpi_scan(&in_mpi, GCRYMPI_FMT_USG, inbuf,
+               inlen, NULL);
+       if (gret) {
+               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
+               ret = -E_MPI_SCAN;
+               goto key_release;
+       }
+       /* in_mpi -> in sexp */
+       gret = gcry_sexp_build(&in, NULL, rsa_decrypt_sexp, in_mpi);
+       if (gret) {
+               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
+               ret = -E_SEXP_BUILD;
+               goto in_mpi_release;
+       }
+
+       /* rsa decryption: in sexp -> out sexp */
+       gret = gcry_pk_decrypt(&out, in, priv_key);
+       if (gret) {
+               PARA_ERROR_LOG("decrypt: %s\n", gcrypt_strerror(gret));
+               ret = -E_SEXP_DECRYPT;
+               goto in_release;
+       }
+       ret = decode_rsa(out, key_size, outbuf, &nbytes);
+       if (ret < 0)
+               goto out_release;
+       PARA_INFO_LOG("successfully decrypted %zu byte message\n", nbytes);
+       ret = nbytes;
+out_release:
+       gcry_sexp_release(out);
+in_release:
+       gcry_sexp_release(in);
+in_mpi_release:
+       gcry_mpi_release(in_mpi);
+key_release:
+       gcry_sexp_release(priv_key);
+free_key:
+       free_asymmetric_key(priv);
+       return ret;
+}
+
+int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
+               unsigned len, unsigned char *outbuf)
+{
+       gcry_error_t gret;
+       gcry_sexp_t pub_key, in, out, out_a;
+       gcry_mpi_t out_mpi = NULL;
+       size_t nbytes;
+       int ret;
+
+       PARA_INFO_LOG("encrypting %u byte input with %d-byte key\n", len, pub->num_bytes);
+
+       /* get pub key */
+       pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0);
+       if (!pub_key)
+               return -E_SEXP_FIND;
+       if (libgcrypt_has_oaep) {
+               gret = gcry_sexp_build(&in, NULL,
+                       "(data(flags oaep)(value %b))", len, inbuf);
+       } else {
+               unsigned char padded_input[256];
+               const size_t pad_size = 256;
+               /* inbuf -> padded inbuf */
+               pad_oaep(inbuf, len, padded_input, pad_size);
+               /* padded inbuf -> in sexp */
+               gret = gcry_sexp_build(&in, NULL,
+                       "(data(flags raw)(value %b))", pad_size, padded_input);
+       }
+       if (gret) {
+               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
+               ret = -E_SEXP_BUILD;
+               goto key_release;
+       }
+       /* rsa sexp encryption: in -> out */
+       gret = gcry_pk_encrypt(&out, in, pub_key);
+       if (gret) {
+               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
+               ret = -E_SEXP_ENCRYPT;
+               goto in_release;
+       }
+       /* extract a, an MPI with the result of the RSA operation */
+       ret = -E_SEXP_FIND;
+       out_a = gcry_sexp_find_token(out, "a", 0);
+       if (!out_a)
+               goto out_release;
+       /* convert sexp out_a -> out_mpi */
+       out_mpi = gcry_sexp_nth_mpi(out_a, 1, GCRYMPI_FMT_USG);
+       if (!out_mpi) {
+               ret = -E_SEXP_FIND;
+               goto out_a_release;
+       }
+       gret = gcry_mpi_print(GCRYMPI_FMT_USG, outbuf, 512 /* FIXME */, &nbytes, out_mpi);
+       if (gret) {
+               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
+               ret = -E_SEXP_ENCRYPT;
+               goto out_mpi_release;
+       }
+       PARA_INFO_LOG("encrypted buffer is %zu bytes\n", nbytes);
+       dump_buffer("enc buf", outbuf, nbytes);
+       ret = nbytes;
+
+out_mpi_release:
+       gcry_mpi_release(out_mpi);
+out_a_release:
+       gcry_sexp_release(out_a);
+out_release:
+       gcry_sexp_release(out);
+in_release:
+       gcry_sexp_release(in);
+key_release:
+       gcry_sexp_release(pub_key);
+       return ret;
+}
+
+struct stream_cipher {
+       gcry_cipher_hd_t handle;
+};
+
+struct stream_cipher *sc_new(const unsigned char *data, int len)
+{
+       gcry_error_t gret;
+
+       struct stream_cipher *sc = para_malloc(sizeof(*sc));
+       gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_ARCFOUR,
+               GCRY_CIPHER_MODE_STREAM, 0);
+       if (gret) {
+               PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret));
+               free(sc);
+               return NULL;
+       }
+       gret = gcry_cipher_setkey(sc->handle, data, (size_t)len);
+       assert(gret == 0);
+       return sc;
+}
+
+void sc_free(struct stream_cipher *sc)
+{
+       if (!sc)
+               return;
+       gcry_cipher_close(sc->handle);
+       free(sc);
+}
+
+int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf,
+               size_t size)
+{
+       gcry_error_t gret;
+       int ret;
+       unsigned char *tmp = para_malloc(size);
+
+       assert(size);
+       gret = gcry_cipher_encrypt(scc->send->handle, tmp, size,
+               (unsigned char *)buf, size);
+       assert(gret == 0);
+       ret = write_all(scc->fd, (char *)tmp, &size);
+       free(tmp);
+       return ret;
+}
+
+int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
+               size_t size)
+{
+       gcry_error_t gret;
+       ssize_t ret = recv(scc->fd, buf, size, 0);
+
+       if (ret < 0)
+               ret = -ERRNO_TO_PARA_ERROR(errno);
+       if (ret <= 0)
+               return ret;
+       /* perform in-place encryption */
+       gret = gcry_cipher_encrypt(scc->recv->handle, (unsigned char *)buf, ret,
+               NULL, 0);
+       assert(gret == 0);
+       return ret;
+}
diff --git a/gui.c b/gui.c
index c872156..a3c07ec 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -99,7 +99,7 @@ static void com_reread_conf(void);
 static void com_enlarge_top_win(void);
 static void com_shrink_top_win(void);
 static void com_version(void);
-static void com_quit(void);
+__noreturn static void com_quit(void);
 static void com_refresh(void);
 static void com_ll_incr(void);
 static void com_ll_decr(void);
index 82f0141..6982f26 100644 (file)
 #define MAD_TO_SHORT(f) (f) >= MAD_F_ONE? SHRT_MAX :\
        (f) <= -MAD_F_ONE? -SHRT_MAX : (signed short) ((f) >> (MAD_F_FRACBITS - 15))
 
-/** State of the decoding process. */
-enum mp3dec_flags {
-       /** Bad main_data_begin pointer encounterd. */
-       MP3DEC_FLAG_BAD_DATA = 1,
-       /** Some output has already been produced. */
-       MP3DEC_FLAG_DECODE_STARTED = 2,
-       MP3DEC_FLAG_NEED_MORE = 4,
-};
-
 /** Data specific to the mp3dec filter. */
 struct private_mp3dec_data {
        /** Information on the current mp3 stream. */
@@ -41,57 +32,22 @@ struct private_mp3dec_data {
        struct mad_frame frame;
        /** Contains the PCM output. */
        struct mad_synth synth;
-       /** See \ref mp3dec_flags. */
-       unsigned flags;
-       /** Defer decoding until this time. */
-       struct timeval stream_start_barrier;
-       /** Wait until this many input bytes are available. */
-       size_t input_len_barrier;
        /** The number of channels of the current stream. */
        unsigned int channels;
        /** Current sample rate in Hz. */
        unsigned int sample_rate;
 };
 
-static int need_bad_data_delay(struct private_mp3dec_data *pmd,
-               size_t bytes_available)
-{
-       if (!(pmd->flags & MP3DEC_FLAG_BAD_DATA))
-               return 0;
-       if (pmd->flags & MP3DEC_FLAG_DECODE_STARTED)
-               return 0;
-       if (bytes_available >= pmd->input_len_barrier)
-               return 0;
-       if (tv_diff(now, &pmd->stream_start_barrier, NULL) > 0)
-               return 0;
-       return 1;
-}
-
-/*
- * Returns negative on serious errors, zero if the error should be ignored and
- * positive on bad data pointer errors at stream start.
- */
-static int handle_decode_error(struct private_mp3dec_data *pmd, size_t len)
+/* Returns negative on serious errors. */
+static int handle_decode_error(struct private_mp3dec_data *pmd)
 {
-       const struct timeval delay = {0, 60 * 1000};
        if (!MAD_RECOVERABLE(pmd->stream.error)
                        && pmd->stream.error != MAD_ERROR_BUFLEN) {
                PARA_ERROR_LOG("%s\n", mad_stream_errorstr(&pmd->stream));
                return -E_MAD_FRAME_DECODE;
        }
        PARA_DEBUG_LOG("%s\n", mad_stream_errorstr(&pmd->stream));
-       if (pmd->stream.error != MAD_ERROR_BADDATAPTR)
-               return 0;
-       if (pmd->flags & MP3DEC_FLAG_DECODE_STARTED)
-               return 0;
-       /*
-        * Bad data pointer at stream start. Defer decoding until the amount of
-        * data we are about to skip is available again, but wait at most 60ms.
-        */
-       pmd->flags |= MP3DEC_FLAG_BAD_DATA;
-       pmd->input_len_barrier = len;
-       tv_add(now, &delay, &pmd->stream_start_barrier);
-       return 1;
+       return 0;
 }
 
 static size_t used_mad_buffer_bytes(struct mad_stream *s, size_t max)
@@ -124,7 +80,7 @@ static void mp3dec_post_select(__a_unused struct sched *s, struct task *t)
        int i, ret;
        struct private_mp3dec_data *pmd = fn->private_data;
        struct btr_node *btrn = fn->btrn;
-       size_t loaded, used, len, iqs;
+       size_t loaded = 0, used, len, iqs;
        char *inbuffer, *outbuffer;
 
 next_buffer:
@@ -134,8 +90,6 @@ next_buffer:
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
        if (ret < 0)
                goto err;
-       if (need_bad_data_delay(pmd, iqs))
-               return;
        if (ret == 0)
                return;
        btr_merge(btrn, fn->min_iqs);
@@ -158,37 +112,37 @@ next_frame:
                                goto err;
                        }
                        fn->min_iqs += 100;
+               }
+               if (loaded == 0)
                        goto next_buffer;
-               } else if (pmd->stream.error != MAD_ERROR_LOSTSYNC)
-                       PARA_DEBUG_LOG("header decode: %s\n",
-                               mad_stream_errorstr(&pmd->stream));
-               goto next_buffer;
+               return;
        }
        fn->min_iqs = 0;
        pmd->sample_rate = pmd->frame.header.samplerate;
        pmd->channels = MAD_NCHANNELS(&pmd->frame.header);
+decode:
        ret = mad_frame_decode(&pmd->frame, &pmd->stream);
        if (ret != 0) {
-               PARA_INFO_LOG("frame decode: %s\n", mad_stream_errorstr(&pmd->stream));
-               used = used_mad_buffer_bytes(&pmd->stream, len);
-               ret = handle_decode_error(pmd, used);
-               btr_consume(btrn, used);
+               ret = handle_decode_error(pmd);
                if (ret < 0)
                        goto err;
-               if (ret == 0)
-                       goto next_buffer;
+               mad_stream_sync(&pmd->stream);
+               if (pmd->stream.error == MAD_ERROR_BUFLEN)
+                       return;
+               if (pmd->stream.error != MAD_ERROR_BADDATAPTR)
+                       goto decode;
+               used = used_mad_buffer_bytes(&pmd->stream, len);
+               btr_consume(btrn, used);
                return;
        }
        mad_synth_frame(&pmd->synth, &pmd->frame);
-       pmd->flags |= MP3DEC_FLAG_DECODE_STARTED;
-
-       outbuffer = para_malloc(pmd->synth.pcm.length * 4);
+       outbuffer = para_malloc(pmd->synth.pcm.length * 2 * pmd->channels);
        loaded = 0;
        for (i = 0; i < pmd->synth.pcm.length; i++) {
                int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
                write_int16_host_endian(outbuffer + loaded, sample);
                loaded += 2;
-               if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */
+               if (pmd->channels == 2) { /* stereo */
                        sample = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]);
                        write_int16_host_endian(outbuffer + loaded, sample);
                        loaded += 2;
diff --git a/net.c b/net.c
index ae596e5..9b58e22 100644 (file)
--- a/net.c
+++ b/net.c
@@ -576,8 +576,12 @@ static inline int estimated_header_overhead(const int af_type)
 }
 
 /**
- * Maximum transport-layer message size (MMS_S) as per RFC 1122, 3.3.3
- * Socket must be connected.
+ * Get the maximum transport-layer message size (MMS_S).
+ *
+ * The socket must be connected. See RFC 1122, 3.3.3.
+ *
+ * \return If the protocol familiy could not be determined, \p AF_INET is
+ * assumed.
  */
 int generic_max_transport_msg_size(int sockfd)
 {
index 50f22be..429c713 100644 (file)
 #include "write.h"
 #include "write_common.h"
 #include "osx_write.cmdline.h"
+#include "ipc.h"
 #include "error.h"
 
 #include <CoreServices/CoreServices.h>
 #include <AudioUnit/AudioUnit.h>
 #include <AudioToolbox/AudioToolbox.h>
 
-/** describes one input buffer for the osx writer */
-struct osx_buffer {
-       /** pointer to the beginning of the buffer */
-       float *buffer;
-       /** the size of this buffer */
-       long size;
-       /** current position in the buffer */
-       float *ptr;
-       /** number of floats not yet consuned */
-       long remaining;
-       /** pointer to the next audio buffer */
-       struct osx_buffer *next;
-};
-
-/** data specific to the osx writer */
+/** Data specific to the osx writer. */
 struct private_osx_write_data {
-       /** the main control structure for audio data manipulation */
+       /** The main CoreAudio handle. */
        AudioUnit audio_unit;
-       /** non-zero if playback has started */
-       char play;
-       /** callback reads audio data from this buffer */
-       struct osx_buffer *from;
-       /** the post_select writes audio data here */
-       struct osx_buffer *to;
-       /** sample rate of the current audio stream */
+       /** True if we wrote some audio data. */
+       bool playing;
+       /** Sample rate of the current audio stream. */
        unsigned sample_rate;
        /** Sample format of the current audio stream */
        unsigned sample_format;
-       /** number of channels of the current audio stream */
+       /** Number of channels of the current audio stream. */
        unsigned channels;
+       /** Serializes access to buffer tree nodes. */
+       int mutex;
+       /** The btr node of the callback. */
+       struct btr_node *callback_btrn;
 };
 
-static void destroy_buffers(struct private_osx_write_data *powd)
-{
-       struct osx_buffer *ptr;
-       struct osx_buffer *ptr2;
-       ptr = powd->to->next;
-       powd->to->next = NULL;
-       while (ptr) {
-               ptr2 = ptr->next;
-               free(ptr->buffer);
-               free(ptr);
-               ptr = ptr2;
-       }
-}
-
-static void init_buffers(struct writer_node *wn)
+/* This function writes the address and the number of bytes to one end of the socket.
+ * The post_select() function then fills the buffer and notifies the callback also
+ * through the socket.
+ */
+static OSStatus osx_callback(void *cb_arg, __a_unused AudioUnitRenderActionFlags *af,
+               __a_unused const AudioTimeStamp *ts, __a_unused  UInt32 bus_number,
+               __a_unused UInt32 num_frames, AudioBufferList *abl)
 {
-       struct private_osx_write_data *powd = wn->private_data;
-       struct osx_write_args_info *conf = wn->conf;
-       struct osx_buffer **ptrptr;
        int i;
+       struct writer_node *wn = cb_arg;
+       struct private_osx_write_data *powd = wn->private_data;
+       size_t samples_have, samples_want = 0;
 
-       ptrptr = &powd->to;
-       for (i = 0; i < conf->numbuffers_arg; i++) {
-               *ptrptr = para_malloc(sizeof(struct osx_buffer));
-               (*ptrptr)->size = 0;
-               (*ptrptr)->remaining = 0;
-               (*ptrptr)->buffer = NULL;
-               ptrptr = &(*ptrptr)->next;
-       }
-       *ptrptr = powd->from = powd->to;
-}
-
-static void fill_buffer(struct private_osx_write_data *powd, char *data, long bytes)
-{
-       struct osx_buffer *b = powd->to;
-       float *dest;
-       long samples;
-       enum sample_format sf = powd->sample_format;
-
-       samples = (sf == SF_S8 || sf == SF_U8)? bytes : bytes / 2;
-       assert(b->remaining == 0 || samples > 0);
-       if (b->size != samples) {
-               b->buffer = para_realloc(b->buffer, samples * sizeof(float));
-               b->size = samples;
-       }
-       dest = b->buffer;
-       switch (powd->sample_format) {
-       case SF_U8: {
-               uint8_t *src = (uint8_t *)data;
-               while (samples--) {
-                       *dest++ = (*src++) / 256.0;
+       mutex_lock(powd->mutex);
+       /*
+        * We fill with zeros if no data was yet written and we do not have
+        * enough to fill all buffers.
+        */
+       if (!powd->playing) {
+               size_t want = 0, have =
+                       btr_get_input_queue_size(powd->callback_btrn);
+               for (i = 0; i < abl->mNumberBuffers; i++)
+                       want += abl->mBuffers[i].mDataByteSize;
+               if (have < want) {
+                       PARA_DEBUG_LOG("deferring playback (have = %zu < %zu = want)\n",
+                               have, want);
+                       for (i = 0; i < abl->mNumberBuffers; i++)
+                               memset(abl->mBuffers[i].mData, 0,
+                                       abl->mBuffers[i].mDataByteSize);
+                       goto out;
                }
-               break;
+               powd->playing = true;
        }
-       case SF_S8: {
-               int8_t *src = (int8_t *)data;
-               while (samples--) {
-                       *dest++ = ((*src++) + 128) / 256.0;
-               }
-               break;
-       }
-       default: {
-               short *src = (short *)data;
-               while (samples--)
-                       *dest++ = (*src++) / 32768.0;
-       }
-       }
-       b->ptr = b->buffer;
-       b->remaining = b->size;
-}
-
-static OSStatus osx_callback(void * inClientData,
-       __a_unused AudioUnitRenderActionFlags *inActionFlags,
-       __a_unused const AudioTimeStamp *inTimeStamp,
-       __a_unused  UInt32 inBusNumber,
-       __a_unused UInt32 inNumFrames,
-       AudioBufferList *outOutputData)
-{
-       long m, n;
-       float *dest;
-       int i;
-       struct private_osx_write_data *powd = inClientData;
 
-//     PARA_INFO_LOG("%p\n", powd);
-       for (i = 0; i < outOutputData->mNumberBuffers; ++i) {
+       for (i = 0; i < abl->mNumberBuffers; i++) {
                /* what we have to fill */
-               m = outOutputData->mBuffers[i].mDataByteSize / sizeof(float);
-               dest = outOutputData->mBuffers[i].mData;
-               while (m > 0) {
-                       n = powd->from->remaining;
-                       if (n <= 0) {
-                               n = powd->from->next->remaining;
-                               if (n <= 0) {
-                                       PARA_INFO_LOG("buffer underrun\n");
-                                       return 0;
-                               }
-                               powd->from = powd->from->next;
-                       }
-//                     PARA_INFO_LOG("buf %p: n = %ld, m= %ld\n", powd->from->buffer, n, m);
-                       /*
-                        * we dump what we can. In fact, just the necessary
-                        * should be sufficient
-                        */
-                       if (n > m)
-                               n = m;
-                       memcpy(dest, powd->from->ptr, n * sizeof(float));
-                       dest += n;
-                       /* remember all done work */
-                       m -= n;
-                       powd->from->ptr += n;
-                       if ((powd->from->remaining -= n) <= 0)
-                               powd->from = powd->from->next;
+               void *dest = abl->mBuffers[i].mData;
+               size_t sz = abl->mBuffers[i].mDataByteSize, samples, bytes;
+
+               samples_want = sz / wn->min_iqs;
+               while (samples_want > 0) {
+                       char *buf;
+                       btr_merge(powd->callback_btrn, wn->min_iqs);
+                       samples_have = btr_next_buffer(powd->callback_btrn, &buf) / wn->min_iqs;
+                       //PARA_INFO_LOG("i: %d want %zu samples to addr %p, have: %zu\n", i, samples_want,
+                       //      dest, samples_have);
+                       samples = PARA_MIN(samples_have, samples_want);
+                       if (samples == 0)
+                               break;
+                       bytes = samples * wn->min_iqs;
+                       memcpy(dest, buf, bytes);
+                       btr_consume(powd->callback_btrn, bytes);
+                       samples_want -= samples;
+                       dest += bytes;
                }
+               if (samples_want == 0)
+                       continue;
+               if (btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF) >= 0)
+                       PARA_INFO_LOG("zero-padding (%zu samples)\n",
+                               samples_want);
+               memset(dest, 0, samples_want * wn->min_iqs);
+               break;
        }
-       return 0;
+out:
+       mutex_unlock(powd->mutex);
+       return noErr;
 }
 
-#ifdef WORDS_BIGENDIAN /* ppc */
-#define ENDIAN_FLAGS kLinearPCMFormatFlagIsBigEndian
-#else
-#define ENDIAN_FLAGS 0
-#endif
-
 static int core_audio_init(struct writer_node *wn)
 {
        struct private_osx_write_data *powd = para_calloc(sizeof(*powd));
-       ComponentDescription desc;
        Component comp;
-       AURenderCallbackStruct inputCallback = {osx_callback, powd};
-       AudioStreamBasicDescription format;
        int ret;
-       struct btr_node *btrn = wn->btrn;
        int32_t val;
+       AURenderCallbackStruct input_callback;
+       ComponentDescription desc = {
+               .componentType = kAudioUnitType_Output,
+               .componentSubType = kAudioUnitSubType_DefaultOutput,
+               .componentManufacturer = kAudioUnitManufacturer_Apple,
+       };
+       AudioStreamBasicDescription format = {
+               .mFormatID = kAudioFormatLinearPCM,
+               .mFramesPerPacket = 1,
+       };
+       struct btr_node *btrn = wn->btrn;
+       struct btr_node_description bnd;
 
-       wn->private_data = powd;
-       init_buffers(wn);
-       /* where did that default audio output go? */
-       desc.componentType = kAudioUnitType_Output;
-       desc.componentSubType = kAudioUnitSubType_DefaultOutput;
-       /* NOTE: and if default output isn't Apple? */
-       desc.componentManufacturer = kAudioUnitManufacturer_Apple;
-       desc.componentFlags = 0;
-       desc.componentFlagsMask = 0;
+       PARA_INFO_LOG("wn: %p\n", wn);
        ret = -E_DEFAULT_COMP;
        comp = FindNextComponent(NULL, &desc);
        if (!comp)
@@ -216,7 +148,6 @@ static int core_audio_init(struct writer_node *wn)
        ret = -E_UNIT_INIT;
        if (AudioUnitInitialize(powd->audio_unit))
                goto e1;
-       powd->play = 0;
        get_btr_sample_rate(btrn, &val);
        powd->sample_rate = val;
        get_btr_channels(btrn, &val);
@@ -230,39 +161,53 @@ static int core_audio_init(struct writer_node *wn)
         * any format conversions necessary from your format to the device's
         * format.
         */
-       format.mFormatID = kAudioFormatLinearPCM;
-       format.mFramesPerPacket = 1;
+
        format.mSampleRate = powd->sample_rate;
-       /* flags specific to each format */
-       format.mFormatFlags = kLinearPCMFormatFlagIsFloat
-               | kLinearPCMFormatFlagIsPacked
-               | ENDIAN_FLAGS;
+       format.mChannelsPerFrame = powd->channels;
+
        switch (powd->sample_format) {
        case SF_S8:
        case SF_U8:
                wn->min_iqs = powd->channels;
+               format.mBitsPerChannel = 8;
+               format.mBytesPerPacket = powd->channels;
+               format.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
                break;
        default:
                wn->min_iqs = powd->channels * 2;
+               format.mBytesPerPacket = powd->channels * 2;
+               format.mBitsPerChannel = 16;
+               format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
        }
-       format.mBitsPerChannel = 8 * sizeof(float);
-       format.mBytesPerPacket = powd->channels * sizeof(float);
        format.mBytesPerFrame = format.mBytesPerPacket;
-       format.mChannelsPerFrame = powd->channels;
 
+       if (powd->sample_format == SF_S16_BE || powd->sample_format == SF_U16_BE)
+               format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
+
+       input_callback = (AURenderCallbackStruct){osx_callback, wn};
        ret = -E_STREAM_FORMAT;
        if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_StreamFormat,
-                       kAudioUnitScope_Input, 0, &format,
-                       sizeof(AudioStreamBasicDescription)))
+                       kAudioUnitScope_Input, 0, &format, sizeof(format)))
                goto e2;
        ret = -E_ADD_CALLBACK;
        if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_SetRenderCallback,
-                       kAudioUnitScope_Input, 0, &inputCallback,
-                       sizeof(inputCallback)) < 0)
-               goto e3;
+                       kAudioUnitScope_Input, 0, &input_callback,
+                       sizeof(input_callback)) < 0)
+               goto e2;
+
+       ret = mutex_new();
+       if (ret < 0)
+               goto e2;
+       powd->mutex = ret;
+       /* set up callback btr node */
+       bnd.name = "cb_node";
+       bnd.parent = btrn;
+       bnd.child = NULL;
+       bnd.handler = NULL;
+       bnd.context = powd;
+       powd->callback_btrn = btr_new_node(&bnd);
+       wn->private_data = powd;
        return 1;
-e3:
-       destroy_buffers(powd);
 e2:
        AudioUnitUninitialize(powd->audio_unit);
 e1:
@@ -294,73 +239,91 @@ static void osx_write_close(struct writer_node *wn)
        if (!powd)
                return;
        PARA_INFO_LOG("closing writer node %p\n", wn);
-       AudioOutputUnitStop(powd->audio_unit);
-       AudioUnitUninitialize(powd->audio_unit);
-       CloseComponent(powd->audio_unit);
-       destroy_buffers(powd);
+       mutex_destroy(powd->mutex);
        free(powd);
+       wn->private_data = NULL;
 }
 
-static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
+/* must be called with the mutex held */
+static inline bool need_drain_delay(struct private_osx_write_data *powd)
+{
+       if (!powd->playing)
+               return false;
+       return btr_get_input_queue_size(powd->callback_btrn) != 0;
+}
+
+static void osx_write_pre_select(struct sched *s, struct task *t)
 {
        struct writer_node *wn = container_of(t, struct writer_node, task);
        struct private_osx_write_data *powd = wn->private_data;
-       struct btr_node *btrn = wn->btrn;
-       char *data;
-       size_t bytes;
-       int ret = 0;
+       int ret;
+       bool drain_delay_nec = false;
 
-       while (!powd || powd->to->remaining <= 0) {
+       if (!powd) {
                ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
-               if (ret <= 0)
-                       break;
-               if (!powd) {
-                       ret = core_audio_init(wn);
-                       if (ret < 0)
-                               break;
-                       powd = wn->private_data;
-               }
-               btr_merge(btrn, 8192);
-               bytes = btr_next_buffer(btrn, &data);
-               //PARA_CRIT_LOG("have: %zu\n", bytes);
-               fill_buffer(powd, data, bytes);
-               btr_consume(btrn, bytes);
-               if (!powd->play) {
-                       ret = -E_UNIT_START;
-                       if (AudioOutputUnitStart(powd->audio_unit))
-                               break;
-                       powd->play = 1;
-               }
-               powd->to = powd->to->next;
-       }
-       if (ret < 0 && (!powd || powd->from->remaining <= 0)) {
-               btr_remove_node(btrn);
-               t->error = ret;
+               if (ret != 0)
+                       sched_min_delay(s);
+               return;
        }
+
+       mutex_lock(powd->mutex);
+       ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+       if (ret < 0)
+               drain_delay_nec = need_drain_delay(powd);
+       mutex_unlock(powd->mutex);
+
+       if (drain_delay_nec)
+               return sched_request_timeout_ms(50, s);
+       if (ret != 0)
+               return sched_min_delay(s);
+       sched_request_timeout_ms(50, s);
 }
 
-static void osx_write_pre_select(struct sched *s, struct task *t)
+static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
 {
        struct writer_node *wn = container_of(t, struct writer_node, task);
        struct private_osx_write_data *powd = wn->private_data;
-       struct timeval tmp = {.tv_sec = 1, .tv_usec = 0}, delay = tmp;
-       unsigned long factor;
-       size_t numbytes;
-       int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+       struct btr_node *btrn = wn->btrn;
+       int ret;
 
-       if (ret == 0)
-               return;
-       if (ret < 0 || !powd)
-               return sched_min_delay(s);
-       assert(powd->sample_rate > 0);
-       assert(wn->min_iqs > 0);
-       numbytes = powd->to->remaining * sizeof(short);
-       factor = numbytes / powd->sample_rate / wn->min_iqs;
-       tv_scale(factor, &tmp, &delay);
-       sched_request_timeout(&delay, s);
+       if (!powd) {
+               ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+               if (ret == 0)
+                       return;
+               if (ret < 0)
+                       goto remove_btrn;
+               ret = core_audio_init(wn);
+               if (ret < 0)
+                       goto remove_btrn;
+               powd = wn->private_data;
+               AudioOutputUnitStart(powd->audio_unit);
+       }
+       mutex_lock(powd->mutex);
+       btr_pushdown(btrn);
+       ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+       if (ret < 0 && need_drain_delay(powd))
+               ret = 0;
+       mutex_unlock(powd->mutex);
+
+       if (ret >= 0)
+               goto out;
+       AudioOutputUnitStop(powd->audio_unit);
+       AudioUnitUninitialize(powd->audio_unit);
+       CloseComponent(powd->audio_unit);
+       btr_remove_node(powd->callback_btrn);
+       btr_free_node(powd->callback_btrn);
+remove_btrn:
+       btr_remove_node(btrn);
+       PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+out:
+       t->error = ret;
 }
 
-/** the init function of the osx writer */
+/**
+ * The init function of the osx writer.
+ *
+ * \param w Filled in by the function.
+ */
 void osx_write_init(struct writer *w)
 {
        struct osx_write_args_info dummy;
index e8dddf5..142e2ab 100644 (file)
--- a/server.c
+++ b/server.c
  *     - Blob tables: \ref blob.c,
  *     - The error subssystem: \ref error.h.
  *     - Access control for paraslash senders: \ref acl.c, \ref acl.h.
+ *     - Internal crypto API: \ref crypt.h.
  *
  * Low-level data structures:
  *
  *     - Doubly linked lists: \ref list.h,
  *     - Ring buffer: \ref ringbuffer.c, \ref ringbuffer.h,
- *     - Crypto: \ref crypt.c, \ref crypt.h.
+ *     - openssl: \ref crypt.c
+ *     - libgcrypt: \ref gcrypt.c
  *     - Forward error correction: \ref fec.c.
  */
 
index 2af59f5..7163bb7 100644 (file)
@@ -202,9 +202,10 @@ In any case you'll need
 
        git clone git://git.tuebingen.mpg.de/osl
 
-       - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/gcc, gcc). The
-       EMPH(gnu compiler collection) is usually shipped with the
-       distro. gcc-3.3 or newer is required.
+       - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/gcc, gcc) or
+       XREFERENCE(http://clang.llvm.org, clang). All gcc versions
+       >= 3.3 are currently supported. Clang version 1.1 or newer
+       should work as well.
 
        - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/make, gnu make) is
        also shipped with the disto. On BSD systems the gnu make
@@ -214,11 +215,13 @@ In any case you'll need
        scripts which run during compilation require the EMPH(Bourne
        again shell).  It is most likely already installed.
 
-       - XREFERENCE(http://www.openssl.org/, openssl). The EMPH(Secure
-       Sockets Layer) library is needed for cryptographic routines
-       on both the server and the client side. It is usually shipped
-       with the distro, but you might have to install the "development
-       package" (called libssl-dev on debian systems) as well.
+       - XREFERENCE(http://www.openssl.org/, openssl) or
+       XREFERENCE(ftp://ftp.gnupg.org/gcrypt/libgcrypt/, libgcrypt).
+       At least one of these two libraries is needed as the backend
+       for cryptographic routines on both the server and the client
+       side. Both openssl and libgcrypt are usually shipped with the
+       distro, but you might have to install the development package
+       (libssl-dev or libgcrypt-dev on debian systems) as well.
 
        - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/help2man, help2man)
        is used to create the man pages.
@@ -495,12 +498,13 @@ as follows:
 
        - para_client connects to para_server and sends an
        authentication request for a user. It does so by connecting
-       to para_server, TCP 2990, the control port of para_server.
+       to TCP port 2990 of the server host. This port is called the
+       para_server _control port_.
 
        - para_server accepts the connection and forks a child process
-       which is supposed to handle the connection. The parent process
-       keeps listening on the control port while the child process
-       (also called para_server below) continues as follows.
+       which handles the incoming request. The parent process keeps
+       listening on the control port while the child process (also
+       called para_server below) continues as follows.
 
        - para_server loads the RSA public key of that user, fills a
        fixed-length buffer with random bytes, encrypts that buffer
@@ -510,7 +514,7 @@ as follows:
        session key.
 
        - para_client receives the encrypted buffer and decrypts it
-       using the user's private key, thereby obtaining the challenge
+       with the user's private key, thereby obtaining the challenge
        buffer and the session key. It sends the SHA1 hash value of
        the challenge back to para_server and stores the session key
        for further use.
@@ -526,10 +530,10 @@ as follows:
        this point on the communication is encrypted using the RC4
        stream cipher with the session key known to both peers.
 
-paraslash relies on the quality of openssl's cryptographically strong
-pseudo-random bytes, on the security of the implementation of the
-openssl RSA and RC4 crypto routines and on the infeasibility to invert
-the SHA1 function.
+paraslash relies on the quality of the pseudo-random bytes provided
+by the crypto library (openssl or libgcrypt), on the security of
+the implementation of the RSA and RC4 crypto routines and on the
+infeasibility to invert the SHA1 function.
 
 Neither para_server or para_client create RSA keys on their own. This
 has to be done once for each user as sketched in REFERENCE(Quick start,
@@ -539,8 +543,8 @@ file, below).
 The user_list file
 ~~~~~~~~~~~~~~~~~~
 
-At startup para_server reads the user list file which must contain
-one line per user. The default location of the user list file may be
+At startup para_server reads the user list file which contains one
+line per user. The default location of the user list file may be
 changed with the --user_list option.
 
 There should be at least one user in this file. Each user must have
@@ -607,10 +611,11 @@ known audio files to those which satisfy certain criteria.  It also
 maintains tables containing images (e.g. album cover art) and lyrics
 that can be associated with one or more audio files.
 
-AFS uses libosl, the object storage layer, as the backend library
-for storing information on audio files, playlists, etc. This library
-offers functionality similar to a relational database, but is much
-more lightweight than a full database backend.
+AFS uses XREFERENCE(http://systemlinux.org/~maan/osl/, libosl), the
+object storage layer library, as the backend library for storing
+information on audio files, playlists, etc. This library offers
+functionality similar to a relational database, but is much more
+lightweight than a full database backend.
 
 In this chapter we sketch the setup of the REFERENCE(The AFS process,
 AFS process) during server startup and proceed with the description
@@ -620,7 +625,7 @@ and moods) explains these two audio file selection mechanisms
 in detail and contains pratical examples. The way REFERENCE(File
 renames and content changes, file renames and content changes) are
 detected is discussed briefly before the REFERENCE(Troubleshooting,
-Troubleshooting) section which concludes the chapter.
+Troubleshooting) section concludes the chapter.
 
 The AFS process
 ~~~~~~~~~~~~~~~
@@ -723,7 +728,7 @@ Similarly, the "test" bit can be removed from an audio file with
        para_client setatt test- /path/to/the/audio/file
 
 Instead of a path you may use a shell wildcard pattern. The attribute
-is applied to all audio files matching that pattern:
+is applied to all audio files matching this pattern:
 
        para_client setatt test+ '/test/directory/*'
 
@@ -779,7 +784,7 @@ can be used.
 
 Note that the images and lyrics are not interpreted at all, and also
 the playlist and the mood blobs are only investigated when the mood
-or playlist is activated by using the select command.
+or playlist is activated with the select command.
 
 *The score table*
 
@@ -795,7 +800,9 @@ next. While doing so, it computes the new score and updates the
 last_played and the num_played fields in the audio file table.
 
 The score table is recomputed by the select command which loads a
-new mood or playlist.
+mood or playlist. Audio files are chosen for streaming from the rows
+of the score table on a highest-score-first basis.
+
 
 Playlists and moods
 ~~~~~~~~~~~~~~~~~~~
@@ -807,17 +814,12 @@ terms of attributes and other type of information available in the
 audio file table. As an example, a mood can define a filename pattern,
 which is then matched against the names of audio files in the table.
 
-Selecting a mood or playlist means the generation of a ranking
-(a score table) for the set of admissible files. Audio files are
-then selected on a highest-score-first basis. The score table is
-recomputed at the moment the mood or playlist is selected.
-
 *Playlists*
 
 Playlists are accommodated in the playlist table of the afs database,
-using the aforementioned blob format for tables. A new filelist is
-created using the addpl command, by specifying the full (absolute)
-paths of all desired audio files, separated by newlines. For example
+using the aforementioned blob format for tables. A new playlist is
+created with the addpl command by specifying the full (absolute)
+paths of all desired audio files, separated by newlines. Example:
 
        find /my/mp3/dir -name "*.mp3" | para addpl my_playlist
 
@@ -837,7 +839,7 @@ A mood consists of a unique name and its *mood definition*, which is
 a set of *mood lines* containing expressions in terms of attributes
 and other data contained in the database.
 
-At any time, at most one mood can be *active* which means that
+At any time at most one mood can be *active* which means that
 para_server is going to select only files from that subset of
 admissible files.
 
@@ -947,7 +949,7 @@ The year tag is special as its value is undefined if the audio file
 has no year tag or the content of the year tag is not a number. Such
 audio files never match. Another difference is the special treatment
 if the year tag is a two-digit number. In this case either 1900 or
-2000 are added to the tag value depending on whether the number is
+2000 is added to the tag value, depending on whether the number is
 greater than 2000 plus the current year.
 
 
@@ -1883,7 +1885,7 @@ the tip of topic branches you are interested in from the output of
 "git log next"). You should be able to safely build on top of them.
 
 However, at times "next" will be rebuilt from the tip of "master" to
-get rid of merge commits that will never be in "master. The commit
+get rid of merge commits that will never be in "master". The commit
 that replaces "next" will usually have the identical tree, but it
 will have different ancestry from the tip of "master".
 
index 137d288..1dde835 100644 (file)
@@ -116,7 +116,7 @@ int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi)
        return 1;
 }
 
-const uint8_t log2_tab[256] = {
+static const uint8_t log2_tab[256] = {
        0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
        4, 4, 4, 4, 4, 4, 4, 4,
        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,