Exemple #1
0
/*
 * zfs_crypto_rename_check
 *
 * Can't rename "out" of same hierarchy if keysource would change.
 *
 * If this dataset isn't encrypted we allow the rename, unless it
 * is being placed "below" an encrypted one.
 */
int
zfs_crypto_rename_check(zfs_handle_t *zhp, zfs_cmd_t *zc)
{
	uint64_t crypt, pcrypt;
	zfs_handle_t *pzhp;
	zprop_source_t propsrctype, ppropsrctype;
	char keysource[ZFS_MAXNAMELEN];
	char pkeysource[ZFS_MAXNAMELEN];
	char propsrc[ZFS_MAXNAMELEN];
	char psource[ZFS_MAXNAMELEN];
	char oparent[ZFS_MAXNAMELEN];
	char nparent[ZFS_MAXNAMELEN];
	char errbuf[1024];

	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
		return (0);

	(void) zfs_parent_name(zc->zc_name, oparent, sizeof (oparent));
	(void) zfs_parent_name(zc->zc_value, nparent, sizeof (nparent));
	/* Simple rename in place */
	if (strcmp(oparent, nparent) == 0) {
		return (0);
	}

	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
	    "cannot rename '%s'"), zfs_get_name(zhp));

	crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);

	/* parent should never be null */
	pzhp = make_dataset_handle(zhp->zfs_hdl, nparent);
	if (pzhp == NULL) {
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "failed to obtain parent to check encryption property."));
		return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
	}
	pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION);

	/* If no crypt involved then we are done. */
	if (crypt == ZIO_CRYPT_OFF && pcrypt == ZIO_CRYPT_OFF) {
		zfs_close(pzhp);
		return (0);
	}

	/* Just like create time no unencrypted below encrypted . */
	if (crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) {
		zfs_close(pzhp);
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "Can not move unencrypted dataset below "
		    "encrypted datasets."));
		return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
	}

	/*
	 * From here on we need to check that keysource is
	 * from the same dataset if it is being inherited
	 */
	if (zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource,
	    ZFS_MAXNAMELEN, &propsrctype,
	    propsrc, sizeof (propsrc), FALSE) != 0) {
		zfs_close(pzhp);
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "keysource must be provided."));
		return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
	}

	if (propsrctype == ZPROP_SRC_LOCAL) {
		zfs_close(pzhp);
		return (0);
	}

	if (zfs_prop_get(pzhp, ZFS_PROP_KEYSOURCE, pkeysource,
	    ZFS_MAXNAMELEN, &ppropsrctype,
	    psource, sizeof (psource), FALSE) != 0) {
		zfs_close(pzhp);
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "keysource must be provided."));
		return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
	}

	if (propsrctype == ZPROP_SRC_INHERITED &&
	    ((strcmp(propsrc, nparent) == 0) ||
	    (strcmp(propsrc, psource) == 0))) {
		zfs_close(pzhp);
		return (0);
	}

	zfs_close(pzhp);
	zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
	    "keysource doesn't allow for rename, make keysource local."));
	return (zfs_error(zhp->zfs_hdl, EZFS_KEYERR, errbuf));
}
Exemple #2
0
int
zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
{
	int ret;
	char errbuf[1024];
	boolean_t is_encroot;
	nvlist_t *props = NULL;
	uint8_t *wkeydata = NULL;
	uint_t wkeylen = 0;
	dcp_cmd_t cmd = (inheritkey) ? DCP_CMD_INHERIT : DCP_CMD_NEW_KEY;
	uint64_t crypt, pcrypt, keystatus, pkeystatus;
	uint64_t keyformat = ZFS_KEYFORMAT_NONE;
	zfs_handle_t *pzhp = NULL;
	char *keylocation = NULL;
	char origin_name[MAXNAMELEN];
	char prop_keylocation[MAXNAMELEN];
	char parent_name[ZFS_MAX_DATASET_NAME_LEN];

	(void) snprintf(errbuf, sizeof (errbuf),
	    dgettext(TEXT_DOMAIN, "Key change error"));

	/* check that encryption is enabled for the pool */
	if (!encryption_feature_is_enabled(zhp->zpool_hdl)) {
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "Encryption feature not enabled."));
		ret = EINVAL;
		goto error;
	}

	/* get crypt from dataset */
	crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
	if (crypt == ZIO_CRYPT_OFF) {
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "Dataset not encrypted."));
		ret = EINVAL;
		goto error;
	}

	/* get the encryption root of the dataset */
	ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);
	if (ret != 0) {
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "Failed to get encryption root for '%s'."),
		    zfs_get_name(zhp));
		goto error;
	}

	/* Clones use their origin's key and cannot rewrap it */
	ret = zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin_name,
	    sizeof (origin_name), NULL, NULL, 0, B_TRUE);
	if (ret == 0 && strcmp(origin_name, "") != 0) {
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "Keys cannot be changed on clones."));
		ret = EINVAL;
		goto error;
	}

	/*
	 * If the user wants to use the inheritkey variant of this function
	 * we don't need to collect any crypto arguments.
	 */
	if (!inheritkey) {
		/* validate the provided properties */
		ret = zfs_crypto_verify_rewrap_nvlist(zhp, raw_props, &props,
		    errbuf);
		if (ret != 0)
			goto error;

		/*
		 * Load keyformat and keylocation from the nvlist. Fetch from
		 * the dataset properties if not specified.
		 */
		(void) nvlist_lookup_uint64(props,
		    zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat);
		(void) nvlist_lookup_string(props,
		    zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation);

		if (is_encroot) {
			/*
			 * If this is already an ecryption root, just keep
			 * any properties not set by the user.
			 */
			if (keyformat == ZFS_KEYFORMAT_NONE) {
				keyformat = zfs_prop_get_int(zhp,
				    ZFS_PROP_KEYFORMAT);
				ret = nvlist_add_uint64(props,
				    zfs_prop_to_name(ZFS_PROP_KEYFORMAT),
				    keyformat);
				if (ret != 0) {
					zfs_error_aux(zhp->zfs_hdl,
					    dgettext(TEXT_DOMAIN, "Failed to "
					    "get existing keyformat "
					    "property."));
					goto error;
				}
			}

			if (keylocation == NULL) {
				ret = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION,
				    prop_keylocation, sizeof (prop_keylocation),
				    NULL, NULL, 0, B_TRUE);
				if (ret != 0) {
					zfs_error_aux(zhp->zfs_hdl,
					    dgettext(TEXT_DOMAIN, "Failed to "
					    "get existing keylocation "
					    "property."));
					goto error;
				}

				keylocation = prop_keylocation;
			}
		} else {
			/* need a new key for non-encryption roots */
			if (keyformat == ZFS_KEYFORMAT_NONE) {
				ret = EINVAL;
				zfs_error_aux(zhp->zfs_hdl,
				    dgettext(TEXT_DOMAIN, "Keyformat required "
				    "for new encryption root."));
				goto error;
			}

			/* default to prompt if no keylocation is specified */
			if (keylocation == NULL) {
				keylocation = "prompt";
				ret = nvlist_add_string(props,
				    zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
				    keylocation);
				if (ret != 0)
					goto error;
			}
		}

		/* fetch the new wrapping key and associated properties */
		ret = populate_create_encryption_params_nvlists(zhp->zfs_hdl,
		    zhp, B_TRUE, keyformat, keylocation, props, &wkeydata,
		    &wkeylen);
		if (ret != 0)
			goto error;
	} else {
		/* check that zhp is an encryption root */
		if (!is_encroot) {
			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Key inheritting can only be performed on "
			    "encryption roots."));
			ret = EINVAL;
			goto error;
		}

		/* get the parent's name */
		ret = zfs_parent_name(zhp, parent_name, sizeof (parent_name));
		if (ret != 0) {
			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Root dataset cannot inherit key."));
			ret = EINVAL;
			goto error;
		}

		/* get a handle to the parent */
		pzhp = make_dataset_handle(zhp->zfs_hdl, parent_name);
		if (pzhp == NULL) {
			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Failed to lookup parent."));
			ret = ENOENT;
			goto error;
		}

		/* parent must be encrypted */
		pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION);
		if (pcrypt == ZIO_CRYPT_OFF) {
			zfs_error_aux(pzhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Parent must be encrypted."));
			ret = EINVAL;
			goto error;
		}

		/* check that the parent's key is loaded */
		pkeystatus = zfs_prop_get_int(pzhp, ZFS_PROP_KEYSTATUS);
		if (pkeystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
			zfs_error_aux(pzhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Parent key must be loaded."));
			ret = EACCES;
			goto error;
		}
	}

	/* check that the key is loaded */
	keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
	if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
		    "Key must be loaded."));
		ret = EACCES;
		goto error;
	}

	/* call the ioctl */
	ret = lzc_change_key(zhp->zfs_name, cmd, props, wkeydata, wkeylen);
	if (ret != 0) {
		switch (ret) {
		case EPERM:
			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Permission denied."));
			break;
		case EINVAL:
			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Invalid properties for key change."));
			break;
		case EACCES:
			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
			    "Key is not currently loaded."));
			break;
		}
		zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf);
	}

	if (pzhp != NULL)
		zfs_close(pzhp);
	if (props != NULL)
		nvlist_free(props);
	if (wkeydata != NULL)
		free(wkeydata);

	return (ret);

