]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Support custom URI schemes for the keylocation property
authorJason King <jasonbking@users.noreply.github.com>
Tue, 28 Apr 2020 17:55:18 +0000 (12:55 -0500)
committerGitHub <noreply@github.com>
Tue, 28 Apr 2020 17:55:18 +0000 (10:55 -0700)
Every platform has their own preferred methods for implementing URI
schemes beyond the currently supported file scheme (e.g. 'https' on
FreeBSD would likely use libfetch, while Linux distros and illumos
would probably use libcurl, etc). It would be helpful if libzfs can
be extended to support additional schemes in a simple manner.

A table of (scheme, handler_function) pairs is added to libzfs_crypto.c,
and the existing functions in libzfs_crypto.c so that when the key
format is ZFS_KEYFORMAT_URI, the scheme from the URI string is
extracted, and a matching handler it located in the aforementioned
table (returning an error if no matching handler is found). The handler
function is then invoked to retrieve the key material (in the format
specified by the keyformat property) and the key is loaded or the
handler can return an error to abort the key loading process.

Reviewed by: Sean Eric Fagan <sef@ixsystems.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Jason King <jason.king@joyent.com>
Closes #10218

include/libzfs_impl.h
lib/libzfs/libzfs_crypto.c
lib/libzfs/libzfs_util.c

index 5cec629132ae3bc31252c4862d0b180ee8fb121d..be11b1fc050e5298a05c5386c4c45c01614eec9a 100644 (file)
@@ -23,6 +23,7 @@
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
  * Copyright (c) 2018 Datto Inc.
+ * Copyright 2020 Joyent, Inc.
  */
 
 #ifndef        _LIBZFS_IMPL_H
@@ -33,6 +34,7 @@
 #include <sys/nvpair.h>
 #include <sys/dmu.h>
 #include <sys/zfs_ioctl.h>
+#include <regex.h>
 
 #include <libuutil.h>
 #include <libzfs.h>
@@ -71,6 +73,7 @@ struct libzfs_handle {
        int libzfs_pool_iter;
        char libzfs_chassis_id[256];
        boolean_t libzfs_prop_debug;
+       regex_t libzfs_urire;
 };
 
 #define        ZFSSHARE_MISS   0x01    /* Didn't find entry in cache */
@@ -124,6 +127,14 @@ typedef enum {
        SHARED_SMB = 0x4
 } zfs_share_type_t;
 
+typedef int (*zfs_uri_handler_fn_t)(struct libzfs_handle *, const char *,
+    const char *, zfs_keyformat_t, boolean_t, uint8_t **, size_t *);
+
+typedef struct zfs_uri_handler {
+       const char *zuh_scheme;
+       zfs_uri_handler_fn_t zuh_handler;
+} zfs_uri_handler_t;
+
 #define        CONFIG_BUF_MINSIZE      262144
 
 int zfs_error(libzfs_handle_t *, int, const char *);
index b7b567ef53c5983c5e6ac37f4da8162f2c6e97a9..8e7005f5aa31913908433f07d8ffbef960195099 100644 (file)
@@ -15,6 +15,7 @@
 
 /*
  * Copyright (c) 2017, Datto, Inc. All rights reserved.
+ * Copyright 2020 Joyent, Inc.
  */
 
 #include <sys/zfs_context.h>
