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-2024 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 <stdlib.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; 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 = malloc(outlen); 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 return (-1); 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 return (-1); 242 } 243 if (tlen != outlen) { 244 cmn_err(CE_WARN, "nsmb_encrypt_mblks outlen %d vs %d", 245 (int)tlen, (int)outlen); 246 return (-1); 247 } 248 249 /* Copy from buf to mblk segs */ 250 err = copy_mblks(buf, outlen, UIO_READ, mp); 251 if (err != 0) 252 return (-1); 253 254 return (0); 255 } 256 257 /* 258 * Decrypt a whole message with scatter/gather (MBLK) 259 */ 260 int 261 nsmb_decrypt_mblks(smb_enc_ctx_t *ctxp, mblk_t *mp, size_t cipherlen) 262 { 263 uint8_t *buf; 264 size_t inlen, outlen; 265 ulong_t tlen; 266 int err; 267 CK_RV rv; 268 269 if (cipherlen <= SMB2_SIG_SIZE) 270 return (-1); 271 inlen = cipherlen; 272 outlen = cipherlen - SMB2_SIG_SIZE; 273 ASSERT(msgsize(mp) >= inlen); 274 275 buf = malloc(inlen); 276 if (buf == NULL) 277 return (-1); 278 279 /* Copy from mblk chain to buf */ 280 err = copy_mblks(buf, inlen, UIO_WRITE, mp); 281 if (err != 0) 282 return (-1); 283 284 /* Decrypt in-place in our work buffer. */ 285 tlen = outlen; 286 rv = C_Decrypt(ctxp->ctx, buf, inlen, buf, &tlen); 287 if (rv != CKR_OK) { 288 cmn_err(CE_WARN, "C_Decrypt failed: 0x%lx", rv); 289 return (-1); 290 } 291 if (tlen != outlen) { 292 cmn_err(CE_WARN, "nsmb_decrypt_mblks outlen %d vs %d", 293 (int)tlen, (int)outlen); 294 return (-1); 295 } 296 297 /* Copy from buf to mblk segs */ 298 err = copy_mblks(buf, outlen, UIO_READ, mp); 299 if (err != 0) 300 return (-1); 301 302 return (0); 303 } 304 305 static int 306 copy_mblks(void *buf, size_t buflen, enum uio_rw rw, mblk_t *m) 307 { 308 uchar_t *p = buf; 309 size_t rem = buflen; 310 size_t len; 311 312 while (rem > 0) { 313 if (m == NULL) 314 return (-1); 315 ASSERT(m->b_datap->db_type == M_DATA); 316 len = MBLKL(m); 317 if (len > rem) 318 len = rem; 319 if (rw == UIO_READ) { 320 /* buf to mblks */ 321 bcopy(p, m->b_rptr, len); 322 } else { 323 /* mblks to buf */ 324 bcopy(m->b_rptr, p, len); 325 } 326 m = m->b_cont; 327 p += len; 328 rem -= len; 329 } 330 return (0); 331 } 332 333 size_t 334 msgsize(mblk_t *mp) 335 { 336 size_t n = 0; 337 338 for (; mp != NULL; mp = mp->b_cont) 339 n += MBLKL(mp); 340 341 return (n); 342 } 343