2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <libintl.h>
2N/A#include <kmfapi.h>
2N/A#include <security/pkcs11.h>
2N/A#include <cryptoutil.h>
2N/A#include <errno.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <fcntl.h>
2N/A#include <sys/mount.h>
2N/A#include <libgen.h>
2N/A#include <sys/fs/zfs.h>
2N/A#include <sys/zio_crypt.h>
2N/A#include <curl/curl.h>
2N/A
2N/A#include "zfs_namecheck.h"
2N/A#include "zfs_prop.h"
2N/A#include "libzfs_impl.h"
2N/A
2N/A#define MAXPROMPTLEN (ZFS_MAXNAMELEN + 35) /* 35 = prompt - dataset name */
2N/A
2N/A/*
2N/A * Constants and functions for parsing/validating
2N/A * the keysource property
2N/A */
2N/Aconst char *FMT_RAW = "raw";
2N/Aconst char *FMT_HEX = "hex";
2N/Aconst char *FMT_PASSPHRASE = "passphrase";
2N/Aconst char *LOC_PROMPT = "prompt";
2N/Aconst char *LOC_FILE = "file:///";
2N/Aconst char *LOC_PKCS11 = "pkcs11:";
2N/Aconst char *LOC_HTTP = "http://";
2N/Aconst char *LOC_HTTPS = "https://";
2N/A
2N/Atypedef enum key_format {
2N/A KEY_FORMAT_NONE = 0,
2N/A KEY_FORMAT_RAW,
2N/A KEY_FORMAT_HEX,
2N/A KEY_FORMAT_PASSPHRASE
2N/A} key_format_t;
2N/A
2N/Atypedef enum key_locator {
2N/A KEY_LOCATOR_NONE = 0,
2N/A KEY_LOCATOR_PROMPT,
2N/A KEY_LOCATOR_FILE_URI,
2N/A KEY_LOCATOR_PKCS11_URI,
2N/A KEY_LOCATOR_HTTPS_URI
2N/A} key_locator_t;
2N/A
2N/A
2N/Astatic boolean_t
2N/Aparse_format(key_format_t *format, char *s, int len)
2N/A{
2N/A
2N/A if (!s)
2N/A return (B_FALSE);
2N/A
2N/A if (strncmp(FMT_RAW, s, len) == 0 && len == strlen(FMT_RAW))
2N/A *format = KEY_FORMAT_RAW;
2N/A else if (strncmp(FMT_HEX, s, len) == 0 && len == strlen(FMT_HEX))
2N/A *format = KEY_FORMAT_HEX;
2N/A else if (strncmp(FMT_PASSPHRASE, s, len) == 0 &&
2N/A len == strlen(FMT_PASSPHRASE))
2N/A *format = KEY_FORMAT_PASSPHRASE;
2N/A else
2N/A return (B_FALSE);
2N/A
2N/A return (B_TRUE);
2N/A}
2N/A
2N/Astatic boolean_t
2N/Aparse_locator(key_locator_t *locator, char *s, int len, char **uri)
2N/A{
2N/A
2N/A if (!s)
2N/A return (B_FALSE);
2N/A
2N/A if (len == strlen(LOC_PROMPT) &&
2N/A strncmp(LOC_PROMPT, s, strlen(LOC_PROMPT)) == 0) {
2N/A *locator = KEY_LOCATOR_PROMPT;
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A if (len > strlen(LOC_FILE) &&
2N/A (strncmp(LOC_FILE, s, strlen(LOC_FILE)) == 0)) {
2N/A *locator = KEY_LOCATOR_FILE_URI;
2N/A *uri = s;
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A if (len > strlen(LOC_PKCS11) &&
2N/A (strncmp(LOC_PKCS11, s, strlen(LOC_PKCS11)) == 0)) {
2N/A pkcs11_uri_t pk11uri;
2N/A
2N/A /*
2N/A * Validate the PKCS#11 URI by parsing it out,
2N/A * and checking that an object is specified.
2N/A * Every other part of the PKCS#11 URI is optional.
2N/A */
2N/A if (pkcs11_parse_uri(s, &pk11uri) != PK11_URI_OK)
2N/A return (B_FALSE);
2N/A if (pk11uri.object == NULL) {
2N/A pkcs11_free_uri(&pk11uri);
2N/A return (B_FALSE);
2N/A }
2N/A pkcs11_free_uri(&pk11uri);
2N/A *locator = KEY_LOCATOR_PKCS11_URI;
2N/A *uri = s;
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A if ((len > strlen(LOC_HTTPS) &&
2N/A (strncmp(LOC_HTTPS, s, strlen(LOC_HTTPS)) == 0)) ||
2N/A (len > strlen(LOC_HTTP) &&
2N/A (strncmp(LOC_HTTP, s, strlen(LOC_HTTP)) == 0))) {
2N/A *locator = KEY_LOCATOR_HTTPS_URI;
2N/A *uri = s;
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/Astatic boolean_t
2N/Akeysource_prop_parser(char *prop_value, key_format_t *format,
2N/A key_locator_t *locator, char **uri)
2N/A{
2N/A int len;
2N/A int prop_len;
2N/A char *s = prop_value;
2N/A
2N/A *format = KEY_FORMAT_NONE;
2N/A *locator = KEY_LOCATOR_NONE;
2N/A
2N/A if (!prop_value)
2N/A return (B_FALSE);
2N/A
2N/A prop_len = strlen(prop_value);
2N/A if (prop_len > ZFS_MAXPROPLEN)
2N/A return (B_FALSE);
2N/A
2N/A for (len = 0; len < prop_len; len++)
2N/A if (s[len] == ',')
2N/A break;
2N/A
2N/A /* If we are at the end of the key property, there is a problem */
2N/A if (len == prop_len)
2N/A return (B_FALSE);
2N/A
2N/A if (!parse_format(format, s, len))
2N/A return (B_FALSE);
2N/A
2N/A s = s + len + 1;
2N/A len = prop_len - len - 1;
2N/A
2N/A return (parse_locator(locator, s, len, uri));
2N/A}
2N/A
2N/Astatic void
2N/Azfs_cmd_target_dsname(zfs_cmd_t *zc, zfs_crypto_zckey_t cmd,
2N/A char *dsname, size_t dsnamelen)
2N/A{
2N/A int at;
2N/A /*
2N/A * The name needs to be that of the dataset we are creating.
2N/A * Using zc_value is wrong when doing a clone because it shows
2N/A * the name of the origin snapshot. However it is correct when
2N/A * doing a zfs recv, use zc_value upto the @ which is the
2N/A * name of the dataset getting created.
2N/A */
2N/A if (cmd == ZFS_CRYPTO_RECV) {
2N/A at = strcspn(zc->zc_value, "@");
2N/A (void) strlcpy(dsname, zc->zc_value,
2N/A MIN(at + 1, dsnamelen));
2N/A if (strlen(dsname) == 0) {
2N/A (void) strlcpy(dsname, zc->zc_name, dsnamelen);
2N/A }
2N/A } else {
2N/A (void) strlcpy(dsname, zc->zc_name, dsnamelen);
2N/A }
2N/A}
2N/A
2N/Astatic boolean_t
2N/Azfs_can_prompt_if_needed(char *keysource)
2N/A{
2N/A key_format_t format;
2N/A key_locator_t locator;
2N/A char *uri;
2N/A const char SMF_FS_LOCAL[] = "svc:/system/filesystem/local:default";
2N/A
2N/A if (!keysource_prop_parser(keysource, &format, &locator, &uri))
2N/A return (B_FALSE);
2N/A
2N/A if (locator != KEY_LOCATOR_PROMPT)
2N/A return (B_TRUE);
2N/A
2N/A if (getenv("SMF_FMRI") != NULL &&
2N/A strcmp(getenv("SMF_FMRI"), SMF_FS_LOCAL) == 0) {
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A return (B_TRUE);
2N/A}
2N/A
2N/Astatic int
2N/Aget_passphrase(libzfs_handle_t *hdl, char **passphrase,
2N/A size_t *passphraselen, key_format_t format, zfs_cmd_t *zc,
2N/A zfs_crypto_zckey_t cmd)
2N/A{
2N/A char prompt[MAXPROMPTLEN];
2N/A char *tmpbuf = NULL;
2N/A int min_psize = MINPASSPHRASELEN;
2N/A char dsname[MAXNAMELEN];
2N/A
2N/A zfs_cmd_target_dsname(zc, cmd, dsname, sizeof (dsname));
2N/A if (format == KEY_FORMAT_HEX) {
2N/A min_psize = MINHEXKEYLEN;
2N/A if (hdl->libzfs_crypt.zc_is_key_change) {
2N/A (void) snprintf(prompt, MAXPROMPTLEN, "%s \'%s\': ",
2N/A dgettext(TEXT_DOMAIN,
2N/A "Enter new hexadecimal key for"),
2N/A dsname);
2N/A } else {
2N/A (void) snprintf(prompt, MAXPROMPTLEN, "%s \'%s\': ",
2N/A dgettext(TEXT_DOMAIN,
2N/A "Enter hexadecimal key for"), dsname);
2N/A }
2N/A } else {
2N/A if (hdl->libzfs_crypt.zc_is_key_change) {
2N/A (void) snprintf(prompt, MAXPROMPTLEN, "%s \'%s\': ",
2N/A dgettext(TEXT_DOMAIN,
2N/A "Enter new passphrase for"), dsname);
2N/A } else {
2N/A (void) snprintf(prompt, MAXPROMPTLEN, "%s \'%s\': ",
2N/A dgettext(TEXT_DOMAIN,
2N/A "Enter passphrase for"), dsname);
2N/A }
2N/A }
2N/A
2N/A tmpbuf = getpassphrase(prompt);
2N/A if (tmpbuf == NULL && errno == ENXIO)
2N/A return (EAGAIN);
2N/A if (tmpbuf == NULL || strlen(tmpbuf) < min_psize) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "Must be at least %d characters.\n"), min_psize);
2N/A return (EAGAIN);
2N/A }
2N/A
2N/A *passphrase = strdup(tmpbuf);
2N/A tmpbuf = NULL;
2N/A
2N/A /*
2N/A * A create/clone/recv or wrapping key change needs to reprompt.
2N/A * Loading the key is the only case were we don't reprompt.
2N/A */
2N/A if (cmd != ZFS_CRYPTO_KEY_LOAD) {
2N/A (void) snprintf(prompt, MAXPROMPTLEN,
2N/A dgettext(TEXT_DOMAIN, "Enter again: "));
2N/A
2N/A tmpbuf = getpassphrase(prompt);
2N/A if (tmpbuf == NULL ||
2N/A strcmp(*passphrase, tmpbuf) != 0) {
2N/A /* clean up */
2N/A free(*passphrase);
2N/A *passphrase = NULL;
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "They don't match.\n"));
2N/A return (EAGAIN);
2N/A }
2N/A }
2N/A
2N/A *passphraselen = strlen(*passphrase);
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Alibzfs_crypto_set_key(libzfs_handle_t *hdl, char *key, size_t keylen)
2N/A{
2N/A hdl->libzfs_crypt.zc_key_data = key;
2N/A hdl->libzfs_crypt.zc_key_data_len = keylen;
2N/A}
2N/A
2N/Avoid
2N/Azfs_crypto_set_key(zfs_handle_t *zhp, char *key, size_t keylen)
2N/A{
2N/A libzfs_crypto_set_key(zhp->zfs_hdl, key, keylen);
2N/A}
2N/A
2N/Avoid
2N/Azfs_crypto_set_clone_newkey(zfs_handle_t *zhp)
2N/A{
2N/A zhp->zfs_hdl->libzfs_crypt.zc_clone_newkey = B_TRUE;
2N/A}
2N/A
2N/Astatic int
2N/Aprompt_pkcs11_pin(libzfs_handle_t *hdl, zfs_cmd_t *zc, zfs_crypto_zckey_t cmd,
2N/A pkcs11_uri_t *p11uri, char **pin, uint32_t *pinlen)
2N/A{
2N/A char prompt[MAXPROMPTLEN];
2N/A char *input;
2N/A char dsname[MAXNAMELEN];
2N/A
2N/A /*
2N/A * If the PKCS#11 uri has a pinfile argument read the pin from
2N/A * there.
2N/A *
2N/A * Otherwise if the libzfs_handle_t has crypto data we assume this is
2N/A * the PIN given we can only be in here with a PKCS#11 uri.
2N/A *
2N/A * Finally if that is empty then if we can prompt then do so using
2N/A * getpassphrase().
2N/A *
2N/A * Abuse zfs_can_prompt_if_needed() by pretending we are
2N/A * "passphrase,prompt".
2N/A */
2N/A if (p11uri->pinfile) {
2N/A struct stat sbuf;
2N/A int pinfd = open(p11uri->pinfile, O_RDONLY);
2N/A char *pbuf;
2N/A
2N/A if (pinfd == -1)
2N/A return (-1);
2N/A if (fstat(pinfd, &sbuf) != 0)
2N/A return (-1);
2N/A pbuf = zfs_alloc(hdl, sbuf.st_size);
2N/A if (read(pinfd, pbuf, sbuf.st_size) != sbuf.st_size) {
2N/A free(pbuf);
2N/A return (-1);
2N/A }
2N/A (void) close(pinfd);
2N/A pbuf[sbuf.st_size] = '\0';
2N/A *pinlen = sbuf.st_size;
2N/A if (pbuf[sbuf.st_size - 1] == '\n' ||
2N/A pbuf[sbuf.st_size - 1] == '\r') {
2N/A *pinlen = *pinlen - 1;
2N/A }
2N/A *pin = pbuf;
2N/A return (0);
2N/A }
2N/A
2N/A if (hdl->libzfs_crypt.zc_key_data != NULL &&
2N/A hdl->libzfs_crypt.zc_key_data_len != 0) {
2N/A *pinlen = hdl->libzfs_crypt.zc_key_data_len;
2N/A *pin = zfs_alloc(hdl, *pinlen);
2N/A bcopy(hdl->libzfs_crypt.zc_key_data, *pin, *pinlen);
2N/A return (0);
2N/A }
2N/A if (!zfs_can_prompt_if_needed("passphrase,prompt")) {
2N/A errno = ENOTTY;
2N/A return (-1);
2N/A }
2N/A
2N/A zfs_cmd_target_dsname(zc, cmd, dsname, sizeof (dsname));
2N/A if (p11uri->token) {
2N/A (void) snprintf(prompt, MAXPROMPTLEN,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Enter '%s' PKCS#11 token PIN for '%s': "),
2N/A p11uri->token, dsname);
2N/A } else {
2N/A (void) snprintf(prompt, MAXPROMPTLEN,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Enter PKCS#11 token PIN for '%s': "), dsname);
2N/A }
2N/A input = getpassphrase(prompt);
2N/A
2N/A if (input != NULL) {
2N/A *pin = strdup(input);
2N/A *pinlen = strlen(*pin);
2N/A } else {
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Aget_pkcs11_key_value(libzfs_handle_t *hdl, zfs_cmd_t *zc,
2N/A zfs_crypto_zckey_t cmd, pkcs11_uri_t *p11uri,
2N/A char **keydata, size_t *keydatalen)
2N/A{
2N/A KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_PK11TOKEN;
2N/A KMF_KEY_CLASS keyclass = KMF_SYMMETRIC;
2N/A KMF_ENCODE_FORMAT format = KMF_FORMAT_RAWKEY;
2N/A KMF_ATTRIBUTE attr[10];
2N/A KMF_KEY_HANDLE keys;
2N/A KMF_CREDENTIAL cred;
2N/A KMF_RAW_SYM_KEY rkey;
2N/A KMF_HANDLE_T kmfh;
2N/A KMF_RETURN err;
2N/A boolean_t true_val = B_TRUE;
2N/A CK_SLOT_ID slot;
2N/A CK_TOKEN_INFO info;
2N/A size_t n = 0;
2N/A uint32_t numkeys = 0; /* Ask for all of the named keys */
2N/A char *token = NULL;
2N/A
2N/A if (kmf_initialize(&kmfh, NULL, NULL) != KMF_OK) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A kmf_set_attr_at_index(attr, n++, KMF_KEYSTORE_TYPE_ATTR, &kstype,
2N/A sizeof (kstype));
2N/A if (p11uri->token) {
2N/A token = strdup((const char *)p11uri->token);
2N/A } else {
2N/A /* If the token wasn't set we assume the metaslot */
2N/A token = strdup(METASLOT_TOKEN_LABEL);
2N/A }
2N/A kmf_set_attr_at_index(attr, n++, KMF_TOKEN_LABEL_ATTR,
2N/A token, strlen(token));
2N/A kmf_set_attr_at_index(attr, n++, KMF_READONLY_ATTR,
2N/A &true_val, sizeof (true_val));
2N/A kmf_set_attr_at_index(attr, n++, KMF_TOKEN_BOOL_ATTR,
2N/A &true_val, sizeof (true_val));
2N/A
2N/A err = kmf_configure_keystore(kmfh, n, attr);
2N/A if (err != KMF_OK)
2N/A goto out;
2N/A
2N/A if ((err = kmf_pk11_token_lookup(kmfh, token, &slot)) != KMF_OK ||
2N/A (err = C_GetTokenInfo(slot, &info)) != CKR_OK)
2N/A goto out;
2N/A /* Always prompt for PIN since the key is likey CKA_SENSITIVE */
2N/A if (prompt_pkcs11_pin(hdl, zc, cmd, p11uri, &cred.cred,
2N/A &cred.credlen) != 0)
2N/A goto out;
2N/A kmf_set_attr_at_index(attr, n++, KMF_CREDENTIAL_ATTR,
2N/A &cred, sizeof (KMF_CREDENTIAL));
2N/A
2N/A kmf_set_attr_at_index(attr, n++, KMF_KEYLABEL_ATTR,
2N/A p11uri->object, strlen((const char *)p11uri->object));
2N/A kmf_set_attr_at_index(attr, n++, KMF_KEYCLASS_ATTR, &keyclass,
2N/A sizeof (keyclass));
2N/A kmf_set_attr_at_index(attr, n++, KMF_ENCODE_FORMAT_ATTR, &format,
2N/A sizeof (format));
2N/A kmf_set_attr_at_index(attr, n++, KMF_COUNT_ATTR,
2N/A &numkeys, sizeof (numkeys));
2N/A
2N/A err = kmf_find_key(kmfh, n, attr);
2N/A if (err != KMF_OK || numkeys != 1)
2N/A goto out;
2N/A
2N/A kmf_set_attr_at_index(attr, n++, KMF_KEY_HANDLE_ATTR, &keys,
2N/A sizeof (KMF_KEY_HANDLE));
2N/A err = kmf_find_key(kmfh, n, attr);
2N/A err = kmf_get_sym_key_value(kmfh, &keys, &rkey);
2N/A if (err != KMF_OK)
2N/A goto out;
2N/A *keydata = zfs_alloc(hdl, rkey.keydata.len);
2N/A bcopy(rkey.keydata.val, *keydata, rkey.keydata.len);
2N/A *keydatalen = rkey.keydata.len;
2N/A kmf_free_bigint(&rkey.keydata);
2N/A
2N/Aout:
2N/A if (token != NULL)
2N/A free(token);
2N/A (void) kmf_finalize(kmfh);
2N/A
2N/A if (numkeys == 1 && err == KMF_OK) {
2N/A return (0);
2N/A } else if (err == KMF_ERR_AUTH_FAILED) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "PKCS#11 token login failed."));
2N/A } else if (numkeys == 0) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "PKCS#11 token object not found."));
2N/A } else if (numkeys > 1) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "keysource points to multiple PKCS#11"
2N/A " objects"));
2N/A }
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/Astruct cb_arg_curl {
2N/A libzfs_handle_t *cb_hdl;
2N/A char *cb_keydata;
2N/A size_t cb_keydatalen;
2N/A};
2N/A
2N/A/*ARGSUSED*/
2N/Astatic size_t
2N/Aget_keydata_curl(void *ptr, size_t size, size_t nmemb, void *arg)
2N/A{
2N/A struct cb_arg_curl *cb = arg;
2N/A size_t datalen = size * nmemb;
2N/A
2N/A if (ptr == NULL || datalen == 0)
2N/A return (0);
2N/A
2N/A cb->cb_keydatalen = datalen;
2N/A cb->cb_keydata = zfs_alloc(cb->cb_hdl, datalen);
2N/A bcopy(ptr, cb->cb_keydata, datalen);
2N/A
2N/A return (datalen);
2N/A}
2N/A
2N/Astatic int
2N/Akey_hdl_to_zc(libzfs_handle_t *hdl, zfs_handle_t *zhp, char *keysource,
2N/A int crypt, zfs_cmd_t *zc, zfs_crypto_zckey_t cmd)
2N/A{
2N/A CK_SESSION_HANDLE session;
2N/A int ret = 0;
2N/A key_format_t format;
2N/A key_locator_t locator;
2N/A char *uri;
2N/A pkcs11_uri_t p11uri;
2N/A size_t keylen = zio_crypt_table[crypt].ci_keylen;
2N/A char *keydata = NULL;
2N/A size_t keydatalen = 0;
2N/A char *tmpkeydata = NULL;
2N/A size_t tmpkeydatalen = 0;
2N/A uint64_t salt;
2N/A struct cb_arg_curl cb_curl = { 0 };
2N/A
2N/A zc->zc_crypto.zic_clone_newkey = hdl->libzfs_crypt.zc_clone_newkey;
2N/A
2N/A if (!keysource_prop_parser(keysource, &format, &locator, &uri)) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "invalid keysource property."));
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * First check if there was anything in the handle already
2N/A * if so we use that and we are done with locating the data.
2N/A * Note that we may be looking at other fields
2N/A * and zic_clone_newkey even if zc_key_data_len is empty.
2N/A *
2N/A * We allow this regardless of the locator so that things
2N/A * like a PAM module can provide the passphrase but the user
2N/A * can still have "passphrase,prompt" to use zfs(1M) interactively.
2N/A */
2N/A if (hdl->libzfs_crypt.zc_key_data_len != 0) {
2N/A keydata = zfs_alloc(hdl, hdl->libzfs_crypt.zc_key_data_len);
2N/A bcopy(hdl->libzfs_crypt.zc_key_data, keydata,
2N/A hdl->libzfs_crypt.zc_key_data_len);
2N/A keydatalen = hdl->libzfs_crypt.zc_key_data_len;
2N/A goto format_key;
2N/A }
2N/A
2N/A /*
2N/A * Get the key from the URI or prompt for it.
2N/A * If the format is raw then prompting is a simple read(2)
2N/A * otherwise we put up a prompt saying what we are asking for.
2N/A * We can't do this with the 'zfs mount -a' that is in
2N/A * sys:/system/filesystem/local:default but we shouldn't
2N/A * cause errors or warnings there either.
2N/A */
2N/A switch (locator) {
2N/A case KEY_LOCATOR_PROMPT:
2N/A if (format == KEY_FORMAT_RAW) {
2N/A keydata = zfs_alloc(hdl, MAXKEYLEN);
2N/A errno = 0;
2N/A keydatalen = read(STDIN_FILENO, keydata, MAXKEYLEN);
2N/A if (!ZFS_IOC_VALIDKEYLEN(keydatalen)) {
2N/A free(keydata);
2N/A return (-1);
2N/A }
2N/A
2N/A } else {
2N/A int tries = 0;
2N/A do {
2N/A /* get_passphrase allocates keydata */
2N/A ret = get_passphrase(hdl, &keydata,
2N/A &keydatalen, format, zc, cmd);
2N/A } while (ret != 0 && ++tries < 3);
2N/A if (ret)
2N/A return (-1);
2N/A }
2N/A break;
2N/A case KEY_LOCATOR_FILE_URI:
2N/A /*
2N/A * Need to tell pkcs11_read_data() how big of a key
2N/A * we want in case the locator URI is a device (eg, /dev/random)
2N/A * to be read from and not a file.
2N/A *
2N/A * Note that pkcs11_read_data allocates memory with malloc
2N/A * that we need to free.
2N/A */
2N/A keydatalen = MAXKEYLEN;
2N/A ret = pkcs11_read_data(&(uri[7]),
2N/A (void **)&keydata, &keydatalen);
2N/A if (ret != 0) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "failed to read key file: %s"), strerror(ret));
2N/A errno = ret;
2N/A return (-1);
2N/A }
2N/A break;
2N/A case KEY_LOCATOR_PKCS11_URI:
2N/A /*
2N/A * Parse out the PKCS#11 URI and
2N/A * get the value of the wrapping key.
2N/A */
2N/A if (pkcs11_parse_uri(uri, &p11uri) != PK11_URI_OK) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A ret = get_pkcs11_key_value(hdl, zc, cmd, &p11uri,
2N/A &keydata, &keydatalen);
2N/A pkcs11_free_uri(&p11uri);
2N/A if (ret != 0) {
2N/A return (-1);
2N/A }
2N/A break;
2N/A case KEY_LOCATOR_HTTPS_URI: {
2N/A CURL *curl_hdl = curl_easy_init();
2N/A CURLcode cerr;
2N/A
2N/A cerr = curl_easy_setopt(curl_hdl, CURLOPT_URL, uri);
2N/A if (cerr != CURLE_OK)
2N/A goto curl_fail;
2N/A cerr = curl_easy_setopt(curl_hdl, CURLOPT_FAILONERROR, 1L);
2N/A if (cerr != CURLE_OK)
2N/A goto curl_fail;
2N/A cerr = curl_easy_setopt(curl_hdl, CURLOPT_WRITEFUNCTION,
2N/A get_keydata_curl);
2N/A if (cerr != CURLE_OK)
2N/A goto curl_fail;
2N/A cb_curl.cb_hdl = hdl;
2N/A cerr = curl_easy_setopt(curl_hdl, CURLOPT_WRITEDATA,
2N/A &cb_curl);
2N/A if (cerr != CURLE_OK)
2N/A goto curl_fail;
2N/A cerr = curl_easy_perform(curl_hdl);
2N/Acurl_fail:
2N/A /*
2N/A * Just deal with libcurl errors here, reading the wrong key
2N/A * size is dealt with generically in the format_key section.
2N/A */
2N/A if (cerr != 0) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "failed to retreive key from '%s': '%s'"),
2N/A uri, curl_easy_strerror(cerr));
2N/A return (-1);
2N/A }
2N/A
2N/A keydata = cb_curl.cb_keydata;
2N/A keydatalen = cb_curl.cb_keydatalen;
2N/A
2N/A curl_easy_cleanup(curl_hdl);
2N/A break;
2N/A }
2N/A }
2N/A
2N/Aformat_key:
2N/A if (keydata == NULL || keydatalen == 0) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "key can not be of zero size"));
2N/A errno = ret;
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Now that we have the key do any transform that is necessary
2N/A * such as turning the hex format into raw or in the case of
2N/A * a passphrase running it through PKCS#5 to get the raw key.
2N/A *
2N/A * Note that zic_keydata is not malloc'd memory so that we
2N/A * don't have to worry about our caller freeing it.
2N/A */
2N/A switch (format) {
2N/A case KEY_FORMAT_RAW:
2N/A bcopy(keydata, zc->zc_crypto.zic_keydata, keydatalen);
2N/A zc->zc_crypto.zic_keydatalen = keydatalen;
2N/A zc->zc_crypto.zic_salt = 0;
2N/A break;
2N/A case KEY_FORMAT_HEX:
2N/A /*
2N/A * If the keylen is not on the byte boundary, in terms of hex
2N/A * format, and that extra char is a linefeed, we can trim it
2N/A */
2N/A if (keydatalen == (keylen * 2) + 1 &&
2N/A keydata[keydatalen] == '\n') {
2N/A keydatalen--;
2N/A }
2N/A
2N/A /*
2N/A * hexstr_to_bytes allocates memory with malloc
2N/A * but we want the data in zic_keydata which isn't malloc'd
2N/A * so to avoid a memory leak we use a tmpkeydata buffer
2N/A * and bcopy it.
2N/A */
2N/A ret = hexstr_to_bytes(keydata, keydatalen,
2N/A (uchar_t **)&tmpkeydata, &tmpkeydatalen);
2N/A
2N/A if (ret) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "invalid hex format key."));
2N/A errno = EACCES;
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A
2N/A bcopy(tmpkeydata, zc->zc_crypto.zic_keydata, tmpkeydatalen);
2N/A bzero(tmpkeydata, tmpkeydatalen);
2N/A free(tmpkeydata);
2N/A zc->zc_crypto.zic_keydatalen = tmpkeydatalen;
2N/A zc->zc_crypto.zic_salt = 0;
2N/A break;
2N/A case KEY_FORMAT_PASSPHRASE:
2N/A /* Remove any extra linefeed that may be on the end */
2N/A if (keydata[keydatalen - 1] == '\n')
2N/A keydatalen--;
2N/A
2N/A if (cmd == ZFS_CRYPTO_KEY_LOAD) {
2N/A salt = zfs_prop_get_int(zhp, ZFS_PROP_SALT);
2N/A } else {
2N/A ret = pkcs11_get_random(&salt, sizeof (uint64_t));
2N/A if (ret) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "failed to obtain salt: %s."),
2N/A pkcs11_strerror(ret));
2N/A errno = EINVAL;
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A ret = SUNW_C_GetMechSession(CKM_PKCS5_PBKD2, &session);
2N/A if (ret) {
2N/A zfs_error_aux(hdl,
2N/A dgettext(TEXT_DOMAIN,
2N/A "failed to access CKM_PKCS5_PBKD2: %s."),
2N/A pkcs11_strerror(ret));
2N/A errno = EINVAL;
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * pkcs11_PasswdToKey allocates memory with malloc
2N/A * but we want the data in zic_keydata which isn't malloc'd
2N/A * so to avoid a memory leak we use a tmpkeydata buffer
2N/A * and bcopy it.
2N/A */
2N/A ret = pkcs11_PasswdToKey(session, keydata, keydatalen,
2N/A (void *)&salt, sizeof (uint64_t), CKK_AES,
2N/A keylen, (void **)&tmpkeydata, &tmpkeydatalen);
2N/A
2N/A (void) C_CloseSession(session);
2N/A
2N/A if (ret) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "failed to generate key: %s."),
2N/A pkcs11_strerror(ret));
2N/A errno = EINVAL;
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A bcopy(tmpkeydata, zc->zc_crypto.zic_keydata, tmpkeydatalen);
2N/A bzero(tmpkeydata, tmpkeydatalen);
2N/A free(tmpkeydata);
2N/A zc->zc_crypto.zic_keydatalen = tmpkeydatalen;
2N/A zc->zc_crypto.zic_salt = salt;
2N/A break;
2N/A default:
2N/A ASSERT(format);
2N/A }
2N/A
2N/A if (!ZFS_IOC_VALIDKEYLEN(zc->zc_crypto.zic_keydatalen)) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "key must be 128, 192 or 256 bits, got %lu bits"),
2N/A zc->zc_crypto.zic_keydatalen * 8);
2N/A errno = EIO;
2N/A ret = -1;
2N/A }
2N/A
2N/A tmpkeydatalen--;
2N/A while (zc->zc_crypto.zic_keydata[tmpkeydatalen] == 0 &&
2N/A tmpkeydatalen > 0)
2N/A tmpkeydatalen--;
2N/A
2N/A if (tmpkeydatalen == 0) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "invalid all zeros key %lu"));
2N/A errno = EIO;
2N/A ret = -1;
2N/A }
2N/Aout:
2N/A if (keydata) {
2N/A bzero(keydata, keydatalen);
2N/A free(keydata);
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Aint
2N/Azfs_key_load(zfs_handle_t *zhp, boolean_t mount, boolean_t share,
2N/A boolean_t recursive)
2N/A{
2N/A zprop_source_t propsrctype;
2N/A char source[ZFS_MAXNAMELEN];
2N/A char keysource[MAXNAMELEN];
2N/A uint64_t ret, crypt, keystatus;
2N/A zfs_cmd_t zc = { 0 };
2N/A char errbuf[1024];
2N/A
2N/A (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name));
2N/A (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2N/A "cannot load key for '%s'"), zc.zc_name);
2N/A
2N/A zfs_refresh_properties(zhp);
2N/A
2N/A crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
2N/A if (crypt == ZIO_CRYPT_OFF) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "encryption not enabled on dataset %s."), zc.zc_name);
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A
2N/A keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
2N/A
2N/A if (keystatus == ZFS_CRYPT_KEY_AVAILABLE && !recursive) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "already loaded."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A
2N/A if (zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, ZFS_MAXNAMELEN,
2N/A &propsrctype, source, sizeof (source), FALSE) != 0) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "no keysource property available."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A
2N/A if (!zfs_can_prompt_if_needed(keysource)) {
2N/A errno = ENOTTY;
2N/A return (-1);
2N/A }
2N/A
2N/A if (propsrctype & ZPROP_SRC_INHERITED) {
2N/A (void) strlcpy(zc.zc_crypto.zic_inherit_dsname, source,
2N/A sizeof (zc.zc_crypto.zic_inherit_dsname));
2N/A ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_INHERIT, &zc);
2N/A goto out;
2N/A }
2N/A
2N/A zc.zc_crypto.zic_crypt = crypt;
2N/A
2N/A ret = key_hdl_to_zc(zhp->zfs_hdl, zhp, keysource, crypt, &zc,
2N/A ZFS_CRYPTO_KEY_LOAD);
2N/A if (ret != 0) {
2N/A if (errno == ENOTTY)
2N/A ret = 0;
2N/A goto out;
2N/A }
2N/A
2N/A ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_LOAD, &zc);
2N/Aout:
2N/A if (ret != 0) {
2N/A if (errno == EACCES) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "incorrect key."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A } else if (strcmp(source, ZONE_INVISIBLE_SOURCE) == 0) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "key must be loaded from global zone."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A } else if (!recursive) {
2N/A if (errno == EEXIST) {
2N/A zfs_error_aux(zhp->zfs_hdl,
2N/A dgettext(TEXT_DOMAIN, "already loaded."));
2N/A } else if (zhp->zfs_hdl->libzfs_desc_active == 0) {
2N/A zfs_error_aux(zhp->zfs_hdl, strerror(errno));
2N/A }
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A }
2N/A
2N/A zfs_refresh_properties(zhp);
2N/A if (mount) {
2N/A if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
2N/A if (recursive) {
2N/A ret = zfs_mountall(zhp, 0);
2N/A } else {
2N/A ret = zfs_mount(zhp, NULL, 0);
2N/A }
2N/A if (ret == 0 && share) {
2N/A ret = zfs_share(zhp);
2N/A }
2N/A }
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Aint
2N/Azfs_key_unload(zfs_handle_t *zhp, boolean_t force)
2N/A{
2N/A zfs_cmd_t zc = { 0 };
2N/A int ret = 0;
2N/A int terrno;
2N/A int type = zfs_get_type(zhp);
2N/A char errbuf[1024];
2N/A
2N/A (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2N/A "cannot unload key for '%s'"), zfs_get_name(zhp));
2N/A
2N/A if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) == ZIO_CRYPT_OFF) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "no key to unload when encryption=off."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A if (zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) !=
2N/A ZFS_CRYPT_KEY_AVAILABLE) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "key not present."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A
2N/A /*
2N/A * We need to be sure that all the data has been written to
2N/A * disk before we unload the key so we first have to attempt
2N/A * an unmount, if that fails we don't continue with the key unload
2N/A * and instead return the error from zfs_umount.
2N/A */
2N/A if (type == ZFS_TYPE_FILESYSTEM) {
2N/A if (zfs_is_mounted(zhp, NULL)) {
2N/A ret = zfs_unmountall(zhp, force ? MS_FORCE : 0);
2N/A if (ret) {
2N/A zfs_error_aux(zhp->zfs_hdl,
2N/A dgettext(TEXT_DOMAIN,
2N/A "failed to unload key: unmount failed"));
2N/A return (zfs_error(zhp->zfs_hdl,
2N/A EZFS_KEYERR, errbuf));
2N/A }
2N/A }
2N/A }
2N/A
2N/A (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name));
2N/A
2N/A errno = 0;
2N/A ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_UNLOAD, &zc);
2N/A terrno = errno;
2N/A if (ret != 0) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "failed to unload key: %s"), strerror(terrno));
2N/A errno = terrno; /* make sure it is the zfs_ioctl errno */
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A zfs_refresh_properties(zhp);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Azfs_key_new(zfs_handle_t *zhp)
2N/A{
2N/A char errbuf[1024];
2N/A zfs_cmd_t zc = { 0 };
2N/A
2N/A (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name));
2N/A
2N/A if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_NEW, &zc) != 0) {
2N/A (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2N/A "cannot create new key for '%s'"), zc.zc_name);
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Azfs_key_change(zfs_handle_t *zhp, boolean_t recursing, nvlist_t *props)
2N/A{
2N/A char errbuf[1024];
2N/A int ret;
2N/A zfs_cmd_t zc = { 0 };
2N/A char keysource[ZFS_MAXNAMELEN];
2N/A uint64_t crypt;
2N/A zprop_source_t propsrctype = ZPROP_SRC_NONE;
2N/A char propsrc[ZFS_MAXNAMELEN] = { 0 };
2N/A
2N/A (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2N/A "cannot change wrapping key for '%s'"), zfs_get_name(zhp));
2N/A crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
2N/A if (crypt == ZIO_CRYPT_OFF) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "cannot change key when encryption=off"));
2N/A goto error;
2N/A }
2N/A
2N/A switch (zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS)) {
2N/A case ZFS_CRYPT_KEY_NONE:
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "inconsistent state encryption enabled but "
2N/A "key not defined."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A case ZFS_CRYPT_KEY_UNAVAILABLE:
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "load existing key first: 'zfs key -l <dataset>'."));
2N/A goto error;
2N/A }
2N/A
2N/A (void) zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource,
2N/A ZFS_MAXNAMELEN, &propsrctype, propsrc, ZFS_MAXNAMELEN, B_TRUE);
2N/A if (!(propsrctype & ZPROP_SRC_LOCAL ||
2N/A propsrctype & ZPROP_SRC_RECEIVED)) {
2N/A if (recursing)
2N/A return (0);
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "keysource property not local, change key on '%s'."),
2N/A propsrc);
2N/A goto error;
2N/A }
2N/A
2N/A zhp->zfs_hdl->libzfs_crypt.zc_is_key_change = B_TRUE;
2N/A
2N/A /*
2N/A * The only thing we currently expect in props is a keysource
2N/A * if we have props without keysource then that isn't valid.
2N/A */
2N/A if (props != NULL) {
2N/A char *nkeysource;
2N/A ret = nvlist_lookup_string(props,
2N/A zfs_prop_to_name(ZFS_PROP_KEYSOURCE), (char **)&nkeysource);
2N/A if (ret != 0) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "invalid props for key change; expected "
2N/A "%s property missing."),
2N/A zfs_prop_to_name(ZFS_PROP_KEYSOURCE));
2N/A goto error;
2N/A }
2N/A (void) strlcpy(keysource, nkeysource, sizeof (keysource));
2N/A }
2N/A
2N/A (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name));
2N/A zc.zc_crypto.zic_crypt = crypt;
2N/A
2N/A if (!zfs_can_prompt_if_needed(keysource)) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "unable to prompt for new wrapping key."));
2N/A errno = ENOTTY;
2N/A goto error;
2N/A }
2N/A
2N/A ret = key_hdl_to_zc(zhp->zfs_hdl, zhp, keysource, crypt, &zc,
2N/A ZFS_CRYPTO_KEY_CHANGE);
2N/A if (props != NULL) {
2N/A if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, props) != 0)
2N/A goto error;
2N/A }
2N/A if (ret == 0) {
2N/A /* Send change to kernel */
2N/A ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CRYPTO_KEY_CHANGE, &zc);
2N/A zcmd_free_nvlists(&zc);
2N/A if (ret != 0) {
2N/A return (zfs_standard_error(zhp->zfs_hdl, NULL,
2N/A zhp->zfs_type, errno, errbuf));
2N/A }
2N/A zfs_refresh_properties(zhp);
2N/A return (ret);
2N/A }
2N/Aerror:
2N/A zcmd_free_nvlists(&zc);
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A}
2N/A
2N/A/*
2N/A * This is to verify that the proposed keysource property change via
2N/A * 'zfs set', and internal functions is valid.
2N/A */
2N/Aboolean_t
2N/Azfs_valid_set_keysource_change(zfs_handle_t *zhp, char *new)
2N/A{
2N/A key_format_t old_format, new_format;
2N/A key_locator_t old_locator, new_locator;
2N/A char *uri;
2N/A boolean_t valid;
2N/A char old[ZFS_MAXNAMELEN];
2N/A zprop_source_t propsrctype = ZPROP_SRC_DEFAULT;
2N/A char propsrc[ZFS_MAXNAMELEN];
2N/A
2N/A /*
2N/A * If we are calling this from a change key operation or a clone
2N/A * the valid keysource changes have no restrictions.
2N/A */
2N/A if ((zhp->zfs_type == ZFS_TYPE_SNAPSHOT &&
2N/A (zhp->zfs_head_type == ZFS_TYPE_VOLUME ||
2N/A zhp->zfs_head_type == ZFS_TYPE_FILESYSTEM)) ||
2N/A (zhp->zfs_hdl->libzfs_crypt.zc_is_key_change == B_TRUE)) {
2N/A return (zfs_valid_keysource(new));
2N/A }
2N/A
2N/A if (zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, old, sizeof (old),
2N/A &propsrctype, propsrc, sizeof (propsrc), FALSE) != 0) {
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A if (propsrctype & ZPROP_SRC_INHERITED) {
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A /*
2N/A * If we are calling this from a set property operation, the valid
2N/A * keysources are limited to the same format
2N/A */
2N/A valid = keysource_prop_parser(new, &new_format, &new_locator, &uri);
2N/A if (!valid) {
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A valid = keysource_prop_parser(old, &old_format, &old_locator, &uri);
2N/A if (old_format != new_format) {
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A return (valid);
2N/A}
2N/A
2N/A/* Validate the keysource provided is a valid keysource */
2N/Aboolean_t
2N/Azfs_valid_keysource(char *keysource)
2N/A{
2N/A key_format_t format;
2N/A key_locator_t locator;
2N/A char *uri;
2N/A
2N/A if (keysource == NULL)
2N/A return (B_FALSE);
2N/A
2N/A return (keysource_prop_parser(keysource, &format, &locator, &uri));
2N/A}
2N/A
2N/A/*
2N/A * zfs_crypto_zckey
2N/A *
2N/A * Called for creating new filesystems and clones and receiving.
2N/A *
2N/A * For encryption != off get the key material.
2N/A */
2N/Aint
2N/Azfs_crypto_zckey(libzfs_handle_t *hdl, zfs_crypto_zckey_t cmd,
2N/A nvlist_t *props, zfs_cmd_t *zc)
2N/A{
2N/A uint64_t crypt = ZIO_CRYPT_INHERIT, pcrypt = ZIO_CRYPT_DEFAULT;
2N/A char *keysource = NULL;
2N/A int ret = 0;
2N/A int keystatus;
2N/A zfs_handle_t *pzhp = NULL;
2N/A boolean_t inherit_crypt = B_TRUE;
2N/A boolean_t inherit_keysource = B_TRUE;
2N/A boolean_t recv_existing = B_FALSE;
2N/A boolean_t recv_clone = B_FALSE;
2N/A boolean_t keysource_free = B_FALSE;
2N/A zprop_source_t propsrctype = ZPROP_SRC_DEFAULT;
2N/A char propsrc[ZFS_MAXNAMELEN];
2N/A char errbuf[1024];
2N/A char target[MAXNAMELEN];
2N/A char parent[MAXNAMELEN];
2N/A char *strval;
2N/A
2N/A zfs_cmd_target_dsname(zc, cmd, target, sizeof (target));
2N/A if (zfs_parent_name(target, parent, sizeof (parent)) != 0)
2N/A parent[0] = '\0';
2N/A (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2N/A "cannot create '%s'"), target);
2N/A
2N/A if (props != NULL) {
2N/A if (nvlist_lookup_string(props,
2N/A zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &strval) == 0) {
2N/A (void) zfs_prop_string_to_index(ZFS_PROP_ENCRYPTION,
2N/A strval, &crypt);
2N/A inherit_crypt = B_FALSE;
2N/A } else if (nvlist_lookup_uint64(props,
2N/A zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt) == 0) {
2N/A inherit_crypt = B_FALSE;
2N/A } else {
2N/A inherit_crypt = B_TRUE;
2N/A }
2N/A if (nvlist_lookup_string(props,
2N/A zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource) == 0) {
2N/A inherit_keysource = B_FALSE;
2N/A }
2N/A }
2N/A
2N/A if (cmd == ZFS_CRYPTO_CREATE) {
2N/A pzhp = make_dataset_handle(hdl, parent);
2N/A } else if (cmd == ZFS_CRYPTO_CLONE) {
2N/A zfs_handle_t *szhp = make_dataset_handle(hdl, zc->zc_value);
2N/A if (szhp == NULL) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "parent not found"));
2N/A (void) zfs_error(hdl, EZFS_NOENT, errbuf);
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A crypt = zfs_prop_get_int(szhp, ZFS_PROP_ENCRYPTION);
2N/A zfs_close(szhp);
2N/A pzhp = make_dataset_handle(hdl, parent);
2N/A } else if (cmd == ZFS_CRYPTO_RECV) {
2N/A if (zfs_dataset_exists(hdl, target, ZFS_TYPE_DATASET)) {
2N/A pzhp = make_dataset_handle(hdl, target);
2N/A pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION);
2N/A if (crypt != pcrypt && crypt != ZIO_CRYPT_INHERIT) {
2N/A const char *stream_crypt_str = NULL;
2N/A const char *pcrypt_str = NULL;
2N/A (void) zfs_prop_index_to_string(
2N/A ZFS_PROP_ENCRYPTION, pcrypt,
2N/A &pcrypt_str);
2N/A (void) zfs_prop_index_to_string(
2N/A ZFS_PROP_ENCRYPTION, crypt,
2N/A &stream_crypt_str);
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "stream encryption '%s'(%llu) differs "
2N/A "from receiving dataset value '%s'(%llu)"),
2N/A stream_crypt_str, crypt,
2N/A pcrypt_str, pcrypt);
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A inherit_crypt = B_TRUE;
2N/A inherit_keysource = B_TRUE;
2N/A recv_existing = B_TRUE;
2N/A } else {
2N/A if (strlen(zc->zc_string) != 0) {
2N/A pzhp = make_dataset_handle(hdl, zc->zc_string);
2N/A recv_clone = B_TRUE;
2N/A } else {
2N/A pzhp = make_dataset_handle(hdl, parent);
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (cmd != ZFS_CRYPTO_PCREATE) {
2N/A if (pzhp == NULL) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "parent not found"));
2N/A (void) zfs_error(hdl, EZFS_NOENT, errbuf);
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION);
2N/A }
2N/A
2N/A if (pcrypt != ZIO_CRYPT_OFF && crypt == ZIO_CRYPT_OFF) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
2N/A "encryption value. dataset must be encrypted."));
2N/A (void) zfs_error(hdl, EZFS_KEYERR, errbuf);
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (crypt == ZIO_CRYPT_INHERIT) {
2N/A crypt = pcrypt;
2N/A }
2N/A
2N/A /*
2N/A * If we have nothing to do then bail out, but make one last check
2N/A * that keysource wasn't specified when there is no crypto going on.
2N/A */
2N/A if (crypt == ZIO_CRYPT_OFF && !inherit_keysource) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "keysource "
2N/A "can not be specified when encryption is off."));
2N/A (void) zfs_error(hdl, EZFS_KEYERR, errbuf);
2N/A ret = -1;
2N/A goto out;
2N/A } else if (crypt == ZIO_CRYPT_OFF) {
2N/A ret = 0;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Need to pass down the inherited crypt value so that
2N/A * dsl_crypto_key_gen() can see the same that we saw.
2N/A */
2N/A zc->zc_crypto.zic_crypt = crypt;
2N/A zc->zc_crypto.zic_clone_newkey = hdl->libzfs_crypt.zc_clone_newkey;
2N/A
2N/A /*
2N/A * Here we have encryption on so we need to find a valid keysource
2N/A * property.
2N/A *
2N/A * Now lets see if we have an explicit setting for keysource and
2N/A * we have validate it; otherwise, if we inherit then it is already
2N/A * validated.
2N/A */
2N/A if (!inherit_keysource) {
2N/A if (!zfs_valid_keysource(keysource)) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "invalid keysource \"%s\""), keysource);
2N/A (void) zfs_error(hdl, EZFS_KEYERR, errbuf);
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A /*
2N/A * If keysource is local then encryption has to be as well
2N/A * otherwise we could end up with the wrong sized keys.
2N/A */
2N/A if (inherit_crypt) {
2N/A VERIFY(nvlist_add_uint64(props,
2N/A zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt) == 0);
2N/A VERIFY(nvlist_add_uint64(props,
2N/A zfs_prop_to_name(ZFS_PROP_CHECKSUM),
2N/A ZIO_CHECKSUM_SHA256_MAC) == 0);
2N/A }
2N/A } else {
2N/A /* Get the already validated keysource from our parent */
2N/A keysource = zfs_alloc(hdl, ZFS_MAXNAMELEN);
2N/A if (keysource == NULL) {
2N/A ret = no_memory(hdl);
2N/A goto out;
2N/A }
2N/A keysource_free = B_TRUE;
2N/A if (pzhp != NULL && zfs_prop_get(pzhp, ZFS_PROP_KEYSOURCE,
2N/A keysource, ZFS_MAXNAMELEN, &propsrctype, propsrc,
2N/A sizeof (propsrc), FALSE) != 0) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "keysource must be provided."));
2N/A (void) zfs_error(hdl, EZFS_KEYERR, errbuf);
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A
2N/A if (recv_existing) {
2N/A (void) strlcpy(propsrc, target, sizeof (propsrc));
2N/A } else if (recv_clone) {
2N/A (void) strlcpy(propsrc,
2N/A zc->zc_string, sizeof (propsrc));
2N/A } else if (propsrctype == ZPROP_SRC_LOCAL ||
2N/A propsrctype == ZPROP_SRC_RECEIVED) {
2N/A (void) strlcpy(propsrc, parent, sizeof (propsrc));
2N/A } else if (propsrctype == ZPROP_SRC_DEFAULT &&
2N/A pcrypt == ZIO_CRYPT_OFF) {
2N/A /*
2N/A * "Default" to "passphrase,prompt". The obvious
2N/A * thing to do would be to set this in zfs_prop.c
2N/A * as the property default. However that doesn't
2N/A * work here because we don't want keysource set
2N/A * for datasets that have encryption=off. If we
2N/A * ever change the default to encryption=on then
2N/A * the default of keysource can change too.
2N/A * This is needed because of how inheritance happens
2N/A * with defaulted properties, they show up as
2N/A * "default" not "inherit" but we need "inherit"
2N/A * to find the wrapping key if we are actually
2N/A * inheriting keysource.
2N/A */
2N/A inherit_keysource = B_FALSE;
2N/A if (props == NULL) {
2N/A VERIFY(0 == nvlist_alloc(&props,
2N/A NV_UNIQUE_NAME, 0));
2N/A }
2N/A (void) strlcpy(keysource, "passphrase,prompt",
2N/A ZFS_MAXNAMELEN);
2N/A VERIFY(nvlist_add_string(props,
2N/A zfs_prop_to_name(ZFS_PROP_KEYSOURCE),
2N/A keysource) == 0);
2N/A VERIFY(nvlist_add_uint64(props,
2N/A zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt) == 0);
2N/A VERIFY(nvlist_add_uint64(props,
2N/A zfs_prop_to_name(ZFS_PROP_CHECKSUM),
2N/A ZIO_CHECKSUM_SHA256_MAC) == 0);
2N/A goto load_key;
2N/A } else if (propsrctype == ZPROP_SRC_DEFAULT &&
2N/A pcrypt != ZIO_CRYPT_OFF) {
2N/A abort();
2N/A } else if (strcmp(propsrc, ZONE_INVISIBLE_SOURCE) == 0) {
2N/A /*
2N/A * Assume key is available and handle failure ioctl
2N/A * ENOKEY errors later.
2N/A */
2N/A zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_INHERIT;
2N/A (void) strlcpy(zc->zc_crypto.zic_inherit_dsname,
2N/A propsrc, sizeof (zc->zc_crypto.zic_inherit_dsname));
2N/A ret = 0;
2N/A goto out;
2N/A } else if (propsrctype != ZPROP_SRC_DEFAULT) {
2N/A if (pzhp != NULL)
2N/A zfs_close(pzhp);
2N/A VERIFY((pzhp = make_dataset_handle(hdl, propsrc)) != 0);
2N/A }
2N/A keystatus = zfs_prop_get_int(pzhp, ZFS_PROP_KEYSTATUS);
2N/A /*
2N/A * AVAILABLE we are done other than filling in who we
2N/A * are inheriting the wrapping key from.
2N/A *
2N/A * UNAVAILABLE we need to load the key of a higher level
2N/A * dataset.
2N/A */
2N/A if (keystatus == ZFS_CRYPT_KEY_AVAILABLE) {
2N/A zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_INHERIT;
2N/A (void) strlcpy(zc->zc_crypto.zic_inherit_dsname,
2N/A propsrc, sizeof (zc->zc_crypto.zic_inherit_dsname));
2N/A ret = 0;
2N/A goto out;
2N/A } else if (keystatus == ZFS_CRYPT_KEY_UNAVAILABLE) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "zfs key -l %s required."), parent);
2N/A (void) zfs_error(hdl, EZFS_KEYERR, errbuf);
2N/A ret = -1;
2N/A goto out;
2N/A }
2N/A }
2N/Aload_key:
2N/A if (!zfs_can_prompt_if_needed(keysource)) {
2N/A zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2N/A "unable to prompt for key material keysource = \"%s\"\n"),
2N/A keysource);
2N/A errno = ENOTTY;
2N/A return (-1);
2N/A }
2N/A ret = key_hdl_to_zc(hdl, NULL, keysource, crypt, zc, cmd);
2N/A if (ret != 0) {
2N/A ret = -1;
2N/A (void) zfs_error(hdl, EZFS_KEYERR, errbuf);
2N/A goto out;
2N/A }
2N/A zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_LOAD;
2N/A ret = 0;
2N/Aout:
2N/A if (pzhp)
2N/A zfs_close(pzhp);
2N/A if (keysource_free)
2N/A free(keysource);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * zfs_crypto_rename_check
2N/A *
2N/A * Can't rename "out" of same hierarchy if keysource would change.
2N/A *
2N/A * If this dataset isn't encrypted we allow the rename, unless it
2N/A * is being placed "below" an encrypted one.
2N/A */
2N/Aint
2N/Azfs_crypto_rename_check(zfs_handle_t *zhp, zfs_cmd_t *zc)
2N/A{
2N/A uint64_t crypt, pcrypt;
2N/A zfs_handle_t *pzhp;
2N/A zprop_source_t propsrctype, ppropsrctype;
2N/A char keysource[ZFS_MAXNAMELEN];
2N/A char pkeysource[ZFS_MAXNAMELEN];
2N/A char propsrc[ZFS_MAXNAMELEN];
2N/A char psource[ZFS_MAXNAMELEN];
2N/A char oparent[ZFS_MAXNAMELEN];
2N/A char nparent[ZFS_MAXNAMELEN];
2N/A char errbuf[1024];
2N/A
2N/A if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
2N/A zhp->zfs_type == ZFS_TYPE_SHARE)
2N/A return (0);
2N/A
2N/A (void) zfs_parent_name(zc->zc_name, oparent, sizeof (oparent));
2N/A (void) zfs_parent_name(zc->zc_value, nparent, sizeof (nparent));
2N/A /* Simple rename in place */
2N/A if (strcmp(oparent, nparent) == 0) {
2N/A return (0);
2N/A }
2N/A
2N/A (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2N/A "cannot rename '%s'"), zfs_get_name(zhp));
2N/A
2N/A crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
2N/A
2N/A /* parent should never be null */
2N/A pzhp = make_dataset_handle(zhp->zfs_hdl, nparent);
2N/A if (pzhp == NULL) {
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "failed to obtain parent to check encryption property."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION);
2N/A
2N/A /* If no crypt involved then we are done. */
2N/A if (crypt == ZIO_CRYPT_OFF && pcrypt == ZIO_CRYPT_OFF) {
2N/A zfs_close(pzhp);
2N/A return (0);
2N/A }
2N/A
2N/A /* Just like create time no unencrypted below encrypted . */
2N/A if (crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) {
2N/A zfs_close(pzhp);
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "Can not move unencrypted dataset below "
2N/A "encrypted datasets."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A
2N/A /*
2N/A * From here on we need to check that keysource is
2N/A * from the same dataset if it is being inherited
2N/A */
2N/A if (zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource,
2N/A ZFS_MAXNAMELEN, &propsrctype,
2N/A propsrc, sizeof (propsrc), FALSE) != 0) {
2N/A zfs_close(pzhp);
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "keysource must be provided."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A
2N/A if (propsrctype == ZPROP_SRC_LOCAL ||
2N/A propsrctype == ZPROP_SRC_RECEIVED) {
2N/A zfs_close(pzhp);
2N/A return (0);
2N/A }
2N/A
2N/A if (zfs_prop_get(pzhp, ZFS_PROP_KEYSOURCE, pkeysource,
2N/A ZFS_MAXNAMELEN, &ppropsrctype,
2N/A psource, sizeof (psource), FALSE) != 0) {
2N/A zfs_close(pzhp);
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "keysource must be provided."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A }
2N/A
2N/A if (propsrctype == ZPROP_SRC_INHERITED &&
2N/A ((strcmp(propsrc, nparent) == 0) ||
2N/A (strcmp(propsrc, psource) == 0))) {
2N/A zfs_close(pzhp);
2N/A return (0);
2N/A }
2N/A
2N/A zfs_close(pzhp);
2N/A zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
2N/A "keysource doesn't allow for rename, make keysource local."));
2N/A return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
2N/A}
2N/A
2N/Aboolean_t
2N/Azfs_is_encrypted(zfs_handle_t *zhp)
2N/A{
2N/A int crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
2N/A
2N/A return (!(crypt == ZIO_CRYPT_OFF));
2N/A}