@@ -62,6 +63,14 @@ typedef enum key_locator {
 
 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 zfs_uri_handler_t uri_handlers[] = {
+       { "file", get_key_material_file },
+       { NULL, NULL }
+};
+
 static int
 pkcs11_get_urandom(uint8_t *buf, size_t bytes)
 {
@@ -85,15 +94,49 @@ pkcs11_get_urandom(uint8_t *buf, size_t bytes)
        return (bytes_read);
 }
 
-static zfs_keylocation_t
-zfs_prop_parse_keylocation(const char *str)
+static int
+zfs_prop_parse_keylocation(libzfs_handle_t *restrict hdl, const char *str,
+    zfs_keylocation_t *restrict locp, char **restrict schemep)
 {
-       if (strcmp("prompt", str) == 0)
-               return (ZFS_KEYLOCATION_PROMPT);
-       else if (strlen(str) > 8 && strncmp("file:///", str, 8) == 0)
-               return (ZFS_KEYLOCATION_URI);
+       *locp = ZFS_KEYLOCATION_NONE;
+       *schemep = NULL;
+
+       if (strcmp("prompt", str) == 0) {
+               *locp = ZFS_KEYLOCATION_PROMPT;
+               return (0);
+       }
+
+       regmatch_t pmatch[2];
+
+       if (regexec(&hdl->libzfs_urire, str, ARRAY_SIZE(pmatch),
+           pmatch, 0) == 0) {
+               size_t scheme_len;
+
+               if (pmatch[1].rm_so == -1) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Invalid URI"));
+                       return (EINVAL);
+               }
+
+               scheme_len = pmatch[1].rm_eo - pmatch[1].rm_so;
+
+               *schemep = calloc(1, scheme_len + 1);
+               if (*schemep == NULL) {
+                       int ret = errno;
+
+                       errno = 0;
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Invalid URI"));
+                       return (ret);
+               }
+
+               (void) memcpy(*schemep, str + pmatch[1].rm_so, scheme_len);
+               *locp = ZFS_KEYLOCATION_URI;
+               return (0);
+       }
 
-       return (ZFS_KEYLOCATION_NONE);
+       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid keylocation"));
+       return (EINVAL);
 }
 
 static int
@@ -146,62 +189,235 @@ get_format_prompt_string(zfs_keyformat_t format)
        }
 }
 
+/* do basic validation of the key material */
 static int
