#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#ifdef USE_OPENSSL
#include <openssl/bio.h>
#include <openssl/ssl.h>
+#endif
+#ifdef USE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#endif
#include "util.h"
#include "net.h"
+/* Crypto types */
+struct crypto_t {
+#ifdef USE_OPENSSL
+ SSL_CTX *ctx;
+ SSL *ssl;
+ BIO *in;
+ BIO *out;
+#endif
+#ifdef USE_GNUTLS
+ gnutls_session_t tls;
+#endif
+};
+
+/* Local data */
+#ifdef USE_GNUTLS
+static gnutls_certificate_credentials_t xcred;
+#endif
+
/* Local functions */
static int flush(net_t *net)
{
+#ifdef USE_OPENSSL
static char buf[NET_BUFFER];
+#endif
int len;
/* State machine */
}
}
if (net->state == NET_ENCRYPTED) {
+#ifdef USE_OPENSSL
if (net->out_len > 0) {
debug("net: flush crypto");
- len = SSL_write(net->ssl,
+ len = SSL_write(net->crypto->ssl,
&net->out_buf[net->out_pos],
net->out_len-net->out_pos);
if (len > 0)
while ((len = BIO_read(net->out, buf, sizeof(buf))) > 0)
send(net->poll.fd, buf, len, 0);
+#endif
+#ifdef USE_GNUTLS
+ if (net->out_len > 0) {
+ len = gnutls_record_send(net->crypto->tls,
+ &net->out_buf[net->out_pos],
+ net->out_len-net->out_pos);
+ if (len > 0)
+ net->out_pos += len;
+ if (net->out_pos == net->out_len) {
+ net->out_pos = 0;
+ net->out_len = 0;
+ }
+ }
+#endif
}
return 1;
}
/* State machine */
+ if (net->state == NET_CLOSED) {
+ done = 1;
+ }
if (net->state == NET_CONNECT) {
debug("net: connect");
net->state = NET_READY;
}
if (net->state == NET_HANDSHAKE) {
debug("net: handshake");
+#ifdef USE_OPENSSL
len = recv(net->poll.fd, buf, sizeof(buf), 0);
if (len < 0)
done = 1;
if (len > 0)
BIO_write(net->in, buf, len);
- SSL_do_handshake(net->ssl);
+ SSL_do_handshake(net->crypto->ssl);
while ((len = BIO_read(net->out, buf, sizeof(buf))) > 0)
send(net->poll.fd, buf, len, 0);
- if (SSL_is_init_finished(net->ssl))
+ if (SSL_is_init_finished(net->crypto->ssl))
+ net->state = NET_ENCRYPTED;
+#endif
+#ifdef USE_GNUTLS
+ err = gnutls_handshake(net->crypto->tls);
+ if (err == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) {
+ gnutls_datum_t out;
+ int type = gnutls_certificate_type_get(net->crypto->tls);
+ unsigned status = gnutls_session_get_verify_cert_status(net->crypto->tls);
+ gnutls_certificate_verification_status_print(status, type, &out, 0);
+ debug("Cert verify failed: %s", out.data);
+ gnutls_free(out.data);
+ net_close(net);
+ } else if (err < 0) {
+ debug("Handshake failed: %s", gnutls_strerror(err));
+ net_close(net);
+ } else {
+ char *desc = gnutls_session_get_desc(net->crypto->tls);
+ debug("Session info: %s", desc);
+ gnutls_free(desc);
net->state = NET_ENCRYPTED;
+ }
+#endif
flush(net);
}
if (net->state == NET_ENCRYPTED) {
debug("net: encrypted");
+#ifdef USE_OPENSSL
len = recv(net->poll.fd, buf, sizeof(buf), 0);
if (len < 0)
done = 1;
if (len == 0)
net_close(net);
if (len > 0)
- BIO_write(net->in, buf, len);
+ BIO_write(net->crypto->in, buf, len);
- while ((len = SSL_read(net->ssl, buf, sizeof(buf))) > 0)
+ while ((len = SSL_read(net->crypto->ssl, buf, sizeof(buf))) > 0)
+ net->recv(net->data, buf, len);
+#endif
+#ifdef USE_GNUTLS
+ len = gnutls_record_recv(net->crypto->tls, buf, sizeof(buf));
+ if (len < 0)
+ done = 1;
+ if (len == 0)
+ net_close(net);
+ if (len > 0)
net->recv(net->data, buf, len);
+#endif
}
}
}
void net_init(void)
{
+#ifdef USE_OPENSSL
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
+#endif
+#ifdef USE_GNUTLS
+ gnutls_global_init();
+ gnutls_certificate_allocate_credentials(&xcred);
+ gnutls_certificate_set_x509_trust_file(xcred,
+ "/etc/ssl/certs/ca-certificates.crt",
+ GNUTLS_X509_FMT_PEM);
+#endif
}
void net_open(net_t *net, const char *host, int port)
poll_add(&net->poll, sock, on_poll, net);
}
-void net_encrypt(net_t *net)
+void net_encrypt(net_t *net, int flags)
{
debug("net: encrypt");
- net->ctx = SSL_CTX_new(TLSv1_2_client_method());
- net->ssl = SSL_new(net->ctx);
-
- net->in = BIO_new(BIO_s_mem());
- net->out = BIO_new(BIO_s_mem());
-
- BIO_set_mem_eof_return(net->in, -1);
- BIO_set_mem_eof_return(net->out, -1);
-
- SSL_set_bio(net->ssl, net->in, net->out);
- SSL_set_connect_state(net->ssl);
+#if defined(USE_OPENSSL)
+ net->crypto = new0(crypto_t);
+ net->crypto->ctx = SSL_CTX_new(TLSv1_2_client_method());
+ net->crypto->ssl = SSL_new(net->ctx);
+
+ net->crypto->in = BIO_new(BIO_s_mem());
+ net->crypto->out = BIO_new(BIO_s_mem());
+
+ BIO_set_mem_eof_return(net->crypto->in, -1);
+ BIO_set_mem_eof_return(net->crypto->out, -1);
+
+ SSL_set_bio(net->crypto->ssl, net->crypto->in, net->crypto->out);
+ SSL_set_connect_state(net->crypto->ssl);
+#elif defined(USE_GNUTLS)
+ net->crypto = new0(crypto_t);
+ gnutls_init(&net->crypto->tls, GNUTLS_CLIENT);
+ gnutls_set_default_priority(net->crypto->tls);
+ gnutls_server_name_set(net->crypto->tls, GNUTLS_NAME_DNS,
+ net->host, strlen(net->host));
+ gnutls_credentials_set(net->crypto->tls, GNUTLS_CRD_CERTIFICATE, xcred);
+ gnutls_handshake_set_timeout(net->crypto->tls, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+ gnutls_transport_set_int(net->crypto->tls, net->poll.fd);
+ if (!(flags & NET_NOVERIFY))
+ gnutls_session_set_verify_cert(net->crypto->tls, net->host, 0);
+#else
+ error("Encryption is not supported");
+#endif
net->state = NET_ENCRYPT;
}
/* Networking Constants */
#define NET_BUFFER 1024
-/* OpenSSL Types */
-typedef struct ssl_ctx_st SSL_CTX;
-typedef struct ssl_st SSL;
-typedef struct bio_st BIO;
-
/* Networking Callbacks */
typedef void (*send_t)(void *data);
typedef void (*recv_t)(void *data, char *buf, int len);
typedef void (*err_t)(void *data, int errno);
/* Networking Types */
+typedef struct crypto_t crypto_t;
+
+typedef enum {
+ NET_NOVERIFY = 0x01,
+} encrypt_t;
+
typedef enum {
NET_CLOSED,
NET_CONNECT,
} nstate_t;
typedef struct {
- char *host;
- int port;
- send_t send;
- recv_t recv;
- err_t err;
- void *data;
-
- SSL_CTX *ctx;
- SSL *ssl;
- BIO *in;
- BIO *out;
+ char *host;
+ int port;
+ send_t send;
+ recv_t recv;
+ err_t err;
+ void *data;
- poll_t poll;
- nstate_t state;
+ poll_t poll;
+ nstate_t state;
+ crypto_t *crypto;
- char out_buf[NET_BUFFER];
- int out_pos;
- int out_len;
+ char out_buf[NET_BUFFER];
+ int out_pos;
+ int out_len;
} net_t;
/* Networking functions */
/* Connection functions */
void net_init(void);
void net_open(net_t *net, const char *host, int port);
-void net_encrypt(net_t *net);
+void net_encrypt(net_t *net, int flags);
int net_send(net_t *net, const char *buf, int len);
int net_print(net_t *net, const char *fmt, ...);
void net_close(net_t *net);