server: Add vty interface for the TLS configuration

Make the priority configurable, load DH params, allow to specify
certificates or anonymous operations.

Change-Id: I8ec3c0f8e1ee2089e1b7dacd9de842260930032f
diff --git a/src/osmo_server_main.c b/src/osmo_server_main.c
index 27fb519..37a9632 100644
--- a/src/osmo_server_main.c
+++ b/src/osmo_server_main.c
@@ -247,6 +247,8 @@
 		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 1b7addc..a854223 100644
--- a/src/osmo_server_network.c
+++ b/src/osmo_server_network.c
@@ -491,7 +491,13 @@
 	client->state = STATE_INITIAL;
 	client->pend = sizeof(*client->data);
 
-	if (client->tls_use) {
+	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;
diff --git a/src/osmo_server_vty.c b/src/osmo_server_vty.c
index 14cdb89..b2919ae 100644
--- a/src/osmo_server_vty.c
+++ b/src/osmo_server_vty.c
@@ -41,6 +41,45 @@
 	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,6 +98,8 @@
 		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%s",
 			conn->name, conn->remote_host,
@@ -272,6 +313,195 @@
 	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);
@@ -285,6 +515,27 @@
 	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
index c42d242..aeecf6d 100644
--- a/src/osmo_tls.c
+++ b/src/osmo_tls.c
@@ -35,15 +35,81 @@
 			exit(1); \
 		}
 
-static gnutls_dh_params_t dh_params;
-static int generate_dh_params (void)
+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);
-	gnutls_dh_params_init (&dh_params);
-	return gnutls_dh_params_generate2 (dh_params, 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,
@@ -105,7 +171,15 @@
 	rc = gnutls_global_init();
 	CHECK_RC(rc, "init failed");
         gnutls_global_set_log_function(tls_log_func);
-	rc = generate_dh_params();
+}
+
+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");
 }
 
@@ -312,10 +386,15 @@
 	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->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(
@@ -328,12 +407,28 @@
 		}
 	}
 
-#if 0
-	if (load_keys(client) != 0) {
-		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;
+		}
 	}
-#endif
+
+	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"