-get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat,
-    boolean_t again, boolean_t newkey, uint8_t **buf, size_t *len_out)
+validate_key(libzfs_handle_t *hdl, zfs_keyformat_t keyformat,
+    const char *key, size_t keylen)
 {
-       int ret = 0, bytes;
+       switch (keyformat) {
+       case ZFS_KEYFORMAT_RAW:
+               /* verify the key length is correct */
+               if (keylen < WRAPPING_KEY_LEN) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Raw key too short (expected %u)."),
+                           WRAPPING_KEY_LEN);
+                       return (EINVAL);
+               }
+
+               if (keylen > WRAPPING_KEY_LEN) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Raw key too long (expected %u)."),
+                           WRAPPING_KEY_LEN);
+                       return (EINVAL);
+               }
+               break;
+       case ZFS_KEYFORMAT_HEX:
+               /* verify the key length is correct */
+               if (keylen < WRAPPING_KEY_LEN * 2) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Hex key too short (expected %u)."),
+                           WRAPPING_KEY_LEN * 2);
+                       return (EINVAL);
+               }
+
+               if (keylen > WRAPPING_KEY_LEN * 2) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Hex key too long (expected %u)."),
+                           WRAPPING_KEY_LEN * 2);
+                       return (EINVAL);
+               }
+
+               /* check for invalid hex digits */
+               for (size_t i = 0; i < WRAPPING_KEY_LEN * 2; i++) {
+                       if (!isxdigit(key[i])) {
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "Invalid hex character detected."));
+                               return (EINVAL);
+                       }
+               }
+               break;
+       case ZFS_KEYFORMAT_PASSPHRASE:
+               /* verify the length is within bounds */
+               if (keylen > MAX_PASSPHRASE_LEN) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Passphrase too long (max %u)."),
+                           MAX_PASSPHRASE_LEN);
+                       return (EINVAL);
+               }
+
+               if (keylen < MIN_PASSPHRASE_LEN) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "Passphrase too short (min %u)."),
+                           MIN_PASSPHRASE_LEN);
+                       return (EINVAL);
+               }
+               break;
+       default:
+               /* can't happen, checked above */
+               break;
+       }
+
+       return (0);
+}
+
+static int
+libzfs_getpassphrase(zfs_keyformat_t keyformat, boolean_t is_reenter,
+    boolean_t new_key, const char *fsname,
+    char **restrict res, size_t *restrict reslen)
+{
+       FILE *f = stdin;
        size_t buflen = 0;
+       ssize_t bytes;
+       int ret = 0;
        struct termios old_term, new_term;
        struct sigaction act, osigint, osigtstp;
 
-       *len_out = 0;
+       *res = NULL;
+       *reslen = 0;
 
-       if (isatty(fileno(fd))) {
-               /*
-                * handle SIGINT and ignore SIGSTP. This is necessary to
-                * restore the state of the terminal.
-                */
-               caught_interrupt = 0;
-               act.sa_flags = 0;
-               (void) sigemptyset(&act.sa_mask);
-               act.sa_handler = catch_signal;
-
-               (void) sigaction(SIGINT, &act, &osigint);
-               act.sa_handler = SIG_IGN;
-               (void) sigaction(SIGTSTP, &act, &osigtstp);
-
-               /* prompt for the key */
-               if (fsname != NULL) {
-                       (void) printf("%s %s%s for '%s': ",
-                           (again) ? "Re-enter" : "Enter",
-                           (newkey) ? "new " : "",
-                           get_format_prompt_string(keyformat), fsname);
-               } else {
-                       (void) printf("%s %s%s: ",
-                           (again) ? "Re-enter" : "Enter",
-                           (newkey) ? "new " : "",
-                           get_format_prompt_string(keyformat));
+       /*
+        * handle SIGINT and ignore SIGSTP. This is necessary to
+        * restore the state of the terminal.
+        */
+       caught_interrupt = 0;
+       act.sa_flags = 0;
+       (void) sigemptyset(&act.sa_mask);
+       act.sa_handler = catch_signal;
+
+       (void) sigaction(SIGINT, &act, &osigint);
+       act.sa_handler = SIG_IGN;
+       (void) sigaction(SIGTSTP, &act, &osigtstp);
+
+       (void) printf("%s %s%s",
+           is_reenter ? "Re-enter" : "Enter",
+           new_key ? "new " : "",
+           get_format_prompt_string(keyformat));
+       if (fsname != NULL)
+               (void) printf(" for '%s'", fsname);
+       (void) fputc(':', stdout);
+       (void) fflush(stdout);
+
+       /* disable the terminal echo for key input */
+       (void) tcgetattr(fileno(f), &old_term);
+
+       new_term = old_term;
+       new_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+
+       ret = tcsetattr(fileno(f), TCSAFLUSH, &new_term);
+       if (ret != 0) {
+               ret = errno;
+               errno = 0;
+               goto out;
+       }
 
-               }
-               (void) fflush(stdout);
+       bytes = getline(res, &buflen, f);
+       if (bytes < 0) {
+               ret = errno;
+               errno = 0;
+               goto out;
+       }
 
-               /* disable the terminal echo for key input */
-               (void) tcgetattr(fileno(fd), &old_term);
+       /* trim the ending newline if it exists */
+       if (bytes > 0 && (*res)[bytes - 1] == '\n') {
+               (*res)[bytes - 1] = '\0';
+               bytes--;
+       }
 
-               new_term = old_term;
-               new_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+       *reslen = bytes;
 
-               ret = tcsetattr(fileno(fd), TCSAFLUSH, &new_term);
-               if (ret != 0) {
-                       ret = errno;
-                       errno = 0;
-                       goto out;
-               }
+out:
+       /* reset the teminal */
+       (void) tcsetattr(fileno(f), TCSAFLUSH, &old_term);
+       (void) sigaction(SIGINT, &osigint, NULL);
+       (void) sigaction(SIGTSTP, &osigtstp, NULL);
+
+       /* if we caught a signal, re-throw it now */
+       if (caught_interrupt != 0)
+               (void) kill(getpid(), caught_interrupt);
+
+       /* print the newline that was not echo'd */
+       (void) printf("\n");
+
+       return (ret);
+}
+
+static int
+get_key_interactive(libzfs_handle_t *restrict hdl, const char *fsname,
+    zfs_keyformat_t keyformat, boolean_t confirm_key, boolean_t newkey,
+    uint8_t **restrict outbuf, size_t *restrict len_out)
+{
+       char *buf = NULL, *buf2 = NULL;
+       size_t buflen = 0, buf2len = 0;
+       int ret = 0;
+
+       ASSERT(isatty(fileno(stdin)));
+
+       /* raw keys cannot be entered on the terminal */
+       if (keyformat == ZFS_KEYFORMAT_RAW) {
+               ret = EINVAL;
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "Cannot enter raw keys on the terminal"));
+               goto out;
+       }
+
+       /* prompt for the key */
+       if ((ret = libzfs_getpassphrase(keyformat, B_FALSE, newkey, fsname,
+           &buf, &buflen)) != 0) {
+               free(buf);
+               buf = NULL;
+               buflen = 0;
+               goto out;
        }
 
