From 9d1abf53df99d3b743fda5e8a012e4185c0cb503 Mon Sep 17 00:00:00 2001
From: Maxime Bizon <mbizon@freebox.fr>
Date: Mon, 12 Oct 2020 12:00:24 +0200
Subject: [PATCH 6/6] support loading CA certs from fbxpki

---
 libpjproject.pc.in           |   3 +-
 pjlib/src/pj/ssl_sock_ossl.c | 157 +++++++++++++++++++++++++++++++++++
 2 files changed, 159 insertions(+), 1 deletion(-)

diff --git a/libpjproject.pc.in b/libpjproject.pc.in
index fa8d39114..19741720a 100644
--- a/libpjproject.pc.in
+++ b/libpjproject.pc.in
@@ -9,5 +9,6 @@ Name: libpjproject
 Description: Multimedia communication library
 URL: http://www.pjsip.org
 Version: @PJ_VERSION@
-Libs: -L${libdir} @PJ_LDXXLIBS@
+Requires.private: fbxpkiipc
+Libs: -L${libdir} @PJ_LDXXLIBS@ -lfbxpkiipc
 Cflags: -I${includedir} @PJ_INSTALL_CXXFLAGS@
diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
index a95b339a5..7747931bb 100644
--- a/pjlib/src/pj/ssl_sock_ossl.c
+++ b/pjlib/src/pj/ssl_sock_ossl.c
@@ -16,6 +16,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
+#define _GNU_SOURCE
+#include <stdio.h>
+
 #include <pj/ssl_sock.h>
 #include <pj/activesock.h>
 #include <pj/compat/socket.h>
@@ -31,6 +34,8 @@
 #include <pj/string.h>
 #include <pj/timer.h>
 
+#include "fbxpki_ipc.h"
+
 /* Only build when PJ_HAS_SSL_SOCK is enabled and when the backend is
  * OpenSSL.
  */
@@ -719,6 +724,144 @@ static pj_status_t set_sigalgs(pj_ssl_sock_t *ssock);
 static void set_entropy(pj_ssl_sock_t *ssock);
 
 
+static X509 *read_from_pem(const char *pem_data, size_t len)
+{
+	FILE *f;
+	X509 *x509;
+
+	f = fmemopen((char *)pem_data, len, "r");
+	if (!f) {
+		fprintf(stderr, "failed to load pem data from memory\n");
+		return NULL;
+	}
+
+	x509 = PEM_read_X509(f, NULL, NULL, NULL);
+	fclose(f);
+	if (!x509) {
+		fprintf(stderr, "failed to load cert from pem data\n");
+		return NULL;
+	}
+
+	return x509;
+}
+
+static int get_fbxpki_cert_list(const char *key,
+				int cmd_type,
+				char **ret_certs,
+				size_t *ret_certs_len)
+{
+	struct fbxpki_ipc_hdr *cmd;
+	uint8_t *certs;
+	size_t cmdlen, certs_len;
+	int fd, ret;
+
+	fd = fbxpki_ipc_open_fd();
+	if (fd < 0)
+		return 1;
+
+	cmdlen = sizeof (*cmd) + strlen(key) + 1;
+	cmd = malloc(cmdlen);
+	if (!cmd) {
+		close(fd);
+		return 1;
+	}
+
+	FBXPKI_IPC_FILL(cmd, cmd_type, 0, cmdlen);
+	memcpy(cmd->data, key, strlen(key) + 1);
+
+	ret = fbxpki_ipc_do_cmd(fd, cmd, cmdlen, &certs, &certs_len);
+	close(fd);
+	free(cmd);
+	if (ret)
+		return 1;
+
+	if (certs_len && certs[certs_len - 1]) {
+		/* must be null terminated */
+		free(certs);
+		return 1;
+	}
+
+	*ret_certs = (char *)certs;
+	*ret_certs_len = certs_len;
+	return 0;
+}
+
+static int verify_fbxpki_add_single_namespace(const char *key,
+					      X509_STORE *store)
+{
+	char *certs, *start, *p;
+	size_t certs_len;
+
+	if (get_fbxpki_cert_list(key, E_CMD_GET_CERT, &certs, &certs_len))
+		return 1;
+
+	if (!certs_len) /* no cert to add */
+		return 0;
+
+	p = start = (char *)certs;
+	while (p < (char *)certs + certs_len) {
+		if (*p) {
+			p++;
+			continue;
+		}
+
+		X509 *x509 = read_from_pem(start, p - start);
+		if (!x509 || !X509_STORE_add_cert(store, x509)) {
+			fprintf(stderr, "failed to add cert to store\n");
+			free(certs);
+			return 1;
+		}
+		X509_free(x509);
+
+		start = p + 1;
+		p = start;
+	}
+
+	free(certs);
+	return 0;
+}
+
+static int verify_fbxpki_setup(SSL_CTX *ssl_ctx,
+			       const char *path,
+			       size_t path_len)
+{
+	X509_STORE *store;
+	char *copy, *p, *end;
+	int ret;
+
+	store = X509_STORE_new();
+	if (!store) {
+		return 1;
+	}
+
+	ret = asprintf(&copy, "%.*s", (int)path_len, path);
+	if (ret < 0)
+		return 1;
+
+	p = copy;
+	while (*p) {
+		end = strchr(p, ',');
+		if (end)
+			*end++ = 0;
+
+		int ret = verify_fbxpki_add_single_namespace(p,
+							     store);
+		if (ret) {
+			free(copy);
+			return ret;
+		}
+
+		if (end)
+			p = end;
+		else
+			break;
+	}
+	free(copy);
+
+	SSL_CTX_set_cert_store(ssl_ctx, store);
+	return 0;
+}
+
 static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool)
 {
     return (pj_ssl_sock_t *)PJ_POOL_ZALLOC_T(pool, ossl_sock_t);
@@ -828,6 +971,20 @@ static pj_status_t ssl_create(pj_ssl_sock_t *ssock)
 
     /* Apply credentials */
     if (cert) {
+	/* Load CA list if one is specified. */
+	if (cert->CA_path.slen > 7 &&
+	    !strncmp(cert->CA_path.ptr, "fbxpki:", 7)) {
+		if (verify_fbxpki_setup(ctx,
+					cert->CA_path.ptr + 7,
+					cert->CA_path.slen - 7)) {
+		    PJ_PERROR(1,(ssock->pool->obj_name, PJ_EUNKNOWN,
+				 "Error loading from fbxpki '%s'",
+				 cert->CA_path.ptr));
+		    SSL_CTX_free(ctx);
+		    return PJ_EUNKNOWN;
+		}
+	}
+
 	/* Load CA list if one is specified. */
 	if (cert->CA_file.slen || cert->CA_path.slen) {
 
-- 
2.17.1