error:
	if (pzhp != NULL)
		zfs_close(pzhp);
	if (props != NULL)
		nvlist_free(props);
	if (wkeydata != NULL)
		free(wkeydata);

	zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf);
	return (ret);
}
Exemple #3
0
/*
 * zfs_crypto_zckey
 *
 * Called for creating new filesystems and clones and receiving.
 *
 * For encryption != off get the key material.
 */
int
zfs_crypto_zckey(libzfs_handle_t *hdl, zfs_crypto_zckey_t cmd,
    nvlist_t *props, zfs_cmd_t *zc)
{
	uint64_t crypt = ZIO_CRYPT_INHERIT, pcrypt = ZIO_CRYPT_DEFAULT;
	char *keysource = NULL;
	int ret = 0;
	int keystatus;
	zfs_handle_t *pzhp = NULL;
	boolean_t inherit_crypt = B_TRUE;
	boolean_t inherit_keysource = B_TRUE;
	boolean_t recv_existing = B_FALSE;
	boolean_t recv_clone = B_FALSE;
	boolean_t keysource_free = B_FALSE;
	zprop_source_t propsrctype = ZPROP_SRC_DEFAULT;
	char propsrc[ZFS_MAXNAMELEN];
	char errbuf[1024];
	char target[MAXNAMELEN];
	char parent[MAXNAMELEN];
	char *strval;

	zfs_cmd_target_dsname(zc, cmd, target, sizeof (target));
	if (zfs_parent_name(target, parent, sizeof (parent)) != 0)
		parent[0] = '\0';
	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
	    "cannot create '%s'"), target);

	if (props != NULL) {
		if (nvlist_lookup_string(props,
		    zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &strval) == 0) {
			(void) zfs_prop_string_to_index(ZFS_PROP_ENCRYPTION,
			    strval, &crypt);
			inherit_crypt = B_FALSE;
		} else if (nvlist_lookup_uint64(props,
		    zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt) == 0) {
			inherit_crypt = B_FALSE;
		} else {
			inherit_crypt = B_TRUE;
		}
		if (nvlist_lookup_string(props,
		    zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource) == 0) {
			inherit_keysource = B_FALSE;
		}
	}

	if (cmd == ZFS_CRYPTO_CREATE) {
		pzhp = make_dataset_handle(hdl, parent);
	} else if (cmd == ZFS_CRYPTO_CLONE) {
		zfs_handle_t *szhp = make_dataset_handle(hdl, zc->zc_value);
		if (szhp == NULL) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "parent not found"));
			(void) zfs_error(hdl, EZFS_NOENT, errbuf);
			ret = -1;
			goto out;
		}
		crypt = zfs_prop_get_int(szhp, ZFS_PROP_ENCRYPTION);
		zfs_close(szhp);
		pzhp = make_dataset_handle(hdl, parent);
	} else if (cmd == ZFS_CRYPTO_RECV) {
		if (zfs_dataset_exists(hdl, target, ZFS_TYPE_DATASET)) {
			pzhp = make_dataset_handle(hdl, target);
			pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION);
			if (crypt != pcrypt && crypt != ZIO_CRYPT_INHERIT) {
				const char *stream_crypt_str = NULL;
				const char *pcrypt_str = NULL;
				(void) zfs_prop_index_to_string(
				    ZFS_PROP_ENCRYPTION, pcrypt,
				    &pcrypt_str);
				(void) zfs_prop_index_to_string(
				    ZFS_PROP_ENCRYPTION, crypt,
				    &stream_crypt_str);
				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
				    "stream encryption '%s'(%llu) differs "
				    "from receiving dataset value '%s'(%llu)"),
				    stream_crypt_str, crypt,
				    pcrypt_str, pcrypt);
				ret = -1;
				goto out;
			}
			inherit_crypt = B_TRUE;
			inherit_keysource = B_TRUE;
			recv_existing = B_TRUE;
		} else {
			if (strlen(zc->zc_string) != 0) {
				pzhp = make_dataset_handle(hdl, zc->zc_string);
				recv_clone = B_TRUE;
			} else {
				pzhp = make_dataset_handle(hdl, parent);
			}
		}
	}

	if (cmd != ZFS_CRYPTO_PCREATE) {
		if (pzhp == NULL) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "parent not found"));
			(void) zfs_error(hdl, EZFS_NOENT, errbuf);
			ret = -1;
			goto out;
		}
		pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION);
	}

	if (pcrypt != ZIO_CRYPT_OFF && crypt == ZIO_CRYPT_OFF) {
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
		    "encryption value. dataset must be encrypted."));
		(void) zfs_error(hdl, EZFS_KEYERR, errbuf);
		ret = -1;
		goto out;
	}

	if (crypt == ZIO_CRYPT_INHERIT) {
		crypt = pcrypt;
	}

	/*
	 * If we have nothing to do then bail out, but make one last check
	 * that keysource wasn't specified when there is no crypto going on.
	 */
	if (crypt == ZIO_CRYPT_OFF && !inherit_keysource) {
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "keysource "
		    "can not be specified when encryption is off."));
		(void) zfs_error(hdl, EZFS_KEYERR, errbuf);
		ret = -1;
		goto out;
	} else if (crypt == ZIO_CRYPT_OFF) {
		ret = 0;
		goto out;
	}

	/*
	 * Need to pass down the inherited crypt value so that
	 * dsl_crypto_key_gen() can see the same that we saw.
	 */
	zc->zc_crypto.zic_crypt = crypt;
	zc->zc_crypto.zic_clone_newkey = hdl->libzfs_crypt.zc_clone_newkey;

	/*
	 * Here we have encryption on so we need to find a valid keysource
	 * property.
	 *
	 * Now lets see if we have an explicit setting for keysource and
	 * we have validate it; otherwise, if we inherit then it is already
	 * validated.
	 */
	if (!inherit_keysource) {
		if (!zfs_valid_keysource(keysource)) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "invalid keysource \"%s\""), keysource);
			(void) zfs_error(hdl, EZFS_KEYERR, errbuf);
			ret = -1;
			goto out;
		}
		/*
		 * If keysource is local then encryption has to be as well
		 * otherwise we could end up with the wrong sized keys.
		 */
		if (inherit_crypt) {
			VERIFY(nvlist_add_uint64(props,
			    zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt) == 0);
			VERIFY(nvlist_add_uint64(props,
			    zfs_prop_to_name(ZFS_PROP_CHECKSUM),
			    ZIO_CHECKSUM_SHA256_MAC) == 0);
		}
	} else {
		/* Get the already validated keysource from our parent */
		keysource = zfs_alloc(hdl, ZFS_MAXNAMELEN);
		if (keysource == NULL) {
			ret = no_memory(hdl);
			goto out;
		}
		keysource_free = B_TRUE;
		if (pzhp != NULL && zfs_prop_get(pzhp, ZFS_PROP_KEYSOURCE,
		    keysource, ZFS_MAXNAMELEN, &propsrctype, propsrc,
		    sizeof (propsrc), FALSE) != 0) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "keysource must be provided."));
			(void) zfs_error(hdl, EZFS_KEYERR, errbuf);
			ret = -1;
			goto out;
		}

		if (recv_existing) {
			(void) strlcpy(propsrc, target, sizeof (propsrc));
		} else if (recv_clone) {
			(void) strlcpy(propsrc,
			    zc->zc_string, sizeof (propsrc));
		} else if (propsrctype == ZPROP_SRC_LOCAL ||
		    propsrctype == ZPROP_SRC_RECEIVED) {
			(void) strlcpy(propsrc, parent, sizeof (propsrc));
		} else if (propsrctype == ZPROP_SRC_DEFAULT &&
		    pcrypt == ZIO_CRYPT_OFF) {
			/*
			 * "Default" to "passphrase,prompt".  The obvious
			 * thing to do would be to set this in zfs_prop.c
			 * as the property default.  However that doesn't
			 * work here because we don't want keysource set
			 * for datasets that have encryption=off.  If we
			 * ever change the default to encryption=on then
			 * the default of keysource can change too.
			 * This is needed because of how inheritance happens
			 * with defaulted properties, they show up as
			 * "default" not "inherit" but we need "inherit"
			 * to find the wrapping key if we are actually
			 * inheriting keysource.
			 */
			inherit_keysource = B_FALSE;
			if (props == NULL) {
				VERIFY(0 == nvlist_alloc(&props,
				    NV_UNIQUE_NAME, 0));
			}
			(void) strlcpy(keysource, "passphrase,prompt",
			    ZFS_MAXNAMELEN);
			VERIFY(nvlist_add_string(props,
			    zfs_prop_to_name(ZFS_PROP_KEYSOURCE),
			    keysource) == 0);
			VERIFY(nvlist_add_uint64(props,
			    zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt) == 0);
			VERIFY(nvlist_add_uint64(props,
			    zfs_prop_to_name(ZFS_PROP_CHECKSUM),
			    ZIO_CHECKSUM_SHA256_MAC) == 0);
			goto load_key;
		} else if (propsrctype == ZPROP_SRC_DEFAULT &&
		    pcrypt != ZIO_CRYPT_OFF) {
			abort();
#if 0 // FIXME
		} else if (strcmp(propsrc, ZONE_INVISIBLE_SOURCE) == 0) {
			/*
			 * Assume key is available and handle failure ioctl
			 * ENOKEY errors later.
			 */
			zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_INHERIT;
			(void) strlcpy(zc->zc_crypto.zic_inherit_dsname,
			    propsrc, sizeof (zc->zc_crypto.zic_inherit_dsname));
			ret = 0;
			goto out;
#endif
		} else if (propsrctype != ZPROP_SRC_DEFAULT) {
			if (pzhp != NULL)
				zfs_close(pzhp);
			VERIFY((pzhp = make_dataset_handle(hdl, propsrc)) != 0);
		}
		keystatus = zfs_prop_get_int(pzhp, ZFS_PROP_KEYSTATUS);
		/*
		 * AVAILABLE we are done other than filling in who we
		 * are inheriting the wrapping key from.
		 *
		 * UNAVAILABLE we need to load the key of a higher level
		 * dataset.
		 */
		if (keystatus == ZFS_CRYPT_KEY_AVAILABLE) {
			zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_INHERIT;
			(void) strlcpy(zc->zc_crypto.zic_inherit_dsname,
			    propsrc, sizeof (zc->zc_crypto.zic_inherit_dsname));
			ret = 0;
			goto out;
		} else if (keystatus == ZFS_CRYPT_KEY_UNAVAILABLE) {
			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
			    "zfs key -l %s required."), parent);
			(void) zfs_error(hdl, EZFS_KEYERR, errbuf);
			ret = -1;
			goto out;
		}
	}
load_key:
	if (!zfs_can_prompt_if_needed(keysource)) {
		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
		    "unable to prompt for key material keysource = \"%s\"\n"),
		    keysource);
		errno = ENOTTY;
		return (-1);
	}
	ret = key_hdl_to_zc(hdl, NULL, keysource, crypt, zc, cmd);
	if (ret != 0) {
		ret = -1;
		(void) zfs_error(hdl, EZFS_KEYERR, errbuf);
		goto out;
	}
	zc->zc_crypto.zic_cmd = ZFS_IOC_CRYPTO_KEY_LOAD;
	ret = 0;
out:
	if (pzhp)
		zfs_close(pzhp);
	if (keysource_free)
		free(keysource);

	return (ret);
}