+       if (!confirm_key)
+               goto out;
+
+       if ((ret = validate_key(hdl, keyformat, buf, buflen)) != 0) {
+               free(buf);
+               return (ret);
+       }
+
+       ret = libzfs_getpassphrase(keyformat, B_TRUE, newkey, fsname, &buf2,
+           &buf2len);
+       if (ret != 0) {
+               free(buf);
+               free(buf2);
+               buf = buf2 = NULL;
+               buflen = buf2len = 0;
+               goto out;
+       }
+
+       if (buflen != buf2len || strcmp(buf, buf2) != 0) {
+               free(buf);
+               buf = NULL;
+               buflen = 0;
+
+               ret = EINVAL;
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "Provided keys do not match."));
+       }
+
+       free(buf2);
+
+out:
+       *outbuf = (uint8_t *)buf;
+       *len_out = buflen;
+       return (ret);
+}
+
+static int
+get_key_material_raw(FILE *fd, zfs_keyformat_t keyformat,
+    uint8_t **buf, size_t *len_out)
+{
+       int ret = 0;
+       size_t buflen = 0;
+
+       *len_out = 0;
+
        /* read the key material */
        if (keyformat != ZFS_KEYFORMAT_RAW) {
+               ssize_t bytes;
+
                bytes = getline((char **)buf, &buflen, fd);
                if (bytes < 0) {
                        ret = errno;
@@ -210,25 +426,29 @@ get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat,
                }
 
                /* trim the ending newline if it exists */
-               if ((*buf)[bytes - 1] == '\n') {
+               if (bytes > 0 && (*buf)[bytes - 1] == '\n') {
                        (*buf)[bytes - 1] = '\0';
                        bytes--;
                }
+
+               *len_out = bytes;
        } else {
+               size_t n;
+
                /*
                 * Raw keys may have newline characters in them and so can't
                 * use getline(). Here we attempt to read 33 bytes so that we
                 * can properly check the key length (the file should only have
                 * 32 bytes).
                 */
-               *buf = malloc((WRAPPING_KEY_LEN + 1) * sizeof (char));
+               *buf = malloc((WRAPPING_KEY_LEN + 1) * sizeof (uint8_t));
                if (*buf == NULL) {
                        ret = ENOMEM;
                        goto out;
                }
 
-               bytes = fread(*buf, 1, WRAPPING_KEY_LEN + 1, fd);
-               if (bytes < 0) {
+               n = fread(*buf, 1, WRAPPING_KEY_LEN + 1, fd);
+               if (n == 0 || ferror(fd)) {
                        /* size errors are handled by the calling function */
                        free(*buf);
                        *buf = NULL;
@@ -236,28 +456,37 @@ get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat,
                        errno = 0;
                        goto out;
                }
+
+               *len_out = n;
        }
+out:
+       return (ret);
+}
 
-       *len_out = bytes;
+static int
+get_key_material_file(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)
+{
+       FILE *f = NULL;
+       int ret = 0;
 
-out:
-       if (isatty(fileno(fd))) {
-               /* reset the terminal */
-               (void) tcsetattr(fileno(fd), TCSAFLUSH, &old_term);
-               (void) sigaction(SIGINT, &osigint, NULL);
-               (void) sigaction(SIGTSTP, &osigtstp, NULL);
-
-               /* if we caught a signal, re-throw it now */
-               if (caught_interrupt != 0) {
-                       (void) kill(getpid(), caught_interrupt);
-               }
+       if (strlen(uri) < 7)
+               return (EINVAL);
 
-               /* print the newline that was not echo'd */
-               printf("\n");
+       if ((f = fopen(uri + 7, "r")) == NULL) {
+               ret = errno;
+               errno = 0;
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "Failed to open key material file"));
+               return (ret);
        }
 
-       return (ret);
+       ret = get_key_material_raw(f, keyformat, buf, len_out);
 
+       (void) fclose(f);
+
+       return (ret);
 }
 
 /*
@@ -271,169 +500,83 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, boolean_t newkey,
     zfs_keyformat_t keyformat, char *keylocation, const char *fsname,
     uint8_t **km_out, size_t *kmlen_out, boolean_t *can_retry_out)
 {
-       int ret, i;
+       int ret;
        zfs_keylocation_t keyloc = ZFS_KEYLOCATION_NONE;
-       FILE *fd = NULL;
-       uint8_t *km = NULL, *km2 = NULL;
-       size_t kmlen, kmlen2;
+       uint8_t *km = NULL;
+       size_t kmlen = 0;
+       char *uri_scheme = NULL;
+       zfs_uri_handler_t *handler = NULL;
        boolean_t can_retry = B_FALSE;
 
        /* verify and parse the keylocation */
-       keyloc = zfs_prop_parse_keylocation(keylocation);
+       ret = zfs_prop_parse_keylocation(hdl, keylocation, &keyloc,
+           &uri_scheme);
+       if (ret != 0)
+               goto error;
 
        /* open the appropriate file descriptor */
        switch (keyloc) {
        case ZFS_KEYLOCATION_PROMPT:
-               fd = stdin;
-               if (isatty(fileno(fd))) {
+               if (isatty(fileno(stdin))) {
                        can_retry = B_TRUE;
-
-                       /* raw keys cannot be entered on the terminal */
-                       if (keyformat == ZFS_KEYFORMAT_RAW) {
-                               ret = EINVAL;
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "Cannot enter raw keys on the terminal"));
-                               goto error;
-                       }
-               }
-               break;
-       case ZFS_KEYLOCATION_URI:
-               fd = fopen(&keylocation[7], "r");
-               if (!fd) {
-                       ret = errno;
-                       errno = 0;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Failed to open key material file"));
-                       goto error;
+                       ret = get_key_interactive(hdl, fsname, keyformat,
+                           do_verify, newkey, km_out, kmlen_out);
+               } else {
+                       /* fetch the key material into the buffer */
+                       ret = get_key_material_raw(stdin, keyformat, &km,
+                           &kmlen);
                }
-               break;
-       default:
-               ret = EINVAL;
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "Invalid keylocation."));
-               goto error;
-       }
 
