crypt: Move implementation-independent code to separate file.
authorAndre Noll <maan@systemlinux.org>
Sat, 5 Mar 2011 20:53:54 +0000 (21:53 +0100)
committerAndre Noll <maan@systemlinux.org>
Wed, 6 Jul 2011 06:41:25 +0000 (08:41 +0200)
This introduces crypt_common.c which contains helper functions from
crypt.c which are independent of openssl.

crypt.common.c contains two types of public functions: Frontend
functions are called by users of the crypto API, and these functions
are exported as usual through the crypt.h header file. Backend
functions, on the other hand, are expected to be called only from
the crypto implementation (i.e. from crypt.c or gcrypt.c). These
functions are exported through the new crypt_backend.h header file.

configure.ac
crypt.c
crypt.h
crypt_backend.h [new file with mode: 0644]
crypt_common.c [new file with mode: 0644]
error.h

index 8904281..1190821 100644 (file)
@@ -92,7 +92,7 @@ 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,7 +115,7 @@ 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
        client_common ggo udp_recv color fec prebuffer_filter audiod_command_list
@@ -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 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 crypt_common"
 client_ldflags=""
 
 gui_cmdline_objs="add_cmdline(gui)"
@@ -807,7 +807,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..7b602d9 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>
@@ -21,6 +22,7 @@
 #include "string.h"
 #include "crypt.h"
 #include "fd.h"
+#include "crypt_backend.h"
 
 struct asymmetric_key {
        RSA *rsa;
@@ -72,19 +74,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 +111,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 +148,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)
@@ -403,7 +184,7 @@ int get_asymmetric_key(const char *key_file, int private,
        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 +215,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);
@@ -600,39 +385,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.
  *
@@ -659,32 +411,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.
  *
diff --git a/crypt.h b/crypt.h
index 21abe41..d2c2b32 100644 (file)
--- a/crypt.h
+++ b/crypt.h
@@ -4,7 +4,16 @@
  * 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;
@@ -17,17 +26,18 @@ int get_asymmetric_key(const char *key_file, int private,
                struct asymmetric_key **result);
 void free_asymmetric_key(struct asymmetric_key *key);
 
+/* random numbers */
 void get_random_bytes_or_die(unsigned char *buf, int num);
 void init_random_seed_or_die(void);
 
-/** Opaque structure for stream cipher crypto. */
-struct stream_cipher;
+/* stream cipher declarations and prototypes */
 
-/** Number of bytes of the session key. */
+/** Opaque structure for stream ciphers. */
+struct stream_cipher;
+/** 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
@@ -42,10 +52,8 @@ struct stream_cipher_context {
        /** Key used for sending data. */
        struct stream_cipher *send;
 };
-
 struct stream_cipher *sc_new(const unsigned char *data, int len);
 void sc_free(struct stream_cipher *sc);
-
 int sc_send_bin_buffer(struct stream_cipher_context *scc, const char *buf,
                size_t len);
 int sc_send_buffer(struct stream_cipher_context *scc, const char *buf);
@@ -55,57 +63,11 @@ int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf,
                size_t size);
 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 **/
+/* hashing */
 
 /** 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.
- *
- * \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.
- */
-_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;
-}
-
-/**
- * Convert a hash value to ascii format.
- *
- * \param hash the hash value.
- * \param asc Result pointer.
- *
- * \a asc must point to an area of at least 2 * \p HASH_SIZE + 1 bytes which
- * 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);
+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..b362651
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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);
diff --git a/crypt_common.c b/crypt_common.c
new file mode 100644 (file)
index 0000000..1308af4
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * 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 the implementation. */
+
+#include <regex.h>
+#include <stdbool.h>
+
+#include "para.h"
+#include "error.h"
+#include "string.h"
+#include "crypt_backend.h"
+#include "crypt.h"
+
+#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 = '=';
+/*
+ * 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);
+}
+
+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;
+}
+
+/*
+ * Can not use the inline functions of portable_io.h here because the byte
+ * order is different.
+ */
+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;
+}
+
+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;
+}
+
+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;
+}
+
+/**
+ * Convert a hash value to ascii format.
+ *
+ * \param hash the hash value.
+ * \param asc Result pointer.
+ *
+ * \a asc must point to an area of at least 2 * \p HASH_SIZE + 1 bytes which
+ * will be filled by the function with the ascii representation of the hash
+ * value given by \a hash, and a terminating \p NULL byte.
+ */
+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';
+}
+
+/**
+ * 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)
+{
+       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;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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;
+}
diff --git a/error.h b/error.h
index 3f5ff73..6c843e4 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,8 +381,6 @@ 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"), \