xref: /illumos-gate/usr/src/uts/common/fs/zfs/zcp_change_key.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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