summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason King <jason.king@joyent.com>2020-05-08 16:51:59 -0500
committerJason King <jason.king@joyent.com>2020-05-28 12:54:20 -0500
commit6c24238b6748a460b462c0b48b263cef4a6cbcdd (patch)
tree558cd73e689af1564bf13099402b4f99e9b8efa9
parent4adc6f153a42519cb03b97bf215e1d4823300ebe (diff)
downloadillumos-joyent-6c24238b6748a460b462c0b48b263cef4a6cbcdd.tar.gz
12709 Support custom URI schemes for the keylocation property
Portions contributed by: Adam D. Moss <c@yotes.com> Reviewed by: Yuri Pankov <ypankov@fastmail.com> Reviewed by: Toomas Soome <tsoome@me.com> Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r--usr/src/lib/libzfs/common/libzfs_crypto.c539
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h48
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c9
3 files changed, 396 insertions, 200 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs_crypto.c b/usr/src/lib/libzfs/common/libzfs_crypto.c
index 72c2992f4b..0d51bea01a 100644
--- a/usr/src/lib/libzfs/common/libzfs_crypto.c
+++ b/usr/src/lib/libzfs/common/libzfs_crypto.c
@@ -15,7 +15,7 @@
/*
* Copyright (c) 2017, Datto, Inc. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
#include <string.h>
@@ -73,15 +73,64 @@ typedef enum key_locator {
static int caught_interrupt;
-static zfs_keylocation_t
-zfs_prop_parse_keylocation(const char *str)
+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
+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);
+ int ret;
+
+ *locp = ZFS_KEYLOCATION_NONE;
+ *schemep = NULL;
+
+ if (strcmp("prompt", str) == 0) {
+ *locp = ZFS_KEYLOCATION_PROMPT;
+ return (0);
+ }
+
+ regmatch_t pmatch[URI_NMATCH];
+ regmatch_t *smatch = &pmatch[URI_SCHEMESUBEXP];
+
+ ret = regexec(&hdl->libzfs_urire, str, ARRAY_SIZE(pmatch), pmatch, 0);
+ switch (ret) {
+ case 0:
+ break;
+ case REG_ESPACE:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Out of memory"));
+ return (ENOMEM);
+ case REG_NOMATCH:
+ goto invalid;
+ default:
+ /*
+ * Any other errors from regexec are a programming bug,
+ * so consider them a fatal error.
+ */
+ (void) fprintf(stderr, "regexec failed: %d\n", ret);
+ abort();
+ }
- return (ZFS_KEYLOCATION_NONE);
+ if (smatch->rm_so == -1)
+ goto invalid;
+
+ *schemep = strndup(str + smatch->rm_so, smatch->rm_eo - smatch->rm_so);
+ if (*schemep == NULL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Out of memory"));
+ return (ENOMEM);
+ }
+
+ *locp = ZFS_KEYLOCATION_URI;
+ return (0);
+
+invalid:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid keylocation"));
+ return (EINVAL);
}
static int
@@ -134,65 +183,235 @@ get_format_prompt_string(zfs_keyformat_t format)
}
}
+/* do basic validation of the key material */
+static int
+validate_key(libzfs_handle_t *hdl, zfs_keyformat_t keyformat,
+ const char *key, size_t keylen)
+{
+ 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
-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)
+libzfs_getpassphrase(zfs_keyformat_t keyformat, boolean_t is_reenter,
+ boolean_t new_key, const char *fsname,
+ char **restrict res, size_t *restrict reslen)
{
- int ret = 0, bytes;
+ 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(
- (zfs_keyformat_t)keyformat),
- fsname);
- } else {
- (void) printf("%s %s%s: ",
- (again) ? "Re-enter" : "Enter",
- (newkey) ? "new " : "",
- get_format_prompt_string(
- (zfs_keyformat_t)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) {
+ freezero(buf, buflen);
+ buf = NULL;
+ buflen = 0;
+ goto out;
}
+ if (!confirm_key)
+ goto out;
+
+ if ((ret = validate_key(hdl, keyformat, buf, buflen)) != 0) {
+ freezero(buf, buflen);
+ return (ret);
+ }
+
+ ret = libzfs_getpassphrase(keyformat, B_TRUE, newkey, fsname, &buf2,
+ &buf2len);
+ if (ret != 0) {
+ freezero(buf, buflen);
+ freezero(buf2, buf2len);
+ buf = buf2 = NULL;
+ buflen = buf2len = 0;
+ goto out;
+ }
+
+ if (buflen != buf2len || strcmp(buf, buf2) != 0) {
+ freezero(buf, buflen);
+ buf = NULL;
+ buflen = 0;
+
+ ret = EINVAL;
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Provided keys do not match."));
+ }
+
+ freezero(buf2, buf2len);
+
+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;
@@ -201,25 +420,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;
@@ -227,28 +450,43 @@ get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat,
errno = 0;
goto out;
}
- }
- *len_out = bytes;
+ *len_out = n;
+ }
out:
- if (isatty(fileno(fd))) {
- /* reset the teminal */
- (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);
- }
+ return (ret);
+}
+
+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)
+{
+ const char *path;
+ FILE *f = NULL;
+ int ret = 0;
+
+ /*
+ * get_key_material() should guarantee we're only called for a file
+ * URI.
+ */
+ VERIFY0(strncmp(uri, "file://", 7));
+ path = uri + 7;
- /* print the newline that was not echo'd */
- (void) printf("\n");
+ if ((f = fopen(path, "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);
}
/*
@@ -262,41 +500,47 @@ 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 *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, &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;
- }
+ ret = get_key_interactive(hdl, fsname, keyformat,
+ do_verify, newkey, &km, &kmlen);
+ } else {
+ /* fetch the key material into the buffer */
+ ret = get_key_material_raw(stdin, keyformat, &km,
+ &kmlen);
}
+ if (ret != 0)
+ 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;
+ for (handler = uri_handlers; handler->zuh_scheme != NULL;
+ handler++) {
+ if (strcmp(handler->zuh_scheme, scheme) != 0)
+ continue;
+ if ((ret = handler->zuh_handler(hdl, keylocation,
+ fsname, keyformat, newkey, &km, &kmlen)) != 0)
+ goto error;
}
+ ret = ENOTSUP;
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "URI scheme is not supported"));
break;
default:
ret = EINVAL;
@@ -305,127 +549,24 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, boolean_t newkey,
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)
+ if ((ret = validate_key(hdl, keyformat, (const char *)km, kmlen)) != 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);
- 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;
- }
-
- /* 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."));
- 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;
- 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;
-
- 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;
- }
- }
-
- if (fd != stdin)
- (void) fclose(fd);
-
- if (km2 != NULL)
- free(km2);
-
*km_out = km;
*kmlen_out = kmlen;
if (can_retry_out != NULL)
*can_retry_out = can_retry;
+ free(scheme);
return (0);
error:
- if (km != NULL)
- free(km);
-
- if (km2 != NULL)
- free(km2);
-
- if (fd != NULL && fd != stdin)
- (void) fclose(fd);
+ freezero(km, kmlen);
+ free(scheme);
*km_out = NULL;
*kmlen_out = 0;
- if (can_retry_out != NULL)
- *can_retry_out = can_retry;
-
return (ret);
}
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index ed5d491b0a..85bcc47e97 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -23,7 +23,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _LIBZFS_IMPL_H
@@ -35,6 +35,7 @@
#include <sys/dmu.h>
#include <sys/zfs_ioctl.h>
#include <synch.h>
+#include <regex.h>
#include <libuutil.h>
#include <libzfs.h>
@@ -91,6 +92,7 @@ struct libzfs_handle {
char libzfs_chassis_id[256];
boolean_t libzfs_prop_debug;
di_devlink_handle_t libzfs_devlink;
+ regex_t libzfs_urire;
};
struct zfs_handle {
@@ -142,6 +144,50 @@ typedef enum {
SHARED_SMB = 0x4
} zfs_share_type_t;
+
+/*
+ * From RFC3986 Appendix B. The regex is a bit of a beast, but with an
+ * example URI of:
+ *
+ * http://www.ics.uci.edu/pub/ietf/uri/#Related
+ *
+ * URI_REGEX should match with the following subexpressions:
+ *
+ * $1 = http:
+ * $2 = http
+ * $3 = //www.ics.uci.edu
+ * $4 = www.ics.uci.edu
+ * $5 = /pub/ietf/uri/
+ * $6 = <undefined>
+ * $7 = <undefined>
+ * $8 = #Related
+ * $9 = Related
+ *
+ * More generally:
+ *
+ * scheme = $2
+ * authority = $4
+ * path = $5
+ * query = $7
+ * fragment = $9
+ *
+ * We only care about the value of the scheme component ($2) in order to
+ * invoke the correct handler. Each handler should do any additional URI
+ * validation as required.
+ */
+#define URI_REGEX \
+ "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"
+#define URI_NMATCH 10 /* whole URI match ($0) + 9 subexps */
+#define URI_SCHEMESUBEXP 2 /* '$2' */
+
+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 *);
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 3285cf7ce0..3539b5a485 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -645,13 +645,20 @@ libzfs_init(void)
return (NULL);
}
+ if (regcomp(&hdl->libzfs_urire, URI_REGEX, REG_EXTENDED) != 0) {
+ free(hdl);
+ return (NULL);
+ }
+
if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
+ regfree(&hdl->libzfs_urire);
free(hdl);
return (NULL);
}
if ((hdl->libzfs_mnttab = fopen(MNTTAB, "rF")) == NULL) {
(void) close(hdl->libzfs_fd);
+ regfree(&hdl->libzfs_urire);
free(hdl);
return (NULL);
}
@@ -662,6 +669,7 @@ libzfs_init(void)
(void) close(hdl->libzfs_fd);
(void) fclose(hdl->libzfs_mnttab);
(void) fclose(hdl->libzfs_sharetab);
+ regfree(&hdl->libzfs_urire);
free(hdl);
return (NULL);
}
@@ -694,6 +702,7 @@ libzfs_fini(libzfs_handle_t *hdl)
namespace_clear(hdl);
libzfs_mnttab_fini(hdl);
libzfs_core_fini();
+ regfree(&hdl->libzfs_urire);
free(hdl);
}