1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * These routines provide the SMB MAC signing for the SMB server. 30 * The routines calculate the signature of a SMB message in an mbuf chain. 31 * 32 */ 33 34 #include <sys/types.h> 35 #include <sys/uio.h> 36 #include <smbsrv/mbuf.h> 37 #include <smbsrv/msgbuf.h> 38 #include <sys/crypto/api.h> 39 #include <smbsrv/smb_incl.h> 40 41 #define SMBAUTH_SESSION_KEY_SZ 16 42 #define SMB_SIG_SIZE 8 43 #define SMB_SIG_OFFS 14 44 45 /* This holds the MD5 mechanism */ 46 static crypto_mechanism_t crypto_mech = {CRYPTO_MECHANISM_INVALID, 0, 0}; 47 48 /* 49 * smb_sign_init 50 * 51 * Intializes MAC key based on the user session key and 52 * NTLM response and store it in the signing structure. 53 */ 54 void 55 smb_sign_init(smb_request_t *sr, smb_session_key_t *session_key, 56 char *resp, int resp_len) 57 { 58 struct smb_sign *sign = &sr->session->signing; 59 60 /* 61 * Initialise the crypto mechanism to MD5 if it not 62 * already initialised. 63 */ 64 65 if (crypto_mech.cm_type == CRYPTO_MECHANISM_INVALID) { 66 crypto_mech.cm_type = crypto_mech2id(SUN_CKM_MD5); 67 if (crypto_mech.cm_type == CRYPTO_MECHANISM_INVALID) { 68 /* 69 * There is no MD5 crypto mechanism 70 * so turn off signing 71 */ 72 sr->sr_cfg->skc_signing_enable = 0; 73 sr->session->secmode &= 74 (~NEGOTIATE_SECURITY_SIGNATURES_ENABLED); 75 cmn_err(CE_WARN, 76 "SmbSignInit: signing disabled (no MD5)"); 77 return; 78 } 79 } 80 81 /* MAC key = concat (SessKey, NTLMResponse) */ 82 83 bcopy(session_key, sign->mackey, sizeof (smb_session_key_t)); 84 bcopy(resp, &(sign->mackey[sizeof (smb_session_key_t)]), 85 resp_len); 86 sign->mackey_len = sizeof (smb_session_key_t) + resp_len; 87 88 sr->reply_seqnum = 1; 89 sign->seqnum = 2; 90 sign->flags = SMB_SIGNING_ENABLED; 91 92 if (sr->sr_cfg->skc_signing_check) 93 sign->flags |= SMB_SIGNING_CHECK; 94 95 } 96 97 /* 98 * smb_sign_calc 99 * 100 * Calculates MAC signature for the given buffer and returns 101 * it in the mac_sign parameter. 102 * 103 * The sequence number is placed in the first four bytes of the signature 104 * field of the signature and the other 4 bytes are zeroed. 105 * The signature is the first 8 bytes of the MD5 result of the 106 * concatenated MAC key and the SMB message. 107 * 108 * MACsig = head(MD5(concat(MACKey, SMBMsg)), 8) 109 * 110 * where 111 * 112 * MACKey = concat( UserSessionKey, NTLMResp ) 113 * 114 * and 115 * 116 * SMBMsg is the SMB message containing the sequence number. 117 * 118 * Return 0 if success else -1 119 * 120 */ 121 static int 122 smb_sign_calc(struct mbuf_chain *mbc, 123 struct smb_sign *sign, 124 uint32_t seqnum, 125 unsigned char *mac_sign) 126 { 127 uint32_t seq_buf[2] = {0, 0}; 128 unsigned char mac[16]; 129 struct mbuf *mbuf = mbc->chain; 130 int offset = mbc->chain_offset; 131 int size; 132 int status; 133 134 crypto_data_t data; 135 crypto_data_t digest; 136 crypto_context_t crypto_ctx; 137 138 data.cd_format = CRYPTO_DATA_RAW; 139 data.cd_offset = 0; 140 data.cd_length = (size_t)-1; 141 data.cd_miscdata = 0; 142 143 digest.cd_format = CRYPTO_DATA_RAW; 144 digest.cd_offset = 0; 145 digest.cd_length = (size_t)-1; 146 digest.cd_miscdata = 0; 147 digest.cd_raw.iov_base = (char *)mac; 148 digest.cd_raw.iov_len = sizeof (mac); 149 150 status = crypto_digest_init(&crypto_mech, &crypto_ctx, 0); 151 if (status != CRYPTO_SUCCESS) 152 goto error; 153 154 /* 155 * Put the sequence number into the first 4 bytes 156 * of the signature field in little endian format. 157 * We are using a buffer to represent the signature 158 * rather than modifying the SMB message. 159 */ 160 #ifdef __sparc 161 { 162 uint32_t temp; 163 ((uint8_t *)&temp)[0] = ((uint8_t *)&seqnum)[3]; 164 ((uint8_t *)&temp)[1] = ((uint8_t *)&seqnum)[2]; 165 ((uint8_t *)&temp)[2] = ((uint8_t *)&seqnum)[1]; 166 ((uint8_t *)&temp)[3] = ((uint8_t *)&seqnum)[0]; 167 168 seq_buf[0] = temp; 169 } 170 #else 171 seq_buf[0] = seqnum; 172 #endif 173 174 /* Digest the MACKey */ 175 data.cd_raw.iov_base = (char *)sign->mackey; 176 data.cd_raw.iov_len = sign->mackey_len; 177 data.cd_length = sign->mackey_len; 178 status = crypto_digest_update(crypto_ctx, &data, 0); 179 if (status != CRYPTO_SUCCESS) 180 goto error; 181 182 /* Find start of data in chain */ 183 while (offset >= mbuf->m_len) { 184 offset -= mbuf->m_len; 185 mbuf = mbuf->m_next; 186 } 187 188 /* Digest the SMB packet up to the signature field */ 189 size = SMB_SIG_OFFS; 190 while (size >= mbuf->m_len - offset) { 191 data.cd_raw.iov_base = &mbuf->m_data[offset]; 192 data.cd_raw.iov_len = mbuf->m_len - offset; 193 data.cd_length = mbuf->m_len - offset; 194 status = crypto_digest_update(crypto_ctx, &data, 0); 195 if (status != CRYPTO_SUCCESS) 196 goto error; 197 198 size -= mbuf->m_len - offset; 199 mbuf = mbuf->m_next; 200 offset = 0; 201 } 202 if (size > 0) { 203 data.cd_raw.iov_base = &mbuf->m_data[offset]; 204 data.cd_raw.iov_len = size; 205 data.cd_length = size; 206 status = crypto_digest_update(crypto_ctx, &data, 0); 207 if (status != CRYPTO_SUCCESS) 208 goto error; 209 offset += size; 210 } 211 212 /* 213 * Digest in the seq_buf instead of the signature 214 * which has the sequence number 215 */ 216 217 data.cd_raw.iov_base = (char *)seq_buf; 218 data.cd_raw.iov_len = SMB_SIG_SIZE; 219 data.cd_length = SMB_SIG_SIZE; 220 status = crypto_digest_update(crypto_ctx, &data, 0); 221 if (status != CRYPTO_SUCCESS) 222 goto error; 223 224 /* Find the end of the signature field */ 225 offset += SMB_SIG_SIZE; 226 while (offset >= mbuf->m_len) { 227 offset -= mbuf->m_len; 228 mbuf = mbuf->m_next; 229 } 230 /* Digest the rest of the SMB packet */ 231 while (mbuf) { 232 data.cd_raw.iov_base = &mbuf->m_data[offset]; 233 data.cd_raw.iov_len = mbuf->m_len - offset; 234 data.cd_length = mbuf->m_len - offset; 235 status = crypto_digest_update(crypto_ctx, &data, 0); 236 if (status != CRYPTO_SUCCESS) 237 goto error; 238 mbuf = mbuf->m_next; 239 offset = 0; 240 } 241 digest.cd_length = SMBAUTH_SESSION_KEY_SZ; 242 status = crypto_digest_final(crypto_ctx, &digest, 0); 243 if (status != CRYPTO_SUCCESS) 244 goto error; 245 bcopy(mac, mac_sign, SMB_SIG_SIZE); 246 return (0); 247 error: 248 cmn_err(CE_WARN, "SmbSignCalc: crypto error %d", status); 249 return (-1); 250 251 } 252 253 254 /* 255 * smb_sign_check_request 256 * 257 * Calculates MAC signature for the request mbuf chain 258 * using the next expected sequence number and compares 259 * it to the given signature. 260 * 261 * Note it does not check the signature for secondary transactions 262 * as their sequence number is the same as the original request. 263 * 264 * Return 0 if the signature verifies, otherwise, returns -1; 265 * 266 */ 267 int 268 smb_sign_check_request(smb_request_t *sr) 269 { 270 struct mbuf_chain command = sr->command; 271 unsigned char mac_sig[SMB_SIG_SIZE]; 272 struct smb_sign *sign = &sr->session->signing; 273 int rtn = 0; 274 275 /* 276 * Don't check secondary transactions - we dont know the sequence 277 * number. 278 */ 279 if (sr->smb_com == SMB_COM_TRANSACTION_SECONDARY || 280 sr->smb_com == SMB_COM_TRANSACTION2_SECONDARY || 281 sr->smb_com == SMB_COM_NT_TRANSACT_SECONDARY) 282 return (0); 283 284 if (sign->flags & SMB_SIGNING_CHECK) { 285 286 /* Reset the offset to begining of header */ 287 command.chain_offset = sr->orig_request_hdr; 288 289 /* calculate mac signature */ 290 if (smb_sign_calc(&command, sign, sign->seqnum, mac_sig) != 0) 291 return (-1); 292 293 /* compare the signatures */ 294 if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) { 295 cmn_err(CE_WARN, "SmbSignCheckRequest: " 296 "bad signature %x %x %x %x %x %x %x %x", 297 sr->smb_sig[0], sr->smb_sig[1], 298 sr->smb_sig[2], sr->smb_sig[3], 299 sr->smb_sig[4], sr->smb_sig[5], 300 sr->smb_sig[6], sr->smb_sig[7]); 301 #ifdef DBG_VERBOSE 302 /* Debug code to hunt for the sequence number */ 303 for (i = sign->seqnum - 6; i <= sign->seqnum + 6; i++) { 304 smb_sign_calc(&command, sign, i, mac_sig); 305 if (memcmp(mac_sig, sr->smb_sig, 306 SMB_SIG_SIZE) == 0) { 307 sign->seqnum = i; 308 goto ok; 309 } 310 } 311 #endif 312 rtn = -1; 313 } 314 } 315 ok: 316 /* 317 * Increament the sequence number for the reply, save the reply 318 * and set it for the next expect command. 319 * There is no reply for NT Cancel so just increament it for the 320 * next expected command. 321 */ 322 sign->seqnum++; 323 324 if (sr->smb_com == SMB_COM_NT_CANCEL) 325 sr->reply_seqnum = 0; 326 else 327 sr->reply_seqnum = sign->seqnum++; 328 329 return (rtn); 330 } 331 332 /* 333 * smb_sign_check_secondary 334 * 335 * Calculates MAC signature for the secondary transaction mbuf chain 336 * and compares it to the given signature. 337 * Return 0 if the signature verifies, otherwise, returns -1; 338 * 339 */ 340 int 341 smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum) 342 { 343 struct mbuf_chain command = sr->command; 344 unsigned char mac_sig[SMB_SIG_SIZE]; 345 struct smb_sign *sign = &sr->session->signing; 346 int rtn = 0; 347 348 if (sign->flags & SMB_SIGNING_CHECK) { 349 /* Reset the offset to begining of header */ 350 command.chain_offset = sr->orig_request_hdr; 351 352 /* calculate mac signature */ 353 if (smb_sign_calc(&command, sign, reply_seqnum - 1, 354 mac_sig) != 0) 355 return (-1); 356 357 358 /* compare the signatures */ 359 if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) { 360 cmn_err(CE_WARN, "SmbSignCheckSecond: bad signature"); 361 rtn = -1; 362 } 363 } 364 /* Save the reply sequence number */ 365 sr->reply_seqnum = reply_seqnum; 366 367 return (rtn); 368 } 369 370 371 372 373 /* 374 * smb_sign_reply 375 * 376 * Calculates MAC signature for the given mbuf chain, 377 * and write it to the signature field in the mbuf. 378 * 379 */ 380 void 381 smb_sign_reply(smb_request_t *sr, struct mbuf_chain *reply) 382 { 383 struct mbuf_chain resp; 384 struct smb_sign *sign = &sr->session->signing; 385 unsigned char signature[SMB_SIG_SIZE]; 386 struct mbuf *mbuf; 387 int size = SMB_SIG_SIZE; 388 unsigned char *sig_ptr = signature; 389 int offset = 0; 390 391 if (reply) 392 resp = *reply; 393 else 394 resp = sr->reply; 395 396 /* Reset offset to start of reply */ 397 resp.chain_offset = 0; 398 mbuf = resp.chain; 399 400 /* 401 * Calculate MAC signature 402 */ 403 if (smb_sign_calc(&resp, sign, sr->reply_seqnum, signature) != 0) 404 return; 405 406 /* 407 * Put signature in the response 408 * 409 * First find start of signature in chain (offset + signature offset) 410 */ 411 offset += SMB_SIG_OFFS; 412 while (offset >= mbuf->m_len) { 413 offset -= mbuf->m_len; 414 mbuf = mbuf->m_next; 415 } 416 417 while (size >= mbuf->m_len - offset) { 418 (void) memcpy(&mbuf->m_data[offset], 419 sig_ptr, mbuf->m_len - offset); 420 offset = 0; 421 sig_ptr += mbuf->m_len - offset; 422 size -= mbuf->m_len - offset; 423 mbuf = mbuf->m_next; 424 } 425 if (size > 0) { 426 (void) memcpy(&mbuf->m_data[offset], sig_ptr, size); 427 } 428 } 429