Support custom URI schemes for the keylocation property
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
This commit is contained in:
parent
89a6610ed0
commit
c14ca1456e
|
@ -23,6 +23,7 @@
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2011, 2020 by Delphix. All rights reserved.
|
* Copyright (c) 2011, 2020 by Delphix. All rights reserved.
|
||||||
* Copyright (c) 2018 Datto Inc.
|
* Copyright (c) 2018 Datto Inc.
|
||||||
|
* Copyright 2020 Joyent, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LIBZFS_IMPL_H
|
#ifndef _LIBZFS_IMPL_H
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
#include <sys/nvpair.h>
|
#include <sys/nvpair.h>
|
||||||
#include <sys/dmu.h>
|
#include <sys/dmu.h>
|
||||||
#include <sys/zfs_ioctl.h>
|
#include <sys/zfs_ioctl.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
#include <libuutil.h>
|
#include <libuutil.h>
|
||||||
#include <libzfs.h>
|
#include <libzfs.h>
|
||||||
|
@ -71,6 +73,7 @@ struct libzfs_handle {
|
||||||
int libzfs_pool_iter;
|
int libzfs_pool_iter;
|
||||||
char libzfs_chassis_id[256];
|
char libzfs_chassis_id[256];
|
||||||
boolean_t libzfs_prop_debug;
|
boolean_t libzfs_prop_debug;
|
||||||
|
regex_t libzfs_urire;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */
|
#define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */
|
||||||
|
@ -124,6 +127,14 @@ typedef enum {
|
||||||
SHARED_SMB = 0x4
|
SHARED_SMB = 0x4
|
||||||
} zfs_share_type_t;
|
} 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
|
#define CONFIG_BUF_MINSIZE 262144
|
||||||
|
|
||||||
int zfs_error(libzfs_handle_t *, int, const char *);
|
int zfs_error(libzfs_handle_t *, int, const char *);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, Datto, Inc. All rights reserved.
|
* Copyright (c) 2017, Datto, Inc. All rights reserved.
|
||||||
|
* Copyright 2020 Joyent, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
|
@ -62,6 +63,14 @@ typedef enum key_locator {
|
||||||
|
|
||||||
static int caught_interrupt;
|
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
|
static int
|
||||||
pkcs11_get_urandom(uint8_t *buf, size_t bytes)
|
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);
|
return (bytes_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
static zfs_keylocation_t
|
static int
|
||||||
zfs_prop_parse_keylocation(const char *str)
|
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)
|
*locp = ZFS_KEYLOCATION_NONE;
|
||||||
return (ZFS_KEYLOCATION_PROMPT);
|
*schemep = NULL;
|
||||||
else if (strlen(str) > 8 && strncmp("file:///", str, 8) == 0)
|
|
||||||
return (ZFS_KEYLOCATION_URI);
|
|
||||||
|
|
||||||
return (ZFS_KEYLOCATION_NONE);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid keylocation"));
|
||||||
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -146,18 +189,92 @@ get_format_prompt_string(zfs_keyformat_t format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* do basic validation of the key material */
|
||||||
static int
|
static int
|
||||||
get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat,
|
validate_key(libzfs_handle_t *hdl, zfs_keyformat_t keyformat,
|
||||||
boolean_t again, boolean_t newkey, uint8_t **buf, size_t *len_out)
|
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;
|
size_t buflen = 0;
|
||||||
|
ssize_t bytes;
|
||||||
|
int ret = 0;
|
||||||
struct termios old_term, new_term;
|
struct termios old_term, new_term;
|
||||||
struct sigaction act, osigint, osigtstp;
|
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
|
* handle SIGINT and ignore SIGSTP. This is necessary to
|
||||||
* restore the state of the terminal.
|
* restore the state of the terminal.
|
||||||
|
@ -171,37 +288,136 @@ get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat,
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
(void) sigaction(SIGTSTP, &act, &osigtstp);
|
(void) sigaction(SIGTSTP, &act, &osigtstp);
|
||||||
|
|
||||||
/* prompt for the key */
|
(void) printf("%s %s%s",
|
||||||
if (fsname != NULL) {
|
is_reenter ? "Re-enter" : "Enter",
|
||||||
(void) printf("%s %s%s for '%s': ",
|
new_key ? "new " : "",
|
||||||
(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));
|
get_format_prompt_string(keyformat));
|
||||||
|
if (fsname != NULL)
|
||||||
}
|
(void) printf(" for '%s'", fsname);
|
||||||
|
(void) fputc(':', stdout);
|
||||||
(void) fflush(stdout);
|
(void) fflush(stdout);
|
||||||
|
|
||||||
/* disable the terminal echo for key input */
|
/* disable the terminal echo for key input */
|
||||||
(void) tcgetattr(fileno(fd), &old_term);
|
(void) tcgetattr(fileno(f), &old_term);
|
||||||
|
|
||||||
new_term = old_term;
|
new_term = old_term;
|
||||||
new_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
|
new_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
|
||||||
|
|
||||||
ret = tcsetattr(fileno(fd), TCSAFLUSH, &new_term);
|
ret = tcsetattr(fileno(f), TCSAFLUSH, &new_term);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
ret = errno;
|
ret = errno;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes = getline(res, &buflen, f);
|
||||||
|
if (bytes < 0) {
|
||||||
|
ret = errno;
|
||||||
|
errno = 0;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* trim the ending newline if it exists */
|
||||||
|
if (bytes > 0 && (*res)[bytes - 1] == '\n') {
|
||||||
|
(*res)[bytes - 1] = '\0';
|
||||||
|
bytes--;
|
||||||
|
}
|
||||||
|
|
||||||
|
*reslen = bytes;
|
||||||
|
|
||||||
|
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 */
|
/* read the key material */
|
||||||
if (keyformat != ZFS_KEYFORMAT_RAW) {
|
if (keyformat != ZFS_KEYFORMAT_RAW) {
|
||||||
|
ssize_t bytes;
|
||||||
|
|
||||||
bytes = getline((char **)buf, &buflen, fd);
|
bytes = getline((char **)buf, &buflen, fd);
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
ret = errno;
|
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 */
|
/* trim the ending newline if it exists */
|
||||||
if ((*buf)[bytes - 1] == '\n') {
|
if (bytes > 0 && (*buf)[bytes - 1] == '\n') {
|
||||||
(*buf)[bytes - 1] = '\0';
|
(*buf)[bytes - 1] = '\0';
|
||||||
bytes--;
|
bytes--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*len_out = bytes;
|
||||||
} else {
|
} else {
|
||||||
|
size_t n;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Raw keys may have newline characters in them and so can't
|
* Raw keys may have newline characters in them and so can't
|
||||||
* use getline(). Here we attempt to read 33 bytes so that we
|
* use getline(). Here we attempt to read 33 bytes so that we
|
||||||
* can properly check the key length (the file should only have
|
* can properly check the key length (the file should only have
|
||||||
* 32 bytes).
|
* 32 bytes).
|
||||||
*/
|
*/
|
||||||
*buf = malloc((WRAPPING_KEY_LEN + 1) * sizeof (char));
|
*buf = malloc((WRAPPING_KEY_LEN + 1) * sizeof (uint8_t));
|
||||||
if (*buf == NULL) {
|
if (*buf == NULL) {
|
||||||
ret = ENOMEM;
|
ret = ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes = fread(*buf, 1, WRAPPING_KEY_LEN + 1, fd);
|
n = fread(*buf, 1, WRAPPING_KEY_LEN + 1, fd);
|
||||||
if (bytes < 0) {
|
if (n == 0 || ferror(fd)) {
|
||||||
/* size errors are handled by the calling function */
|
/* size errors are handled by the calling function */
|
||||||
free(*buf);
|
free(*buf);
|
||||||
*buf = NULL;
|
*buf = NULL;
|
||||||
|
@ -236,28 +456,37 @@ get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat,
|
||||||
errno = 0;
|
errno = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*len_out = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
*len_out = bytes;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (isatty(fileno(fd))) {
|
return (ret);
|
||||||
/* 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 */
|
static int
|
||||||
if (caught_interrupt != 0) {
|
get_key_material_file(libzfs_handle_t *hdl, const char *uri,
|
||||||
(void) kill(getpid(), caught_interrupt);
|
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;
|
||||||
|
|
||||||
|
if (strlen(uri) < 7)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print the newline that was not echo'd */
|
ret = get_key_material_raw(f, keyformat, buf, len_out);
|
||||||
printf("\n");
|
|
||||||
}
|
(void) fclose(f);
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -271,41 +500,54 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, boolean_t newkey,
|
||||||
zfs_keyformat_t keyformat, char *keylocation, const char *fsname,
|
zfs_keyformat_t keyformat, char *keylocation, const char *fsname,
|
||||||
uint8_t **km_out, size_t *kmlen_out, boolean_t *can_retry_out)
|
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;
|
zfs_keylocation_t keyloc = ZFS_KEYLOCATION_NONE;
|
||||||
FILE *fd = NULL;
|
uint8_t *km = NULL;
|
||||||
uint8_t *km = NULL, *km2 = NULL;
|
size_t kmlen = 0;
|
||||||
size_t kmlen, kmlen2;
|
char *uri_scheme = NULL;
|
||||||
|
zfs_uri_handler_t *handler = NULL;
|
||||||
boolean_t can_retry = B_FALSE;
|
boolean_t can_retry = B_FALSE;
|
||||||
|
|
||||||
/* verify and parse the keylocation */
|
/* 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 */
|
/* open the appropriate file descriptor */
|
||||||
switch (keyloc) {
|
switch (keyloc) {
|
||||||
case ZFS_KEYLOCATION_PROMPT:
|
case ZFS_KEYLOCATION_PROMPT:
|
||||||
fd = stdin;
|
if (isatty(fileno(stdin))) {
|
||||||
if (isatty(fileno(fd))) {
|
|
||||||
can_retry = B_TRUE;
|
can_retry = B_TRUE;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/* raw keys cannot be entered on the terminal */
|
if (ret != 0)
|
||||||
if (keyformat == ZFS_KEYFORMAT_RAW) {
|
|
||||||
ret = EINVAL;
|
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"Cannot enter raw keys on the terminal"));
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ZFS_KEYLOCATION_URI:
|
case ZFS_KEYLOCATION_URI:
|
||||||
fd = fopen(&keylocation[7], "r");
|
for (handler = uri_handlers; handler->zuh_scheme != NULL;
|
||||||
if (!fd) {
|
handler++) {
|
||||||
ret = errno;
|
if (strcmp(handler->zuh_scheme, uri_scheme) != 0)
|
||||||
errno = 0;
|
continue;
|
||||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
||||||
"Failed to open key material file"));
|
if ((ret = handler->zuh_handler(hdl, keylocation,
|
||||||
|
fsname, keyformat, newkey, &km, &kmlen)) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = ENOTSUP;
|
||||||
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
"URI scheme is not supported"));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = EINVAL;
|
ret = EINVAL;
|
||||||
|
@ -314,126 +556,27 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, boolean_t newkey,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fetch the key material into the buffer */
|
if ((ret = validate_key(hdl, keyformat, (const char *)km, kmlen)) != 0)
|
||||||
ret = get_key_material_raw(fd, fsname, keyformat, B_FALSE, newkey,
|
|
||||||
&km, &kmlen);
|
|
||||||
if (ret != 0)
|
|
||||||
goto error;
|
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)
|
|
||||||
fclose(fd);
|
|
||||||
|
|
||||||
if (km2 != NULL)
|
|
||||||
free(km2);
|
|
||||||
|
|
||||||
*km_out = km;
|
*km_out = km;
|
||||||
*kmlen_out = kmlen;
|
*kmlen_out = kmlen;
|
||||||
if (can_retry_out != NULL)
|
if (can_retry_out != NULL)
|
||||||
*can_retry_out = can_retry;
|
*can_retry_out = can_retry;
|
||||||
|
|
||||||
|
free(uri_scheme);
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (km != NULL)
|
|
||||||
free(km);
|
free(km);
|
||||||
|
|
||||||
if (km2 != NULL)
|
|
||||||
free(km2);
|
|
||||||
|
|
||||||
if (fd != NULL && fd != stdin)
|
|
||||||
fclose(fd);
|
|
||||||
|
|
||||||
*km_out = NULL;
|
*km_out = NULL;
|
||||||
*kmlen_out = 0;
|
*kmlen_out = 0;
|
||||||
|
|
||||||
if (can_retry_out != NULL)
|
if (can_retry_out != NULL)
|
||||||
*can_retry_out = can_retry;
|
*can_retry_out = can_retry;
|
||||||
|
|
||||||
|
free(uri_scheme);
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
* 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 (c) 2011, 2018 by Delphix. All rights reserved.
|
||||||
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||||
* Copyright (c) 2017 Datto Inc.
|
* Copyright (c) 2017 Datto Inc.
|
||||||
|
@ -55,6 +55,12 @@
|
||||||
#include <zfs_fletcher.h>
|
#include <zfs_fletcher.h>
|
||||||
#include <libzutil.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
|
int
|
||||||
libzfs_errno(libzfs_handle_t *hdl)
|
libzfs_errno(libzfs_handle_t *hdl)
|
||||||
|
@ -881,6 +887,11 @@ libzfs_init(void)
|
||||||
return (NULL);
|
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) {
|
if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR|O_EXCL)) < 0) {
|
||||||
free(hdl);
|
free(hdl);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
@ -953,6 +964,7 @@ libzfs_fini(libzfs_handle_t *hdl)
|
||||||
namespace_clear(hdl);
|
namespace_clear(hdl);
|
||||||
libzfs_mnttab_fini(hdl);
|
libzfs_mnttab_fini(hdl);
|
||||||
libzfs_core_fini();
|
libzfs_core_fini();
|
||||||
|
regfree(&hdl->libzfs_urire);
|
||||||
fletcher_4_fini();
|
fletcher_4_fini();
|
||||||
free(hdl);
|
free(hdl);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue