-------------------------------------------
- para_gui no longer waits up to one second to update the screen when
-the geometry of the terminal changes.
-
+ the geometry of the terminal changes.
- Minor documentation improvements.
+- Improvements to the crypto subsystem.
----------------------------------------
0.6.1 (2017-09-23) "segmented iteration"
version_handle_flag("audiod", OPT_GIVEN(VERSION));
handle_help_flags();
parse_config_or_die();
- init_random_seed_or_die();
+ crypt_init();
daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
recv_init();
if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
audiod_cleanup();
sched_shutdown(&sched);
signal_shutdown(signal_task);
-
+ crypt_shutdown();
out:
lls_free_parse_result(lpr, CMD_PTR);
if (errctx)
{
int ret;
- init_random_seed_or_die();
+ crypt_init();
sched.default_timeout.tv_sec = 1;
ret = client_parse_config(argc, argv, &ct, &client_loglevel);
}
}
sched_shutdown(&sched);
+ crypt_shutdown();
out:
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
}
n = sbb.iov.iov_len;
PARA_INFO_LOG("<-- [challenge] (%zu bytes)\n", n);
- ret = priv_decrypt(ct->key_file, crypt_buf,
+ ret = apc_priv_decrypt(ct->key_file, crypt_buf,
sbb.iov.iov_base, n);
free(sbb.iov.iov_base);
if (ret < 0)
goto out;
ct->challenge_hash = para_malloc(HASH_SIZE);
- hash_function((char *)crypt_buf, CHALLENGE_SIZE, ct->challenge_hash);
- ct->scc.send = sc_new(crypt_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
- ct->scc.recv = sc_new(crypt_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
+ hash_function((char *)crypt_buf, APC_CHALLENGE_SIZE, ct->challenge_hash);
+ ct->scc.send = sc_new(crypt_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
+ ct->scc.recv = sc_new(crypt_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
SESSION_KEY_LEN);
hash_to_asc(ct->challenge_hash, buf);
PARA_INFO_LOG("--> %s\n", buf);
* the function if the connection was not authenticated when the timeout
* expires.
*
- * \sa alarm(2), \ref crypt.c, \ref crypt.h.
+ * \sa alarm(2), \ref openssl.c, \ref crypt.h.
*/
__noreturn void handle_connect(int fd)
{
int ret;
- unsigned char rand_buf[CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
+ unsigned char rand_buf[APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN];
unsigned char challenge_hash[HASH_SIZE];
char *command = NULL, *buf = para_malloc(HANDSHAKE_BUFSIZE) /* must be on the heap */;
size_t numbytes;
goto net_err;
if (cc->u) {
get_random_bytes_or_die(rand_buf, sizeof(rand_buf));
- ret = pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
+ ret = apc_pub_encrypt(cc->u->pubkey, rand_buf, sizeof(rand_buf),
(unsigned char *)buf);
if (ret < 0)
goto net_err;
get_random_bytes_or_die((unsigned char *)buf, numbytes);
}
PARA_DEBUG_LOG("sending %d byte challenge + session key (%zu bytes)\n",
- CHALLENGE_SIZE, numbytes);
+ APC_CHALLENGE_SIZE, numbytes);
ret = send_sb(&cc->scc, buf, numbytes, SBD_CHALLENGE, false);
buf = NULL;
if (ret < 0)
if (!cc->u)
goto net_err;
/*
- * The correct response is the hash of the first CHALLENGE_SIZE bytes
+ * The correct response is the hash of the first APC_CHALLENGE_SIZE bytes
* of the random data.
*/
ret = -E_BAD_AUTH;
if (numbytes != HASH_SIZE)
goto net_err;
- hash_function((char *)rand_buf, CHALLENGE_SIZE, challenge_hash);
+ hash_function((char *)rand_buf, APC_CHALLENGE_SIZE, challenge_hash);
if (memcmp(challenge_hash, buf, HASH_SIZE))
goto net_err;
/* auth successful */
alarm(0);
PARA_INFO_LOG("good auth for %s\n", cc->u->name);
/* init stream cipher keys with the second part of the random buffer */
- cc->scc.recv = sc_new(rand_buf + CHALLENGE_SIZE, SESSION_KEY_LEN);
- cc->scc.send = sc_new(rand_buf + CHALLENGE_SIZE + SESSION_KEY_LEN,
+ cc->scc.recv = sc_new(rand_buf + APC_CHALLENGE_SIZE, SESSION_KEY_LEN);
+ cc->scc.send = sc_new(rand_buf + APC_CHALLENGE_SIZE + SESSION_KEY_LEN,
SESSION_KEY_LEN);
ret = send_sb(&cc->scc, NULL, 0, SBD_PROCEED, false);
if (ret < 0)
version
"
if test "$CRYPTOLIB" = openssl; then
- server_errlist_objs="$server_errlist_objs crypt"
+ server_errlist_objs="$server_errlist_objs openssl"
else
server_errlist_objs="$server_errlist_objs gcrypt"
fi
version
"
if test "$CRYPTOLIB" = openssl; then
- client_errlist_objs="$client_errlist_objs crypt"
+ client_errlist_objs="$client_errlist_objs openssl"
else
client_errlist_objs="$client_errlist_objs gcrypt"
fi
sync_filter
"
if test "$CRYPTOLIB" = openssl; then
- audiod_errlist_objs="$audiod_errlist_objs crypt"
+ audiod_errlist_objs="$audiod_errlist_objs openssl"
else
audiod_errlist_objs="$audiod_errlist_objs gcrypt"
fi
+++ /dev/null
-/* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
-
-/** \file crypt.c Openssl-based encryption/decryption routines. */
-
-#include <regex.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <openssl/rand.h>
-#include <openssl/err.h>
-#include <openssl/pem.h>
-#include <openssl/sha.h>
-#include <openssl/bn.h>
-#include <openssl/aes.h>
-
-#include "para.h"
-#include "error.h"
-#include "string.h"
-#include "crypt.h"
-#include "fd.h"
-#include "crypt_backend.h"
-#include "base64.h"
-#include "portable_io.h"
-
-struct asymmetric_key {
- RSA *rsa;
-};
-
-void get_random_bytes_or_die(unsigned char *buf, int num)
-{
- unsigned long err;
-
- /* RAND_bytes() returns 1 on success, 0 otherwise. */
- if (RAND_bytes(buf, num) == 1)
- return;
- err = ERR_get_error();
- PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err));
- exit(EXIT_FAILURE);
-}
-
-/*
- * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Seed the PRNG
- * used by random(3) with a random seed obtained from SSL. If /dev/urandom is
- * not readable, the function calls exit().
- *
- * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3),
- * random(3), \ref para_random().
- */
-void init_random_seed_or_die(void)
-{
- int seed, ret = RAND_load_file("/dev/urandom", 64);
-
- if (ret != 64) {
- PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret);
- exit(EXIT_FAILURE);
- }
- get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
- srandom(seed);
-}
-
-static int get_private_key(const char *path, RSA **rsa)
-{
- EVP_PKEY *pkey;
- BIO *bio = BIO_new(BIO_s_file());
-
- *rsa = NULL;
- if (!bio)
- return -E_PRIVATE_KEY;
- if (BIO_read_filename(bio, path) <= 0)
- goto bio_free;
- pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
- if (!pkey)
- goto bio_free;
- *rsa = EVP_PKEY_get1_RSA(pkey);
- EVP_PKEY_free(pkey);
-bio_free:
- BIO_free(bio);
- return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
-}
-
-/*
- * The public key loading functions below were inspired by corresponding code
- * of openssh-5.2p1, Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo,
- * Finland. However, not much of the original code remains.
- */
-
-static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
-{
- const unsigned char *p = buf, *end = buf + len;
- uint32_t bnsize;
- BIGNUM *bn;
-
- if (p + 4 < p)
- return -E_BIGNUM;
- if (p + 4 > end)
- return -E_BIGNUM;
- bnsize = read_u32_be(p);
- PARA_DEBUG_LOG("bnsize: %u\n", bnsize);
- p += 4;
- if (p + bnsize < p)
- return -E_BIGNUM;
- if (p + bnsize > end)
- return -E_BIGNUM;
- if (bnsize > 8192)
- return -E_BIGNUM;
- bn = BN_bin2bn(p, bnsize, NULL);
- if (!bn)
- return -E_BIGNUM;
- *result = bn;
- return bnsize + 4;
-}
-
-static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
-{
- int ret;
- RSA *rsa;
- BIGNUM *n, *e;
- const unsigned char *p = blob, *end = blob + blen;
-
- rsa = RSA_new();
- if (!rsa)
- return -E_BIGNUM;
- ret = read_bignum(p, end - p, &e);
- if (ret < 0)
- goto fail;
- p += ret;
- ret = read_bignum(p, end - p, &n);
- if (ret < 0)
- goto fail;
-#ifdef HAVE_RSA_SET0_KEY
- RSA_set0_key(rsa, n, e, NULL);
-#else
- rsa->n = n;
- rsa->e = e;
-#endif
- *result = rsa;
- return 1;
-fail:
- RSA_free(rsa);
- return ret;
-}
-
-int get_public_key(const char *key_file, struct asymmetric_key **result)
-{
- struct asymmetric_key *key = NULL;
- void *map = NULL;
- unsigned char *blob = NULL;
- size_t map_size, encoded_size, decoded_size;
- int ret, ret2;
- char *cp;
-
- key = para_malloc(sizeof(*key));
- ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
- if (ret < 0)
- goto out;
- ret = is_ssh_rsa_key(map, map_size);
- if (!ret) {
- ret = -E_SSH_PARSE;
- goto out_unmap;
- }
- cp = map + ret;
- encoded_size = map_size - ret;
- PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file);
- ret = uudecode(cp, encoded_size, (char **)&blob, &decoded_size);
- if (ret < 0)
- goto out_unmap;
- ret = check_ssh_key_header(blob, decoded_size);
- if (ret < 0)
- goto out_unmap;
- ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
- if (ret < 0)
- goto out_unmap;
- ret = RSA_size(key->rsa);
-out_unmap:
- ret2 = para_munmap(map, map_size);
- if (ret >= 0 && ret2 < 0)
- ret = ret2;
-out:
- if (ret < 0) {
- free(key);
- *result = NULL;
- PARA_ERROR_LOG("key %s: %s\n", key_file, para_strerror(-ret));
- } else
- *result = key;
- free(blob);
- return ret;
-}
-
-void free_public_key(struct asymmetric_key *key)
-{
- if (!key)
- return;
- RSA_free(key->rsa);
- free(key);
-}
-
-int priv_decrypt(const char *key_file, unsigned char *outbuf,
- unsigned char *inbuf, int inlen)
-{
- struct asymmetric_key *priv;
- int ret;
-
- ret = check_private_key_file(key_file);
- if (ret < 0)
- return ret;
- if (inlen < 0)
- return -E_RSA;
- priv = para_malloc(sizeof(*priv));
- ret = get_private_key(key_file, &priv->rsa);
- if (ret < 0) {
- free(priv);
- return ret;
- }
- /*
- * RSA is vulnerable to timing attacks. Generate a random blinding
- * factor to protect against this kind of attack.
- */
- ret = -E_BLINDING;
- if (RSA_blinding_on(priv->rsa, NULL) == 0)
- goto out;
- ret = RSA_private_decrypt(inlen, inbuf, outbuf, priv->rsa,
- RSA_PKCS1_OAEP_PADDING);
- RSA_blinding_off(priv->rsa);
- if (ret <= 0)
- ret = -E_DECRYPT;
-out:
- RSA_free(priv->rsa);
- free(priv);
- return ret;
-}
-
-int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
- unsigned len, unsigned char *outbuf)
-{
- int ret, flen = len; /* RSA_public_encrypt expects a signed int */
-
- if (flen < 0)
- return -E_ENCRYPT;
- ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa,
- RSA_PKCS1_OAEP_PADDING);
- return ret < 0? -E_ENCRYPT : ret;
-}
-
-struct stream_cipher {
- EVP_CIPHER_CTX *aes;
-};
-
-struct stream_cipher *sc_new(const unsigned char *data, int len)
-{
- struct stream_cipher *sc = para_malloc(sizeof(*sc));
-
- assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
- sc->aes = EVP_CIPHER_CTX_new();
- EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
- data + AES_CRT128_BLOCK_SIZE);
- return sc;
-}
-
-void sc_free(struct stream_cipher *sc)
-{
- if (!sc)
- return;
- EVP_CIPHER_CTX_free(sc->aes);
- free(sc);
-}
-
-static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
- struct iovec *dst)
-{
- int ret, inlen = src->iov_len, outlen, tmplen;
-
- *dst = (typeof(*dst)) {
- /* Add one for the terminating zero byte. */
- .iov_base = para_malloc(inlen + 1),
- .iov_len = inlen
- };
- ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
- assert(ret != 0);
- ret = EVP_EncryptFinal_ex(ctx, dst->iov_base + outlen, &tmplen);
- assert(ret != 0);
- outlen += tmplen;
- ((char *)dst->iov_base)[outlen] = '\0';
- dst->iov_len = outlen;
-}
-
-void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
-{
- return aes_ctr128_crypt(sc->aes, src, dst);
-}
-
-void hash_function(const char *data, unsigned long len, unsigned char *hash)
-{
- SHA_CTX c;
- SHA1_Init(&c);
- SHA1_Update(&c, data, len);
- SHA1_Final(hash, &c);
-}
/** \file crypt.h Public crypto interface. */
+/*
+ * Asymmetric pubkey cryptosystem (apc).
+ *
+ * This is just RSA, but this fact is a hidden implementation detail.
+ */
-/* These are used to distinguish between loading of private/public key. */
-
-/** The key to load is a public key. */
-#define LOAD_PUBLIC_KEY 0
-/** The key to load is a private key. */
-#define LOAD_PRIVATE_KEY 1
/** The size of the challenge sent to the client. */
-#define CHALLENGE_SIZE 64
+#define APC_CHALLENGE_SIZE 64
/** Opaque structure for public and private keys. */
struct asymmetric_key;
*
* \return The size of the encrypted data on success, negative on errors.
*/
-int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
+int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
unsigned len, unsigned char *outbuf);
/**
*
* \return The size of the recovered plaintext on success, negative on errors.
*/
-int priv_decrypt(const char *key_file, unsigned char *outbuf,
+int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
unsigned char *inbuf, int inlen);
/**
*
* \return The size of the key on success, negative on errors.
*/
-int get_public_key(const char *key_file, struct asymmetric_key **result);
+int apc_get_pubkey(const char *key_file, struct asymmetric_key **result);
/**
* Deallocate a public key.
*
* \param key Pointer to the key structure to free.
*
- * This should be called for keys obtained by get_public_key() if the key is no
+ * This should be called for keys obtained by \ref apc_get_pubkey() if the key is no
* longer needed.
*/
-void free_public_key(struct asymmetric_key *key);
+void apc_free_pubkey(struct asymmetric_key *key);
/**
void get_random_bytes_or_die(unsigned char *buf, int num);
/**
- * Seed pseudo random number generators.
+ * Initialize the crypto backend.
*
- * This function seeds the PRNG used by random() with a random seed obtained
- * from the crypto implementation. On errors, an error message is logged and
- * the function calls exit().
+ * This function initializes the crypto library and seeds the pseudo random
+ * number generator used by random() with a random seed obtained from the
+ * crypto implementation. On errors, an error message is logged and the
+ * function calls exit().
*
* \sa \ref get_random_bytes_or_die(), srandom(3), random(3), \ref
* para_random().
*/
-void init_random_seed_or_die(void);
+void crypt_init(void);
+/** Allocate all resources of the crypto backend. */
+void crypt_shutdown(void);
/** Opaque structure for stream ciphers. */
struct stream_cipher;
/** AES block size in bytes. */
#define AES_CRT128_BLOCK_SIZE 16
-size_t is_ssh_rsa_key(char *data, size_t size);
-int check_ssh_key_header(const unsigned char *blob, int blen);
+int decode_ssh_key(const char *filename, unsigned char **blob,
+ size_t *decoded_size);
int check_private_key_file(const char *file);
#include "crypt.h"
#include "crypt_backend.h"
#include "portable_io.h"
+#include "fd.h"
+#include "base64.h"
/** If the key begins with this text, we treat it as an ssh key. */
#define KEY_TYPE_TXT "ssh-rsa"
-/**
- * Check if given buffer starts with a ssh rsa key signature.
- *
- * \param data The buffer.
- * \param size Number of data bytes.
+/*
+ * Check if the given buffer starts with an ssh rsa key signature.
*
- * \return Number of header bytes to be skipped on success, zero if
- * ssh rsa signature was not found.
+ * Returns number of header bytes to be skipped on success, zero if no ssh rsa
+ * signature was found.
*/
-size_t is_ssh_rsa_key(char *data, size_t size)
+static size_t is_ssh_rsa_key(char *data, size_t size)
{
char *cp;
return cp - data;
}
-/**
- * Sanity checks for the header of an ssh key.
- *
- * \param blob The buffer.
- * \param blen The number of bytes of \a blob.
+/*
+ * Perform some sanity checks on the decoded ssh key.
*
- * This performs some checks to make sure we really have an ssh key. It also
- * computes the offset in bytes of the start of the key values (modulus,
- * exponent..).
- *
- * \return The number of bytes to skip until the start of the first encoded
- * number (usually 11).
+ * This function returns the size of the header. Usually, the header is 11
+ * bytes long: four bytes for the length field, and the string "ssh-rsa".
*/
-int check_ssh_key_header(const unsigned char *blob, int blen)
+static int check_ssh_key_header(const unsigned char *blob, int blen)
{
const unsigned char *p = blob, *end = blob + blen;
uint32_t rlen;
return 4 + rlen;
}
+/**
+ * Perform sanity checks and base64-decode an ssh-rsa key.
+ *
+ * \param filename The public key file (usually id_rsa.pub).
+ * \param blob Pointer to base64-decoded blob is returned here.
+ * \param decoded_size The size of the decoded blob.
+ *
+ * The memory pointed at by the returned blob pointer has to be freed by the
+ * caller.
+ *
+ * \return On success, the offset in bytes of the start of the key values
+ * (modulus, exponent..). This is the number of bytes to skip from the blob
+ * until the start of the first encoded number. On failure, a negative error
+ * code is returned.
+ *
+ * \sa \ref uudecode().
+ */
+int decode_ssh_key(const char *filename, unsigned char **blob,
+ size_t *decoded_size)
+{
+ int ret, ret2;
+ void *map;
+ size_t map_size;
+
+ ret = mmap_full_file(filename, O_RDONLY, &map, &map_size, NULL);
+ if (ret < 0)
+ return ret;
+ ret = is_ssh_rsa_key(map, map_size);
+ if (ret == 0) {
+ ret = -E_SSH_PARSE;
+ goto unmap;
+ }
+ ret = uudecode(map + ret, map_size - ret, (char **)blob, decoded_size);
+ if (ret < 0)
+ goto unmap;
+ ret = check_ssh_key_header(*blob, *decoded_size);
+ if (ret < 0)
+ goto unmap;
+unmap:
+ ret2 = para_munmap(map, map_size);
+ if (ret >= 0 && ret2 < 0)
+ ret = ret2;
+ return ret;
+}
+
/**
* Check existence and permissions of a private key file.
*
* call to gcry_check_version() initializes the gcrypt library and checks that
* we have at least the minimal required version.
*/
-void init_random_seed_or_die(void)
+void crypt_init(void)
{
const char *req_ver = "1.5.0";
int seed;
req_ver, gcry_check_version(NULL));
exit(EXIT_FAILURE);
}
+
+ /*
+ * Allocate a pool of secure memory. This also drops privileges where
+ * needed.
+ */
+ gcry_control(GCRYCTL_INIT_SECMEM, 65536, 0);
+
+ /* Tell Libgcrypt that initialization has completed. */
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
srandom(seed);
}
+void crypt_shutdown(void)
+{
+ /*
+ * WK does not see a way to apply a patch for the sake of Valgrind, so
+ * as of 2018 libgrypt has no deinitialization routine to free the
+ * resources on exit.
+ */
+}
+
/** S-expression for the public part of an RSA key. */
#define RSA_PUBKEY_SEXP "(public-key (rsa (n %m) (e %m)))"
/** S-expression for a private RSA key. */
return ret;
}
-static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result)
+int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
{
+ unsigned char *blob, *p, *end;
int ret;
gcry_error_t gret;
- unsigned char *blob = NULL, *p, *end;
size_t nr_scanned, erroff, decoded_size;
- gcry_mpi_t e = NULL, n = NULL;
+ gcry_mpi_t e, n;
+ gcry_sexp_t sexp;
+ struct asymmetric_key *key;
- PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size);
- ret = uudecode((char *)data, size, (char **)&blob, &decoded_size);
+ ret = decode_ssh_key(key_file, &blob, &decoded_size);
if (ret < 0)
- goto free_blob;
- end = blob + decoded_size;
- dump_buffer("decoded key", blob, decoded_size);
- ret = check_ssh_key_header(blob, decoded_size);
- if (ret < 0)
- goto free_blob;
+ return ret;
p = blob + ret;
- ret = -E_SSH_PARSE;
- if (p >= end)
- goto free_blob;
+ end = blob + decoded_size;
PARA_DEBUG_LOG("scanning modulus and public exponent\n");
gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
if (gret) {
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;
goto release_e;
}
PARA_DEBUG_LOG("scanned n (%zu bytes)\n", nr_scanned);
-// gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, rsa_n);
-// PARA_CRIT_LOG("n: %s\n", buf);
- gret = gcry_sexp_build(result, &erroff, RSA_PUBKEY_SEXP, n, e);
+ gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e);
if (gret) {
PARA_ERROR_LOG("offset %zu: %s\n", erroff,
gcry_strerror(gcry_err_code(gret)));
ret = -E_SEXP_BUILD;
goto release_n;
}
- ret = nr_scanned / 32 * 32;
+ ret = ROUND_DOWN(nr_scanned, 32);
PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8);
+ key = para_malloc(sizeof(*key));
+ key->num_bytes = ret;
+ key->sexp = sexp;
+ *result = key;
release_n:
gcry_mpi_release(n);
release_e:
return ret;
}
-int get_public_key(const char *key_file, struct asymmetric_key **result)
-{
- int ret, ret2;
- void *map;
- size_t map_size;
- unsigned char *start, *end;
- gcry_sexp_t sexp;
- struct asymmetric_key *key;
-
- ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
- if (ret < 0)
- return ret;
- ret = is_ssh_rsa_key(map, map_size);
- if (!ret) {
- para_munmap(map, map_size);
- return -E_SSH_PARSE;
- }
- start = map + ret;
- end = map + map_size;
- ret = -E_SSH_PARSE;
- if (start >= end)
- goto unmap;
- ret = get_ssh_public_key(start, end - start, &sexp);
- if (ret < 0)
- goto unmap;
- key = para_malloc(sizeof(*key));
- key->num_bytes = ret;
- key->sexp = sexp;
- *result = key;
-unmap:
- ret2 = para_munmap(map, map_size);
- if (ret >= 0 && ret2 < 0)
- ret = ret2;
- return ret;
-}
-
-void free_public_key(struct asymmetric_key *key)
+void apc_free_pubkey(struct asymmetric_key *key)
{
if (!key)
return;
return 1;
}
-int priv_decrypt(const char *key_file, unsigned char *outbuf,
+int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
unsigned char *inbuf, int inlen)
{
gcry_error_t gret;
return ret;
}
-int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
+int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
unsigned len, unsigned char *outbuf)
{
gcry_error_t gret;
--- /dev/null
+/* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+/** \file openssl.c Openssl-based encryption/decryption routines. */
+
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/sha.h>
+#include <openssl/bn.h>
+#include <openssl/aes.h>
+
+#include "para.h"
+#include "error.h"
+#include "string.h"
+#include "crypt.h"
+#include "crypt_backend.h"
+#include "portable_io.h"
+
+struct asymmetric_key {
+ RSA *rsa;
+};
+
+void get_random_bytes_or_die(unsigned char *buf, int num)
+{
+ unsigned long err;
+
+ /* RAND_bytes() returns 1 on success, 0 otherwise. */
+ if (RAND_bytes(buf, num) == 1)
+ return;
+ err = ERR_get_error();
+ PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err));
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Seed the PRNG
+ * used by random(3) with a random seed obtained from SSL. If /dev/urandom is
+ * not readable, the function calls exit().
+ *
+ * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3),
+ * random(3), \ref para_random().
+ */
+void crypt_init(void)
+{
+ int seed, ret = RAND_load_file("/dev/urandom", 64);
+
+ if (ret != 64) {
+ PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret);
+ exit(EXIT_FAILURE);
+ }
+ get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
+ srandom(seed);
+}
+
+void crypt_shutdown(void)
+{
+ CRYPTO_cleanup_all_ex_data();
+}
+
+static int get_private_key(const char *path, RSA **rsa)
+{
+ EVP_PKEY *pkey;
+ BIO *bio = BIO_new(BIO_s_file());
+
+ *rsa = NULL;
+ if (!bio)
+ return -E_PRIVATE_KEY;
+ if (BIO_read_filename(bio, path) <= 0)
+ goto bio_free;
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (!pkey)
+ goto bio_free;
+ *rsa = EVP_PKEY_get1_RSA(pkey);
+ EVP_PKEY_free(pkey);
+bio_free:
+ BIO_free(bio);
+ return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
+}
+
+/*
+ * The public key loading functions below were inspired by corresponding code
+ * of openssh-5.2p1, Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo,
+ * Finland. However, not much of the original code remains.
+ */
+
+static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
+{
+ const unsigned char *p = buf, *end = buf + len;
+ uint32_t bnsize;
+ BIGNUM *bn;
+
+ if (p + 4 < p)
+ return -E_BIGNUM;
+ if (p + 4 > end)
+ return -E_BIGNUM;
+ bnsize = read_u32_be(p);
+ PARA_DEBUG_LOG("bnsize: %u\n", bnsize);
+ p += 4;
+ if (p + bnsize < p)
+ return -E_BIGNUM;
+ if (p + bnsize > end)
+ return -E_BIGNUM;
+ if (bnsize > 8192)
+ return -E_BIGNUM;
+ bn = BN_bin2bn(p, bnsize, NULL);
+ if (!bn)
+ return -E_BIGNUM;
+ *result = bn;
+ return bnsize + 4;
+}
+
+static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result)
+{
+ int ret;
+ RSA *rsa;
+ BIGNUM *n, *e;
+ const unsigned char *p = blob, *end = blob + blen;
+
+ rsa = RSA_new();
+ if (!rsa)
+ return -E_BIGNUM;
+ ret = read_bignum(p, end - p, &e);
+ if (ret < 0)
+ goto fail;
+ p += ret;
+ ret = read_bignum(p, end - p, &n);
+ if (ret < 0)
+ goto fail;
+#ifdef HAVE_RSA_SET0_KEY
+ RSA_set0_key(rsa, n, e, NULL);
+#else
+ rsa->n = n;
+ rsa->e = e;
+#endif
+ *result = rsa;
+ return 1;
+fail:
+ RSA_free(rsa);
+ return ret;
+}
+
+int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
+{
+ unsigned char *blob;
+ size_t decoded_size;
+ int ret;
+ struct asymmetric_key *key = para_malloc(sizeof(*key));
+
+ ret = decode_ssh_key(key_file, &blob, &decoded_size);
+ if (ret < 0)
+ goto out;
+ ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
+ if (ret < 0)
+ goto free_blob;
+ ret = RSA_size(key->rsa);
+ assert(ret > 0);
+ *result = key;
+free_blob:
+ free(blob);
+out:
+ if (ret < 0) {
+ free(key);
+ *result = NULL;
+ PARA_ERROR_LOG("can not load key %s\n", key_file);
+ }
+ return ret;
+}
+
+void apc_free_pubkey(struct asymmetric_key *key)
+{
+ if (!key)
+ return;
+ RSA_free(key->rsa);
+ free(key);
+}
+
+int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
+ unsigned char *inbuf, int inlen)
+{
+ struct asymmetric_key *priv;
+ int ret;
+
+ ret = check_private_key_file(key_file);
+ if (ret < 0)
+ return ret;
+ if (inlen < 0)
+ return -E_RSA;
+ priv = para_malloc(sizeof(*priv));
+ ret = get_private_key(key_file, &priv->rsa);
+ if (ret < 0) {
+ free(priv);
+ return ret;
+ }
+ /*
+ * RSA is vulnerable to timing attacks. Generate a random blinding
+ * factor to protect against this kind of attack.
+ */
+ ret = -E_BLINDING;
+ if (RSA_blinding_on(priv->rsa, NULL) == 0)
+ goto out;
+ ret = RSA_private_decrypt(inlen, inbuf, outbuf, priv->rsa,
+ RSA_PKCS1_OAEP_PADDING);
+ RSA_blinding_off(priv->rsa);
+ if (ret <= 0)
+ ret = -E_DECRYPT;
+out:
+ RSA_free(priv->rsa);
+ free(priv);
+ return ret;
+}
+
+int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
+ unsigned len, unsigned char *outbuf)
+{
+ int ret, flen = len; /* RSA_public_encrypt expects a signed int */
+
+ if (flen < 0)
+ return -E_ENCRYPT;
+ ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa,
+ RSA_PKCS1_OAEP_PADDING);
+ return ret < 0? -E_ENCRYPT : ret;
+}
+
+struct stream_cipher {
+ EVP_CIPHER_CTX *aes;
+};
+
+struct stream_cipher *sc_new(const unsigned char *data, int len)
+{
+ struct stream_cipher *sc = para_malloc(sizeof(*sc));
+
+ assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
+ sc->aes = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
+ data + AES_CRT128_BLOCK_SIZE);
+ return sc;
+}
+
+void sc_free(struct stream_cipher *sc)
+{
+ if (!sc)
+ return;
+ EVP_CIPHER_CTX_free(sc->aes);
+ free(sc);
+}
+
+static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
+ struct iovec *dst)
+{
+ int ret, inlen = src->iov_len, outlen, tmplen;
+
+ *dst = (typeof(*dst)) {
+ /* Add one for the terminating zero byte. */
+ .iov_base = para_malloc(inlen + 1),
+ .iov_len = inlen
+ };
+ ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
+ assert(ret != 0);
+ ret = EVP_EncryptFinal_ex(ctx, dst->iov_base + outlen, &tmplen);
+ assert(ret != 0);
+ outlen += tmplen;
+ ((char *)dst->iov_base)[outlen] = '\0';
+ dst->iov_len = outlen;
+}
+
+void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
+{
+ return aes_ctr128_crypt(sc->aes, src, dst);
+}
+
+void hash_function(const char *data, unsigned long len, unsigned char *hash)
+{
+ SHA_CTX c;
+ SHA1_Init(&c);
+ SHA1_Update(&c, data, len);
+ SHA1_Final(hash, &c);
+}
int i;
afs_pid = getpid();
+ crypt_shutdown();
for (i = argc - 1; i >= 0; i--)
memset(argv[i], 0, strlen(argv[i]));
i = argc - lls_num_inputs(cmdline_lpr) - 1;
/* become daemon */
if (OPT_GIVEN(DAEMON))
daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
- init_random_seed_or_die();
+ crypt_init();
daemon_log_welcome("server");
init_ipc_or_die(); /* init mmd struct and mmd->lock */
daemon_set_start_time();
mutex_lock(mmd_mutex);
ret = schedule(&sched);
sched_shutdown(&sched);
+ crypt_shutdown();
lls_free_parse_result(server_lpr, CMD_PTR);
if (server_lpr != cmdline_lpr)
lls_free_parse_result(cmdline_lpr, CMD_PTR);
if (strcmp(w, "user"))
continue;
PARA_DEBUG_LOG("found entry for user %s\n", n);
- ret = get_public_key(k, &pubkey);
+ ret = apc_get_pubkey(k, &pubkey);
if (ret < 0) {
PARA_NOTICE_LOG("skipping entry for user %s: %s\n", n,
para_strerror(-ret));
continue;
}
/*
- * In order to encrypt len := CHALLENGE_SIZE + 2 * SESSION_KEY_LEN
+ * In order to encrypt len := APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN
* bytes using RSA_public_encrypt() with EME-OAEP padding mode,
* RSA_size(rsa) must be greater than len + 41. So ignore keys
* which are too short. For details see RSA_public_encrypt(3).
*/
- if (ret <= CHALLENGE_SIZE + 2 * SESSION_KEY_LEN + 41) {
+ if (ret <= APC_CHALLENGE_SIZE + 2 * SESSION_KEY_LEN + 41) {
PARA_WARNING_LOG("public key %s too short (%d)\n",
k, ret);
- free_public_key(pubkey);
+ apc_free_pubkey(pubkey);
continue;
}
u = para_malloc(sizeof(*u));
list_for_each_entry_safe(u, tmp, &user_list, node) {
list_del(&u->node);
free(u->name);
- free_public_key(u->pubkey);
+ apc_free_pubkey(u->pubkey);
free(u);
}
} else