summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorнаб <[email protected]>2021-05-13 06:21:35 +0200
committerTony Hutter <[email protected]>2022-02-16 17:58:37 -0800
commit9cbc2ed20f710326d16e8fe7357999eaa3f90142 (patch)
treec02322a179938cb917608e5404e0f8eff85eaaca /lib
parent9b185de6fa9f1b3a7614448fe0116ed370ec7e2f (diff)
libzfs: add keylocation=https://, backed by fetch(3) or libcurl
Add support for http and https to the keylocation properly to allow encryption keys to be fetched from the specified URL. Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Ryan Moeller <[email protected]> Signed-off-by: Ahelenia Ziemiańska <[email protected]> Issue #9543 Closes #9947 Closes #11956
Diffstat (limited to 'lib')
-rw-r--r--lib/libzfs/Makefile.am2
-rw-r--r--lib/libzfs/libzfs.abi73
-rw-r--r--lib/libzfs/libzfs_crypto.c186
-rw-r--r--lib/libzfs/libzfs_util.c8
4 files changed, 248 insertions, 21 deletions
diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am
index 1a7698b47..31267fd9a 100644
--- a/lib/libzfs/Makefile.am
+++ b/lib/libzfs/Makefile.am
@@ -75,7 +75,7 @@ libzfs_la_LIBADD = \
$(abs_top_builddir)/lib/libnvpair/libnvpair.la \
$(abs_top_builddir)/lib/libuutil/libuutil.la
-libzfs_la_LIBADD += -lm $(LIBCRYPTO_LIBS) $(ZLIB_LIBS) $(LTLIBINTL)
+libzfs_la_LIBADD += -lm $(LIBCRYPTO_LIBS) $(ZLIB_LIBS) $(LIBFETCH_LIBS) $(LTLIBINTL)
libzfs_la_LDFLAGS = -pthread
diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi
index 82f8b7dc8..14e03ee28 100644
--- a/lib/libzfs/libzfs.abi
+++ b/lib/libzfs/libzfs.abi
@@ -551,10 +551,6 @@
<parameter type-id='e1c52942'/>
<return type-id='95e97e5e'/>
</function-decl>
- <function-decl name='unlink' visibility='default' binding='global' size-in-bits='64'>
- <parameter type-id='80f4b756'/>
- <return type-id='95e97e5e'/>
- </function-decl>
</abi-instr>
<abi-instr address-size='64' path='os/linux/smb.c' language='LANG_C99'>
<array-type-def dimensions='1' type-id='a84c031d' size-in-bits='2040' id='11641789'>
@@ -1373,7 +1369,7 @@
<typedef-decl name='zpool_handle_t' type-id='67002a8a' id='b1efc708'/>
<typedef-decl name='libzfs_handle_t' type-id='c8a9d9d8' id='95942d0c'/>
<typedef-decl name='zfs_iter_f' type-id='5571cde4' id='d8e49ab9'/>
- <class-decl name='libzfs_handle' size-in-bits='20224' is-struct='yes' visibility='default' id='c8a9d9d8'>
+ <class-decl name='libzfs_handle' size-in-bits='20352' is-struct='yes' visibility='default' id='c8a9d9d8'>
<data-member access='public' layout-offset-in-bits='0'>
<var-decl name='libzfs_error' type-id='95e97e5e' visibility='default'/>
</data-member>
@@ -1434,6 +1430,12 @@
<data-member access='public' layout-offset-in-bits='20160'>
<var-decl name='libzfs_max_nvlist' type-id='9c313c2d' visibility='default'/>
</data-member>
+ <data-member access='public' layout-offset-in-bits='20224'>
+ <var-decl name='libfetch' type-id='eaa32e2f' visibility='default'/>
+ </data-member>
+ <data-member access='public' layout-offset-in-bits='20288'>
+ <var-decl name='libfetch_load_error' type-id='26a90f95' visibility='default'/>
+ </data-member>
</class-decl>
<class-decl name='zfs_handle' size-in-bits='4928' is-struct='yes' visibility='default' id='f6ee4445'>
<data-member access='public' layout-offset-in-bits='0'>
@@ -3190,6 +3192,19 @@
<function-decl name='__ctype_b_loc' visibility='default' binding='global' size-in-bits='64'>
<return type-id='c59e1ef0'/>
</function-decl>
+ <function-decl name='dlopen' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='80f4b756'/>
+ <parameter type-id='95e97e5e'/>
+ <return type-id='eaa32e2f'/>
+ </function-decl>
+ <function-decl name='dlsym' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='1b7446cd'/>
+ <parameter type-id='9d26089a'/>
+ <return type-id='eaa32e2f'/>
+ </function-decl>
+ <function-decl name='dlerror' visibility='default' binding='global' size-in-bits='64'>
+ <return type-id='26a90f95'/>
+ </function-decl>
<function-decl name='PKCS5_PBKDF2_HMAC_SHA1' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='80f4b756'/>
<parameter type-id='95e97e5e'/>
@@ -3231,6 +3246,11 @@
<parameter type-id='822cd80b'/>
<return type-id='95e97e5e'/>
</function-decl>
+ <function-decl name='fdopen' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='95e97e5e'/>
+ <parameter type-id='80f4b756'/>
+ <return type-id='822cd80b'/>
+ </function-decl>
<function-decl name='printf' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='80f4b756'/>
<parameter is-variadic='yes'/>
@@ -3243,6 +3263,12 @@
<parameter is-variadic='yes'/>
<return type-id='95e97e5e'/>
</function-decl>
+ <function-decl name='asprintf' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='8c85230f'/>
+ <parameter type-id='9d26089a'/>
+ <parameter is-variadic='yes'/>
+ <return type-id='95e97e5e'/>
+ </function-decl>
<function-decl name='fputc' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='95e97e5e'/>
<parameter type-id='822cd80b'/>
@@ -3262,6 +3288,10 @@
<parameter type-id='e75a27e9'/>
<return type-id='b59d7dce'/>
</function-decl>
+ <function-decl name='rewind' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='822cd80b'/>
+ <return type-id='48b5725f'/>
+ </function-decl>
<function-decl name='ferror' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='822cd80b'/>
<return type-id='95e97e5e'/>
@@ -3285,6 +3315,10 @@
<parameter type-id='b59d7dce'/>
<return type-id='eaa32e2f'/>
</function-decl>
+ <function-decl name='strdup' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='80f4b756'/>
+ <return type-id='26a90f95'/>
+ </function-decl>
<function-decl name='strerror' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='95e97e5e'/>
<return type-id='26a90f95'/>
@@ -3317,6 +3351,10 @@
<parameter type-id='95e97e5e'/>
<return type-id='95e97e5e'/>
</function-decl>
+ <function-decl name='unlink' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='80f4b756'/>
+ <return type-id='95e97e5e'/>
+ </function-decl>
<function-type size-in-bits='64' id='ee076206'>
<return type-id='48b5725f'/>
</function-type>
@@ -4425,12 +4463,6 @@
<parameter is-variadic='yes'/>
<return type-id='95e97e5e'/>
</function-decl>
- <function-decl name='asprintf' visibility='default' binding='global' size-in-bits='64'>
- <parameter type-id='8c85230f'/>
- <parameter type-id='9d26089a'/>
- <parameter is-variadic='yes'/>
- <return type-id='95e97e5e'/>
- </function-decl>
<function-decl name='strtol' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='9d26089a'/>
<parameter type-id='8c85230f'/>
@@ -4452,10 +4484,6 @@
<parameter type-id='b59d7dce'/>
<return type-id='26a90f95'/>
</function-decl>
- <function-decl name='strdup' visibility='default' binding='global' size-in-bits='64'>
- <parameter type-id='80f4b756'/>
- <return type-id='26a90f95'/>
- </function-decl>
<function-decl name='strrchr' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='80f4b756'/>
<parameter type-id='95e97e5e'/>
@@ -4620,11 +4648,6 @@
<parameter type-id='4051f5e7'/>
<return type-id='95e97e5e'/>
</function-decl>
- <function-decl name='fdopen' visibility='default' binding='global' size-in-bits='64'>
- <parameter type-id='95e97e5e'/>
- <parameter type-id='80f4b756'/>
- <return type-id='822cd80b'/>
- </function-decl>
<function-decl name='pipe2' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='7292109c'/>
<parameter type-id='95e97e5e'/>
@@ -6705,6 +6728,12 @@
<parameter type-id='95e97e5e'/>
<return type-id='48b5725f'/>
</function-decl>
+ <function-decl name='avl_insert' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='a3681dea'/>
+ <parameter type-id='eaa32e2f'/>
+ <parameter type-id='fba6cb51'/>
+ <return type-id='48b5725f'/>
+ </function-decl>
<function-decl name='nvlist_lookup_boolean' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='5ce45b60'/>
<parameter type-id='80f4b756'/>
@@ -7235,6 +7264,10 @@
<function-decl name='__ctype_toupper_loc' visibility='default' binding='global' size-in-bits='64'>
<return type-id='24f95ba5'/>
</function-decl>
+ <function-decl name='dlclose' visibility='default' binding='global' size-in-bits='64'>
+ <parameter type-id='eaa32e2f'/>
+ <return type-id='95e97e5e'/>
+ </function-decl>
<function-decl name='regcomp' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='5c53ba29'/>
<parameter type-id='9d26089a'/>
diff --git a/lib/libzfs/libzfs_crypto.c b/lib/libzfs/libzfs_crypto.c
index e31d4ce44..644dd2685 100644
--- a/lib/libzfs/libzfs_crypto.c
+++ b/lib/libzfs/libzfs_crypto.c
@@ -26,6 +26,16 @@
#include <signal.h>
#include <errno.h>
#include <openssl/evp.h>
+#if LIBFETCH_DYNAMIC
+#include <dlfcn.h>
+#endif
+#if LIBFETCH_IS_FETCH
+#include <sys/param.h>
+#include <stdio.h>
+#include <fetch.h>
+#elif LIBFETCH_IS_LIBCURL
+#include <curl/curl.h>
+#endif
#include <libzfs.h>
#include "libzfs_impl.h"
#include "zfeature_common.h"
@@ -59,9 +69,13 @@ static int caught_interrupt;
static int get_key_material_file(libzfs_handle_t *, const char *, const char *,
zfs_keyformat_t, boolean_t, uint8_t **, size_t *);
+static int get_key_material_https(libzfs_handle_t *, const char *, const char *,
+ zfs_keyformat_t, boolean_t, uint8_t **, size_t *);
static zfs_uri_handler_t uri_handlers[] = {
{ "file", get_key_material_file },
+ { "https", get_key_material_https },
+ { "http", get_key_material_https },
{ NULL, NULL }
};
@@ -483,6 +497,178 @@ get_key_material_file(libzfs_handle_t *hdl, const char *uri,
return (ret);
}
+static int
+get_key_material_https(libzfs_handle_t *hdl, const char *uri,
+ const char *fsname, zfs_keyformat_t keyformat, boolean_t newkey,
+ uint8_t **restrict buf, size_t *restrict len_out)
+{
+ int ret = 0;
+ FILE *key = NULL;
+ boolean_t is_http = strncmp(uri, "http:", strlen("http:")) == 0;
+
+ if (strlen(uri) < (is_http ? 7 : 8)) {
+ ret = EINVAL;
+ goto end;
+ }
+
+#if LIBFETCH_DYNAMIC
+#define LOAD_FUNCTION(func) \
+ __typeof__(func) *func = dlsym(hdl->libfetch, #func);
+
+ if (hdl->libfetch == NULL)
+ hdl->libfetch = dlopen(LIBFETCH_SONAME, RTLD_LAZY);
+
+ if (hdl->libfetch == NULL) {
+ hdl->libfetch = (void *)-1;
+ char *err = dlerror();
+ if (err)
+ hdl->libfetch_load_error = strdup(err);
+ }
+
+ if (hdl->libfetch == (void *)-1) {
+ ret = ENOSYS;
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Couldn't load %s: %s"),
+ LIBFETCH_SONAME, hdl->libfetch_load_error ?: "(?)");
+ goto end;
+ }
+
+ boolean_t ok;
+#if LIBFETCH_IS_FETCH
+ LOAD_FUNCTION(fetchGetURL);
+ char *fetchLastErrString = dlsym(hdl->libfetch, "fetchLastErrString");
+
+ ok = fetchGetURL && fetchLastErrString;
+#elif LIBFETCH_IS_LIBCURL
+ LOAD_FUNCTION(curl_easy_init);
+ LOAD_FUNCTION(curl_easy_setopt);
+ LOAD_FUNCTION(curl_easy_perform);
+ LOAD_FUNCTION(curl_easy_cleanup);
+ LOAD_FUNCTION(curl_easy_strerror);
+ LOAD_FUNCTION(curl_easy_getinfo);
+
+ ok = curl_easy_init && curl_easy_setopt && curl_easy_perform &&
+ curl_easy_cleanup && curl_easy_strerror && curl_easy_getinfo;
+#endif
+ if (!ok) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "keylocation=%s back-end %s missing symbols."),
+ is_http ? "http://" : "https://", LIBFETCH_SONAME);
+ ret = ENOSYS;
+ goto end;
+ }
+#endif
+
+#if LIBFETCH_IS_FETCH
+ key = fetchGetURL(uri, "");
+ if (key == NULL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Couldn't GET %s: %s"),
+ uri, fetchLastErrString);
+ ret = ENETDOWN;
+ }
+#elif LIBFETCH_IS_LIBCURL
+ CURL *curl = curl_easy_init();
+ if (curl == NULL) {
+ ret = ENOTSUP;
+ goto end;
+ }
+
+ int kfd = -1;
+#ifdef O_TMPFILE
+ kfd = open(getenv("TMPDIR") ?: "/tmp",
+ O_RDWR | O_TMPFILE | O_EXCL | O_CLOEXEC, 0600);
+ if (kfd != -1)
+ goto kfdok;
+#endif
+
+ char *path;
+ if (asprintf(&path,
+ "%s/libzfs-XXXXXXXX.https", getenv("TMPDIR") ?: "/tmp") == -1) {
+ ret = ENOMEM;
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s"),
+ strerror(ret));
+ goto end;
+ }
+
+ kfd = mkostemps(path, strlen(".https"), O_CLOEXEC);
+ if (kfd == -1) {
+ ret = errno;
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Couldn't create temporary file %s: %s"),
+ path, strerror(ret));
+ free(path);
+ goto end;
+ }
+ (void) unlink(path);
+ free(path);
+
+kfdok:
+ if ((key = fdopen(kfd, "r+")) == NULL) {
+ ret = errno;
+ free(path);
+ (void) close(kfd);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Couldn't reopen temporary file: %s"), strerror(ret));
+ goto end;
+ }
+
+ char errbuf[CURL_ERROR_SIZE] = "";
+ char *cainfo = getenv("SSL_CA_CERT_FILE"); /* matches fetch(3) */
+ char *capath = getenv("SSL_CA_CERT_PATH"); /* matches fetch(3) */
+ char *clcert = getenv("SSL_CLIENT_CERT_FILE"); /* matches fetch(3) */
+ char *clkey = getenv("SSL_CLIENT_KEY_FILE"); /* matches fetch(3) */
+ (void) curl_easy_setopt(curl, CURLOPT_URL, uri);
+ (void) curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ (void) curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 30000L);
+ (void) curl_easy_setopt(curl, CURLOPT_WRITEDATA, key);
+ (void) curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
+ if (cainfo != NULL)
+ (void) curl_easy_setopt(curl, CURLOPT_CAINFO, cainfo);
+ if (capath != NULL)
+ (void) curl_easy_setopt(curl, CURLOPT_CAPATH, capath);
+ if (clcert != NULL)
+ (void) curl_easy_setopt(curl, CURLOPT_SSLCERT, clcert);
+ if (clkey != NULL)
+ (void) curl_easy_setopt(curl, CURLOPT_SSLKEY, clkey);
+
+ CURLcode res = curl_easy_perform(curl);
+
+ if (res != CURLE_OK) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Failed to connect to %s: %s"),
+ uri, strlen(errbuf) ? errbuf : curl_easy_strerror(res));
+ ret = ENETDOWN;
+ } else {
+ long resp = 200;
+ (void) curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &resp);
+
+ if (resp < 200 || resp >= 300) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Couldn't GET %s: %ld"),
+ uri, resp);
+ ret = ENOENT;
+ } else
+ rewind(key);
+ }
+
+ curl_easy_cleanup(curl);
+#else
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "No keylocation=%s back-end."), is_http ? "http://" : "https://");
+ ret = ENOSYS;
+#endif
+
+end:
+ if (ret == 0)
+ ret = get_key_material_raw(key, keyformat, buf, len_out);
+
+ if (key != NULL)
+ fclose(key);
+
+ return (ret);
+}
+
/*
* Attempts to fetch key material, no matter where it might live. The key
* material is allocated and returned in km_out. *can_retry_out will be set
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index 6e57d8e42..7dd38bb3d 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -44,6 +44,9 @@
#include <strings.h>
#include <unistd.h>
#include <math.h>
+#if LIBFETCH_DYNAMIC
+#include <dlfcn.h>
+#endif
#include <sys/stat.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
@@ -1101,6 +1104,11 @@ libzfs_fini(libzfs_handle_t *hdl)
libzfs_core_fini();
regfree(&hdl->libzfs_urire);
fletcher_4_fini();
+#if LIBFETCH_DYNAMIC
+ if (hdl->libfetch != (void *)-1 && hdl->libfetch != NULL)
+ (void) dlclose(hdl->libfetch);
+ free(hdl->libfetch_load_error);
+#endif
free(hdl);
}