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 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/smbsrv/smb3_encrypt_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 (smb_kcrypt.h). 27 */ 28 29 #include <security/cryptoki.h> 30 #include <security/pkcs11.h> 31 #include <smbsrv/smb_kcrypt.h> 32 33 #include <sys/cmn_err.h> 34 #include <sys/debug.h> 35 #include <stdlib.h> 36 #include <strings.h> 37 38 /* 39 * Common function to see if a mech is available. 40 */ 41 static int 42 find_mech(CK_MECHANISM_TYPE id) 43 { 44 CK_SESSION_HANDLE hdl; 45 CK_RV rv; 46 47 rv = SUNW_C_GetMechSession(id, &hdl); 48 if (rv != CKR_OK) { 49 return (-1); 50 } 51 (void) C_CloseSession(hdl); 52 53 return (0); 54 } 55 56 /* 57 * SMB3 encryption helpers: 58 * (getmech, init, update, final) 59 */ 60 61 int 62 smb3_aes_ccm_getmech(smb_crypto_mech_t *mech) 63 { 64 65 if (find_mech(CKM_AES_CCM) != 0) { 66 cmn_err(CE_NOTE, "PKCS#11: no mech AES_CCM"); 67 return (-1); 68 } 69 70 mech->mechanism = CKM_AES_CCM; 71 return (0); 72 } 73 74 int 75 smb3_aes_gcm_getmech(smb_crypto_mech_t *mech) 76 { 77 78 if (find_mech(CKM_AES_GCM) != 0) { 79 cmn_err(CE_NOTE, "PKCS#11: no mech CKM_AES_GCM"); 80 return (-1); 81 } 82 83 mech->mechanism = CKM_AES_GCM; 84 return (0); 85 } 86 87 void 88 smb3_crypto_init_ccm_param(smb_enc_ctx_t *ctx, 89 uint8_t *nonce, size_t noncesize, 90 uint8_t *auth, size_t authsize, 91 size_t datasize) 92 { 93 94 ASSERT3U(noncesize, >=, SMB3_AES_CCM_NONCE_SIZE); 95 96 /* CK_CCM_PARAMS */ 97 ctx->param.ccm.ulDataLen = datasize; 98 ctx->param.ccm.pNonce = nonce; 99 ctx->param.ccm.ulNonceLen = SMB3_AES_CCM_NONCE_SIZE; 100 ctx->param.ccm.pAAD = auth; 101 ctx->param.ccm.ulAADLen = authsize; 102 ctx->param.ccm.ulMACLen = SMB2_SIG_SIZE; 103 104 ctx->mech.pParameter = (caddr_t)&ctx->param.ccm; 105 ctx->mech.ulParameterLen = sizeof (ctx->param.ccm); 106 } 107 108 void 109 smb3_crypto_init_gcm_param(smb_enc_ctx_t *ctx, 110 uint8_t *nonce, size_t noncesize, 111 uint8_t *auth, size_t authsize) 112 { 113 114 ASSERT3U(noncesize, >=, SMB3_AES_GCM_NONCE_SIZE); 115 116 /* CK_GCM_PARAMS */ 117 ctx->param.gcm.pIv = nonce; 118 ctx->param.gcm.ulIvLen = SMB3_AES_GCM_NONCE_SIZE; 119 ctx->param.gcm.pAAD = auth; /* auth data */ 120 ctx->param.gcm.ulAADLen = authsize; /* auth data len */ 121 ctx->param.gcm.ulTagBits = SMB2_SIG_SIZE << 3; /* bytes to bits */ 122 123 ctx->mech.pParameter = (caddr_t)&ctx->param.gcm; 124 ctx->mech.ulParameterLen = sizeof (ctx->param.gcm); 125 } 126 127 /* 128 * Start the KCF encrypt session, load the key 129 * If this returns zero, the caller should call 130 * smb3_enc_ctx_done to cleanup the context, 131 * even if there are intervening errors. 132 */ 133 int 134 smb3_encrypt_init(smb_enc_ctx_t *ctxp, 135 uint8_t *key, size_t keylen) 136 { 137 CK_OBJECT_HANDLE hkey = 0; 138 CK_MECHANISM *mech = &ctxp->mech; 139 CK_RV rv; 140 141 rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx); 142 if (rv != CKR_OK) 143 return (-1); 144 145 rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism, 146 key, keylen, &hkey); 147 if (rv != CKR_OK) 148 return (-1); 149 150 rv = C_EncryptInit(ctxp->ctx, mech, hkey); 151 if (rv != CKR_OK) { 152 cmn_err(CE_WARN, "C_EncryptInit failed: 0x%lx", rv); 153 } 154 (void) C_DestroyObject(ctxp->ctx, hkey); 155 156 return (rv == CKR_OK ? 0 : -1); 157 } 158 159 /* 160 * Start the KCF decrypt session, load the key 161 * If this returns zero, the caller should call 162 * smb3_enc_ctx_done to cleanup the context, 163 * even if there are intervening errors. 164 */ 165 int 166 smb3_decrypt_init(smb_enc_ctx_t *ctxp, 167 uint8_t *key, size_t keylen) 168 { 169 CK_OBJECT_HANDLE hkey = 0; 170 CK_MECHANISM *mech = &ctxp->mech; 171 CK_RV rv; 172 173 rv = SUNW_C_GetMechSession(mech->mechanism, &ctxp->ctx); 174 if (rv != CKR_OK) 175 return (-1); 176 177 rv = SUNW_C_KeyToObject(ctxp->ctx, mech->mechanism, 178 key, keylen, &hkey); 179 if (rv != CKR_OK) 180 return (-1); 181 182 rv = C_DecryptInit(ctxp->ctx, mech, hkey); 183 if (rv != CKR_OK) { 184 cmn_err(CE_WARN, "C_DecryptInit failed: 0x%lx", rv); 185 } 186 (void) C_DestroyObject(ctxp->ctx, hkey); 187 188 return (rv == CKR_OK ? 0 : -1); 189 } 190 191 /* 192 * Encrypt a whole message with scatter/gather (UIO) 193 * 194 * While the PKCS#11 implementation internally has the ability to 195 * handle scatter/gather, it currently presents no interface for it. 196 * As this library is used primarily for debugging, performance in 197 * here is not a big concern, so we'll get around the limitation of 198 * libpkcs11 by copying to/from a contiguous working buffer. 199 */ 200 int 201 smb3_encrypt_uio(smb_enc_ctx_t *ctxp, uio_t *in, uio_t *out) 202 { 203 uint8_t *buf = NULL; 204 size_t inlen, outlen; 205 ulong_t tlen; 206 int err, rc = -1; 207 CK_RV rv; 208 209 if (in->uio_resid <= 0) 210 return (-1); 211 inlen = in->uio_resid; 212 outlen = inlen + 16; 213 buf = malloc(outlen); 214 if (buf == NULL) 215 return (-1); 216 217 /* Copy from uio segs to buf */ 218 err = uiomove(buf, inlen, UIO_WRITE, in); 219 if (err != 0) 220 goto out; 221 222 /* Encrypt in-place in our work buffer. */ 223 tlen = outlen; 224 rv = C_Encrypt(ctxp->ctx, buf, inlen, buf, &tlen); 225 if (rv != CKR_OK) { 226 cmn_err(CE_WARN, "C_Encrypt failed: 0x%lx", rv); 227 goto out; 228 } 229 if (tlen != outlen) { 230 cmn_err(CE_WARN, "smb3_encrypt_uio outlen %d vs %d", 231 (int)tlen, (int)outlen); 232 goto out; 233 } 234 235 /* Copy from buf to uio segs */ 236 err = uiomove(buf, outlen, UIO_READ, out); 237 if (err != 0) 238 goto out; 239 240 rc = 0; 241 out: 242 free(buf); 243 244 return (rc); 245 } 246 247 int 248 smb3_decrypt_uio(smb_enc_ctx_t *ctxp, uio_t *in, uio_t *out) 249 { 250 uint8_t *buf = NULL; 251 size_t inlen, outlen; 252 ulong_t tlen; 253 int err, rc = -1; 254 CK_RV rv; 255 256 if (in->uio_resid <= 16) 257 return (-1); 258 inlen = in->uio_resid; 259 outlen = inlen - 16; 260 buf = malloc(inlen); 261 if (buf == NULL) 262 return (-1); 263 264 /* Copy from uio segs to buf */ 265 err = uiomove(buf, inlen, UIO_WRITE, in); 266 if (err != 0) 267 goto out; 268 269 /* Decrypt in-place in our work buffer. */ 270 tlen = outlen; 271 rv = C_Decrypt(ctxp->ctx, buf, inlen, buf, &tlen); 272 if (rv != CKR_OK) { 273 cmn_err(CE_WARN, "C_Decrypt failed: 0x%lx", rv); 274 goto out; 275 } 276 if (tlen != outlen) { 277 cmn_err(CE_WARN, "smb3_decrypt_uio outlen %d vs %d", 278 (int)tlen, (int)outlen); 279 goto out; 280 } 281 282 /* Copy from buf to uio segs */ 283 err = uiomove(buf, outlen, UIO_READ, out); 284 if (err != 0) 285 goto out; 286 287 rc = 0; 288 out: 289 free(buf); 290 291 return (rc); 292 } 293 294 void 295 smb3_enc_ctx_done(smb_enc_ctx_t *ctxp) 296 { 297 if (ctxp->ctx != 0) { 298 (void) C_CloseSession(ctxp->ctx); 299 ctxp->ctx = 0; 300 } 301 } 302