From 9e8d603d14561095fb8d08871319a315d3bf6763 Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Fri, 2 Aug 2019 19:12:34 +0200 Subject: lib: Add per-message encryption with OpenSSL This adds a per-message symmetric encryption using the OpenSSL library. At flow allocation, an Elliptic Curve Diffie-Hellman exchange is performed to derive a shared secret, which is then hashed using SHA3-256 to be used as a key for symmetric AES-256 encryption. Each message on an encrypted flow adds a small crypto header that includes a random 128-bit Initialization Vector (IV). If the server does not have OpenSSL enabled, the flow allocation will fail with an -ECRYPT error. Future optimizations are to piggyback the public keys on the flow allocation message, and to enable per-flow encryption that maintains the context of the encryption over multiple packets and doesn't require sending IVs. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- doc/man/flow_alloc.3 | 3 + include/ouroboros/errno.h | 9 +- include/ouroboros/qos.h | 74 ++++++- include/ouroboros/sockets.h.in | 4 +- src/ipcpd/eth/eth.c | 3 + src/ipcpd/udp/main.c | 4 + src/ipcpd/unicast/fa.c | 3 + src/lib/config.h.in | 4 + src/lib/crypt.c | 490 +++++++++++++++++++++++++++++++++++++++++ src/lib/dev.c | 121 ++++++++-- src/lib/qosspec.proto | 1 + src/lib/sockets.c | 6 +- 12 files changed, 693 insertions(+), 29 deletions(-) create mode 100644 src/lib/crypt.c diff --git a/doc/man/flow_alloc.3 b/doc/man/flow_alloc.3 index f4a6cf8d..48923fde 100644 --- a/doc/man/flow_alloc.3 +++ b/doc/man/flow_alloc.3 @@ -94,6 +94,9 @@ Not enough system memory resources available to allocate the flow. .B -ETIMEDOUT Flow allocation timed out. +.B -ECRYPT +The requested encryption is not supported. + .SH ATTRIBUTES For an explanation of the terms used in this section, see \fBattributes\fR(7). diff --git a/include/ouroboros/errno.h b/include/ouroboros/errno.h index 27b21e82..20049751 100644 --- a/include/ouroboros/errno.h +++ b/include/ouroboros/errno.h @@ -25,11 +25,12 @@ #include -#define ENOTALLOC 1000 /* Flow is not allocated */ -#define EIPCPTYPE 1001 /* Unknown IPCP type */ +#define ENOTALLOC 1000 /* Flow is not allocated */ +#define EIPCPTYPE 1001 /* Unknown IPCP type */ #define EIRMD 1002 /* Failed to communicate with IRMD */ #define EIPCP 1003 /* Failed to communicate with IPCP */ -#define EIPCPSTATE 1004 /* Target in wrong state */ -#define EFLOWDOWN 1005 /* Flow is down */ +#define EIPCPSTATE 1004 /* Target in wrong state */ +#define EFLOWDOWN 1005 /* Flow is down */ +#define ECRYPT 1006 /* Encryption error */ #endif /* OUROBOROS_ERRNO_H */ diff --git a/include/ouroboros/qos.h b/include/ouroboros/qos.h index 0e4518f4..f5becaa6 100644 --- a/include/ouroboros/qos.h +++ b/include/ouroboros/qos.h @@ -34,6 +34,7 @@ typedef struct qos_spec { uint32_t ber; /* Bit error rate, errors per billion bits */ uint8_t in_order; /* In-order delivery, enables FRCT */ uint32_t max_gap; /* In ms */ + uint32_t cypher_s; /* Cypher strength, 0 = no encryption */ } qosspec_t; static const qosspec_t qos_raw = { @@ -43,7 +44,8 @@ static const qosspec_t qos_raw = { .loss = 1, .ber = 1, .in_order = 0, - .max_gap = UINT32_MAX + .max_gap = UINT32_MAX, + .cypher_s = 0 }; static const qosspec_t qos_raw_no_errors = { @@ -53,7 +55,19 @@ static const qosspec_t qos_raw_no_errors = { .loss = 1, .ber = 0, .in_order = 0, - .max_gap = UINT32_MAX + .max_gap = UINT32_MAX, + .cypher_s = 0 +}; + +static const qosspec_t qos_raw_crypt = { + .delay = UINT32_MAX, + .bandwidth = 0, + .availability = 0, + .loss = 1, + .ber = 0, + .in_order = 0, + .max_gap = UINT32_MAX, + .cypher_s = 256 }; static const qosspec_t qos_best_effort = { @@ -63,7 +77,19 @@ static const qosspec_t qos_best_effort = { .loss = 1, .ber = 0, .in_order = 1, - .max_gap = UINT32_MAX + .max_gap = UINT32_MAX, + .cypher_s = 0 +}; + +static const qosspec_t qos_best_effort_crypt = { + .delay = UINT32_MAX, + .bandwidth = 0, + .availability = 0, + .loss = 1, + .ber = 0, + .in_order = 1, + .max_gap = UINT32_MAX, + .cypher_s = 256 }; static const qosspec_t qos_video = { @@ -73,7 +99,19 @@ static const qosspec_t qos_video = { .loss = 1, .ber = 0, .in_order = 1, - .max_gap = 100 + .max_gap = 100, + .cypher_s = 0 +}; + +static const qosspec_t qos_video_crypt = { + .delay = 100, + .bandwidth = UINT64_MAX, + .availability = 3, + .loss = 1, + .ber = 0, + .in_order = 1, + .max_gap = 100, + .cypher_s = 256 }; static const qosspec_t qos_voice = { @@ -83,7 +121,19 @@ static const qosspec_t qos_voice = { .loss = 1, .ber = 0, .in_order = 1, - .max_gap = 50 + .max_gap = 50, + .cypher_s = 0 +}; + +static const qosspec_t qos_voice_crypt = { + .delay = 50, + .bandwidth = 100000, + .availability = 5, + .loss = 1, + .ber = 0, + .in_order = 1, + .max_gap = 50, + .cypher_s = 256 }; static const qosspec_t qos_data = { @@ -93,7 +143,19 @@ static const qosspec_t qos_data = { .loss = 0, .ber = 0, .in_order = 1, - .max_gap = 2000 + .max_gap = 2000, + .cypher_s = 0 +}; + +static const qosspec_t qos_data_crypt = { + .delay = 1000, + .bandwidth = 0, + .availability = 0, + .loss = 0, + .ber = 0, + .in_order = 1, + .max_gap = 2000, + .cypher_s = 256 }; #endif /* OUROBOROS_QOS_H */ diff --git a/include/ouroboros/sockets.h.in b/include/ouroboros/sockets.h.in index 1e9dc9ca..4f03ca46 100644 --- a/include/ouroboros/sockets.h.in +++ b/include/ouroboros/sockets.h.in @@ -60,8 +60,8 @@ irm_msg_t * send_recv_irm_msg(irm_msg_t * msg); /* qos message conversion needed in different components */ -qosspec_msg_t spec_to_msg(qosspec_t * qs); +qosspec_msg_t spec_to_msg(const qosspec_t * qs); -qosspec_t msg_to_spec(qosspec_msg_t * msg); +qosspec_t msg_to_spec(const qosspec_msg_t * msg); #endif diff --git a/src/ipcpd/eth/eth.c b/src/ipcpd/eth/eth.c index 1a332272..dccfd190 100644 --- a/src/ipcpd/eth/eth.c +++ b/src/ipcpd/eth/eth.c @@ -162,6 +162,7 @@ struct mgmt_msg { uint32_t ber; uint32_t max_gap; uint32_t delay; + uint16_t cypher_s; uint8_t in_order; #if defined (BUILD_ETH_DIX) uint8_t code; @@ -485,6 +486,7 @@ static int eth_ipcp_alloc(const uint8_t * dst_addr, msg->ber = hton32(qs.ber); msg->in_order = qs.in_order; msg->max_gap = hton32(qs.max_gap); + msg->cypher_s = hton16(qs.cypher_s); memcpy(msg + 1, hash, ipcp_dir_hash_len()); @@ -731,6 +733,7 @@ static int eth_ipcp_mgmt_frame(const uint8_t * buf, qs.ber = ntoh32(msg->ber); qs.in_order = msg->in_order; qs.max_gap = ntoh32(msg->max_gap); + qs.cypher_s = hton32(msg->cypher_s); if (shim_data_reg_has(eth_data.shim_data, buf + sizeof(*msg))) { diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/main.c index a1dcb602..f6aa57d1 100644 --- a/src/ipcpd/udp/main.c +++ b/src/ipcpd/udp/main.c @@ -91,6 +91,7 @@ struct mgmt_msg { uint32_t loss; uint32_t ber; uint32_t max_gap; + uint32_t cypher_s; } __attribute__((packed)); struct mgmt_frame { @@ -209,6 +210,7 @@ static int ipcp_udp_port_alloc(int skfd, msg->ber = hton32(qs.ber); msg->in_order = qs.in_order; msg->max_gap = hton32(qs.max_gap); + msg->cypher_s = hton32(qs.cypher_s); memcpy(msg + 1, dst, ipcp_dir_hash_len()); @@ -383,6 +385,8 @@ static int ipcp_udp_mgmt_frame(const uint8_t * buf, qs.ber = ntoh32(msg->ber); qs.in_order = msg->in_order; qs.max_gap = ntoh32(msg->max_gap); + qs.cypher_s = ntoh16(msg->cypher_s); + return ipcp_udp_port_req(&c_saddr, ntoh32(msg->s_eid), (uint8_t *) (msg + 1), qs); case FLOW_REPLY: diff --git a/src/ipcpd/unicast/fa.c b/src/ipcpd/unicast/fa.c index fbcbc6fa..c1cb065f 100644 --- a/src/ipcpd/unicast/fa.c +++ b/src/ipcpd/unicast/fa.c @@ -66,6 +66,7 @@ struct fa_msg { uint32_t loss; uint32_t ber; uint32_t max_gap; + uint16_t cypher_s; } __attribute__((packed)); struct cmd { @@ -217,6 +218,7 @@ static void * fa_handle_packet(void * o) qs.ber = ntoh32(msg->ber); qs.in_order = msg->in_order; qs.max_gap = ntoh32(msg->max_gap); + qs.cypher_s = ntoh16(msg->cypher_s); fd = ipcp_flow_req_arr((uint8_t *) (msg + 1), ipcp_dir_hash_len(), @@ -386,6 +388,7 @@ int fa_alloc(int fd, msg->ber = hton32(qs.ber); msg->in_order = qs.in_order; msg->max_gap = hton32(qs.max_gap); + msg->cypher_s = hton16(qs.cypher_s); memcpy(msg + 1, dst, ipcp_dir_hash_len()); diff --git a/src/lib/config.h.in b/src/lib/config.h.in index 3e5a7b1e..70261cab 100644 --- a/src/lib/config.h.in +++ b/src/lib/config.h.in @@ -24,6 +24,10 @@ #cmakedefine HAVE_LIBGCRYPT #cmakedefine HAVE_OPENSSL +#ifdef HAVE_OPENSSL +#define HAVE_ENCRYPTION +#endif + #define SYS_MAX_FLOWS @SYS_MAX_FLOWS@ #cmakedefine SHM_RBUFF_LOCKLESS diff --git a/src/lib/crypt.c b/src/lib/crypt.c new file mode 100644 index 00000000..94f1b50e --- /dev/null +++ b/src/lib/crypt.c @@ -0,0 +1,490 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2019 + * + * Elliptic curve Diffie-Hellman key exchange and + * AES encryption for flows using OpenSSL + * + * Dimitri Staessens + * Sander Vrijders + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifdef HAVE_OPENSSL + +#include +#include +#include + +#define MSGBUFSZ 2048 +#define IVSZ 16 +#define DH_TIMEO 2 /* seconds */ +/* SYMMKEYSZ defined in dev.c */ + +/* + * Derive the common secret from + * your public key pair (kp) + * the remote public key (pub). + * Store it in a preallocated buffer (s). + */ +static int __openssl_ecdh_derive_secret(EVP_PKEY * kp, + EVP_PKEY * pub, + uint8_t * s) +{ + EVP_PKEY_CTX * ctx; + int ret; + uint8_t * secret; + size_t secret_len; + + ctx = EVP_PKEY_CTX_new(kp, NULL); + if (ctx == NULL) + goto fail_new; + + ret = EVP_PKEY_derive_init(ctx); + if (ret != 1) + goto fail_ctx; + + ret = EVP_PKEY_derive_set_peer(ctx, pub); + if (ret != 1) + goto fail_ctx; + + ret = EVP_PKEY_derive(ctx, NULL, &secret_len); + if (ret != 1) + goto fail_ctx; + + if (secret_len < SYMMKEYSZ) + goto fail_ctx; + + secret = OPENSSL_malloc(secret_len); + if (secret == NULL) + goto fail_ctx; + + ret = EVP_PKEY_derive(ctx, secret, &secret_len); + if (ret != 1) + goto fail_derive; + + /* Hash the secret for use as AES key. */ + mem_hash(HASH_SHA3_256, s, secret, secret_len); + + OPENSSL_free(secret); + EVP_PKEY_CTX_free(ctx); + + return 0; + + fail_derive: + OPENSSL_free(s); + fail_ctx: + EVP_PKEY_CTX_free(ctx); + fail_new: + return -ECRYPT; +} + +static int __openssl_ecdh_gen_key(EVP_PKEY ** kp) +{ + EVP_PKEY_CTX * ctx = NULL; + EVP_PKEY_CTX * kctx = NULL; + EVP_PKEY * params = NULL; + int ret; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (ctx == NULL) + goto fail_new_id; + + ret = EVP_PKEY_paramgen_init(ctx); + if (ret != 1) + goto fail_paramgen; + + ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_X9_62_prime256v1); + if (ret != 1) + goto fail_paramgen; + + ret = EVP_PKEY_paramgen(ctx, ¶ms); + if (ret != 1) + goto fail_paramgen; + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (kctx == NULL) + goto fail_keygen_init; + + ret = EVP_PKEY_keygen_init(kctx); + if (ret != 1) + goto fail_keygen; + + ret = EVP_PKEY_keygen(kctx, kp); + if (ret != 1) + goto fail_keygen; + + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + EVP_PKEY_CTX_free(ctx); + + return 0; + + fail_keygen: + EVP_PKEY_CTX_free(kctx); + fail_keygen_init: + EVP_PKEY_free(params); + fail_paramgen: + EVP_PKEY_CTX_free(ctx); + fail_new_id: + return -ECRYPT; +} + +/* ECDH from the server side. */ +static int openssl_ecdh_srv(int fd, + uint8_t * s) +{ + EVP_PKEY * kp = NULL; + EVP_PKEY * pub = NULL; + uint8_t buf[MSGBUFSZ]; + ssize_t len; + int buf_sz; + uint8_t * pos; + struct timespec timeo = {DH_TIMEO,0}; + + assert(s != NULL); + + (void) fd; + (void) s; + + if (__openssl_ecdh_gen_key(&kp) < 0) + goto fail_gen_key; + + fccntl(fd, FLOWSRCVTIMEO, &timeo); + + len = flow_read(fd, buf, MSGBUFSZ); + if (len < 0) { + fccntl(fd, FLOWSRCVTIMEO, NULL); + goto fail_get_key; + } + + fccntl(fd, FLOWSRCVTIMEO, NULL); + + pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */ + pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) len); + + pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */ + buf_sz = i2d_PUBKEY(kp, &pos); + if (buf_sz < 0) + goto fail_get_key; + + if (flow_write(fd, buf, (size_t) buf_sz) < 0) + goto fail_get_key; + + if (__openssl_ecdh_derive_secret(kp, pub, s) < 0) + goto fail_get_key; + + EVP_PKEY_free(kp); + EVP_PKEY_free(pub); + + return 0; + + fail_get_key: + EVP_PKEY_free(kp); + fail_gen_key: + return -ECRYPT; +} + +/* ECDH from the client side. */ +static int openssl_ecdh_clt(int fd, + uint8_t * s) +{ + EVP_PKEY * kp = NULL; + EVP_PKEY * pub = NULL; + uint8_t buf[MSGBUFSZ]; + int buf_sz; + uint8_t * pos; + ssize_t len; + struct timespec timeo = {DH_TIMEO,0}; + + assert(s != NULL); + + (void) fd; + (void) s; + + if (__openssl_ecdh_gen_key(&kp) < 0) + goto fail_gen_key; + + pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */ + buf_sz = i2d_PUBKEY(kp, &pos); + if (buf_sz < 0) + goto fail_get_key; + + if (flow_write(fd, buf, (size_t) buf_sz) < 0) + goto fail_get_key; + + fccntl(fd, FLOWSRCVTIMEO, &timeo); + + len = flow_read(fd, buf, MSGBUFSZ); + if (len < 0) { + fccntl(fd, FLOWSRCVTIMEO, NULL); + goto fail_get_key; + } + + fccntl(fd, FLOWSRCVTIMEO, NULL); + + pos = buf; /* i2d_PUBKEY increments the pointer, don't use buf! */ + pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) len); + + if (__openssl_ecdh_derive_secret(kp, pub, s) < 0) + goto fail_get_key; + + EVP_PKEY_free(kp); + EVP_PKEY_free(pub); + + return 0; + + fail_get_key: + EVP_PKEY_free(kp); + fail_gen_key: + return -ECRYPT; +} + +/* + * AES encryption calls. If FRCT is disabled, we should generate a + * 128-bit random IV and append it to the packet. If the flow is + * reliable, we could initialize the context once, and consider the + * stream a single encrypted message to avoid initializing the + * encryption context for each packet. + */ + +static int openssl_encrypt(struct flow * f, + struct shm_du_buff * sdb) +{ + uint8_t * out; + uint8_t * in; + uint8_t * head; + uint8_t iv[IVSZ]; + int in_sz; + int out_sz; + int tmp_sz; + int ret; + + in = shm_du_buff_head(sdb); + in_sz = shm_du_buff_tail(sdb) - in; + + if (random_buffer(iv, IVSZ) < 0) + goto fail_iv; + + out = malloc(in_sz + EVP_MAX_BLOCK_LENGTH); + if (out == NULL) + goto fail_iv; + + EVP_CIPHER_CTX_reset(f->ctx); + + ret = EVP_EncryptInit_ex(f->ctx, + EVP_aes_256_cbc(), + NULL, + f->key, + iv); + if (ret != 1) + goto fail_encrypt_init; + + ret = EVP_EncryptUpdate(f->ctx, out, &tmp_sz, in, in_sz); + if (ret != 1) + goto fail_encrypt; + + out_sz = tmp_sz; + ret = EVP_EncryptFinal_ex(f->ctx, out + tmp_sz, &tmp_sz); + if (ret != 1) + goto fail_encrypt; + + out_sz += tmp_sz; + + EVP_CIPHER_CTX_cleanup(f->ctx); + + assert(out_sz >= in_sz); + + head = shm_du_buff_head_alloc(sdb, IVSZ); + if (head == NULL) + goto fail_encrypt; + + if (shm_du_buff_tail_alloc(sdb, out_sz - in_sz) == NULL) + goto fail_tail_alloc; + + memcpy(head, iv, IVSZ); + memcpy(in, out, out_sz); + + free(out); + + return 0; + + fail_tail_alloc: + shm_du_buff_head_release(sdb, IVSZ); + fail_encrypt: + EVP_CIPHER_CTX_cleanup(f->ctx); + fail_encrypt_init: + free(out); + fail_iv: + return -ECRYPT; +} + +static int openssl_decrypt(struct flow * f, + struct shm_du_buff * sdb) +{ + uint8_t * in; + uint8_t * out; + uint8_t iv[IVSZ]; + int ret; + int out_sz; + int in_sz; + int tmp_sz; + + in = shm_du_buff_head_release(sdb, IVSZ); + + memcpy(iv, in, IVSZ); + + in = shm_du_buff_head(sdb); + + in_sz = shm_du_buff_tail(sdb) - shm_du_buff_head(sdb); + + out = malloc(in_sz); + if (out == NULL) + goto fail_malloc; + + EVP_CIPHER_CTX_reset(f->ctx); + + ret = EVP_DecryptInit_ex(f->ctx, + EVP_aes_256_cbc(), + NULL, + f->key, + iv); + if (ret != 1) + goto fail_decrypt_init; + + ret = EVP_DecryptUpdate(f->ctx, out, &tmp_sz, in, in_sz); + if (ret != 1) + goto fail_decrypt; + + out_sz = tmp_sz; + + ret = EVP_DecryptFinal_ex(f->ctx, out + tmp_sz, &tmp_sz); + if (ret != 1) + goto fail_decrypt; + + out_sz += tmp_sz; + + assert(out_sz <= in_sz); + + shm_du_buff_tail_release(sdb, in_sz - out_sz); + + memcpy(in, out, out_sz); + + free(out); + + return 0; + + fail_decrypt: + EVP_CIPHER_CTX_cleanup(f->ctx); + fail_decrypt_init: + free(out); + fail_malloc: + return -ECRYPT; + +} + +static int openssl_crypt_init(struct flow * f) +{ + f->ctx = EVP_CIPHER_CTX_new(); + if (f->ctx == NULL) + goto fail_new; + + return 0; + + fail_new: + return -ECRYPT; +} + +static void openssl_crypt_fini(struct flow * f) +{ + EVP_CIPHER_CTX_free(f->ctx); + f->ctx = NULL; +} + +#endif /* HAVE_OPENSSL */ + +static int crypt_dh_srv(int fd, + uint8_t * s) +{ +#ifdef HAVE_OPENSSL + return openssl_ecdh_clt(fd, s); +#else + (void) fd; + + memset(s, 0, SYMMKEYSZ); + + return -ECRYPT; +#endif +} + +static int crypt_dh_clt(int fd, + uint8_t * s) +{ +#ifdef HAVE_OPENSSL + return openssl_ecdh_srv(fd, s); +#else + (void) fd; + + memset(s, 0, SYMMKEYSZ); + + return 0; +#endif +} + +static int crypt_encrypt(struct flow * f, + struct shm_du_buff * sdb) +{ +#ifdef HAVE_OPENSSL + return openssl_encrypt(f, sdb); +#else + (void) f; + (void) sdb; + + return 0; +#endif +} + +static int crypt_decrypt(struct flow * f, + struct shm_du_buff * sdb) +{ +#ifdef HAVE_OPENSSL + return openssl_decrypt(f, sdb); +#else + (void) f; + (void) sdb; + + return 0; +#endif +} + + +static int crypt_init(struct flow * f) +{ +#ifdef HAVE_OPENSSL + return openssl_crypt_init(f); +#else + (void) f; + + return 0; +#endif +} + +static void crypt_fini(struct flow * f) +{ +#ifdef HAVE_OPENSSL + openssl_crypt_fini(f); +#else + (void) f; +#endif +} diff --git a/src/lib/dev.c b/src/lib/dev.c index 229a1470..1e0a177d 100644 --- a/src/lib/dev.c +++ b/src/lib/dev.c @@ -29,6 +29,7 @@ #include "config.h" #include +#include #include #include #include @@ -59,6 +60,8 @@ #define DONE_PART -2 #define CRCLEN (sizeof(uint32_t)) +#define SECMEMSZ 16384 +#define SYMMKEYSZ 32 struct flow_set { size_t idx; @@ -98,6 +101,9 @@ struct flow { qosspec_t spec; ssize_t part_idx; + void * ctx; + uint8_t key[SYMMKEYSZ]; + pid_t pid; bool snd_timesout; @@ -237,6 +243,8 @@ static void flow_clear(int fd) ai.flows[fd].pid = -1; } +#include "crypt.c" + static void flow_fini(int fd) { assert(fd >= 0 && fd < SYS_MAX_FLOWS); @@ -266,6 +274,9 @@ static void flow_fini(int fd) if (ai.flows[fd].frcti != NULL) frcti_destroy(ai.flows[fd].frcti); + if (ai.flows[fd].spec.cypher_s > 0) + crypt_fini(&ai.flows[fd]); + flow_clear(fd); } @@ -286,15 +297,15 @@ static int flow_init(int flow_id, ai.flows[fd].rx_rb = shm_rbuff_open(ai.pid, flow_id); if (ai.flows[fd].rx_rb == NULL) - goto fail; + goto fail_rx_rb; ai.flows[fd].tx_rb = shm_rbuff_open(pid, flow_id); if (ai.flows[fd].tx_rb == NULL) - goto fail; + goto fail_tx_rb; ai.flows[fd].set = shm_flow_set_open(pid); if (ai.flows[fd].set == NULL) - goto fail; + goto fail_set; ai.flows[fd].flow_id = flow_id; ai.flows[fd].oflags = FLOWFDEFAULT; @@ -302,6 +313,9 @@ static int flow_init(int flow_id, ai.flows[fd].part_idx = NO_PART; ai.flows[fd].spec = qs; + if (qs.cypher_s > 0 && crypt_init(&ai.flows[fd]) < 0) + goto fail_crypt; + ai.ports[flow_id].fd = fd; port_set_state(&ai.ports[flow_id], PORT_ID_ASSIGNED); @@ -310,8 +324,14 @@ static int flow_init(int flow_id, return fd; - fail: - flow_fini(fd); + fail_crypt: + shm_flow_set_close(ai.flows[fd].set); + fail_set: + shm_rbuff_close(ai.flows[fd].tx_rb); + fail_tx_rb: + shm_rbuff_close(ai.flows[fd].rx_rb); + fail_rx_rb: + bmp_release(ai.fds, fd); fail_fds: pthread_rwlock_unlock(&ai.lock); return err; @@ -344,12 +364,11 @@ static void init(int argc, ai.pid = getpid(); #ifdef HAVE_LIBGCRYPT - if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) { + if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { if (!gcry_check_version(GCRYPT_VERSION)) goto fail_fds; - /* Needs to be enabled when we add encryption. */ - gcry_control (GCRYCTL_DISABLE_SECMEM, 0); - gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } #endif ai.fds = bmp_create(PROG_MAX_FLOWS - PROG_RES_FDS, PROG_RES_FDS); @@ -550,8 +569,8 @@ int flow_accept(qosspec_t * qs, if (ai.flows[fd].spec.in_order != 0) { ai.flows[fd].frcti = frcti_create(fd); if (ai.flows[fd].frcti == NULL) { - flow_fini(fd); pthread_rwlock_unlock(&ai.lock); + flow_dealloc(fd); return -ENOMEM; } } @@ -561,6 +580,31 @@ int flow_accept(qosspec_t * qs, pthread_rwlock_unlock(&ai.lock); + /* TODO: piggyback public keys at flow allocation. */ + if (ai.flows[fd].spec.cypher_s > 0) { + uint8_t key[SYMMKEYSZ]; + uint16_t tmp; + + pthread_rwlock_wrlock(&ai.lock); + + tmp = ai.flows[fd].spec.cypher_s; + ai.flows[fd].spec.cypher_s = 0; + + pthread_rwlock_unlock(&ai.lock); + + if (crypt_dh_srv(fd, key) < 0) { + flow_dealloc(fd); + return -ECRYPT; + } + + pthread_rwlock_wrlock(&ai.lock); + + ai.flows[fd].spec.cypher_s = tmp; + memcpy(ai.flows[fd].key, key, SYMMKEYSZ); + + pthread_rwlock_unlock(&ai.lock); + } + return fd; } @@ -579,9 +623,9 @@ static int __flow_alloc(const char * dst, qs->ber = 1; #endif if (join) - msg.code = IRM_MSG_CODE__IRM_FLOW_JOIN; + msg.code = IRM_MSG_CODE__IRM_FLOW_JOIN; else - msg.code = IRM_MSG_CODE__IRM_FLOW_ALLOC; + msg.code = IRM_MSG_CODE__IRM_FLOW_ALLOC; msg.dst = (char *) dst; msg.has_pid = true; msg.pid = ai.pid; @@ -605,7 +649,7 @@ static int __flow_alloc(const char * dst, } if (recv_msg->result != 0) { - int res = recv_msg->result; + int res = recv_msg->result; irm_msg__free_unpacked(recv_msg, NULL); return res; } @@ -630,14 +674,39 @@ static int __flow_alloc(const char * dst, if (ai.flows[fd].spec.in_order != 0) { ai.flows[fd].frcti = frcti_create(fd); if (ai.flows[fd].frcti == NULL) { - flow_fini(fd); pthread_rwlock_unlock(&ai.lock); + flow_dealloc(fd); return -ENOMEM; } } pthread_rwlock_unlock(&ai.lock); + /* TODO: piggyback public keys at flow allocation. */ + if (!join && ai.flows[fd].spec.cypher_s > 0) { + uint8_t key[SYMMKEYSZ]; + uint16_t tmp; + + pthread_rwlock_wrlock(&ai.lock); + + tmp = ai.flows[fd].spec.cypher_s; + ai.flows[fd].spec.cypher_s = 0; + + pthread_rwlock_unlock(&ai.lock); + + if (crypt_dh_clt(fd, key) < 0) { + flow_dealloc(fd); + return -ECRYPT; + } + + pthread_rwlock_wrlock(&ai.lock); + + ai.flows[fd].spec.cypher_s = tmp; + memcpy(ai.flows[fd].key, key, SYMMKEYSZ); + + pthread_rwlock_unlock(&ai.lock); + } + return fd; } @@ -931,6 +1000,15 @@ ssize_t flow_write(int fd, return -ENOMEM; } + pthread_rwlock_wrlock(&ai.lock); + if (flow->spec.cypher_s > 0) + if (crypt_encrypt(flow, sdb) < 0) { + pthread_rwlock_unlock(&ai.lock); + shm_rdrbuff_remove(ai.rdrb, idx); + return -ENOMEM; + } + pthread_rwlock_unlock(&ai.lock); + if (flow->spec.ber == 0 && add_crc(sdb) != 0) { shm_rdrbuff_remove(ai.rdrb, idx); return -ENOMEM; @@ -1009,9 +1087,22 @@ ssize_t flow_read(int fd, shm_rbuff_read_b(rb, abstime); if (idx < 0) return idx; + sdb = shm_rdrbuff_get(ai.rdrb, idx); - if (flow->spec.ber == 0 && chk_crc(sdb) != 0) + if (flow->spec.ber == 0 && chk_crc(sdb) != 0) { + shm_rdrbuff_remove(ai.rdrb, idx); continue; + } + + pthread_rwlock_wrlock(&ai.lock); + if (flow->spec.cypher_s > 0) + if (crypt_decrypt(flow, sdb) < 0) { + pthread_rwlock_unlock(&ai.lock); + shm_rdrbuff_remove(ai.rdrb, + idx); + return -ENOMEM; + } + pthread_rwlock_unlock(&ai.lock); } while (frcti_rcv(flow->frcti, sdb) != 0); } } diff --git a/src/lib/qosspec.proto b/src/lib/qosspec.proto index c9e5a6e6..0cbcd41a 100644 --- a/src/lib/qosspec.proto +++ b/src/lib/qosspec.proto @@ -30,4 +30,5 @@ message qosspec_msg { required uint32 ber = 5; /* Bit error rate, ppb */ required uint32 in_order = 6; /* In-order delivery */ required uint32 max_gap = 7; /* In ms */ + required uint32 cypher_s = 8; /* Crypto strength in bits */ }; diff --git a/src/lib/sockets.c b/src/lib/sockets.c index b08bae8e..347e9244 100644 --- a/src/lib/sockets.c +++ b/src/lib/sockets.c @@ -166,7 +166,7 @@ char * ipcp_sock_path(pid_t pid) return full_name; } -qosspec_msg_t spec_to_msg(qosspec_t * qs) +qosspec_msg_t spec_to_msg(const qosspec_t * qs) { qosspec_t spec; qosspec_msg_t msg = QOSSPEC_MSG__INIT; @@ -180,11 +180,12 @@ qosspec_msg_t spec_to_msg(qosspec_t * qs) msg.ber = spec.ber; msg.in_order = spec.in_order; msg.max_gap = spec.max_gap; + msg.cypher_s = spec.cypher_s; return msg; } -qosspec_t msg_to_spec(qosspec_msg_t * msg) +qosspec_t msg_to_spec(const qosspec_msg_t * msg) { qosspec_t spec; @@ -197,6 +198,7 @@ qosspec_t msg_to_spec(qosspec_msg_t * msg) spec.ber = msg->ber; spec.in_order = msg->in_order; spec.max_gap = msg->max_gap; + spec.cypher_s = msg->cypher_s; return spec; } -- cgit v1.2.3