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 2021-2025 RackTop Systems, Inc. 15 */ 16 17 /* 18 * Helper functions for SMB3 encryption using PKCS#11 19 * 20 * There are two implementations of these functions: 21 * This one (for user space) and another for kernel. 22 * See: uts/common/fs/smbclnt/netsmb/smb_crypt_kcf.c 23 * 24 * Contrary to what one might assume from the file name, 25 * there should be NO SMB implementation knowledge here 26 * beyond a few carefully selected things (nsmb_kcrypt.h). 27 */ 28 29 #include <security/cryptoki.h> 30 #include <security/pkcs11.h> 31 #include <netsmb/nsmb_kcrypt.h> 32 33 #include <sys/cmn_err.h> 34 #include <sys/debug.h> 35 #include <sys/stream.h> 36 #include <sys/strsun.h> 37 #include <umem.h> 38 #include <strings.h> 39 40 size_t msgsize(mblk_t *); 41 static int copy_mblks(void *buf, size_t buflen, enum uio_rw, mblk_t *m); 42 43 /* 44 * Common function to see if a mech is available. 45 */ 46 static int 47 find_mech(CK_MECHANISM_TYPE id) 48 { 49 CK_SESSION_HANDLE hdl; 50 CK_RV rv; 51 52 rv = SUNW_C_GetMechSession(id, &hdl); 53 if (rv != CKR_OK) { 54 return (-1); 55 } 56 (void) C_CloseSession(hdl); 57 58 return (0); 59 } 60 61 /* 62 * SMB3 encryption helpers: 63 * (getmech, init, update, final) 64 */ 65 66 int 67 nsmb_aes_ccm_getmech(smb_crypto_mech_t *mech) 68 { 69 70 if (find_mech(CKM_AES_CCM) != 0) { 71 cmn_err(CE_NOTE, "PKCS#11: no mech AES_CCM"); 72 return (-1); 73 } 74 75 mech->mechanism = CKM_AES_CCM; 76 return (0); 77 } 78 79 int 80 nsmb_aes_gcm_getmech(smb_crypto_mech_t *mech) 81 { 82 83 if (find_mech(CKM_AES_GCM) != 0) { 84 cmn_err(CE_NOTE, "PKCS#11: no mech AES_GCM"); 85 return (-1); 86 } 87 88 mech->mechanism = CKM_AES_GCM; 89 return (0); 90 } 91 92 void 93 nsmb_crypto_init_ccm_param(smb_enc_ctx_t *ctx, 94 uint8_t *nonce, size_t noncesize, 95 uint8_t *auth, size_t authsize, 96 size_t datasize) 97 { 98 99 ASSERT3U(noncesize, >=, SMB3_AES_CCM_NONCE_SIZE); 100 101 /* CK_CCM_PARAMS */ 102 ctx->param.ccm.ulDataLen = datasize; 103 ctx->param.ccm.pNonce = nonce; 104 ctx->param.ccm.ulNonceLen = SMB3_AES_CCM_NONCE_SIZE; 105 ctx->param.ccm.pAAD = auth; 106 ctx->param.ccm.ulAADLen = authsize; 107 ctx->param.ccm.ulMACLen = SMB2_SIG_SIZE; 108 109 ctx->mech.pParameter = (caddr_t)&ctx->param.ccm; 110 ctx->mech.ulParameterLen = sizeof (ctx->param.ccm); 111 } 112 113 void 114 nsmb_crypto_init_gcm_param(smb_enc_ctx_t *ctx, 115 uint8_t *nonce, size_t noncesize, 116 uint8_t *auth, size_t authsize) 117 { 118 119 ASSERT3U(noncesize, >=, SMB3_AES_GCM_NONCE_SIZE); 120 121 /* CK_GCM_PARAMS */ 122 ctx->param.gcm.pIv = nonce; 123 ctx->param.gcm.ulIvLen = SMB3_AES_GCM_NONCE_SIZE; 124 ctx->param.gcm.pAAD = auth; /* auth data */ 125 ctx->param.gcm.ulAADLen = authsize; /* auth data len */ 126 ctx->param.gcm.ulTagBits = SMB2_SIG_SIZE << 3; /* bytes to bits */ 127 128 ctx->mech.pParameter = (caddr_t)&ctx->param.gcm; 129 ctx->mech.ulParameterLen = sizeof (ctx->param.gcm); 130 } 131 132 /* 133 * Start the KCF encrypt session, load the key 134 * If this returns zero, the caller should call 135 * nsmb_enc_ctx_done to cleanup the context, 136 * even if there are intervening errors. 137 */ 138 int 139 nsmb_encrypt_init(smb_enc_ctx_t *ctxp, 140 uint8_t *key, size_t keylen) 141 { 142 CK_OBJECT_HANDLE hkey = 0; 143 CK_MECHANISM *mech = &ctxp->mech; 144 CK_RV rv; 145 146 rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx); 147 if (rv != CKR_OK) 148 return (-1); 149 150 rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism, 151 key, keylen, &hkey); 152 if (rv != CKR_OK) 153 return (-1); 154 155 rv = C_EncryptInit(ctxp->ctx, mech, hkey); 156 if (rv != CKR_OK) { 157 cmn_err(CE_WARN, "C_EncryptInit failed: 0x%lx", rv); 158 } 159 (void) C_DestroyObject(ctxp->ctx, hkey); 160 161 return (rv == CKR_OK ? 0 : -1); 162 } 163 164 /* 165 * Start the KCF decrypt session, load the key 166 * If this returns zero, the caller should call 167 * nsmb_enc_ctx_done to cleanup the context, 168 * even if there are intervening errors. 169 */ 170 int 171 nsmb_decrypt_init(smb_enc_ctx_t *ctxp, 172 uint8_t *key, size_t keylen) 173 { 174 CK_OBJECT_HANDLE hkey = 0; 175 CK_MECHANISM *mech = &ctxp->mech; 176 CK_RV rv; 177 178 rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx); 179 if (rv != CKR_OK) 180 return (-1); 181 182 rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism, 183 key, keylen, &hkey); 184 if (rv != CKR_OK) 185 return (-1); 186 187 rv = C_DecryptInit(ctxp->ctx, mech, hkey); 188 if (rv != CKR_OK) { 189 cmn_err(CE_WARN, "C_DecryptInit failed: 0x%lx", rv); 190 } 191 (void) C_DestroyObject(ctxp->ctx, hkey); 192 193 return (rv == CKR_OK ? 0 : -1); 194 } 195 196 void 197 nsmb_enc_ctx_done(smb_enc_ctx_t *ctxp) 198 { 199 if (ctxp->ctx != 0) { 200 (void) C_CloseSession(ctxp->ctx); 201 ctxp->ctx = 0; 202 } 203 } 204 205 /* 206 * Encrypt a whole message with scatter/gather (MBLK) 207 * 208 * While the PKCS#11 implementation internally has the ability to 209 * handle scatter/gather, it currently presents no interface for it. 210 * As this library is used primarily for debugging, performance in 211 * here is not a big concern, so we'll get around the limitation of 212 * libpkcs11 by copying to/from a contiguous working buffer. 213 */ 214 int 215 nsmb_encrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t clearlen) 216 { 217 uint8_t *buf = NULL; 218 size_t inlen, outlen; 219 ulong_t tlen; 220 int err; 221 CK_RV rv; 222 223 inlen = clearlen; 224 outlen = clearlen + SMB2_SIG_SIZE; 225 ASSERT(msgsize(mp) >= outlen); 226 227 buf = umem_alloc(outlen, 0); 228 if (buf == NULL) 229 return (-1); 230 231 /* Copy from mblk chain to buf */ 232 err = copy_mblks(buf, inlen, UIO_WRITE, mp); 233 if (err != 0) 234 goto out; 235 236 /* Encrypt in-place in our work buffer. */ 237 tlen = outlen; 238 rv = C_Encrypt(ctxp->ctx, buf, inlen, buf, &tlen); 239 if (rv != CKR_OK) { 240 cmn_err(CE_WARN, "C_Encrypt failed: 0x%lx", rv); 241 err = -1; 242 goto out; 243 } 244 if (tlen != outlen) { 245 cmn_err(CE_WARN, "nsmb_encrypt_mblks outlen %d vs %d", 246 (int)tlen, (int)outlen); 247 err = -1; 248 goto out; 249 } 250 251 /* Copy from buf to mblk segs */ 252 err = copy_mblks(buf, outlen, UIO_READ, mp); 253 254 out: 255 if (buf != NULL) 256 umem_free(buf, outlen); 257 258 return (err); 259 } 260 261 /* 262 * Decrypt a whole message with scatter/gather (MBLK) 263 */ 264 int 265 nsmb_decrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t cipherlen) 266 { 267 uint8_t *buf = NULL; 268 size_t inlen, outlen; 269 ulong_t tlen; 270 int err; 271 CK_RV rv; 272 273 if (cipherlen <= SMB2_SIG_SIZE) 274 return (-1); 275 inlen = cipherlen; 276 outlen = cipherlen - SMB2_SIG_SIZE; 277 ASSERT(msgsize(mp) >= inlen); 278 279 buf = umem_alloc(inlen, 0); 280 if (buf == NULL) 281 return (-1); 282 283 /* Copy from mblk chain to buf */ 284 err = copy_mblks(buf, inlen, UIO_WRITE, mp); 285 if (err != 0) 286 goto out; 287 288 /* Decrypt in-place in our work buffer. */ 289 tlen = outlen; 290 rv = C_Decrypt(ctxp->ctx, buf, inlen, buf, &tlen); 291 if (rv != CKR_OK) { 292 cmn_err(CE_WARN, "C_Decrypt failed: 0x%lx", rv); 293 err = -1; 294 goto out; 295 } 296 if (tlen != outlen) { 297 cmn_err(CE_WARN, "nsmb_decrypt_mblks outlen %d vs %d", 298 (int)tlen, (int)outlen); 299 err = -1; 300 goto out; 301 } 302 303 /* Copy from buf to mblk segs */ 304 err = copy_mblks(buf, outlen, UIO_READ, mp); 305 306 out: 307 if (buf != NULL) 308 umem_free(buf, inlen); 309 310 return (err); 311 } 312 313 static int 314 copy_mblks(void *buf, size_t buflen, enum uio_rw rw, mblk_t *m) 315 { 316 uchar_t *p = buf; 317 size_t rem = buflen; 318 size_t len; 319 320 while (rem > 0) { 321 if (m == NULL) 322 return (-1); 323 ASSERT(m->b_datap->db_type == M_DATA); 324 len = MBLKL(m); 325 if (len > rem) 326 len = rem; 327 if (rw == UIO_READ) { 328 /* buf to mblks */ 329 bcopy(p, m->b_rptr, len); 330 } else { 331 /* mblks to buf */ 332 bcopy(m->b_rptr, p, len); 333 } 334 m = m->b_cont; 335 p += len; 336 rem -= len; 337 } 338 return (0); 339 } 340 341 size_t 342 msgsize(mblk_t *mp) 343 { 344 size_t n = 0; 345 346 for (; mp != NULL; mp = mp->b_cont) 347 n += MBLKL(mp); 348 349 return (n); 350 } 351