diff options
author | Jason King <jason.king@joyent.com> | 2020-05-08 16:51:59 -0500 |
---|---|---|
committer | Jason King <jason.king@joyent.com> | 2020-05-28 12:54:20 -0500 |
commit | 6c24238b6748a460b462c0b48b263cef4a6cbcdd (patch) | |
tree | 558cd73e689af1564bf13099402b4f99e9b8efa9 | |
parent | 4adc6f153a42519cb03b97bf215e1d4823300ebe (diff) | |
download | illumos-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.c | 539 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_impl.h | 48 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_util.c | 9 |
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); } |