-       /* fetch the key material into the buffer */
-       ret = get_key_material_raw(fd, fsname, keyformat, B_FALSE, newkey,
-           &km, &kmlen);
-       if (ret != 0)
-               goto error;
-
-       /* do basic validation of the key material */
-       switch (keyformat) {
-       case ZFS_KEYFORMAT_RAW:
-               /* verify the key length is correct */
-               if (kmlen < WRAPPING_KEY_LEN) {
-                       ret = EINVAL;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Raw key too short (expected %u)."),
-                           WRAPPING_KEY_LEN);
+               if (ret != 0)
                        goto error;
-               }
 
-               if (kmlen > WRAPPING_KEY_LEN) {
-                       ret = EINVAL;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Raw key too long (expected %u)."),
-                           WRAPPING_KEY_LEN);
-                       goto error;
-               }
                break;
-       case ZFS_KEYFORMAT_HEX:
-               /* verify the key length is correct */
-               if (kmlen < WRAPPING_KEY_LEN * 2) {
-                       ret = EINVAL;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Hex key too short (expected %u)."),
-                           WRAPPING_KEY_LEN * 2);
-                       goto error;
-               }
-
-               if (kmlen > WRAPPING_KEY_LEN * 2) {
-                       ret = EINVAL;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Hex key too long (expected %u)."),
-                           WRAPPING_KEY_LEN * 2);
-                       goto error;
-               }
+       case ZFS_KEYLOCATION_URI:
+               for (handler = uri_handlers; handler->zuh_scheme != NULL;
+                   handler++) {
+                       if (strcmp(handler->zuh_scheme, uri_scheme) != 0)
+                               continue;
 
-               /* check for invalid hex digits */
-               for (i = 0; i < WRAPPING_KEY_LEN * 2; i++) {
-                       if (!isxdigit((char)km[i])) {
-                               ret = EINVAL;
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "Invalid hex character detected."));
+                       if ((ret = handler->zuh_handler(hdl, keylocation,
+                           fsname, keyformat, newkey, &km, &kmlen)) != 0)
                                goto error;
-                       }
-               }
-               break;
-       case ZFS_KEYFORMAT_PASSPHRASE:
-               /* verify the length is within bounds */
-               if (kmlen > MAX_PASSPHRASE_LEN) {
-                       ret = EINVAL;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Passphrase too long (max %u)."),
-                           MAX_PASSPHRASE_LEN);
-                       goto error;
-               }
 
