aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <holger@moiji-mobile.com>2016-09-08 16:32:36 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2016-09-08 16:32:36 +0200
commit0381276993737708b061905959ab3e57523631fd (patch)
tree0ba5f3cfd0fb0adbabb1131dbf7cd4d9591e9c28
parent0b4b824887db23fb3b922286c3229be75393a92d (diff)
parent22acd211f1d1f0a8b3f6a0ab77fc93273821913d (diff)
Merge branch 'feature/tls'
Add TLS support to the client and server. What is known working is support of anonymous mode with generated DH params. Mildly tested by hand over localhost.
-rw-r--r--.travis.yml2
-rw-r--r--TLS_TODO16
-rw-r--r--configure.ac1
-rw-r--r--contrib/osmo-pcap-client-tls.cfg16
-rw-r--r--contrib/osmo-pcap-server-tls.cfg27
-rw-r--r--doc/tls.txt76
-rw-r--r--include/osmo-pcap/Makefile.am2
-rw-r--r--include/osmo-pcap/common.h1
-rw-r--r--include/osmo-pcap/osmo_pcap_client.h18
-rw-r--r--include/osmo-pcap/osmo_pcap_server.h26
-rw-r--r--include/osmo-pcap/osmo_tls.h80
-rw-r--r--osmoappdesc.py4
-rw-r--r--src/Makefile.am13
-rw-r--r--src/osmo_client_main.c4
-rw-r--r--src/osmo_client_network.c41
-rw-r--r--src/osmo_client_vty.c194
-rw-r--r--src/osmo_common.c6
-rw-r--r--src/osmo_server_main.c5
-rw-r--r--src/osmo_server_network.c170
-rw-r--r--src/osmo_server_vty.c303
-rw-r--r--src/osmo_tls.c558
21 files changed, 1501 insertions, 62 deletions
diff --git a/.travis.yml b/.travis.yml
index f602bfd..ebec2d9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,7 @@ language: c
os:
- linux
sudo: required
+dist: trusty
addons:
apt:
packages:
@@ -18,6 +19,7 @@ addons:
- libpcsclite-dev
- libpcap-dev
- libzmq3-dev
+ - libgnutls28-dev
script:
- contrib/travis.sh
diff --git a/TLS_TODO b/TLS_TODO
new file mode 100644
index 0000000..501f2a1
--- /dev/null
+++ b/TLS_TODO
@@ -0,0 +1,16 @@
+= Goals
+
+Secure communication between client and server. The captured
+data might go through different interfaces than the one used
+for capturing.
+
+Instead of rolling a custom protocol the idea is to adopt TLS
+1.2 to achieve client authentication and ciphering.
+
+Neither the client nor the server should block during the key
+exchange. Most TLS implementations do block and this is a problem
+for a single threaded server. Ideally the same library is used
+in the client and the server.
+
+In practice libraries might block during the handshake and this
+is a big deal for the server (other clients block).
diff --git a/configure.ac b/configure.ac
index fbd1331..4c0a12f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,6 +63,7 @@ PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.2)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.2)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
PKG_CHECK_MODULES(LIBZMQ, libzmq >= 3.2.2)
+PKG_CHECK_MODULES(LIBGNUTLS, gnutls)
# Coverage build taken from WebKit's configure.in
diff --git a/contrib/osmo-pcap-client-tls.cfg b/contrib/osmo-pcap-client-tls.cfg
new file mode 100644
index 0000000..73d1535
--- /dev/null
+++ b/contrib/osmo-pcap-client-tls.cfg
@@ -0,0 +1,16 @@
+!
+! OsmoPCAPClient (UNKNOWN-dirty) configuration saved from vty
+!!
+!
+!
+line vty
+ no login
+!
+client
+ pcap device any
+ pcap filter host www.google.com
+ pcap detect-loop 0
+ server ip 127.0.0.1
+ server port 6001
+ enable tls
+ tls priority NORMAL
diff --git a/contrib/osmo-pcap-server-tls.cfg b/contrib/osmo-pcap-server-tls.cfg
new file mode 100644
index 0000000..52f66f5
--- /dev/null
+++ b/contrib/osmo-pcap-server-tls.cfg
@@ -0,0 +1,27 @@
+!
+! OsmoPCAPServer (UNKNOWN) configuration saved from vty
+!!
+!
+log stderr
+ logging color 1
+ logging timestamp 0
+ logging level all everything
+ logging level pcap notice
+ logging level client notice
+ logging level server notice
+ logging level vty notice
+!
+line vty
+ no login
+!
+server
+ base-path /tmp
+ server ip 127.0.0.1
+ server port 6001
+ max-file-size 262144000
+ client zecke1 127.0.0.1
+ client zecke2 127.0.0.1 store tls
+ client zecke3 127.0.0.2 no-store tls
+ client zecke4 127.0.0.3 no-store
+ enable tls
+ tls priority SECURE
diff --git a/doc/tls.txt b/doc/tls.txt
new file mode 100644
index 0000000..d2015f0
--- /dev/null
+++ b/doc/tls.txt
@@ -0,0 +1,76 @@
+TLS support
+===========
+
+Protect forwarded PCAP packet against eave-dropping by using
+TLS between client and server.
+
+Anonymous TLS
+^^^^^^^^^^^^^
+
+The minimal configuration will use TLS with perfect forward
+secrecy but not use X509 certificates. This means a client
+will not know if it connects to the intended server but an
+attacker listening will not be able to determine the content
+of the messages.
+
+Client::
+---
+ enable tls
+ tls dh generate
+ tls priority NORMAL:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:+ANON-ECDH:+ANON-DH
+----
+
+Server::
+----
+ enable tls
+ tls dh generate
+ tls allow-auth anonymous
+----
+
+
+Authenticate Server
+^^^^^^^^^^^^^^^^^^^
+
+This will use x509 certificates and allows a client to verify
+it connects to a server with the right credentials. This will
+protect messages against eaves-dropping and sending data to the
+wrong system.
+
+
+
+Client::
+
+----
+ enable tls
+ tls verify-cert
+ tls capath /etc/osmocom/ca.pem
+----
+
+Server::
+
+----
+ enable tls
+ tls allow-auth x509
+ tls capath /etc/osmocom/ca.pem
+ tls crlfile /etc/osmocom/server.crl
+ tls server-cert /etc/osmocom/server.crt
+ tls server-key /etc/osmosomc/server.key
+ client NAME IP store tls
+----
+
+Client certificate
+^^^^^^^^^^^^^^^^^^
+
+Currently this is not implemented. In the future a client
+can be authenticated based on the SN/CN of a certificate.
+
+Debugging
+=========
+
+GNUtls debugging can be enabled by setting the TLS debug
+region to debug and then setting the _tls loglevel N_. The
+setting will be applied on the next connection using TLS.
+
+----
+ logging level tls debug
+ tls loglevel 9
diff --git a/include/osmo-pcap/Makefile.am b/include/osmo-pcap/Makefile.am
index 1a446bc..b71e70c 100644
--- a/include/osmo-pcap/Makefile.am
+++ b/include/osmo-pcap/Makefile.am
@@ -1 +1 @@
-noinst_HEADERS = common.h osmo_pcap_client.h osmo_pcap_server.h wireformat.h
+noinst_HEADERS = common.h osmo_pcap_client.h osmo_pcap_server.h wireformat.h osmo_tls.h
diff --git a/include/osmo-pcap/common.h b/include/osmo-pcap/common.h
index b8f8110..fff452f 100644
--- a/include/osmo-pcap/common.h
+++ b/include/osmo-pcap/common.h
@@ -34,6 +34,7 @@ enum {
DCLIENT,
DSERVER,
DVTY,
+ DTLS,
Debug_LastEntry,
};
diff --git a/include/osmo-pcap/osmo_pcap_client.h b/include/osmo-pcap/osmo_pcap_client.h
index ee81e50..b8ceb38 100644
--- a/include/osmo-pcap/osmo_pcap_client.h
+++ b/include/osmo-pcap/osmo_pcap_client.h
@@ -20,6 +20,8 @@
*
*/
+#include "osmo_tls.h"
+
#include <inttypes.h>
#include <pcap.h>
@@ -64,6 +66,20 @@ struct osmo_pcap_client {
struct osmo_wqueue wqueue;
struct osmo_timer_list timer;
+ /* TLS handling */
+ bool tls_on;
+ bool tls_verify;
+ char *tls_hostname;
+ char *tls_capath;
+ char *tls_priority;
+
+ char *tls_client_cert;
+ char *tls_client_key;
+
+ unsigned tls_log_level;
+
+ struct osmo_tls_session tls_session;
+
/* statistics */
struct rate_ctr_group *ctrg;
};
@@ -79,3 +95,5 @@ void osmo_client_send_data(struct osmo_pcap_client *client,
struct pcap_pkthdr *hdr, const uint8_t *data);
void osmo_client_send_link(struct osmo_pcap_client *client);
void osmo_client_connect(struct osmo_pcap_client *);
+
+void osmo_client_reconnect(struct osmo_pcap_client *);
diff --git a/include/osmo-pcap/osmo_pcap_server.h b/include/osmo-pcap/osmo_pcap_server.h
index a386a2a..c1d318e 100644
--- a/include/osmo-pcap/osmo_pcap_server.h
+++ b/include/osmo-pcap/osmo_pcap_server.h
@@ -24,9 +24,11 @@
#define OSMO_PCAP_SERVER_H
#include "wireformat.h"
+#include "osmo_tls.h"
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/write_queue.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -34,6 +36,7 @@
#include <pcap.h>
+#include <stdbool.h>
#include <time.h>
struct rate_ctr_group;
@@ -74,7 +77,7 @@ struct osmo_pcap_conn {
struct in_addr remote_addr;
/* Remote connection */
- struct osmo_fd rem_fd;
+ struct osmo_wqueue rem_wq;
int local_fd;
char *curr_filename;
@@ -93,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 {
@@ -108,6 +117,20 @@ struct osmo_pcap_server {
void *zmq_ctx;
void *zmq_publ;
+ /* tls base */
+ bool tls_on;
+ bool tls_allow_anon;
+ bool tls_allow_x509;
+ unsigned tls_log_level;
+ char *tls_priority;
+ char *tls_capath;
+ char *tls_crlfile;
+ char *tls_server_cert;
+ char *tls_server_key;
+ char *tls_dh_pkcs3;
+ gnutls_dh_params_t dh_params;
+ bool dh_params_allocated;
+
char *base_path;
off_t max_size;
@@ -125,5 +148,6 @@ struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *ser,
void osmo_pcap_server_delete(struct osmo_pcap_conn *conn);
void vty_server_init(struct osmo_pcap_server *server);
void osmo_pcap_server_close_trace(struct osmo_pcap_conn *conn);
+void osmo_pcap_server_close_conn(struct osmo_pcap_conn *conn);
#endif
diff --git a/include/osmo-pcap/osmo_tls.h b/include/osmo-pcap/osmo_tls.h
new file mode 100644
index 0000000..0637739
--- /dev/null
+++ b/include/osmo-pcap/osmo_tls.h
@@ -0,0 +1,80 @@
+/*
+ * osmo-pcap TLS code
+ *
+ * (C) 2016 by Holger Hans Peter Freyther <holger@moiji-mobile.com>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <gnutls/gnutls.h>
+#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;
+ bool need_handshake;
+ bool need_resend;
+ gnutls_session_t 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;
+ gnutls_certificate_credentials_t cert_cred;
+
+ /* the private certificate */
+ bool pcert_alloc;
+ gnutls_pcert_st pcert;
+
+ /* the private key in _RAM_ */
+ bool privk_alloc;
+ gnutls_privkey_t privk;
+
+ 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);
+};
+
+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);
+void osmo_tls_server_init(struct osmo_pcap_server *server);
+
+void osmo_tls_dh_load(struct osmo_pcap_server *server);
+void osmo_tls_dh_generate(struct osmo_pcap_server *server);
diff --git a/osmoappdesc.py b/osmoappdesc.py
index fce58fb..39928eb 100644
--- a/osmoappdesc.py
+++ b/osmoappdesc.py
@@ -15,8 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
app_configs = {
- "osmo-pcap-client": ["contrib/osmo-pcap-client.cfg"],
- "osmo-pcap-server": ["contrib/osmo-pcap-server.cfg"]
+ "osmo-pcap-client": ["contrib/osmo-pcap-client.cfg", "contrib/osmo-pcap-client-tls.cfg"],
+ "osmo-pcap-server": ["contrib/osmo-pcap-server.cfg", "contrib/osmo-pcap-server-tls.cfg"]
}
apps = [
diff --git a/src/Makefile.am b/src/Makefile.am
index 9674cdb..0532acf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,13 +1,16 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(PCAP_CFLAGS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(PCAP_CFLAGS) $(LIBGNUTLS_CFLAGS)
bin_PROGRAMS = osmo_pcap_client osmo_pcap_server
osmo_pcap_client_SOURCES = osmo_client_main.c osmo_common.c \
osmo_client_core.c osmo_client_vty.c \
- osmo_client_network.c
-osmo_pcap_client_LDADD = $(PCAP_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOGSM_LIBS)
+ osmo_client_network.c osmo_tls.c
+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_client_main.c b/src/osmo_client_main.c
index f5ba41a..0bbeb7a 100644
--- a/src/osmo_client_main.c
+++ b/src/osmo_client_main.c
@@ -22,6 +22,7 @@
#include <osmo-pcap/common.h>
#include <osmo-pcap/osmo_pcap_client.h>
+#include <osmo-pcap/osmo_tls.h>
#include <osmocom/core/application.h>
#include <osmocom/core/rate_ctr.h>
@@ -203,6 +204,8 @@ int main(int argc, char **argv)
signal(SIGUSR1, &signal_handler);
osmo_init_ignore_signals();
+ osmo_tls_init();
+
rc = telnet_init(tall_bsc_ctx, NULL, 4240);
if (rc < 0) {
LOGP(DCLIENT, LOGL_ERROR, "Failed to bind telnet interface\n");
@@ -215,6 +218,7 @@ int main(int argc, char **argv)
exit(1);
}
pcap_client->fd.fd = -1;
+ pcap_client->tls_verify = true;
vty_client_init(pcap_client);
/* initialize the queue */
diff --git a/src/osmo_client_network.c b/src/osmo_client_network.c
index 2400f3a..1bd5898 100644
--- a/src/osmo_client_network.c
+++ b/src/osmo_client_network.c
@@ -47,6 +47,7 @@ static void _osmo_client_connect(void *_data)
static void lost_connection(struct osmo_pcap_client *client)
{
if (client->wqueue.bfd.fd >= 0) {
+ osmo_tls_release(&client->tls_session);
osmo_fd_unregister(&client->wqueue.bfd);
close(client->wqueue.bfd.fd);
client->wqueue.bfd.fd = -1;
@@ -100,6 +101,22 @@ static int write_cb(struct osmo_fd *fd, struct msgb *msg)
return 0;
}
+static void handshake_done_cb(struct osmo_tls_session *session)
+{
+ struct osmo_pcap_client *client;
+
+ client = container_of(session, struct osmo_pcap_client, tls_session);
+ osmo_client_send_link(client);
+}
+
+static void tls_error_cb(struct osmo_tls_session *session)
+{
+ struct osmo_pcap_client *client;
+
+ client = container_of(session, struct osmo_pcap_client, tls_session);
+ lost_connection(client);
+}
+
void osmo_client_send_data(struct osmo_pcap_client *client,
struct pcap_pkthdr *in_hdr, const uint8_t *data)
{
@@ -177,7 +194,6 @@ void osmo_client_connect(struct osmo_pcap_client *client)
client->wqueue.read_cb = read_cb;
client->wqueue.write_cb = write_cb;
client->wqueue.bfd.when = BSC_FD_READ;
- client->wqueue.bfd.data = client;
osmo_wqueue_clear(&client->wqueue);
fd = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
@@ -199,5 +215,26 @@ void osmo_client_connect(struct osmo_pcap_client *client)
}
rate_ctr_inc(&client->ctrg->ctr[CLIENT_CTR_CONNECT]);
- osmo_client_send_link(client);
+
+ /*
+ * The write queue needs to work differently for GNUtls. Before we can
+ * send data we will need to complete handshake.
+ */
+ if (client->tls_on) {
+ if (!osmo_tls_init_client_session(client)) {
+ lost_connection(client);
+ return;
+ }
+ client->tls_session.handshake_done = handshake_done_cb;
+ client->tls_session.error = tls_error_cb;
+ } else {
+ client->wqueue.bfd.cb = osmo_wqueue_bfd_cb;
+ client->wqueue.bfd.data = client;
+ osmo_client_send_link(client);
+ }
+}
+
+void osmo_client_reconnect(struct osmo_pcap_client *client)
+{
+ lost_connection(client);
}
diff --git a/src/osmo_client_vty.c b/src/osmo_client_vty.c
index a8739b1..a409cf4 100644
--- a/src/osmo_client_vty.c
+++ b/src/osmo_client_vty.c
@@ -1,7 +1,7 @@
/*
* osmo-pcap-client code
*
- * (C) 2011 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2011-2016 by Holger Hans Peter Freyther <holger@moiji-mobile.com>
* (C) 2011 by On-Waves
* All Rights Reserved
*
@@ -62,6 +62,26 @@ static int config_write_client(struct vty *vty)
if (pcap_client->gprs_filtering)
vty_out(vty, " pcap add-filter gprs%s", VTY_NEWLINE);
+ if (pcap_client->tls_on) {
+ vty_out(vty, " enable tls%s", VTY_NEWLINE);
+ vty_out(vty, " tls hostname %s%s", pcap_client->tls_hostname, VTY_NEWLINE);
+ vty_out(vty, " %stls verify-cert%s",
+ pcap_client->tls_verify ? "" : "no ", VTY_NEWLINE);
+ if (pcap_client->tls_capath)
+ vty_out(vty, " tls capath %s%s", pcap_client->tls_capath, VTY_NEWLINE);
+ if (pcap_client->tls_client_cert)
+ vty_out(vty, " tls client-cert %s%s",
+ pcap_client->tls_client_cert, VTY_NEWLINE);
+ if (pcap_client->tls_client_key)
+ vty_out(vty, " tls client-key %s%s",
+ pcap_client->tls_client_key, VTY_NEWLINE);
+ if (pcap_client->tls_priority)
+ vty_out(vty, " tls priority %s%s",
+ pcap_client->tls_priority, VTY_NEWLINE);
+ vty_out(vty, " tls log-level %d%s",
+ pcap_client->tls_log_level, VTY_NEWLINE);
+ }
+
if (pcap_client->srv_ip)
vty_out(vty, " server ip %s%s",
pcap_client->srv_ip, VTY_NEWLINE);
@@ -131,6 +151,162 @@ DEFUN(cfg_client_loop,
return CMD_SUCCESS;
}
+
+#define TLS_STR "Transport Layer Security\n"
+
+DEFUN(cfg_enable_tls,
+ cfg_enable_tls_cmd,
+ "enable tls",
+ "Enable\n" "Transport Layer Security\n")
+{
+ if (!pcap_client->tls_on) {
+ if (pcap_client->wqueue.bfd.fd >= 0)
+ osmo_client_reconnect(pcap_client);
+ }
+
+ pcap_client->tls_on = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_disable_tls,
+ cfg_disable_tls_cmd,
+ "disable tls",
+ "Disable\n" "Transport Layer Security\n")
+{
+ if (pcap_client->tls_on)
+ osmo_client_reconnect(pcap_client);
+
+ pcap_client->tls_on = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_hostname,
+ cfg_tls_hostname_cmd,
+ "tls hostname NAME",
+ TLS_STR "hostname for certificate validation\n" "name\n")
+{
+ talloc_free(pcap_client->tls_hostname);
+ pcap_client->tls_hostname = talloc_strdup(pcap_client, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_hostname,
+ cfg_no_tls_hostname_cmd,
+ "no tls hostname",
+ NO_STR TLS_STR "hostname for certificate validation\n")
+{
+ talloc_free(pcap_client->tls_hostname);
+ pcap_client->tls_hostname = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_verify,
+ cfg_tls_verify_cmd,
+ "tls verify-cert",
+ TLS_STR "Verify certificates\n")
+{
+ pcap_client->tls_verify = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_verify,
+ cfg_no_tls_verify_cmd,
+ "no tls verify-cert",
+ NO_STR TLS_STR "Verify certificates\n")
+{
+ pcap_client->tls_verify = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_capath,
+ cfg_tls_capath_cmd,
+ "tls capath .PATH",
+ TLS_STR "Trusted root certificates\n" "Filename\n")
+{
+ talloc_free(pcap_client->tls_capath);
+ pcap_client->tls_capath = talloc_strdup(pcap_client, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_capath,
+ cfg_no_tls_capath_cmd,
+ "no tls capath",
+ NO_STR TLS_STR "Trusted root certificates\n")
+{
+ talloc_free(pcap_client->tls_capath);
+ pcap_client->tls_capath = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_client_cert,
+ cfg_tls_client_cert_cmd,
+ "tls client-cert .PATH",
+ TLS_STR "Client certificate for authentication\n" "Filename\n")
+{
+ talloc_free(pcap_client->tls_client_cert);
+ pcap_client->tls_client_cert = talloc_strdup(pcap_client, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_client_cert,
+ cfg_no_tls_client_cert_cmd,
+ "no tls client-cert",
+ NO_STR TLS_STR "Client certificate for authentication\n")
+{
+ talloc_free(pcap_client->tls_client_cert);
+ pcap_client->tls_client_cert = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_client_key,
+ cfg_tls_client_key_cmd,
+ "tls client-key .PATH",
+ TLS_STR "Client private key\n" "Filename\n")
+{
+ talloc_free(pcap_client->tls_client_key);
+ pcap_client->tls_client_key = talloc_strdup(pcap_client, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_client_key,
+ cfg_no_tls_client_key_cmd,
+ "no tls client-key",
+ NO_STR TLS_STR "Client private key\n")
+{
+ talloc_free(pcap_client->tls_client_key);
+ pcap_client->tls_client_key = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_priority,
+ cfg_tls_priority_cmd,
+ "tls priority STR",
+ TLS_STR "Priority string for GNUtls\n" "Priority string\n")
+{
+ talloc_free(pcap_client->tls_priority);
+ pcap_client->tls_priority = talloc_strdup(pcap_client, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_priority,
+ cfg_no_tls_priority_cmd,
+ "no tls priority",
+ NO_STR TLS_STR "Priority string for GNUtls\n")
+{
+ talloc_free(pcap_client->tls_priority);
+ pcap_client->tls_priority = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_log_level,
+ cfg_tls_log_level_cmd,
+ "tls log-level <0-255>",
+ TLS_STR "Log-level\n" "GNUtls debug level\n")
+{
+ pcap_client->tls_log_level = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_server_ip,
cfg_server_ip_cmd,
"server ip A.B.C.D",
@@ -164,6 +340,22 @@ int vty_client_init(struct osmo_pcap_client *pcap)
install_element(CLIENT_NODE, &cfg_server_ip_cmd);
install_element(CLIENT_NODE, &cfg_server_port_cmd);
+ install_element(CLIENT_NODE, &cfg_enable_tls_cmd);
+ install_element(CLIENT_NODE, &cfg_disable_tls_cmd);
+ install_element(CLIENT_NODE, &cfg_tls_hostname_cmd);
+ install_element(CLIENT_NODE, &cfg_no_tls_hostname_cmd);
+ install_element(CLIENT_NODE, &cfg_tls_verify_cmd);
+ install_element(CLIENT_NODE, &cfg_no_tls_verify_cmd);
+ install_element(CLIENT_NODE, &cfg_tls_capath_cmd);
+ install_element(CLIENT_NODE, &cfg_no_tls_capath_cmd);
+ install_element(CLIENT_NODE, &cfg_tls_client_cert_cmd);
+ install_element(CLIENT_NODE, &cfg_no_tls_client_cert_cmd);
+ install_element(CLIENT_NODE, &cfg_tls_client_key_cmd);
+ install_element(CLIENT_NODE, &cfg_no_tls_client_key_cmd);
+ install_element(CLIENT_NODE, &cfg_tls_priority_cmd);
+ install_element(CLIENT_NODE, &cfg_no_tls_priority_cmd);
+ install_element(CLIENT_NODE, &cfg_tls_log_level_cmd);
+
install_element(CLIENT_NODE, &cfg_client_add_gprs_cmd);
install_element(CLIENT_NODE, &cfg_client_del_gprs_cmd);
diff --git a/src/osmo_common.c b/src/osmo_common.c
index 33ec1b2..bb7d011 100644
--- a/src/osmo_common.c
+++ b/src/osmo_common.c
@@ -49,6 +49,12 @@ static const struct log_info_cat default_categories[] = {
.color = "\033[1;34m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DTLS] = {
+ .name = "DTLS",
+ .description = "TLS code",
+ .color = "\033[1;34m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
const struct log_info log_info = {
diff --git a/src/osmo_server_main.c b/src/osmo_server_main.c
index bb94ec4..37a9632 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");
@@ -244,6 +247,8 @@ int main(int argc, char **argv)
exit(1);
}
+ osmo_tls_server_init(pcap_server);
+
/* attempt to connect to the remote */
if (osmo_pcap_server_listen(pcap_server) != 0) {
LOGP(DSERVER, LOGL_ERROR,
diff --git a/src/osmo_server_network.c b/src/osmo_server_network.c
index 8d41eaf..a854223 100644
--- a/src/osmo_server_network.c
+++ b/src/osmo_server_network.c
@@ -1,7 +1,7 @@
/*
* osmo-pcap-server code
*
- * (C) 2011-2017 by Holger Hans Peter Freyther <holger@moiji-mobile.com>
+ * (C) 2011-2016 by Holger Hans Peter Freyther <holger@moiji-mobile.com>
* (C) 2011 by On-Waves
* All Rights Reserved
*
@@ -129,16 +129,22 @@ void osmo_pcap_server_close_trace(struct osmo_pcap_conn *conn)
static void close_connection(struct osmo_pcap_conn *conn)
{
- if (conn->rem_fd.fd >= 0) {
- close(conn->rem_fd.fd);
- conn->rem_fd.fd = -1;
- osmo_fd_unregister(&conn->rem_fd);
+ 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);
}
osmo_pcap_server_close_trace(conn);
client_event(conn, "disconnect", NULL);
}
+void osmo_pcap_server_close_conn(struct osmo_pcap_conn *conn)
+{
+ return close_connection(conn);
+}
+
static void restart_pcap(struct osmo_pcap_conn *conn)
{
time_t now = time(NULL);
@@ -182,14 +188,13 @@ static void restart_pcap(struct osmo_pcap_conn *conn)
conn->last_write = *tm;
}
-static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
+static int link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
{
struct pcap_file_header *hdr;
if (data->len != sizeof(*hdr)) {
LOGP(DSERVER, LOGL_ERROR, "The pcap_file_header does not fit.\n");
- close_connection(conn);
- return;
+ return -1;
}
hdr = (struct pcap_file_header *) &data->data[0];
@@ -200,12 +205,14 @@ static void link_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
conn->file_hdr = *hdr;
restart_pcap(conn);
}
+
+ return 1;
}
/*
* Check if we are past the limit or on a day change
*/
-static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
+static int write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
{
time_t now = time(NULL);
struct tm *tm = localtime(&now);
@@ -215,13 +222,12 @@ static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
if (conn->no_store) {
conn->last_write = *tm;
- return;
+ return 1;
}
if (conn->local_fd < -1) {
LOGP(DSERVER, LOGL_ERROR, "No file is open. close connection.\n");
- close_connection(conn);
- return;
+ return -1;
}
off_t cur = lseek(conn->local_fd, 0, SEEK_CUR);
@@ -239,8 +245,9 @@ static void write_data(struct osmo_pcap_conn *conn, struct osmo_pcap_data *data)
rc = write(conn->local_fd, &data->data[0], data->len);
if (rc != data->len) {
LOGP(DSERVER, LOGL_ERROR, "Failed to write for %s\n", conn->name);
- close_connection(conn);
+ return -1;
}
+ return 1;
}
@@ -303,7 +310,9 @@ struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *server,
conn->name = talloc_strdup(conn, name);
- conn->rem_fd.fd = -1;
+ /* we never write */
+ osmo_wqueue_init(&conn->rem_wq, 0);
+ conn->rem_wq.bfd.fd = -1;
conn->local_fd = -1;
conn->server = server;
conn->data = (struct osmo_pcap_data *) &conn->buf[0];
@@ -311,14 +320,29 @@ struct osmo_pcap_conn *osmo_pcap_server_find(struct osmo_pcap_server *server,
return conn;
}
-static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *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)
+{
+ 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)
{
int rc;
- rc = read(fd->fd, &conn->buf[sizeof(*conn->data) - conn->pend], conn->pend);
+
+ rc = do_read(conn, &conn->buf[sizeof(*conn->data) - conn->pend], conn->pend);
if (rc <= 0) {
LOGP(DSERVER, LOGL_ERROR,
"Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
- close_connection(conn);
return -1;
}
@@ -326,7 +350,6 @@ static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
if (conn->pend < 0) {
LOGP(DSERVER, LOGL_ERROR,
"Someone got the pending read wrong: %d\n", conn->pend);
- close_connection(conn);
return -1;
} else if (conn->pend == 0) {
conn->data->len = ntohs(conn->data->len);
@@ -334,7 +357,6 @@ static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
if (conn->data->len > SERVER_MAX_DATA_SIZE) {
LOGP(DSERVER, LOGL_ERROR,
"Implausible data length: %u\n", conn->data->len);
- close_connection(conn);
return -1;
}
@@ -342,17 +364,17 @@ static int read_cb_initial(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
conn->pend = conn->data->len;
}
- return 0;
+ return 1;
}
-static int read_cb_data(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
+static int read_cb_data(struct osmo_pcap_conn *conn)
{
int rc;
- rc = read(fd->fd, &conn->data->data[conn->data->len - conn->pend], conn->pend);
+
+ rc = do_read(conn, &conn->data->data[conn->data->len - conn->pend], conn->pend);
if (rc <= 0) {
LOGP(DSERVER, LOGL_ERROR,
"Too short packet. Got %d, wanted %d\n", rc, conn->data->len);
- close_connection(conn);
return -1;
}
@@ -360,7 +382,6 @@ static int read_cb_data(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
if (conn->pend < 0) {
LOGP(DSERVER, LOGL_ERROR,
"Someone got the pending read wrong: %d\n", conn->pend);
- close_connection(conn);
return -1;
} else if (conn->pend == 0) {
conn->state = STATE_INITIAL;
@@ -376,58 +397,121 @@ static int read_cb_data(struct osmo_fd *fd, struct osmo_pcap_conn *conn)
switch (conn->data->type) {
case PKT_LINK_HDR:
- link_data(conn, conn->data);
+ return link_data(conn, conn->data);
break;
case PKT_LINK_DATA:
- write_data(conn, conn->data);
+ return write_data(conn, conn->data);
break;
}
}
- return 0;
+ return 1;
}
-static int read_cb(struct osmo_fd *fd, unsigned int what)
+static int dispatch_read(struct osmo_pcap_conn *conn)
{
- struct osmo_pcap_conn *conn;
-
- conn = fd->data;
-
if (conn->state == STATE_INITIAL) {
if (conn->reopen) {
LOGP(DSERVER, LOGL_INFO, "Reopening log for %s now.\n", conn->name);
restart_pcap(conn);
conn->reopen = 0;
}
- return read_cb_initial(fd, conn);
+ return read_cb_initial(conn);
} else if (conn->state == STATE_DATA) {
- return read_cb_data(fd, conn);
+ return read_cb_data(conn);
}
return 0;
}
+static int read_cb(struct osmo_fd *fd)
+{
+ struct osmo_pcap_conn *conn;
+ int rc;
+
+ conn = fd->data;
+ rc = dispatch_read(conn);
+ if (rc <= 0)
+ close_connection(conn);
+ 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)
{
close_connection(client);
memset(&client->file_hdr, 0, sizeof(client->file_hdr));
- client->rem_fd.fd = new_fd;
- if (osmo_fd_register(&client->rem_fd) != 0) {
+ client->rem_wq.bfd.fd = new_fd;
+ if (osmo_fd_register(&client->rem_wq.bfd) != 0) {
LOGP(DSERVER, LOGL_ERROR, "Failed to register fd.\n");
- client->rem_fd.fd = -1;
+ client->rem_wq.bfd.fd = -1;
close(new_fd);
return;
}
rate_ctr_inc(&client->ctrg->ctr[PEER_CTR_CONNECT]);
- client->rem_fd.data = client;
- client->rem_fd.when = BSC_FD_READ;
- client->rem_fd.cb = read_cb;
client->state = STATE_INITIAL;
client->pend = sizeof(*client->data);
+
+ if (client->tls_use && !server->tls_on) {
+ LOGP(DSERVER, LOGL_NOTICE,
+ "Require TLS but not enabled on conn=%s\n",
+ client->name);
+ close_connection(client);
+ return;
+ } else 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)
@@ -460,6 +544,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_server_vty.c b/src/osmo_server_vty.c
index d13ea6f..b2919ae 100644
--- a/src/osmo_server_vty.c
+++ b/src/osmo_server_vty.c
@@ -41,6 +41,45 @@ static struct cmd_node server_node = {
1,
};
+static void write_tls(struct vty *vty, struct osmo_pcap_server *pcap_server)
+{
+ if (!pcap_server->tls_on)
+ return;
+
+ vty_out(vty, " enable tls%s", VTY_NEWLINE);
+ vty_out(vty, " tls log-level %d%s",
+ pcap_server->tls_log_level, VTY_NEWLINE);
+
+ if (pcap_server->tls_allow_anon)
+ vty_out(vty, " tls allow-auth anonymous%s", VTY_NEWLINE);
+
+ if (pcap_server->tls_allow_x509)
+ vty_out(vty, " tls allow-auth x509%s", VTY_NEWLINE);
+
+ if (pcap_server->tls_priority)
+ vty_out(vty, " tls priority %s%s",
+ pcap_server->tls_priority, VTY_NEWLINE);
+ if (pcap_server->tls_capath)
+ vty_out(vty, " tls capath %s%s", pcap_server->tls_capath, VTY_NEWLINE);
+
+ if (pcap_server->tls_crlfile)
+ vty_out(vty, " tls crlfile %s%s", pcap_server->tls_crlfile, VTY_NEWLINE);
+
+ if (pcap_server->tls_server_cert)
+ vty_out(vty, " tls server-cert %s%s",
+ pcap_server->tls_server_cert, VTY_NEWLINE);
+
+ if (pcap_server->tls_server_key)
+ vty_out(vty, " tls server-key %s%s",
+ pcap_server->tls_server_key, VTY_NEWLINE);
+
+ if (pcap_server->tls_dh_pkcs3)
+ vty_out(vty, " tls dh pkcs3 %s%s",
+ pcap_server->tls_dh_pkcs3, VTY_NEWLINE);
+ else
+ vty_out(vty, " tls dh generate%s", VTY_NEWLINE);
+}
+
static int config_write_server(struct vty *vty)
{
struct osmo_pcap_conn *conn;
@@ -59,10 +98,13 @@ static int config_write_server(struct vty *vty)
vty_out(vty, " zeromq-publisher %s %d%s",
pcap_server->zmq_ip, pcap_server->zmq_port, VTY_NEWLINE);
+ write_tls(vty, pcap_server);
+
llist_for_each_entry(conn, &pcap_server->conn, entry) {
- vty_out(vty, " client %s %s%s%s",
+ vty_out(vty, " client %s %s%s%s%s",
conn->name, conn->remote_host,
- conn->no_store ? " no-store" : "",
+ conn->no_store ? " no-store" : " store",
+ conn->tls_use ? " tls" : "",
VTY_NEWLINE);
}
@@ -116,32 +158,62 @@ DEFUN(cfg_server_max_size,
return CMD_SUCCESS;
}
-DEFUN(cfg_server_client,
- cfg_server_client_cmd,
- "client NAME A.B.C.D [no-store]",
- CLIENT_STR "Remote name used in filenames\n" "IP of the remote\n" "Do not store traffic\n")
+static int manage_client(struct osmo_pcap_server *pcap_server,
+ struct vty *vty,
+ const char *name, const char *remote_host,
+ bool no_store, bool use_tls)
{
struct osmo_pcap_conn *conn;
- conn = osmo_pcap_server_find(pcap_server, argv[0]);
+ conn = osmo_pcap_server_find(pcap_server, name);
if (!conn) {
vty_out(vty, "Failed to create a pcap server.\n");
return CMD_WARNING;
}
talloc_free(conn->remote_host);
- conn->remote_host = talloc_strdup(pcap_server, argv[1]);
- inet_aton(argv[1], &conn->remote_addr);
+ conn->remote_host = talloc_strdup(pcap_server, remote_host);
+ inet_aton(remote_host, &conn->remote_addr);
/* Checking no-store and maybe closing a pcap file */
- if (argc >= 3) {
+ if (no_store) {
osmo_pcap_server_close_trace(conn);
conn->no_store = 1;
} else
conn->no_store = 0;
+ if (use_tls) {
+ /* force moving to TLS */
+ if (!conn->tls_use)
+ osmo_pcap_server_close_conn(conn);
+ conn->tls_use = true;
+ } else {
+ conn->tls_use = false;
+ }
+
return CMD_SUCCESS;
}
+
+DEFUN(cfg_server_client,
+ cfg_server_client_cmd,
+ "client NAME A.B.C.D [no-store] [tls]",
+ CLIENT_STR "Remote name used in filenames\n"
+ "IP of the remote\n" "Do not store traffic\n"
+ "Use Transport Level Security\n")
+{
+ return manage_client(pcap_server, vty, argv[0], argv[1], argc >= 3, argc >= 4);
+}
+
+DEFUN(cfg_server_client_store_tls,
+ cfg_server_client_store_tls_cmd,
+ "client NAME A.B.C.D store [tls]",
+ CLIENT_STR "Remote name used in filenames\n"
+ "IP of the remote\n" "Do not store traffic\n"
+ "Use Transport Level Security\n")
+{
+ return manage_client(pcap_server, vty, argv[0], argv[1], false, argc >= 3);
+}
+
DEFUN(cfg_server_no_client,
cfg_server_no_client_cmd,
"no client NAME",
@@ -241,6 +313,195 @@ DEFUN(cfg_no_server_zmq_ip_port,
return CMD_SUCCESS;
}
+#define TLS_STR "Transport Layer Security\n"
+
+DEFUN(cfg_enable_tls,
+ cfg_enable_tls_cmd,
+ "enable tls",
+ "Enable\n" "Transport Layer Security\n")
+{
+ pcap_server->tls_on = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_disable_tls,
+ cfg_disable_tls_cmd,
+ "disable tls",
+ "Disable\n" "Transport Layer Security\n")
+{
+ pcap_server->tls_on = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_log_level,
+ cfg_tls_log_level_cmd,
+ "tls log-level <0-255>",
+ TLS_STR "Log-level\n" "GNUtls debug level\n")
+{
+ pcap_server->tls_log_level = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_allow_anon,
+ cfg_tls_allow_anon_cmd,
+ "tls allow-auth anonymous",
+ TLS_STR "allow authentication\n" "for anonymous\n")
+{
+ pcap_server->tls_allow_anon = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_allow_anon,
+ cfg_no_tls_allow_anon_cmd,
+ "no tls allow-auth anonymous",
+ NO_STR TLS_STR "allow authentication\n" "for anonymous\n")
+{
+ pcap_server->tls_allow_anon = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_allow_x509,
+ cfg_tls_allow_x509_cmd,
+ "tls allow-auth x509",
+ TLS_STR "allow authentication\n" "for certificates\n")
+{
+ pcap_server->tls_allow_x509 = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_allow_x509,
+ cfg_no_tls_allow_x509_cmd,
+ "no tls allow-auth x509",
+ NO_STR TLS_STR "allow authentication\n" "for certificates\n")
+{
+ pcap_server->tls_allow_x509 = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_priority,
+ cfg_tls_priority_cmd,
+ "tls priority STR",
+ TLS_STR "Priority string for GNUtls\n" "Priority string\n")
+{
+ talloc_free(pcap_server->tls_priority);
+ pcap_server->tls_priority = talloc_strdup(pcap_server, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_priority,
+ cfg_no_tls_priority_cmd,
+ "no tls priority",
+ NO_STR TLS_STR "Priority string for GNUtls\n")
+{
+ talloc_free(pcap_server->tls_priority);
+ pcap_server->tls_priority = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_capath,
+ cfg_tls_capath_cmd,
+ "tls capath .PATH",
+ TLS_STR "Trusted root certificates\n" "Filename\n")
+{
+ talloc_free(pcap_server->tls_capath);
+ pcap_server->tls_capath = talloc_strdup(pcap_server, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_capath,
+ cfg_no_tls_capath_cmd,
+ "no tls capath",
+ NO_STR TLS_STR "Trusted root certificates\n")
+{
+ talloc_free(pcap_server->tls_capath);
+ pcap_server->tls_capath = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_crlfile,
+ cfg_tls_crlfile_cmd,
+ "tls crlfile .PATH",
+ TLS_STR "CRL file\n" "Filename\n")
+{
+ talloc_free(pcap_server->tls_crlfile);
+ pcap_server->tls_crlfile = talloc_strdup(pcap_server, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_crlfile,
+ cfg_no_tls_crlfile_cmd,
+ "no tls crlfile",
+ NO_STR TLS_STR "CRL file\n")
+{
+ talloc_free(pcap_server->tls_crlfile);
+ pcap_server->tls_crlfile = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_server_cert,
+ cfg_tls_server_cert_cmd,
+ "tls server-cert .PATH",
+ TLS_STR "Server certificate\n" "Filename\n")
+{
+ talloc_free(pcap_server->tls_server_cert);
+ pcap_server->tls_server_cert = talloc_strdup(pcap_server, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_server_cert,
+ cfg_no_tls_server_cert_cmd,
+ "no tls server-cert",
+ NO_STR TLS_STR "Server certificate\n")
+{
+ talloc_free(pcap_server->tls_server_cert);
+ pcap_server->tls_server_cert = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_server_key,
+ cfg_tls_server_key_cmd,
+ "tls server-key .PATH",
+ TLS_STR "Server private key\n" "Filename\n")
+{
+ talloc_free(pcap_server->tls_server_key);
+ pcap_server->tls_server_key = talloc_strdup(pcap_server, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_tls_server_key,
+ cfg_no_tls_server_key_cmd,
+ "no tls server-key",
+ NO_STR TLS_STR "Server private key\n")
+{
+ talloc_free(pcap_server->tls_server_key);
+ pcap_server->tls_server_key = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_dh_pkcs3,
+ cfg_tls_dh_pkcs3_cmd,
+ "tls dh pkcs .FILE",
+ TLS_STR "Diffie-Hellman Key Exchange\n" "PKCS3\n" "Filename\n")
+{
+ talloc_free(pcap_server->tls_dh_pkcs3);
+ pcap_server->tls_dh_pkcs3 = talloc_strdup(pcap_server, argv[0]);
+
+ osmo_tls_dh_load(pcap_server);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tls_dh_generate,
+ cfg_tls_dh_generate_cmd,
+ "tls dh generate",
+ TLS_STR "Diffie-Hellman Key Exchange\n" "Generate prime\n")
+{
+ talloc_free(pcap_server->tls_dh_pkcs3);
+ pcap_server->tls_dh_pkcs3 = NULL;
+
+ osmo_tls_dh_generate(pcap_server);
+ return CMD_SUCCESS;
+}
+
void vty_server_init(struct osmo_pcap_server *server)
{
install_element(CONFIG_NODE, &cfg_server_cmd);
@@ -254,6 +515,28 @@ void vty_server_init(struct osmo_pcap_server *server)
install_element(SERVER_NODE, &cfg_server_zmq_ip_port_cmd);
install_element(SERVER_NODE, &cfg_no_server_zmq_ip_port_cmd);
+ /* tls for the server */
+ install_element(SERVER_NODE, &cfg_enable_tls_cmd);
+ install_element(SERVER_NODE, &cfg_disable_tls_cmd);
+ install_element(SERVER_NODE, &cfg_tls_log_level_cmd);
+ install_element(SERVER_NODE, &cfg_tls_allow_anon_cmd);
+ install_element(SERVER_NODE, &cfg_no_tls_allow_anon_cmd);
+ install_element(SERVER_NODE, &cfg_tls_allow_x509_cmd);
+ install_element(SERVER_NODE, &cfg_no_tls_allow_x509_cmd);
+ install_element(SERVER_NODE, &cfg_tls_priority_cmd);
+ install_element(SERVER_NODE, &cfg_no_tls_priority_cmd);
+ install_element(SERVER_NODE, &cfg_tls_capath_cmd);
+ install_element(SERVER_NODE, &cfg_no_tls_capath_cmd);
+ install_element(SERVER_NODE, &cfg_tls_crlfile_cmd);
+ install_element(SERVER_NODE, &cfg_no_tls_crlfile_cmd);
+ install_element(SERVER_NODE, &cfg_tls_server_cert_cmd);
+ install_element(SERVER_NODE, &cfg_no_tls_server_cert_cmd);
+ install_element(SERVER_NODE, &cfg_tls_server_key_cmd);
+ install_element(SERVER_NODE, &cfg_no_tls_server_key_cmd);
+ install_element(SERVER_NODE, &cfg_tls_dh_generate_cmd);
+ install_element(SERVER_NODE, &cfg_tls_dh_pkcs3_cmd);
+
install_element(SERVER_NODE, &cfg_server_client_cmd);
+ install_element(SERVER_NODE, &cfg_server_client_store_tls_cmd);
install_element(SERVER_NODE, &cfg_server_no_client_cmd);
}
diff --git a/src/osmo_tls.c b/src/osmo_tls.c
new file mode 100644
index 0000000..aeecf6d
--- /dev/null
+++ b/src/osmo_tls.c
@@ -0,0 +1,558 @@
+/*
+ * osmo-pcap TLS code
+ *
+ * (C) 2016 by Holger Hans Peter Freyther <holger@moiji-mobile.com>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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>
+#include <osmocom/core/talloc.h>
+
+#include <string.h>
+
+#define CHECK_RC(rc, str) \
+ if (rc != 0) { \
+ LOGP(DTLS, LOGL_ERROR, "%s with rc=%d\n", str, rc); \
+ exit(1); \
+ }
+
+static int generate_dh_params(struct osmo_pcap_server *server)
+{
+ int rc;
+ 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);
+
+ /* allocate it */
+ rc = gnutls_dh_params_init (&server->dh_params);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to allocate DH params rc=%d\n", rc);
+ server->dh_params_allocated = false;
+ return rc;
+ }
+
+ /* generate and check */
+ rc = gnutls_dh_params_generate2 (server->dh_params, bits);
+ if (rc == GNUTLS_E_SUCCESS)
+ server->dh_params_allocated = true;
+ else {
+ LOGP(DTLS, LOGL_ERROR, "Failed to generate DH params rc=%d\n", rc);
+ server->dh_params_allocated = false;
+ gnutls_dh_params_deinit(server->dh_params);
+ }
+ return rc;
+}
+
+void osmo_tls_dh_load(struct osmo_pcap_server *server)
+{
+ gnutls_datum_t data;
+ int rc;
+
+ /* free it before we start */
+ if (server->dh_params_allocated) {
+ gnutls_dh_params_deinit(server->dh_params);
+ server->dh_params_allocated = false;
+ }
+ /* check if we have all data */
+ if (!server->tls_dh_pkcs3) {
+ LOGP(DTLS, LOGL_ERROR, "Can not generate missing pkcs3=%p\n",
+ server->tls_dh_pkcs3);
+ return;
+ }
+ /* initialize it again */
+ rc = gnutls_dh_params_init (&server->dh_params);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to allocate DH params rc=%d\n", rc);
+ server->dh_params_allocated = false;
+ return;
+ }
+ /* load prime and generator */
+ rc = gnutls_load_file(server->tls_dh_pkcs3, &data);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to load DH params from=%s rc=%d\n",
+ server->tls_dh_pkcs3, rc);
+ gnutls_dh_params_deinit(server->dh_params);
+ return;
+ }
+ rc = gnutls_dh_params_import_pkcs3(server->dh_params, &data, GNUTLS_X509_FMT_PEM);
+ gnutls_free(data.data);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to import DH params rc=%d\n", rc);
+ gnutls_dh_params_deinit(server->dh_params);
+ return;
+ }
+ /* done */
+ server->dh_params_allocated = true;
+}
+
+void osmo_tls_dh_generate(struct osmo_pcap_server *server)
+{
+ if (server->dh_params_allocated)
+ gnutls_dh_params_deinit(server->dh_params);
+ generate_dh_params(server);
+}
+
+static int cert_callback(gnutls_session_t tls_session,
+ const gnutls_datum_t * req_ca_rdn, int nreqs,
+ const gnutls_pk_algorithm_t * sign_algos,
+ int sign_algos_length, gnutls_pcert_st ** pcert,
+ unsigned int *pcert_length, gnutls_privkey_t * pkey)
+{
+ struct osmo_tls_session *sess = gnutls_session_get_ptr(tls_session);
+ gnutls_certificate_type_t type;
+
+ LOGP(DTLS, LOGL_DEBUG, "cert callback from server\n");
+ type = gnutls_certificate_type_get(tls_session);
+ if (type != GNUTLS_CRT_X509)
+ return -1;
+
+ *pcert_length = 1;
+ *pcert = &sess->pcert;
+ *pkey = sess->privk;
+ return 0;
+}
+
+static void tls_log_func(int level, const char *str)
+{
+ LOGP(DTLS, LOGL_DEBUG, "GNUtls: |<%d>| %s", level, str);
+}
+
+static int verify_cert_cb(gnutls_session_t session)
+{
+ const char *hostname;
+ unsigned int status;
+ int ret;
+
+ hostname = gnutls_session_get_ptr(session);
+ ret = gnutls_certificate_verify_peers3(session,
+ hostname, &status);
+ if (ret != 0)
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ if (status != 0)
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ return 0;
+}
+
+static void release_keys(struct osmo_tls_session *sess)
+{
+ if (sess->pcert_alloc) {
+ gnutls_pcert_deinit(&sess->pcert);
+ sess->pcert_alloc = false;
+ }
+ if (sess->privk_alloc) {
+ gnutls_privkey_deinit(sess->privk);
+ sess->privk_alloc = false;
+ }
+}
+
+void osmo_tls_init(void)
+{
+ int rc;
+ rc = gnutls_global_init();
+ CHECK_RC(rc, "init failed");
+ gnutls_global_set_log_function(tls_log_func);
+}
+
+void osmo_tls_server_init(struct osmo_pcap_server *server)
+{
+ int rc;
+
+ if (server->dh_params_allocated)
+ return;
+ rc = generate_dh_params(server);
+ CHECK_RC(rc, "dh params failed");
+}
+
+static int need_handshake(struct osmo_tls_session *tls_session)
+{
+ int rc;
+
+ rc = gnutls_handshake(tls_session->session);
+ if (rc == 0) {
+ /* handshake is done. start writing if we are allowed to */
+ LOGP(DTLS, LOGL_NOTICE, "TLS handshake done.\n");
+ if (!llist_empty(&tls_session->wqueue->msg_queue))
+ tls_session->wqueue->bfd.when = BSC_FD_WRITE | BSC_FD_READ;
+ else
+ tls_session->wqueue->bfd.when = BSC_FD_READ;
+ tls_session->need_handshake = false;
+ release_keys(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)) {
+ /* it failed for good.. */
+ LOGP(DTLS, LOGL_ERROR, "handshake failed rc=%d str=%s\n",
+ rc, gnutls_strerror(rc));
+ tls_session->wqueue->bfd.when = 0;
+ tls_session->error(tls_session);
+ }
+ return 0;
+}
+
+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;
+}
+
+static int tls_write(struct osmo_tls_session *sess)
+{
+ int rc;
+ sess->wqueue->bfd.when &= ~BSC_FD_WRITE;
+
+ if (llist_empty(&sess->wqueue->msg_queue))
+ return 0;
+
+ if (sess->need_resend) {
+ rc = gnutls_record_send(sess->session, NULL, 0);
+ } else {
+ struct msgb *msg;
+ msg = (struct msgb *) sess->wqueue->msg_queue.next;
+ rc = gnutls_record_send(sess->session, msg->data, msg->len);
+ }
+
+ if (rc > 0) {
+ sess->wqueue->current_length -= 1;
+ sess->need_resend = false;
+ struct msgb *msg = msgb_dequeue(&sess->wqueue->msg_queue);
+ msgb_free(msg);
+ } else if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) {
+ sess->need_resend = true;
+ } else if (gnutls_error_is_fatal(rc)) {
+ return rc;
+ }
+
+ if (sess->need_resend || !llist_empty(&sess->wqueue->msg_queue))
+ sess->wqueue->bfd.when |= BSC_FD_WRITE;
+ return rc;
+}
+
+int osmo_tls_client_bfd_cb(struct osmo_fd *fd, unsigned what)
+{
+ struct osmo_tls_session *sess = fd->data;
+
+ if (sess->need_handshake)
+ return need_handshake(sess);
+
+ if (what & BSC_FD_READ) {
+ int rc = tls_read(sess);
+ if (rc <= 0) {
+ sess->error(sess);
+ return rc;
+ }
+ }
+ if (what & BSC_FD_WRITE) {
+ int rc = tls_write(sess);
+ if (rc < 0) {
+ sess->error(sess);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int load_keys(struct osmo_pcap_client *client)
+{
+ struct osmo_tls_session *sess = &client->tls_session;
+ gnutls_datum_t data;
+ int rc;
+
+ if (!client->tls_client_cert || !client->tls_client_key) {
+ LOGP(DTLS, LOGL_DEBUG, "Skipping x509 client cert %p %p\n",
+ client->tls_client_cert, client->tls_client_key);
+ return 0;
+ }
+
+
+ rc = gnutls_load_file(client->tls_client_cert, &data);
+ if (rc < 0) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to load file=%s rc=%d\n",
+ client->tls_client_cert, rc);
+ return -1;
+ }
+ rc = gnutls_pcert_import_x509_raw(&sess->pcert, &data, GNUTLS_X509_FMT_PEM, 0);
+ gnutls_free(data.data);
+ if (rc < 0) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to import file=%s rc=%d\n",
+ client->tls_client_cert, rc);
+ return -1;
+ }
+ sess->pcert_alloc = true;
+
+ /* copied to RAM.. nothing we can do about it */
+ rc = gnutls_load_file(client->tls_client_key, &data);
+ if (rc < 0) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to load file=%s rc=%d\n",
+ client->tls_client_key, rc);
+ return -1;
+ }
+ gnutls_privkey_init(&sess->privk);
+ rc = gnutls_privkey_import_x509_raw(sess->privk, &data, GNUTLS_X509_FMT_PEM, NULL, 0);
+ gnutls_free(data.data);
+ if (rc < 0) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to load file=%s rc=%d\n",
+ client->tls_client_key, rc);
+ release_keys(sess);
+ return -1;
+ }
+ sess->privk_alloc = true;
+ 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 */
+ if (server->dh_params_allocated) {
+ gnutls_anon_set_server_dh_params(sess->anon_serv_cred, server->dh_params);
+ gnutls_certificate_set_dh_params(sess->cert_cred, server->dh_params);
+ }
+
+ if (server->tls_allow_anon)
+ gnutls_credentials_set(sess->session, GNUTLS_CRD_ANON, sess->anon_serv_cred);
+ if (server->tls_allow_x509)
+ 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 (server->tls_crlfile) {
+ rc = gnutls_certificate_set_x509_crl_file(
+ sess->cert_cred, server->tls_crlfile, GNUTLS_X509_FMT_PEM);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to load crlfile from path=%s rc=%d\n",
+ server->tls_crlfile, rc);
+ osmo_tls_release(sess);
+ return false;
+ }
+ }
+
+ if (server->tls_server_cert && server->tls_server_key) {
+ rc = gnutls_certificate_set_x509_key_file(
+ sess->cert_cred, server->tls_server_cert, server->tls_server_key,
+ GNUTLS_X509_FMT_PEM);
+ if (rc != GNUTLS_E_SUCCESS) {
+ LOGP(DTLS, LOGL_ERROR, "Failed to load crt/key from path=%s/%s rc=%d\n",
+ server->tls_server_cert, server->tls_server_key, rc);
+ osmo_tls_release(sess);
+ return false;
+ }
+ }
+
+ #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;
+ struct osmo_wqueue *wq = &client->wqueue;
+ unsigned int status;
+ int rc;
+
+ gnutls_global_set_log_level(client->tls_log_level);
+
+ memset(sess, 0, sizeof(*sess));
+ sess->in_use = sess->anon_alloc = sess->cert_alloc = false;
+ rc = gnutls_init(&sess->session, GNUTLS_CLIENT | 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 (client->tls_priority) {
+ const char *err;
+ rc = gnutls_priority_set_direct(sess->session, client->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_client_credentials(&sess->anon_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_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 */
+ gnutls_credentials_set(sess->session, GNUTLS_CRD_ANON, sess->anon_cred);
+ gnutls_credentials_set(sess->session, GNUTLS_CRD_CERTIFICATE, sess->cert_cred);
+
+ if (client->tls_capath) {
+ rc = gnutls_certificate_set_x509_trust_file(
+ sess->cert_cred, client->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",
+ client->tls_capath, rc);
+ osmo_tls_release(sess);
+ return false;
+ }
+ }
+
+ if (load_keys(client) != 0) {
+ osmo_tls_release(sess);
+ return false;
+ }
+
+ gnutls_certificate_set_retrieve_function2(sess->cert_cred, cert_callback);
+
+ /* set the hostname if we have one */
+ if (client->tls_hostname)
+ gnutls_server_name_set(sess->session, GNUTLS_NAME_DNS,
+ client->tls_hostname, strlen(client->tls_hostname));
+
+ /* do the verification */
+ if (client->tls_verify) {
+ gnutls_certificate_set_verify_function(sess->cert_cred, verify_cert_cb);
+ gnutls_certificate_verify_peers3(sess->session, client->tls_hostname, &status);
+ } else
+ LOGP(DTLS, LOGL_NOTICE, "Not going to validate certs as configured\n");
+
+ 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;
+}
+
+void osmo_tls_release(struct osmo_tls_session *session)
+{
+ if (!session->in_use)
+ return;
+
+ gnutls_deinit(session->session);
+
+ release_keys(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;
+}