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
hexval(char c)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
hex_to_raw(const char * key,uint8_t * buf,size_t buflen)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
zcp_synctask_change_key_create_params(const char * key,size_t keylen,zfs_keyformat_t keyformat,dsl_crypto_params_t ** dcpp)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
zcp_synctask_change_key_cleanup(void * arg)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
zcp_synctask_change_key_check(void * arg,dmu_tx_t * tx)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
zcp_synctask_change_key_sync(void * arg,dmu_tx_t * tx)141 zcp_synctask_change_key_sync(void *arg, dmu_tx_t *tx)
142 {
143 spa_keystore_change_key_sync(arg, tx);
144 }
145