-               if (kmlen < MIN_PASSPHRASE_LEN) {
-                       ret = EINVAL;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Passphrase too short (min %u)."),
-                           MIN_PASSPHRASE_LEN);
-                       goto error;
+                       break;
                }
-               break;
-       default:
-               /* can't happen, checked above */
-               break;
-       }
 
-       if (do_verify && isatty(fileno(fd))) {
-               ret = get_key_material_raw(fd, fsname, keyformat, B_TRUE,
-                   newkey, &km2, &kmlen2);
-               if (ret != 0)
-                       goto error;
+               ret = ENOTSUP;
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "URI scheme is not supported"));
 
-               if (kmlen2 != kmlen ||
-                   (memcmp((char *)km, (char *)km2, kmlen) != 0)) {
-                       ret = EINVAL;
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "Provided keys do not match."));
-                       goto error;
-               }
+               break;
+       default:
+               ret = EINVAL;
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "Invalid keylocation."));
+               goto error;
        }
 
-       if (fd != stdin)
-               fclose(fd);
-
-       if (km2 != NULL)
-               free(km2);
+       if ((ret = validate_key(hdl, keyformat, (const char *)km, kmlen)) != 0)
+               goto error;
 
        *km_out = km;
        *kmlen_out = kmlen;
        if (can_retry_out != NULL)
                *can_retry_out = can_retry;
 
+       free(uri_scheme);
        return (0);
 
 error:
-       if (km != NULL)
-               free(km);
-
-       if (km2 != NULL)
-               free(km2);
-
-       if (fd != NULL && fd != stdin)
-               fclose(fd);
+       free(km);
 
        *km_out = NULL;
        *kmlen_out = 0;
+
        if (can_retry_out != NULL)
                *can_retry_out = can_retry;
 
+       free(uri_scheme);
        return (ret);
 }
 
index 71ac72ee1ae3f5cae5fceb91fa1482fb2adcb823..cb96d8da88791c0d4c20dcc0d22f322c631f2470 100644 (file)
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc. All rights reserved.
+ * Copyright 2020 Joyent, Inc. All rights reserved.
  * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  * Copyright (c) 2017 Datto Inc.
 #include <zfs_fletcher.h>
 #include <libzutil.h>
 
+/*
+ * We only care about the scheme in order to match the scheme
+ * with the handler. Each handler should validate the full URI
+ * as necessary.
+ */
+#define        URI_REGEX       "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):"
 
 int
 libzfs_errno(libzfs_handle_t *hdl)
@@ -881,6 +887,11 @@ libzfs_init(void)
                return (NULL);
        }
 
+       if (regcomp(&hdl->libzfs_urire, URI_REGEX, 0) != 0) {
+               free(hdl);
+               return (NULL);
+       }
+
        if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR|O_EXCL)) < 0) {
                free(hdl);
                return (NULL);
@@ -953,6 +964,7 @@ libzfs_fini(libzfs_handle_t *hdl)
        namespace_clear(hdl);
        libzfs_mnttab_fini(hdl);
        libzfs_core_fini();
+       regfree(&hdl->libzfs_urire);
        fletcher_4_fini();
        free(hdl);
 }