/* * 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)); }
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); }
/* * 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); }