From 2f1be98e83feb90665b9292eff8bb734537fd491 Mon Sep 17 00:00:00 2001
From: "djm@openbsd.org" <djm@openbsd.org>
Date: Wed, 19 Jul 2023 14:02:27 +0000
Subject: [PATCH 03/12] upstream: Ensure FIDO/PKCS11 libraries contain expected
 symbols

This checks via nlist(3) that candidate provider libraries contain one
of the symbols that we will require prior to dlopen(), which can cause
a number of side effects, including execution of constructors.

Feedback deraadt; ok markus

OpenBSD-Commit-ID: 1508a5fbd74e329e69a55b56c453c292029aefbe

Upstream-Status: Backport [https://github.com/openssh/openssh-portable/commit/29ef8a04866ca14688d5b7fed7b8b9deab851f77]
CVE: CVE-2023-38408
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
---
 misc.c       | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 misc.h       |  1 +
 ssh-pkcs11.c |  4 +++
 ssh-sk.c     |  6 ++--
 4 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/misc.c b/misc.c
index 3a31d5c..8a107e4 100644
--- a/misc.c
+++ b/misc.c
@@ -28,6 +28,7 @@

 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -41,6 +42,9 @@
 #ifdef HAVE_POLL_H
 #include <poll.h>
 #endif
+#ifdef HAVE_NLIST_H
+#include <nlist.h>
+#endif
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -2266,3 +2270,76 @@ ssh_signal(int signum, sshsig_t handler)
	}
	return osa.sa_handler;
 }
+
+
+/*
+ * Returns zero if the library at 'path' contains symbol 's', nonzero
+ * otherwise.
+ */
+int
+lib_contains_symbol(const char *path, const char *s)
+{
+#ifdef HAVE_NLIST_H
+	struct nlist nl[2];
+	int ret = -1, r;
+
+	memset(nl, 0, sizeof(nl));
+	nl[0].n_name = xstrdup(s);
+	nl[1].n_name = NULL;
+	if ((r = nlist(path, nl)) == -1) {
+		error("%s: nlist failed for %s", __func__, path);
+		goto out;
+	}
+	if (r != 0 || nl[0].n_value == 0 || nl[0].n_type == 0) {
+		error("%s: library %s does not contain symbol %s", __func__, path, s);
+		goto out;
+	}
+	/* success */
+	ret = 0;
+ out:
+	free(nl[0].n_name);
+	return ret;
+#else /* HAVE_NLIST_H */
+	int fd, ret = -1;
+	struct stat st;
+	void *m = NULL;
+	size_t sz = 0;
+
+	memset(&st, 0, sizeof(st));
+	if ((fd = open(path, O_RDONLY)) < 0) {
+		error("%s: open %s: %s", __func__, path, strerror(errno));
+		return -1;
+	}
+	if (fstat(fd, &st) != 0) {
+		error("%s: fstat %s: %s", __func__, path, strerror(errno));
+		goto out;
+	}
+	if (!S_ISREG(st.st_mode)) {
+		error("%s: %s is not a regular file", __func__, path);
+		goto out;
+	}
+	if (st.st_size < 0 ||
+	    (size_t)st.st_size < strlen(s) ||
+	    st.st_size >= INT_MAX/2) {
+		error("%s: %s bad size %lld", __func__, path, (long long)st.st_size);
+		goto out;
+	}
+	sz = (size_t)st.st_size;
+	if ((m = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED ||
+	    m == NULL) {
+		error("%s: mmap %s: %s", __func__, path, strerror(errno));
+		goto out;
+	}
+	if (memmem(m, sz, s, strlen(s)) == NULL) {
+		error("%s: %s does not contain expected string %s", __func__, path, s);
+		goto out;
+	}
+	/* success */
+	ret = 0;
+ out:
+	if (m != NULL && m != MAP_FAILED)
+		munmap(m, sz);
+	close(fd);
+	return ret;
+#endif /* HAVE_NLIST_H */
+}
diff --git a/misc.h b/misc.h
index 4a05db2..3f9f4db 100644
--- a/misc.h
+++ b/misc.h
@@ -86,6 +86,7 @@ const char *atoi_err(const char *, int *);
 int	 parse_absolute_time(const char *, uint64_t *);
 void	 format_absolute_time(uint64_t, char *, size_t);
 int	 path_absolute(const char *);
+int	 lib_contains_symbol(const char *, const char *);

 void	 sock_set_v6only(int);

diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
index b56a41b..639a6f7 100644
--- a/ssh-pkcs11.c
+++ b/ssh-pkcs11.c
@@ -1499,6 +1499,10 @@ pkcs11_register_provider(char *provider_id, char *pin,
		    __func__, provider_id);
		goto fail;
	}
+	if (lib_contains_symbol(provider_id, "C_GetFunctionList") != 0) {
+		error("provider %s is not a PKCS11 library", provider_id);
+		goto fail;
+	}
	/* open shared pkcs11-library */
	if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
		error("dlopen %s failed: %s", provider_id, dlerror());
diff --git a/ssh-sk.c b/ssh-sk.c
index 5ff9381..9df12cc 100644
--- a/ssh-sk.c
+++ b/ssh-sk.c
@@ -119,10 +119,12 @@ sshsk_open(const char *path)
 #endif
		return ret;
	}
-	if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
-		error("Provider \"%s\" dlopen failed: %s", path, dlerror());
+	if (lib_contains_symbol(path, "sk_api_version") != 0) {
+		error("provider %s is not an OpenSSH FIDO library", path);
		goto fail;
	}
+	if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL)
+		fatal("Provider \"%s\" dlopen failed: %s", path, dlerror());
	if ((ret->sk_api_version = dlsym(ret->dlhandle,
	    "sk_api_version")) == NULL) {
		error("Provider \"%s\" dlsym(sk_api_version) failed: %s",
--
2.41.0
