diff --git a/libknet/handle.c b/libknet/handle.c index 0748041e..da807244 100644 --- a/libknet/handle.c +++ b/libknet/handle.c @@ -1,429 +1,427 @@ #include "config.h" #include #include #include #include #include #include #include "libknet-private.h" #include "nsscrypto.h" #define KNET_MAX_EVENTS 8 #define KNET_PING_TIMERES 200000 -#define KNET_DATABUFSIZE 131072 /* 128k */ -#define KNET_PINGBUFSIZE sizeof(struct knet_frame) static void *_handle_tap_to_links_thread(void *data); static void *_handle_recv_from_links_thread(void *data); static void *_handle_heartbt_thread(void *data); knet_handle_t knet_handle_new(const struct knet_handle_cfg *knet_handle_cfg) { knet_handle_t knet_h; struct epoll_event ev; /* * validate incoming config request */ if (knet_handle_cfg == NULL) { errno = EINVAL; return NULL; } if (knet_handle_cfg->fd <= 0) { errno = EINVAL; return NULL; } if ((knet_h = malloc(sizeof(struct knet_handle))) == NULL) return NULL; memset(knet_h, 0, sizeof(struct knet_handle)); if (crypto_init(knet_h, knet_handle_cfg) < 0) goto exit_fail1; if ((knet_h->tap_to_links_buf = malloc(KNET_DATABUFSIZE))== NULL) goto exit_fail2; memset(knet_h->tap_to_links_buf, 0, KNET_DATABUFSIZE); if ((knet_h->recv_from_links_buf = malloc(KNET_DATABUFSIZE))== NULL) goto exit_fail3; memset(knet_h->recv_from_links_buf, 0, KNET_DATABUFSIZE); if ((knet_h->pingbuf = malloc(KNET_PINGBUFSIZE))== NULL) goto exit_fail4; memset(knet_h->pingbuf, 0, KNET_PINGBUFSIZE); if (pthread_rwlock_init(&knet_h->list_rwlock, NULL) != 0) goto exit_fail5; knet_h->sockfd = knet_handle_cfg->fd; knet_h->tap_to_links_epollfd = epoll_create(KNET_MAX_EVENTS); knet_h->recv_from_links_epollfd = epoll_create(KNET_MAX_EVENTS); knet_h->node_id = knet_handle_cfg->node_id; if ((knet_h->tap_to_links_epollfd < 0) || (knet_h->recv_from_links_epollfd < 0)) goto exit_fail6; if ((_fdset_cloexec(knet_h->tap_to_links_epollfd) != 0) || (_fdset_cloexec(knet_h->recv_from_links_epollfd != 0))) goto exit_fail6; memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = knet_h->sockfd; if (epoll_ctl(knet_h->tap_to_links_epollfd, EPOLL_CTL_ADD, knet_h->sockfd, &ev) != 0) goto exit_fail6; if (pthread_create(&knet_h->tap_to_links_thread, 0, _handle_tap_to_links_thread, (void *) knet_h) != 0) goto exit_fail6; if (pthread_create(&knet_h->recv_from_links_thread, 0, _handle_recv_from_links_thread, (void *) knet_h) != 0) goto exit_fail7; if (pthread_create(&knet_h->heartbt_thread, 0, _handle_heartbt_thread, (void *) knet_h) != 0) goto exit_fail8; return knet_h; exit_fail8: pthread_cancel(knet_h->recv_from_links_thread); exit_fail7: pthread_cancel(knet_h->tap_to_links_thread); exit_fail6: if (knet_h->tap_to_links_epollfd >= 0) close(knet_h->tap_to_links_epollfd); if (knet_h->recv_from_links_epollfd >= 0) close(knet_h->recv_from_links_epollfd); pthread_rwlock_destroy(&knet_h->list_rwlock); exit_fail5: free(knet_h->pingbuf); exit_fail4: free(knet_h->recv_from_links_buf); exit_fail3: free(knet_h->tap_to_links_buf); exit_fail2: crypto_fini(knet_h); exit_fail1: free(knet_h); return NULL; } int knet_handle_free(knet_handle_t knet_h) { void *retval; if ((knet_h->host_head != NULL) || (knet_h->listener_head != NULL)) goto exit_busy; pthread_cancel(knet_h->heartbt_thread); pthread_join(knet_h->heartbt_thread, &retval); if (retval != PTHREAD_CANCELED) goto exit_busy; pthread_cancel(knet_h->tap_to_links_thread); pthread_join(knet_h->tap_to_links_thread, &retval); if (retval != PTHREAD_CANCELED) goto exit_busy; pthread_cancel(knet_h->recv_from_links_thread); pthread_join(knet_h->recv_from_links_thread, &retval); if (retval != PTHREAD_CANCELED) goto exit_busy; close(knet_h->tap_to_links_epollfd); close(knet_h->recv_from_links_epollfd); pthread_rwlock_destroy(&knet_h->list_rwlock); free(knet_h->tap_to_links_buf); free(knet_h->recv_from_links_buf); free(knet_h->pingbuf); crypto_fini(knet_h); free(knet_h); return 0; exit_busy: errno = EBUSY; return -EBUSY; } void knet_handle_setfwd(knet_handle_t knet_h, int enabled) { knet_h->enabled = (enabled == 1) ? 1 : 0; } void knet_link_timeout(struct knet_link *lnk, time_t interval, time_t timeout, int precision) { lnk->ping_interval = interval * 1000; /* microseconds */ lnk->pong_timeout = timeout * 1000; /* microseconds */ lnk->latency_fix = precision; lnk->latency_exp = precision - \ ((lnk->ping_interval * precision) / 8000000); } static void _handle_tap_to_links(knet_handle_t knet_h) { int j; ssize_t len, snt; struct knet_host *i; len = read(knet_h->sockfd, knet_h->tap_to_links_buf->kf_data, KNET_DATABUFSIZE - KNET_FRAME_SIZE); if (len == 0) { /* TODO: disconnection, should never happen! */ return; } len += KNET_FRAME_SIZE; if (knet_h->enabled != 1) /* data forward is disabled */ return; /* TODO: packet inspection */ if (pthread_rwlock_rdlock(&knet_h->list_rwlock) != 0) return; /* TODO: add encryption */ for (i = knet_h->host_head; i != NULL; i = i->next) { for (j = 0; j < KNET_MAX_LINK; j++) { if (i->link[j].enabled != 1) /* link is disabled */ continue; snt = sendto(i->link[j].sock, knet_h->tap_to_links_buf, len, MSG_DONTWAIT, (struct sockaddr *) &i->link[j].address, sizeof(struct sockaddr_storage)); if ((i->active == 0) && (snt == len)) break; } } pthread_rwlock_unlock(&knet_h->list_rwlock); } static void _handle_recv_from_links(knet_handle_t knet_h, int sockfd) { ssize_t len; struct sockaddr_storage address; socklen_t addrlen; struct knet_host *src_host; struct knet_link *src_link; unsigned long long latency_last; if (pthread_rwlock_rdlock(&knet_h->list_rwlock) != 0) return; addrlen = sizeof(struct sockaddr_storage); len = recvfrom(sockfd, knet_h->recv_from_links_buf, KNET_DATABUFSIZE, MSG_DONTWAIT, (struct sockaddr *) &address, &addrlen); /* TODO add decryption */ if (len < (KNET_FRAME_SIZE + 1)) goto exit_unlock; if (ntohl(knet_h->recv_from_links_buf->kf_magic) != KNET_FRAME_MAGIC) goto exit_unlock; if (knet_h->recv_from_links_buf->kf_version != KNET_FRAME_VERSION) goto exit_unlock; src_host = NULL; src_link = NULL; if ((knet_h->recv_from_links_buf->kf_type & KNET_FRAME_PMSK) != 0) { knet_h->recv_from_links_buf->kf_node = ntohs(knet_h->recv_from_links_buf->kf_node); src_host = knet_h->host_index[knet_h->recv_from_links_buf->kf_node]; if (src_host == NULL) /* host not found */ goto exit_unlock; src_link = src_host->link + (knet_h->recv_from_links_buf->kf_link % KNET_MAX_LINK); } switch (knet_h->recv_from_links_buf->kf_type) { case KNET_FRAME_DATA: if (knet_h->enabled != 1) /* data forward is disabled */ break; write(knet_h->sockfd, knet_h->recv_from_links_buf->kf_data, len - KNET_FRAME_SIZE); break; case KNET_FRAME_PING: knet_h->recv_from_links_buf->kf_type = KNET_FRAME_PONG; knet_h->recv_from_links_buf->kf_node = htons(knet_h->node_id); sendto(src_link->sock, knet_h->recv_from_links_buf, len, MSG_DONTWAIT, (struct sockaddr *) &src_link->address, sizeof(struct sockaddr_storage)); break; case KNET_FRAME_PONG: clock_gettime(CLOCK_MONOTONIC, &src_link->pong_last); timespec_diff(knet_h->recv_from_links_buf->kf_time, src_link->pong_last, &latency_last); src_link->latency = ((src_link->latency * src_link->latency_exp) + ((latency_last / 1000llu) * (src_link->latency_fix - src_link->latency_exp))) / src_link->latency_fix; if (src_link->latency < src_link->pong_timeout) src_link->enabled = 1; break; default: goto exit_unlock; } exit_unlock: pthread_rwlock_unlock(&knet_h->list_rwlock); } static void _handle_check_each(knet_handle_t knet_h, struct knet_link *dst_link) { int len; struct timespec clock_now, pong_last; unsigned long long diff_ping; /* caching last pong to avoid race conditions */ pong_last = dst_link->pong_last; if (clock_gettime(CLOCK_MONOTONIC, &clock_now) != 0) return; timespec_diff(dst_link->ping_last, clock_now, &diff_ping); if (diff_ping >= (dst_link->ping_interval * 1000llu)) { knet_h->pingbuf->kf_time = clock_now; knet_h->pingbuf->kf_link = dst_link->link_id; /* TODO add encryption */ len = sendto(dst_link->sock, knet_h->pingbuf, KNET_PINGBUFSIZE, MSG_DONTWAIT, (struct sockaddr *) &dst_link->address, sizeof(struct sockaddr_storage)); if (len == KNET_PINGBUFSIZE) dst_link->ping_last = clock_now; } if (dst_link->enabled == 1) { timespec_diff(pong_last, clock_now, &diff_ping); if (diff_ping >= (dst_link->pong_timeout * 1000llu)) dst_link->enabled = 0; /* TODO: might need write lock */ } } static void *_handle_heartbt_thread(void *data) { int j; knet_handle_t knet_h; struct knet_host *i; knet_h = (knet_handle_t) data; /* preparing ping buffer */ knet_h->pingbuf->kf_magic = htonl(KNET_FRAME_MAGIC); knet_h->pingbuf->kf_version = KNET_FRAME_VERSION; knet_h->pingbuf->kf_type = KNET_FRAME_PING; knet_h->pingbuf->kf_node = htons(knet_h->node_id); while (1) { usleep(KNET_PING_TIMERES); if (pthread_rwlock_rdlock(&knet_h->list_rwlock) != 0) continue; for (i = knet_h->host_head; i != NULL; i = i->next) { for (j = 0; j < KNET_MAX_LINK; j++) { if (i->link[j].ready != 1) continue; _handle_check_each(knet_h, &i->link[j]); } } pthread_rwlock_unlock(&knet_h->list_rwlock); } return NULL; } static void *_handle_tap_to_links_thread(void *data) { knet_handle_t knet_h; struct epoll_event events[KNET_MAX_EVENTS]; knet_h = (knet_handle_t) data; /* preparing data buffer */ knet_h->tap_to_links_buf->kf_magic = htonl(KNET_FRAME_MAGIC); knet_h->tap_to_links_buf->kf_version = KNET_FRAME_VERSION; knet_h->tap_to_links_buf->kf_type = KNET_FRAME_DATA; while (1) { if (epoll_wait(knet_h->tap_to_links_epollfd, events, KNET_MAX_EVENTS, -1) >= 1) _handle_tap_to_links(knet_h); } return NULL; } static void *_handle_recv_from_links_thread(void *data) { int i, nev; knet_handle_t knet_h; struct epoll_event events[KNET_MAX_EVENTS]; knet_h = (knet_handle_t) data; /* preparing data buffer */ knet_h->recv_from_links_buf->kf_magic = htonl(KNET_FRAME_MAGIC); knet_h->recv_from_links_buf->kf_version = KNET_FRAME_VERSION; while (1) { nev = epoll_wait(knet_h->recv_from_links_epollfd, events, KNET_MAX_EVENTS, -1); for (i = 0; i < nev; i++) { _handle_recv_from_links(knet_h, events[i].data.fd); } } return NULL; } diff --git a/libknet/libknet-private.h b/libknet/libknet-private.h index 74971960..eb3a8465 100644 --- a/libknet/libknet-private.h +++ b/libknet/libknet-private.h @@ -1,41 +1,45 @@ #ifndef __KNETHANDLE_H__ #define __KNETHANDLE_H__ /* NOTE: you shouldn't need to include this header normally, it is provided for * testing purpose only. */ #include "libknet.h" +#define KNET_DATABUFSIZE 131072 /* 128k */ +#define KNET_PINGBUFSIZE sizeof(struct knet_frame) + #define timespec_diff(start, end, diff) \ do { \ if (end.tv_sec > start.tv_sec) \ *(diff) = ((end.tv_sec - start.tv_sec) * 1000000000llu) \ + end.tv_nsec - start.tv_nsec; \ else \ *(diff) = end.tv_nsec - start.tv_nsec; \ } while (0); struct knet_handle { int sockfd; int tap_to_links_epollfd; int recv_from_links_epollfd; uint16_t node_id; unsigned int enabled:1; struct knet_host *host_head; struct knet_host *host_index[KNET_MAX_HOST]; struct knet_listener *listener_head; struct knet_frame *tap_to_links_buf; char *tap_to_links_buf_crypt; struct knet_frame *recv_from_links_buf; struct knet_frame *pingbuf; + char *pingbuf_crypt; pthread_t tap_to_links_thread; pthread_t recv_from_links_thread; pthread_t heartbt_thread; pthread_rwlock_t list_rwlock; struct crypto_instance *crypto_instance; }; int _fdset_cloexec(int fd); #endif diff --git a/libknet/nsscrypto.c b/libknet/nsscrypto.c index 2e5aca54..d66b028b 100644 --- a/libknet/nsscrypto.c +++ b/libknet/nsscrypto.c @@ -1,699 +1,634 @@ #include "config.h" #include #include #include #include #include #include #include "nsscrypto.h" #include "libknet-private.h" -/* - * define onwire crypto header - */ - -struct crypto_config_header { - uint8_t crypto_cipher_type; - uint8_t crypto_hash_type; - uint8_t __pad0; - uint8_t __pad1; -} __attribute__((packed)); - /* * crypto definitions and conversion tables */ #define SALT_SIZE 16 +#define KNET_DATABUFSIZE_CRYPT KNET_DATABUFSIZE * 2 enum crypto_crypt_t { CRYPTO_CIPHER_TYPE_NONE = 0, CRYPTO_CIPHER_TYPE_AES256 = 1 }; CK_MECHANISM_TYPE cipher_to_nss[] = { 0, /* CRYPTO_CIPHER_TYPE_NONE */ CKM_AES_CBC_PAD /* CRYPTO_CIPHER_TYPE_AES256 */ }; size_t cipher_key_len[] = { 0, /* CRYPTO_CIPHER_TYPE_NONE */ 32, /* CRYPTO_CIPHER_TYPE_AES256 */ }; size_t cypher_block_len[] = { 0, /* CRYPTO_CIPHER_TYPE_NONE */ AES_BLOCK_SIZE /* CRYPTO_CIPHER_TYPE_AES256 */ }; /* * hash definitions and conversion tables */ enum crypto_hash_t { CRYPTO_HASH_TYPE_NONE = 0, CRYPTO_HASH_TYPE_MD5 = 1, CRYPTO_HASH_TYPE_SHA1 = 2, CRYPTO_HASH_TYPE_SHA256 = 3, CRYPTO_HASH_TYPE_SHA384 = 4, CRYPTO_HASH_TYPE_SHA512 = 5 }; CK_MECHANISM_TYPE hash_to_nss[] = { 0, /* CRYPTO_HASH_TYPE_NONE */ CKM_MD5_HMAC, /* CRYPTO_HASH_TYPE_MD5 */ CKM_SHA_1_HMAC, /* CRYPTO_HASH_TYPE_SHA1 */ CKM_SHA256_HMAC, /* CRYPTO_HASH_TYPE_SHA256 */ CKM_SHA384_HMAC, /* CRYPTO_HASH_TYPE_SHA384 */ CKM_SHA512_HMAC /* CRYPTO_HASH_TYPE_SHA512 */ }; size_t hash_len[] = { 0, /* CRYPTO_HASH_TYPE_NONE */ MD5_LENGTH, /* CRYPTO_HASH_TYPE_MD5 */ SHA1_LENGTH, /* CRYPTO_HASH_TYPE_SHA1 */ SHA256_LENGTH, /* CRYPTO_HASH_TYPE_SHA256 */ SHA384_LENGTH, /* CRYPTO_HASH_TYPE_SHA384 */ SHA512_LENGTH /* CRYPTO_HASH_TYPE_SHA512 */ }; size_t hash_block_len[] = { 0, /* CRYPTO_HASH_TYPE_NONE */ MD5_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_MD5 */ SHA1_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_SHA1 */ SHA256_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_SHA256 */ SHA384_BLOCK_LENGTH, /* CRYPTO_HASH_TYPE_SHA384 */ SHA512_BLOCK_LENGTH /* CRYPTO_HASH_TYPE_SHA512 */ }; struct crypto_instance { PK11SymKey *nss_sym_key; PK11SymKey *nss_sym_key_sign; unsigned char *private_key; unsigned int private_key_len; enum crypto_crypt_t crypto_cipher_type; enum crypto_hash_t crypto_hash_type; - - unsigned int crypto_header_size; }; /* * crypt/decrypt functions */ static int string_to_crypto_cipher_type(const char* crypto_cipher_type) { if (strcmp(crypto_cipher_type, "none") == 0) { return CRYPTO_CIPHER_TYPE_NONE; } else if (strcmp(crypto_cipher_type, "aes256") == 0) { return CRYPTO_CIPHER_TYPE_AES256; } return -1; } static int init_nss_crypto(struct crypto_instance *instance) { PK11SlotInfo* crypt_slot = NULL; SECItem crypt_param; if (!cipher_to_nss[instance->crypto_cipher_type]) { return 0; } crypt_param.type = siBuffer; crypt_param.data = instance->private_key; crypt_param.len = cipher_key_len[instance->crypto_cipher_type]; crypt_slot = PK11_GetBestSlot(cipher_to_nss[instance->crypto_cipher_type], NULL); if (crypt_slot == NULL) { // log_printf(instance->log_level_security, "Unable to find security slot (err %d)", // PR_GetError()); return -1; } instance->nss_sym_key = PK11_ImportSymKey(crypt_slot, cipher_to_nss[instance->crypto_cipher_type], PK11_OriginUnwrap, CKA_ENCRYPT|CKA_DECRYPT, &crypt_param, NULL); if (instance->nss_sym_key == NULL) { // log_printf(instance->log_level_security, "Failure to import key into NSS (err %d)", // PR_GetError()); return -1; } PK11_FreeSlot(crypt_slot); return 0; } static int encrypt_nss( struct crypto_instance *instance, const unsigned char *buf_in, const size_t buf_in_len, unsigned char *buf_out, size_t *buf_out_len) { PK11Context* crypt_context = NULL; SECItem crypt_param; SECItem *nss_sec_param = NULL; int tmp1_outlen = 0; unsigned int tmp2_outlen = 0; unsigned char *salt = buf_out; unsigned char *data = buf_out + SALT_SIZE; int err = -1; if (!cipher_to_nss[instance->crypto_cipher_type]) { memcpy(buf_out, buf_in, buf_in_len); *buf_out_len = buf_in_len; return 0; } if (PK11_GenerateRandom (salt, SALT_SIZE) != SECSuccess) { //log_printf(instance->log_level_security, // "Failure to generate a random number %d", // PR_GetError()); goto out; } crypt_param.type = siBuffer; crypt_param.data = salt; crypt_param.len = SALT_SIZE; nss_sec_param = PK11_ParamFromIV (cipher_to_nss[instance->crypto_cipher_type], &crypt_param); if (nss_sec_param == NULL) { //log_printf(instance->log_level_security, // "Failure to set up PKCS11 param (err %d)", // PR_GetError()); goto out; } /* * Create cipher context for encryption */ crypt_context = PK11_CreateContextBySymKey (cipher_to_nss[instance->crypto_cipher_type], CKA_ENCRYPT, instance->nss_sym_key, nss_sec_param); if (!crypt_context) { //log_printf(instance->log_level_security, // "PK11_CreateContext failed (encrypt) crypt_type=%d (err %d)", // (int)cipher_to_nss[instance->crypto_cipher_type], // PR_GetError()); goto out; } if (PK11_CipherOp(crypt_context, data, &tmp1_outlen, - KNET_FRAME_SIZE - instance->crypto_header_size, + KNET_DATABUFSIZE_CRYPT, (unsigned char *)buf_in, buf_in_len) != SECSuccess) { //log_printf(instance->log_level_security, // "PK11_CipherOp failed (encrypt) crypt_type=%d (err %d)", // (int)cipher_to_nss[instance->crypto_cipher_type], // PR_GetError()); goto out; } if (PK11_DigestFinal(crypt_context, data + tmp1_outlen, - &tmp2_outlen, KNET_FRAME_SIZE - tmp1_outlen) != SECSuccess) { + &tmp2_outlen, KNET_DATABUFSIZE_CRYPT - tmp1_outlen) != SECSuccess) { //log_printf(instance->log_level_security, // "PK11_DigestFinal failed (encrypt) crypt_type=%d (err %d)", // (int)cipher_to_nss[instance->crypto_cipher_type], // PR_GetError()); goto out; } *buf_out_len = tmp1_outlen + tmp2_outlen + SALT_SIZE; err = 0; out: if (crypt_context) { PK11_DestroyContext(crypt_context, PR_TRUE); } if (nss_sec_param) { SECITEM_FreeItem(nss_sec_param, PR_TRUE); } return err; } static int decrypt_nss ( struct crypto_instance *instance, unsigned char *buf, int *buf_len) { PK11Context* decrypt_context = NULL; SECItem decrypt_param; int tmp1_outlen = 0; unsigned int tmp2_outlen = 0; unsigned char *salt = buf; unsigned char *data = salt + SALT_SIZE; int datalen = *buf_len - SALT_SIZE; - unsigned char outbuf[KNET_FRAME_SIZE]; + unsigned char outbuf[KNET_DATABUFSIZE_CRYPT]; int outbuf_len; int err = -1; if (!cipher_to_nss[instance->crypto_cipher_type]) { return 0; } /* Create cipher context for decryption */ decrypt_param.type = siBuffer; decrypt_param.data = salt; decrypt_param.len = SALT_SIZE; decrypt_context = PK11_CreateContextBySymKey(cipher_to_nss[instance->crypto_cipher_type], CKA_DECRYPT, instance->nss_sym_key, &decrypt_param); if (!decrypt_context) { //log_printf(instance->log_level_security, // "PK11_CreateContext (decrypt) failed (err %d)", // PR_GetError()); goto out; } if (PK11_CipherOp(decrypt_context, outbuf, &tmp1_outlen, sizeof(outbuf), data, datalen) != SECSuccess) { //log_printf(instance->log_level_security, // "PK11_CipherOp (decrypt) failed (err %d)", // PR_GetError()); goto out; } if (PK11_DigestFinal(decrypt_context, outbuf + tmp1_outlen, &tmp2_outlen, sizeof(outbuf) - tmp1_outlen) != SECSuccess) { //log_printf(instance->log_level_security, // "PK11_DigestFinal (decrypt) failed (err %d)", // PR_GetError()); goto out; } outbuf_len = tmp1_outlen + tmp2_outlen; memset(buf, 0, *buf_len); memcpy(buf, outbuf, outbuf_len); *buf_len = outbuf_len; err = 0; out: if (decrypt_context) { PK11_DestroyContext(decrypt_context, PR_TRUE); } return err; } /* * hash/hmac/digest functions */ static int string_to_crypto_hash_type(const char* crypto_hash_type) { if (strcmp(crypto_hash_type, "none") == 0) { return CRYPTO_HASH_TYPE_NONE; } else if (strcmp(crypto_hash_type, "md5") == 0) { return CRYPTO_HASH_TYPE_MD5; } else if (strcmp(crypto_hash_type, "sha1") == 0) { return CRYPTO_HASH_TYPE_SHA1; } else if (strcmp(crypto_hash_type, "sha256") == 0) { return CRYPTO_HASH_TYPE_SHA256; } else if (strcmp(crypto_hash_type, "sha384") == 0) { return CRYPTO_HASH_TYPE_SHA384; } else if (strcmp(crypto_hash_type, "sha512") == 0) { return CRYPTO_HASH_TYPE_SHA512; } return -1; } static int init_nss_hash(struct crypto_instance *instance) { PK11SlotInfo* hash_slot = NULL; SECItem hash_param; if (!hash_to_nss[instance->crypto_hash_type]) { return 0; } hash_param.type = siBuffer; hash_param.data = 0; hash_param.len = 0; hash_slot = PK11_GetBestSlot(hash_to_nss[instance->crypto_hash_type], NULL); if (hash_slot == NULL) { //log_printf(instance->log_level_security, "Unable to find security slot (err %d)", // PR_GetError()); return -1; } instance->nss_sym_key_sign = PK11_ImportSymKey(hash_slot, hash_to_nss[instance->crypto_hash_type], PK11_OriginUnwrap, CKA_SIGN, &hash_param, NULL); if (instance->nss_sym_key_sign == NULL) { //log_printf(instance->log_level_security, "Failure to import key into NSS (err %d)", // PR_GetError()); return -1; } PK11_FreeSlot(hash_slot); return 0; } static int calculate_nss_hash( struct crypto_instance *instance, const unsigned char *buf, const size_t buf_len, unsigned char *hash) { PK11Context* hash_context = NULL; SECItem hash_param; unsigned int hash_tmp_outlen = 0; unsigned char hash_block[hash_block_len[instance->crypto_hash_type]]; int err = -1; /* Now do the digest */ hash_param.type = siBuffer; hash_param.data = 0; hash_param.len = 0; hash_context = PK11_CreateContextBySymKey(hash_to_nss[instance->crypto_hash_type], CKA_SIGN, instance->nss_sym_key_sign, &hash_param); if (!hash_context) { //log_printf(instance->log_level_security, // "PK11_CreateContext failed (hash) hash_type=%d (err %d)", // (int)hash_to_nss[instance->crypto_hash_type], // PR_GetError()); goto out; } if (PK11_DigestBegin(hash_context) != SECSuccess) { //log_printf(instance->log_level_security, // "PK11_DigestBegin failed (hash) hash_type=%d (err %d)", // (int)hash_to_nss[instance->crypto_hash_type], // PR_GetError()); goto out; } if (PK11_DigestOp(hash_context, buf, buf_len) != SECSuccess) { //log_printf(instance->log_level_security, // "PK11_DigestOp failed (hash) hash_type=%d (err %d)", // (int)hash_to_nss[instance->crypto_hash_type], // PR_GetError()); goto out; } if (PK11_DigestFinal(hash_context, hash_block, &hash_tmp_outlen, hash_block_len[instance->crypto_hash_type]) != SECSuccess) { //log_printf(instance->log_level_security, // "PK11_DigestFinale failed (hash) hash_type=%d (err %d)", // (int)hash_to_nss[instance->crypto_hash_type], // PR_GetError()); goto out; } memcpy(hash, hash_block, hash_len[instance->crypto_hash_type]); err = 0; out: if (hash_context) { PK11_DestroyContext(hash_context, PR_TRUE); } return err; } /* * global/glue nss functions */ static int init_nss_db(struct crypto_instance *instance) { if ((!cipher_to_nss[instance->crypto_cipher_type]) && (!hash_to_nss[instance->crypto_hash_type])) { return 0; } if (NSS_NoDB_Init(".") != SECSuccess) { //log_printf(instance->log_level_security, "NSS DB initialization failed (err %d)", // PR_GetError()); return -1; } return 0; } static int init_nss(struct crypto_instance *instance) { //log_printf(instance->log_level_notice, // "Initializing transmit/receive security (NSS) crypto: %s hash: %s", // crypto_cipher_type, crypto_hash_type); if (init_nss_db(instance) < 0) { return -1; } if (init_nss_crypto(instance) < 0) { return -1; } if (init_nss_hash(instance) < 0) { return -1; } return 0; } static int encrypt_and_sign_nss ( struct crypto_instance *instance, const unsigned char *buf_in, const size_t buf_in_len, unsigned char *buf_out, size_t *buf_out_len) { unsigned char *hash = buf_out; unsigned char *data = hash + hash_len[instance->crypto_hash_type]; if (encrypt_nss(instance, buf_in, buf_in_len, data, buf_out_len) < 0) { return -1; } if (hash_to_nss[instance->crypto_hash_type]) { if (calculate_nss_hash(instance, data, *buf_out_len, hash) < 0) { return -1; } *buf_out_len = *buf_out_len + hash_len[instance->crypto_hash_type]; } return 0; } static int authenticate_and_decrypt_nss ( struct crypto_instance *instance, unsigned char *buf, int *buf_len) { if (hash_to_nss[instance->crypto_hash_type]) { unsigned char tmp_hash[hash_len[instance->crypto_hash_type]]; unsigned char *hash = buf; unsigned char *data = hash + hash_len[instance->crypto_hash_type]; int datalen = *buf_len - hash_len[instance->crypto_hash_type]; if (calculate_nss_hash(instance, data, datalen, tmp_hash) < 0) { return -1; } if (memcmp(tmp_hash, hash, hash_len[instance->crypto_hash_type]) != 0) { //log_printf(instance->log_level_error, "Digest does not match"); return -1; } memmove(buf, data, datalen); *buf_len = datalen; } if (decrypt_nss(instance, buf, buf_len) < 0) { return -1; } return 0; } /* * exported API */ -size_t crypto_sec_header_size( - const char *crypto_cipher_type, - const char *crypto_hash_type) -{ - int crypto_cipher = string_to_crypto_cipher_type(crypto_cipher_type); - int crypto_hash = string_to_crypto_hash_type(crypto_hash_type); - size_t hdr_size = 0; - - hdr_size = sizeof(struct crypto_config_header); - - if (crypto_hash) { - hdr_size += hash_len[crypto_hash]; - } - - if (crypto_cipher) { - hdr_size += SALT_SIZE; - hdr_size += cypher_block_len[crypto_cipher]; - } - - return hdr_size; -} int crypto_encrypt_and_sign ( struct crypto_instance *instance, const unsigned char *buf_in, const size_t buf_in_len, unsigned char *buf_out, size_t *buf_out_len) { - struct crypto_config_header *cch = (struct crypto_config_header *)buf_out; - int err; - - cch->crypto_cipher_type = instance->crypto_cipher_type; - cch->crypto_hash_type = instance->crypto_hash_type; - cch->__pad0 = 0; - cch->__pad1 = 0; - - buf_out += sizeof(struct crypto_config_header); - - err = encrypt_and_sign_nss(instance, + return encrypt_and_sign_nss(instance, buf_in, buf_in_len, buf_out, buf_out_len); - - *buf_out_len = *buf_out_len + sizeof(struct crypto_config_header); - - return err; } int crypto_authenticate_and_decrypt (struct crypto_instance *instance, unsigned char *buf, int *buf_len) { - struct crypto_config_header *cch = (struct crypto_config_header *)buf; - - /* - * decode crypto config of incoming packets - */ - - if (cch->crypto_cipher_type != instance->crypto_cipher_type) { - //log_printf(instance->log_level_security, - // "Incoming packet has different crypto type. Rejecting"); - return -1; - } - - if (cch->crypto_hash_type != instance->crypto_hash_type) { - //log_printf(instance->log_level_security, - // "Incoming packet has different hash type. Rejecting"); - return -1; - } - - if ((cch->__pad0 != 0) || (cch->__pad1 != 0)) { - //log_printf(instance->log_level_security, - // "Incoming packet appears to have features not supported by this version of corosync. Rejecting"); - return -1; - } - - /* - * invalidate config header and kill it - */ - cch = NULL; - *buf_len -= sizeof(struct crypto_config_header); - memmove(buf, buf + sizeof(struct crypto_config_header), *buf_len); - return authenticate_and_decrypt_nss(instance, buf, buf_len); } int crypto_init( knet_handle_t knet_h, const struct knet_handle_cfg *knet_handle_cfg) { knet_h->crypto_instance = malloc(sizeof(struct crypto_instance)); if (!knet_h->crypto_instance) { return -1; } memset(knet_h->crypto_instance, 0, sizeof(struct crypto_instance)); if (!knet_handle_cfg->crypto_cipher_type) { goto out_err; } knet_h->crypto_instance->crypto_cipher_type = string_to_crypto_cipher_type(knet_handle_cfg->crypto_cipher_type); if (knet_h->crypto_instance->crypto_cipher_type < 0) { goto out_err; } if (!knet_handle_cfg->crypto_hash_type) { goto out_err; } knet_h->crypto_instance->crypto_hash_type = string_to_crypto_hash_type(knet_handle_cfg->crypto_hash_type); if (knet_h->crypto_instance->crypto_hash_type < 0) { goto out_err; } knet_h->crypto_instance->private_key = knet_handle_cfg->private_key; knet_h->crypto_instance->private_key_len = knet_handle_cfg->private_key_len; if ((knet_h->crypto_instance->crypto_cipher_type > 0) || (knet_h->crypto_instance->crypto_hash_type > 0)) { if ((!knet_h->crypto_instance->private_key) || (knet_h->crypto_instance->private_key_len < 1024)) { goto out_err; } + + knet_h->tap_to_links_buf_crypt = malloc(KNET_DATABUFSIZE_CRYPT); + if (!knet_h->tap_to_links_buf_crypt) + goto out_err; + + knet_h->pingbuf_crypt = malloc(KNET_DATABUFSIZE_CRYPT); + if (!knet_h->pingbuf_crypt) + goto out_err; + + } else { + knet_h->tap_to_links_buf_crypt = (char *)knet_h->tap_to_links_buf; + knet_h->pingbuf_crypt = (char *)knet_h->pingbuf; } knet_h->crypto_instance->private_key = knet_handle_cfg->private_key; knet_h->crypto_instance->private_key_len = knet_handle_cfg->private_key_len; - knet_h->crypto_instance->crypto_header_size = crypto_sec_header_size(knet_handle_cfg->crypto_cipher_type, knet_handle_cfg->crypto_hash_type); - if (init_nss(knet_h->crypto_instance) < 0) { goto out_err; } return 0; out_err: - free(knet_h->crypto_instance); - knet_h->crypto_instance = NULL; + crypto_fini(knet_h); return -1; } void crypto_fini( knet_handle_t knet_h) { if (knet_h->crypto_instance) { if (knet_h->crypto_instance->nss_sym_key) PK11_FreeSymKey(knet_h->crypto_instance->nss_sym_key); if (knet_h->crypto_instance->nss_sym_key_sign) PK11_FreeSymKey(knet_h->crypto_instance->nss_sym_key_sign); + if (knet_h->pingbuf_crypt != (char *)knet_h->pingbuf) + free(knet_h->pingbuf_crypt); + if (knet_h->tap_to_links_buf_crypt != (char *)knet_h->tap_to_links_buf) + free(knet_h->tap_to_links_buf_crypt); free(knet_h->crypto_instance); knet_h->crypto_instance = NULL; } - + return; } diff --git a/libknet/nsscrypto.h b/libknet/nsscrypto.h index 124dbe52..38cd5711 100644 --- a/libknet/nsscrypto.h +++ b/libknet/nsscrypto.h @@ -1,32 +1,28 @@ #ifndef NSSCRYPTO_H_DEFINED #define NSSCRYPTO_H_DEFINED #include #include "libknet.h" struct crypto_instance; -size_t crypto_sec_header_size( - const char *crypto_cipher_type, - const char *crypto_hash_type); - int crypto_authenticate_and_decrypt ( struct crypto_instance *instance, unsigned char *buf, int *buf_len); int crypto_encrypt_and_sign ( struct crypto_instance *instance, const unsigned char *buf_in, const size_t buf_in_len, unsigned char *buf_out, size_t *buf_out_len); int crypto_init( knet_handle_t knet_h, const struct knet_handle_cfg *knet_handle_cfg); void crypto_fini( knet_handle_t knet_h); #endif /* NSSCRYPTO_H_DEFINED */