1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2018-2021 Tintri by DDN, Inc. All rights reserved.
14 * Copyright 2020-2024 RackTop Systems, Inc.
15 */
16
17 /*
18 * Helper functions for SMB3 encryption using the
19 * Kernel Cryptographic Framework (KCF)
20 *
21 * There are two implementations of these functions:
22 * This one (for kernel) and another for user space:
23 * See: lib/smbclnt/libfknsmb/common/fksmb_crypt_pkcs.c
24 *
25 * Contrary to what one might assume from the file name,
26 * there should be NO SMB implementation knowledge here
27 * beyond a few carefully selected things (nsmb_kcrypt.h).
28 */
29
30 #include <sys/types.h>
31 #include <sys/crypto/api.h>
32
33 #include <netsmb/nsmb_kcrypt.h>
34
35 #include <sys/cmn_err.h>
36 #include <sys/strsun.h>
37 #include <sys/sunddi.h>
38
39 /*
40 * Common function to see if a mech is available.
41 */
42 static int
find_mech(smb_crypto_mech_t * mech,const char * name)43 find_mech(smb_crypto_mech_t *mech, const char *name)
44 {
45 crypto_mech_type_t t;
46
47 t = crypto_mech2id(name);
48 if (t == CRYPTO_MECH_INVALID) {
49 cmn_err(CE_NOTE, "nsmb: no kcf mech: %s", name);
50 return (-1);
51 }
52 mech->cm_type = t;
53 return (0);
54 }
55
56 /*
57 * SMB3 encryption helpers:
58 * (getmech, init, update, final)
59 */
60
61 int
nsmb_aes_ccm_getmech(smb_crypto_mech_t * mech)62 nsmb_aes_ccm_getmech(smb_crypto_mech_t *mech)
63 {
64 return (find_mech(mech, SUN_CKM_AES_CCM));
65 }
66
67 int
nsmb_aes_gcm_getmech(smb_crypto_mech_t * mech)68 nsmb_aes_gcm_getmech(smb_crypto_mech_t *mech)
69 {
70 return (find_mech(mech, SUN_CKM_AES_GCM));
71 }
72
73 void
nsmb_crypto_init_ccm_param(smb_enc_ctx_t * ctx,uint8_t * nonce,size_t noncesize,uint8_t * auth,size_t authsize,size_t datasize)74 nsmb_crypto_init_ccm_param(smb_enc_ctx_t *ctx,
75 uint8_t *nonce, size_t noncesize,
76 uint8_t *auth, size_t authsize,
77 size_t datasize)
78 {
79
80 ASSERT3U(noncesize, >=, SMB3_AES_CCM_NONCE_SIZE);
81
82 ctx->param.ccm.ulMACSize = SMB2_SIG_SIZE;
83 ctx->param.ccm.ulNonceSize = SMB3_AES_CCM_NONCE_SIZE;
84 ctx->param.ccm.nonce = nonce;
85 ctx->param.ccm.ulDataSize = datasize;
86 ctx->param.ccm.ulAuthDataSize = authsize;
87 ctx->param.ccm.authData = auth;
88
89 ctx->mech.cm_param = (caddr_t)&ctx->param.ccm;
90 ctx->mech.cm_param_len = sizeof (ctx->param.ccm);
91 }
92
93 void
nsmb_crypto_init_gcm_param(smb_enc_ctx_t * ctx,uint8_t * nonce,size_t noncesize,uint8_t * auth,size_t authsize)94 nsmb_crypto_init_gcm_param(smb_enc_ctx_t *ctx,
95 uint8_t *nonce, size_t noncesize,
96 uint8_t *auth, size_t authsize)
97 {
98
99 ASSERT3U(noncesize, >=, SMB3_AES_GCM_NONCE_SIZE);
100
101 ctx->param.gcm.pIv = nonce;
102 ctx->param.gcm.ulIvLen = SMB3_AES_GCM_NONCE_SIZE;
103 ctx->param.gcm.ulTagBits = SMB2_SIG_SIZE << 3; /* bytes to bits */
104 ctx->param.gcm.pAAD = auth; /* auth data */
105 ctx->param.gcm.ulAADLen = authsize; /* auth data len */
106
107 ctx->mech.cm_param = (caddr_t)&ctx->param.gcm;
108 ctx->mech.cm_param_len = sizeof (ctx->param.gcm);
109 }
110
111 /*
112 * KCF doesn't need anything to happen in this call, but
113 * wants that key when we call encrypt or decrypt, so
114 * just stash the key here.
115 */
116 int
nsmb_encrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)117 nsmb_encrypt_init(smb_enc_ctx_t *ctxp,
118 uint8_t *key, size_t keylen)
119 {
120 bzero(&ctxp->ckey, sizeof (ctxp->ckey));
121 ctxp->ckey.ck_format = CRYPTO_KEY_RAW;
122 ctxp->ckey.ck_data = key;
123 ctxp->ckey.ck_length = keylen * 8; /* in bits */
124
125 return (0);
126 }
127
128 int
nsmb_decrypt_init(smb_enc_ctx_t * ctxp,uint8_t * key,size_t keylen)129 nsmb_decrypt_init(smb_enc_ctx_t *ctxp,
130 uint8_t *key, size_t keylen)
131 {
132 bzero(&ctxp->ckey, sizeof (ctxp->ckey));
133 ctxp->ckey.ck_format = CRYPTO_KEY_RAW;
134 ctxp->ckey.ck_data = key;
135 ctxp->ckey.ck_length = keylen * 8; /* in bits */
136
137 return (0);
138 }
139
140 /*
141 * Nothing to cleanup after crypto_encrypt, crypto_decrypt
142 * The user space variant has work to do.
143 */
144 void
nsmb_enc_ctx_done(smb_enc_ctx_t * ctxp)145 nsmb_enc_ctx_done(smb_enc_ctx_t *ctxp)
146 {
147 }
148
149 /*
150 * Encrypt a whole message with scatter/gather (MBLK)
151 */
152 int
nsmb_encrypt_mblks(smb_enc_ctx_t * ctxp,mblk_t * mp,size_t clearlen)153 nsmb_encrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t clearlen)
154 {
155 crypto_data_t in_cd, out_cd;
156 size_t inlen, outlen;
157 int rv;
158
159 inlen = clearlen;
160 outlen = clearlen + SMB2_SIG_SIZE;
161 ASSERT(msgsize(mp) >= outlen);
162
163 bzero(&in_cd, sizeof (crypto_data_t));
164 in_cd.cd_format = CRYPTO_DATA_MBLK;
165 in_cd.cd_length = inlen;
166 in_cd.cd_mp = mp;
167
168 bzero(&out_cd, sizeof (crypto_data_t));
169 out_cd.cd_format = CRYPTO_DATA_MBLK;
170 out_cd.cd_length = outlen;
171 out_cd.cd_mp = mp;
172
173 rv = crypto_encrypt(&ctxp->mech, &in_cd,
174 &ctxp->ckey, NULL, &out_cd, NULL);
175 if (rv != CRYPTO_SUCCESS) {
176 cmn_err(CE_WARN, "nsmb: crypto_encrypt failed: 0x%x", rv);
177 return (-1);
178 }
179
180 return (0);
181 }
182
183 /*
184 * Decrypt a whole message with scatter/gather (MBLK)
185 */
186 int
nsmb_decrypt_mblks(smb_enc_ctx_t * ctxp,mblk_t * mp,size_t cipherlen)187 nsmb_decrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t cipherlen)
188 {
189 crypto_data_t in_cd, out_cd;
190 size_t inlen, outlen;
191 int rv;
192
193 if (cipherlen <= SMB2_SIG_SIZE)
194 return (-1);
195 inlen = cipherlen;
196 outlen = cipherlen - SMB2_SIG_SIZE;
197 ASSERT(msgsize(mp) >= inlen);
198
199 /* In is ciphertext */
200 bzero(&in_cd, sizeof (crypto_data_t));
201 in_cd.cd_format = CRYPTO_DATA_MBLK;
202 in_cd.cd_length = inlen;
203 in_cd.cd_mp = mp;
204
205 /* Out is plaintext */
206 bzero(&out_cd, sizeof (crypto_data_t));
207 out_cd.cd_format = CRYPTO_DATA_MBLK;
208 out_cd.cd_length = outlen;
209 out_cd.cd_mp = mp;
210
211 rv = crypto_decrypt(&ctxp->mech, &in_cd,
212 &ctxp->ckey, NULL, &out_cd, NULL);
213 if (rv != CRYPTO_SUCCESS) {
214 cmn_err(CE_WARN, "nsmb: crypto_decrypt failed: 0x%x", rv);
215 return (-1);
216 }
217
218 return (0);
219 }
220