1 /* 2 * CDDL HEADER START 3 * 4 * This file and its contents are supplied under the terms of the 5 * Common Development and Distribution License ("CDDL"), version 1.0. 6 * You may only use this file in accordance with the terms of version 7 * 1.0 of the CDDL. 8 * 9 * A full copy of the text of the CDDL should have accompanied this 10 * source. A copy of the CDDL is also available via the Internet at 11 * http://www.illumos.org/license/CDDL. 12 * 13 * CDDL HEADER END 14 */ 15 16 /* 17 * Copyright 2020 Joyent, Inc. 18 */ 19 20 #include <sys/ctype.h> 21 #include <sys/zcp.h> 22 #include <sys/zcp_change_key.h> 23 24 static uint8_t 25 hexval(char c) 26 { 27 if (c >= '0' && c <= '9') 28 return (c - '0'); 29 else if (c >= 'a' && c <= 'f') 30 return (c - 'a' + 10); 31 else if (c >= 'A' && c <= 'F') 32 return (c - 'A' + 10); 33 34 panic("invalid hex value"); 35 } 36 37 static int 38 hex_to_raw(const char *key, uint8_t *buf, size_t buflen) 39 { 40 uint8_t *p; 41 size_t srclen = strlen(key); 42 size_t i; 43 44 if (buflen * 2 != srclen) 45 return (SET_ERROR(EINVAL)); 46 47 for (i = 0, p = buf; i < srclen; i += 2, p++) { 48 if (!isxdigit(key[i]) || !isxdigit(key[i + 1])) 49 return (SET_ERROR(EINVAL)); 50 51 *p = hexval(key[i]) << 4 | hexval(key[i + 1]); 52 } 53 54 return (0); 55 } 56 57 int 58 zcp_synctask_change_key_create_params(const char *key, size_t keylen, 59 zfs_keyformat_t keyformat, dsl_crypto_params_t **dcpp) 60 { 61 nvlist_t *args = fnvlist_alloc(); 62 nvlist_t *hidden_args = fnvlist_alloc(); 63 uint8_t rawkey[WRAPPING_KEY_LEN]; 64 uint_t rawlen = 0; 65 int err = 0; 66 67 /* 68 * Currently, only raw and hex keys are supported in channel 69 * programs (there is no pbkdf2 support in the kernel to convert 70 * a passphrase). 71 */ 72 switch (keyformat) { 73 case ZFS_KEYFORMAT_RAW: 74 /* 75 * dsl_crypto_params_create_nvlist() also verifies the 76 * raw key is WRAPPING_KEY_LEN bytes, so this is 77 * _almost_ redundant -- however we still want to 78 * guarantee we won't overflow rawkey when copying 79 * the contents over. 80 */ 81 if (keylen != WRAPPING_KEY_LEN) { 82 err = SET_ERROR(EINVAL); 83 goto done; 84 } 85 86 bcopy(key, rawkey, keylen); 87 rawlen = keylen; 88 break; 89 case ZFS_KEYFORMAT_HEX: 90 /* 91 * hex_to_raw() will reject any input that doesn't exactly 92 * fit into rawkey 93 */ 94 err = hex_to_raw(key, rawkey, sizeof (rawkey)); 95 if (err != 0) 96 goto done; 97 rawlen = sizeof (rawkey); 98 break; 99 default: 100 err = SET_ERROR(EINVAL); 101 goto done; 102 } 103 104 fnvlist_add_uint64(args, zfs_prop_to_name(ZFS_PROP_KEYFORMAT), 105 (uint64_t)keyformat); 106 fnvlist_add_uint8_array(hidden_args, "wkeydata", rawkey, rawlen); 107 108 err = dsl_crypto_params_create_nvlist(DCP_CMD_NEW_KEY, args, 109 hidden_args, dcpp); 110 111 done: 112 fnvlist_free(args); 113 fnvlist_free(hidden_args); 114 bzero(rawkey, sizeof (rawkey)); 115 116 return (err); 117 } 118 119 void 120 zcp_synctask_change_key_cleanup(void *arg) 121 { 122 spa_keystore_change_key_args_t *skcka = arg; 123 124 dsl_crypto_params_free(skcka->skcka_cp, B_TRUE); 125 } 126 127 int 128 zcp_synctask_change_key_check(void *arg, dmu_tx_t *tx) 129 { 130 /* 131 * zcp_synctask_change_key_create_params() already validates that 132 * the new key is in an acceptable format and size for a channel 133 * program. Any future channel program specific checks would go here. 134 * For now, we just perform all the same checks done for 135 * 'zfs change-key' by calling spa_keystore_change_key_check(). 136 */ 137 return (spa_keystore_change_key_check(arg, tx)); 138 } 139 140 void 141 zcp_synctask_change_key_sync(void *arg, dmu_tx_t *tx) 142 { 143 spa_keystore_change_key_sync(arg, tx); 144 } 145