Backport of: From 29ef8a04866ca14688d5b7fed7b8b9deab851f77 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Wed, 19 Jul 2023 14:02:27 +0000 Subject: [PATCH] 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 --- misc.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++- misc.h | 3 +- ssh-pkcs11.c | 6 +++- 3 files changed, 83 insertions(+), 4 deletions(-) --- a/misc.c +++ b/misc.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,9 @@ #ifdef HAVE_LIBGEN_H # include #endif +#ifdef HAVE_NLIST_H +#include +#endif #include #include #include @@ -2085,3 +2089,75 @@ localtime_r(&tt, &tm); strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm); } + +/* + * 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("nlist failed for %s", path); + goto out; + } + if (r != 0 || nl[0].n_value == 0 || nl[0].n_type == 0) { + error("library %s does not contain symbol %s", 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("open %s: %s", path, strerror(errno)); + return -1; + } + if (fstat(fd, &st) != 0) { + error("fstat %s: %s", path, strerror(errno)); + goto out; + } + if (!S_ISREG(st.st_mode)) { + error("%s is not a regular file", path); + goto out; + } + if (st.st_size < 0 || + (size_t)st.st_size < strlen(s) || + st.st_size >= INT_MAX/2) { + error("%s bad size %lld", 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("mmap %s: %s", path, strerror(errno)); + goto out; + } + if (memmem(m, sz, s, strlen(s)) == NULL) { + error("%s does not contain expected string %s", 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 */ +} --- a/misc.h +++ b/misc.h @@ -78,6 +78,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 lib_contains_symbol(const char *, const char *); void sock_set_v6only(int); --- a/ssh-pkcs11.c +++ b/ssh-pkcs11.c @@ -607,6 +607,10 @@ __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-libarary */ if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { error("dlopen %s failed: %s", provider_id, dlerror());