aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <holger@moiji-mobile.com>2016-09-06 11:38:56 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2016-09-08 16:17:01 +0200
commit9ea4da4bbbf90396b9b0694c0bf91712afce44f4 (patch)
tree2671c874efb167e6950d2fdf552ac1532b93b243
parent064d2242881acff7a0d7f4877179559454163a57 (diff)
server: Introduce tls mode for the server
Using tls priority of NORMAL:+ANON-ECDH:+ANON-DH already allows a client to connect to a server and protect the data using tls. Generate the dh params on load (and do that for the client right now as well) but that will go away soon. Change-Id: Ifa2ad24c0a631573c259a3bf94b91a946ad9ec9d
-rw-r--r--include/osmo-pcap/osmo_pcap_server.h15
-rw-r--r--include/osmo-pcap/osmo_tls.h10
-rw-r--r--src/Makefile.am6
-rw-r--r--src/osmo_server_main.c3
-rw-r--r--src/osmo_server_network.c74
-rw-r--r--src/osmo_tls.c114
6 files changed, 215 insertions, 7 deletions
diff --git a/include/osmo-pcap/osmo_pcap_server.h b/include/osmo-pcap/osmo_pcap_server.h
index 6facbe3..89c3df2 100644
--- a/include/osmo-pcap/osmo_pcap_server.h
+++ b/include/osmo-pcap/osmo_pcap_server.h
@@ -24,6 +24,7 @@
#define OSMO_PCAP_SERVER_H
#include "wireformat.h"
+#include "osmo_tls.h"
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
@@ -35,6 +36,7 @@
#include <pcap.h>
+#include <stdbool.h>
#include <time.h>
struct rate_ctr_group;
@@ -94,6 +96,12 @@ struct osmo_pcap_conn {
/* statistics */
struct rate_ctr_group *ctrg;
+
+ /* tls */
+ bool tls_use;
+ bool direct_read;
+ size_t tls_limit_read;
+ struct osmo_tls_session tls_session;
};
struct osmo_pcap_server {
@@ -109,6 +117,13 @@ struct osmo_pcap_server {
void *zmq_ctx;
void *zmq_publ;
+ /* tls base */
+ unsigned tls_log_level;
+ char *tls_priority;
+ char *tls_capath;
+ char *tls_server_cert;
+ char *tls_server_key;
+
char *base_path;
off_t max_size;
diff --git a/include/osmo-pcap/osmo_tls.h b/include/osmo-pcap/osmo_tls.h
index bfc813e..54fea4d 100644
--- a/include/osmo-pcap/osmo_tls.h
+++ b/include/osmo-pcap/osmo_tls.h
@@ -24,10 +24,13 @@
#include <gnutls/abstract.h>
#include <stdbool.h>
+#include <stdint.h>
struct osmo_fd;
struct osmo_wqueue;
struct osmo_pcap_client;
+struct osmo_pcap_conn;
+struct osmo_pcap_server;
struct osmo_tls_session {
bool in_use;
@@ -38,6 +41,8 @@ struct osmo_tls_session {
/* any credentials */
bool anon_alloc;
gnutls_anon_client_credentials_t anon_cred;
+ bool anon_serv_alloc;
+ gnutls_anon_server_credentials_t anon_serv_cred;
/* a x509 cert credential */
bool cert_alloc;
@@ -53,6 +58,7 @@ struct osmo_tls_session {
struct osmo_wqueue *wqueue;
+ int (*read)(struct osmo_tls_session *session);
void (*error)(struct osmo_tls_session *session);
void (*handshake_done)(struct osmo_tls_session *session);
};
@@ -60,6 +66,10 @@ struct osmo_tls_session {
void osmo_tls_init(void);
bool osmo_tls_init_client_session(struct osmo_pcap_client *client);
+
+bool osmo_tls_init_server_session(struct osmo_pcap_conn *conn, struct osmo_pcap_server *server);
void osmo_tls_release(struct osmo_tls_session *);
int osmo_tls_client_bfd_cb(struct osmo_fd *fd, unsigned int what);
+
+size_t osmo_tls_pending(struct osmo_tls_session *session);
diff --git a/src/Makefile.am b/src/Makefile.am
index 83409db..0532acf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,5 +10,7 @@ osmo_pcap_client_LDADD = $(PCAP_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) $(LIBGNUTLS_LIBS)
osmo_pcap_server_SOURCES = osmo_server_main.c osmo_common.c \
- osmo_server_vty.c osmo_server_network.c
-osmo_pcap_server_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBZMQ_LIBS)
+ osmo_server_vty.c osmo_server_network.c \
+ osmo_tls.c
+osmo_pcap_server_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBZMQ_LIBS) \
+ $(LIBGNUTLS_LIBS)
diff --git a/src/osmo_server_main.c b/src/osmo_server_main.c
index bb94ec4..27fb519 100644
--- a/src/osmo_server_main.c
+++ b/src/osmo_server_main.c
@@ -22,6 +22,7 @@
#include <osmo-pcap/common.h>
#include <osmo-pcap/osmo_pcap_server.h>
+#include <osmo-pcap/osmo_tls.h>
#include <osmocom/core/application.h>
#include <osmocom/core/rate_ctr.h>
@@ -216,6 +217,8 @@ int main(int argc, char **argv)
osmo_init_ignore_signals();
signal(SIGHUP, &signal_handler);
+ osmo_tls_init();
+
rc = telnet_init(tall_bsc_ctx, NULL, 4241);
if (rc < 0) {
LOGP(DCLIENT, LOGL_ERROR, "Failed to bind telnet interface\n");
diff --git a/src/osmo_server_network.c b/src/osmo_server_network.c
index 52abb2b..1b7addc 100644
--- a/src/osmo_server_network.c
+++ b/src/osmo_server_network.c
@@ -132,6 +132,7 @@ static void close_connection(struct osmo_pcap_conn *conn)
if (conn->rem_wq.bfd.fd >= 0) {
close(conn->rem_wq.bfd.fd);
conn->rem_wq.bfd.fd = -1;
+ osmo_tls_release(&conn->tls_session);
osmo_fd_unregister(&conn->rem_wq.bfd);
}
@@ -319,9 +320,19 @@ struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *server,
return conn;
}
+static int do_read_tls(struct osmo_pcap_conn *conn, void *buf, size_t want_size)
+{
+ size_t size = want_size;
+ if (conn->tls_limit_read && size > conn->tls_limit_read)
+ size = conn->tls_limit_read;
+ return gnutls_record_recv(conn->tls_session.session, buf, size);
+}
+
static int do_read(struct osmo_pcap_conn *conn, void *buf, size_t size)
{
- return read(conn->rem_wq.bfd.fd, buf, size);
+ if (conn->direct_read)
+ return read(conn->rem_wq.bfd.fd, buf, size);
+ return do_read_tls(conn, buf, size);
}
static int read_cb_initial(struct osmo_pcap_conn *conn)
@@ -425,6 +436,42 @@ static int read_cb(struct osmo_fd *fd)
return 0;
}
+static void tls_error_cb(struct osmo_tls_session *session)
+{
+ struct osmo_pcap_conn *conn;
+ conn = container_of(session, struct osmo_pcap_conn, tls_session);
+ close_connection(conn);
+}
+
+static int tls_read_cb(struct osmo_tls_session *session)
+{
+ struct osmo_pcap_conn *conn;
+ size_t pend;
+ int rc;
+
+ conn = container_of(session, struct osmo_pcap_conn, tls_session);
+ conn->tls_limit_read = 0;
+ rc = dispatch_read(conn);
+ if (rc <= 0)
+ return rc;
+
+ /**
+ * This is a weakness of a single select approach and the
+ * buffered reading here. We need to read everything as
+ * otherwise we do not receive a ready-read. But at the
+ * same time don't read more than is buffered! So cap what
+ * can be read right now.
+ */
+ while ((pend = osmo_tls_pending(session)) > 0) {
+ conn->tls_limit_read = pend;
+ rc = dispatch_read(conn);
+ if (rc <= 0)
+ return rc;
+ }
+
+ return 1;
+}
+
static void new_connection(struct osmo_pcap_server *server,
struct osmo_pcap_conn *client, int new_fd)
{
@@ -441,11 +488,24 @@ static void new_connection(struct osmo_pcap_server *server,
rate_ctr_inc(&client->ctrg->ctr[PEER_CTR_CONNECT]);
- client->rem_wq.bfd.data = client;
- client->rem_wq.bfd.when = BSC_FD_READ;
- client->rem_wq.read_cb = read_cb;
client->state = STATE_INITIAL;
client->pend = sizeof(*client->data);
+
+ if (client->tls_use) {
+ if (!osmo_tls_init_server_session(client, server)) {
+ close_connection(client);
+ return;
+ }
+ client->tls_session.error = tls_error_cb;
+ client->tls_session.read = tls_read_cb;
+ client->direct_read = false;
+ } else {
+ client->rem_wq.bfd.cb = osmo_wqueue_bfd_cb;
+ client->rem_wq.bfd.data = client;
+ client->rem_wq.bfd.when = BSC_FD_READ;
+ client->rem_wq.read_cb = read_cb;
+ client->direct_read = true;
+ }
}
static int accept_cb(struct osmo_fd *fd, unsigned int when)
@@ -478,6 +538,12 @@ static int accept_cb(struct osmo_fd *fd, unsigned int when)
}
rate_ctr_inc(&server->ctrg->ctr[SERVER_CTR_NOCLIENT]);
+
+ /*
+ * TODO: In the future start with a tls handshake and see if we know
+ * this client.
+ */
+
LOGP(DSERVER, LOGL_ERROR,
"Failed to find client for %s\n", inet_ntoa(addr.sin_addr));
close(new_fd);
diff --git a/src/osmo_tls.c b/src/osmo_tls.c
index ae957e6..c42d242 100644
--- a/src/osmo_tls.c
+++ b/src/osmo_tls.c
@@ -21,6 +21,7 @@
#include <osmo-pcap/osmo_tls.h>
#include <osmo-pcap/osmo_pcap_client.h>
+#include <osmo-pcap/osmo_pcap_server.h>
#include <osmo-pcap/common.h>
#include <osmocom/core/write_queue.h>
@@ -34,6 +35,16 @@
exit(1); \
}
+static gnutls_dh_params_t dh_params;
+static int generate_dh_params (void)
+{
+ unsigned int bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
+ GNUTLS_SEC_PARAM_HIGH);
+
+ LOGP(DTLS, LOGL_NOTICE, "Going to create DH params for %d bits\n", bits);
+ gnutls_dh_params_init (&dh_params);
+ return gnutls_dh_params_generate2 (dh_params, bits);
+}
static int cert_callback(gnutls_session_t tls_session,
const gnutls_datum_t * req_ca_rdn, int nreqs,
@@ -94,6 +105,8 @@ void osmo_tls_init(void)
rc = gnutls_global_init();
CHECK_RC(rc, "init failed");
gnutls_global_set_log_function(tls_log_func);
+ rc = generate_dh_params();
+ CHECK_RC(rc, "dh params failed");
}
static int need_handshake(struct osmo_tls_session *tls_session)
@@ -110,7 +123,8 @@ static int need_handshake(struct osmo_tls_session *tls_session)
tls_session->wqueue->bfd.when = BSC_FD_READ;
tls_session->need_handshake = false;
release_keys(tls_session);
- tls_session->handshake_done(tls_session);
+ if (tls_session->handshake_done)
+ tls_session->handshake_done(tls_session);
} else if (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED) {
LOGP(DTLS, LOGL_DEBUG, "rc=%d will wait for writable again.\n", rc);
} else if (gnutls_error_is_fatal(rc)) {
@@ -128,6 +142,9 @@ static int tls_read(struct osmo_tls_session *sess)
char buf[1024];
int rc;
+ if (sess->read)
+ return sess->read(sess);
+
memset(buf, 0, sizeof(buf));
rc = gnutls_record_recv(sess->session, buf, sizeof(buf) - 1);
return rc;
@@ -238,6 +255,99 @@ static int load_keys(struct osmo_pcap_client *client)
return 0;
}
+size_t osmo_tls_pending(struct osmo_tls_session *sess)
+{
+ return gnutls_record_check_pending(sess->session);
+}
+
+bool osmo_tls_init_server_session(struct osmo_pcap_conn *conn,
+ struct osmo_pcap_server *server)
+{
+ struct osmo_tls_session *sess = &conn->tls_session;
+ struct osmo_wqueue *wq = &conn->rem_wq;
+ int rc;
+
+ gnutls_global_set_log_level(server->tls_log_level);
+
+ memset(sess, 0, sizeof(*sess));
+ sess->in_use = sess->anon_alloc = sess->cert_alloc = false;
+ rc = gnutls_init(&sess->session, GNUTLS_SERVER | GNUTLS_NONBLOCK);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "gnutls_init failed with rc=%d\n", rc);
+ return false;
+ }
+ gnutls_session_set_ptr(sess->session, sess);
+ sess->in_use = true;
+
+ /* use default or string */
+ if (server->tls_priority) {
+ const char *err;
+ rc = gnutls_priority_set_direct(sess->session, server->tls_priority, &err);
+ } else {
+ rc = gnutls_set_default_priority(sess->session);
+ }
+
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "def prio failed with rc=%d\n", rc);
+ osmo_tls_release(sess);
+ return false;
+ }
+
+ /* allow username/password operation */
+ rc = gnutls_anon_allocate_server_credentials(&sess->anon_serv_cred);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to allocate anon cred rc=%d\n", rc);
+ osmo_tls_release(sess);
+ return false;
+ }
+ sess->anon_serv_alloc = true;
+
+ /* x509 certificate handling */
+ rc = gnutls_certificate_allocate_credentials(&sess->cert_cred);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to allocate x509 cred rc=%d\n", rc);
+ osmo_tls_release(sess);
+ return false;
+ }
+ sess->cert_alloc = true;
+
+ /* set the credentials now */
+#warning "Anon?"
+ gnutls_anon_set_server_dh_params (sess->anon_serv_cred, dh_params);
+ gnutls_credentials_set(sess->session, GNUTLS_CRD_ANON, sess->anon_serv_cred);
+ gnutls_credentials_set(sess->session, GNUTLS_CRD_CERTIFICATE, sess->cert_cred);
+
+ if (server->tls_capath) {
+ rc = gnutls_certificate_set_x509_trust_file(
+ sess->cert_cred, server->tls_capath, GNUTLS_X509_FMT_PEM);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to load capath from path=%s rc=%d\n",
+ server->tls_capath, rc);
+ osmo_tls_release(sess);
+ return false;
+ }
+ }
+
+#if 0
+ if (load_keys(client) != 0) {
+ osmo_tls_release(sess);
+ return false;
+ }
+#endif
+
+ #warning "TODO client certificates"
+
+ gnutls_transport_set_int(sess->session, wq->bfd.fd);
+ gnutls_handshake_set_timeout(sess->session,
+ GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+ wq->bfd.cb = osmo_tls_client_bfd_cb;
+ wq->bfd.data = sess;
+ wq->bfd.when = BSC_FD_READ | BSC_FD_WRITE;
+ sess->need_handshake = true;
+ sess->wqueue = wq;
+ return true;
+}
+
bool osmo_tls_init_client_session(struct osmo_pcap_client *client)
{
struct osmo_tls_session *sess = &client->tls_session;
@@ -345,6 +455,8 @@ void osmo_tls_release(struct osmo_tls_session *session)
if (session->anon_alloc)
gnutls_anon_free_client_credentials(session->anon_cred);
+ if (session->anon_serv_alloc)
+ gnutls_anon_free_server_credentials(session->anon_serv_cred);
if (session->cert_alloc)
gnutls_certificate_free_credentials(session->cert_cred);
session->in_use = false;