Home | History | Annotate | Download | only in smbsrv
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * These routines provide the SMB MAC signing for the SMB server.
     28  * The routines calculate the signature of a SMB message in an mbuf chain.
     29  *
     30  * The following table describes the client server
     31  * signing registry relationship
     32  *
     33  *		| Required	| Enabled     | Disabled
     34  * -------------+---------------+------------ +--------------
     35  * Required	| Signed	| Signed      | Fail
     36  * -------------+---------------+-------------+-----------------
     37  * Enabled	| Signed	| Signed      | Not Signed
     38  * -------------+---------------+-------------+----------------
     39  * Disabled	| Fail		| Not Signed  | Not Signed
     40  */
     41 
     42 #include <sys/uio.h>
     43 #include <smbsrv/smb_kproto.h>
     44 #include <smbsrv/msgbuf.h>
     45 #include <sys/crypto/api.h>
     46 
     47 #define	SMBAUTH_SESSION_KEY_SZ 16
     48 #define	SMB_SIG_SIZE	8
     49 #define	SMB_SIG_OFFS	14
     50 
     51 int
     52 smb_sign_calc(struct mbuf_chain *mbc,
     53     struct smb_sign *sign,
     54     uint32_t seqnum,
     55     unsigned char *mac_sign);
     56 
     57 #ifdef DEBUG
     58 void smb_sign_find_seqnum(
     59     struct smb_sign *sign,
     60     struct mbuf_chain *command,
     61     unsigned char *mac_sig,
     62     unsigned char *sr_sig,
     63     boolean_t *found);
     64 
     65 #define	SMB_CHECK_SEQNUM(sign, command, mac_sig, sr_sig, found) \
     66 { \
     67 	if (smb_sign_debug) \
     68 		smb_sign_find_seqnum(sign, command, mac_sig, sr_sig, found); \
     69 }
     70 #else
     71 #define	SMB_CHECK_SEQNUM(sign, command, mac_sig, sr_sig, found) \
     72 	{  }
     73 #endif
     74 
     75 #ifdef DEBUG
     76 void
     77 smb_sign_find_seqnum(
     78     struct smb_sign *sign,
     79     struct mbuf_chain *command,
     80     unsigned char *mac_sig,
     81     unsigned char *sr_sig,
     82     boolean_t *found)
     83 {
     84 int start_seqnum;
     85 int i;
     86 
     87 	/* Debug code to hunt for the sequence number */
     88 	*found = B_FALSE;
     89 	start_seqnum = (int)sign->seqnum - 6;
     90 	if (start_seqnum < 0)
     91 		start_seqnum = 0;
     92 	for (i = start_seqnum; i <= start_seqnum + 6; i++) {
     93 		(void) smb_sign_calc(command, sign, i, mac_sig);
     94 		if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) {
     95 			sign->seqnum = i;
     96 			*found = B_TRUE;
     97 			break;
     98 		}
     99 		cmn_err(CE_WARN, "smb_sign_find_seqnum: seqnum%d mismatch", i);
    100 	}
    101 	cmn_err(CE_WARN, "smb_sign_find_seqnum: found=%d", *found);
    102 }
    103 #endif
    104 /* This holds the MD5 mechanism */
    105 static	crypto_mechanism_t crypto_mech = {CRYPTO_MECHANISM_INVALID, 0, 0};
    106 
    107 /*
    108  * smb_sign_init
    109  *
    110  * Intializes MAC key based on the user session key and
    111  * NTLM response and store it in the signing structure.
    112  */
    113 void
    114 smb_sign_init(smb_request_t *sr, smb_session_key_t *session_key,
    115 	char *resp, int resp_len)
    116 {
    117 	struct smb_sign *sign = &sr->session->signing;
    118 
    119 	/*
    120 	 * Initialise the crypto mechanism to MD5 if it not
    121 	 * already initialised.
    122 	 */
    123 	if (crypto_mech.cm_type ==  CRYPTO_MECHANISM_INVALID) {
    124 		crypto_mech.cm_type = crypto_mech2id(SUN_CKM_MD5);
    125 		if (crypto_mech.cm_type == CRYPTO_MECHANISM_INVALID) {
    126 			/*
    127 			 * There is no MD5 crypto mechanism
    128 			 * so turn off signing
    129 			 */
    130 			sr->sr_cfg->skc_signing_enable = 0;
    131 			sr->session->secmode &=
    132 			    (~NEGOTIATE_SECURITY_SIGNATURES_ENABLED);
    133 			cmn_err(CE_WARN,
    134 			    "SmbSignInit: signing disabled (no MD5)");
    135 			return;
    136 		}
    137 	}
    138 
    139 	/* MAC key = concat (SessKey, NTLMResponse) */
    140 
    141 	bcopy(session_key, sign->mackey, sizeof (smb_session_key_t));
    142 	bcopy(resp, &(sign->mackey[sizeof (smb_session_key_t)]),
    143 	    resp_len);
    144 	sign->mackey_len = sizeof (smb_session_key_t) + resp_len;
    145 
    146 	sr->reply_seqnum = 1;
    147 	sign->seqnum = 2;
    148 	sign->flags = SMB_SIGNING_ENABLED;
    149 
    150 }
    151 
    152 /*
    153  * smb_sign_calc
    154  *
    155  * Calculates MAC signature for the given buffer and returns
    156  * it in the mac_sign parameter.
    157  *
    158  * The sequence number is placed in the first four bytes of the signature
    159  * field of the signature and the other 4 bytes are zeroed.
    160  * The signature is the first 8 bytes of the MD5 result of the
    161  * concatenated MAC key and the SMB message.
    162  *
    163  * MACsig = head(MD5(concat(MACKey, SMBMsg)), 8)
    164  *
    165  * where
    166  *
    167  *	MACKey = concat( UserSessionKey, NTLMResp )
    168  *
    169  * and
    170  *
    171  *	SMBMsg is the SMB message containing the sequence number.
    172  *
    173  * Return 0 if  success else -1
    174  *
    175  */
    176 int
    177 smb_sign_calc(struct mbuf_chain *mbc,
    178     struct smb_sign *sign,
    179     uint32_t seqnum,
    180     unsigned char *mac_sign)
    181 {
    182 	uint32_t seq_buf[2] = {0, 0};
    183 	unsigned char mac[16];
    184 	struct mbuf *mbuf = mbc->chain;
    185 	int offset = mbc->chain_offset;
    186 	int size;
    187 	int status;
    188 
    189 	crypto_data_t data;
    190 	crypto_data_t digest;
    191 	crypto_context_t crypto_ctx;
    192 
    193 	data.cd_format = CRYPTO_DATA_RAW;
    194 	data.cd_offset = 0;
    195 	data.cd_length = (size_t)-1;
    196 	data.cd_miscdata = 0;
    197 
    198 	digest.cd_format = CRYPTO_DATA_RAW;
    199 	digest.cd_offset = 0;
    200 	digest.cd_length = (size_t)-1;
    201 	digest.cd_miscdata = 0;
    202 	digest.cd_raw.iov_base = (char *)mac;
    203 	digest.cd_raw.iov_len = sizeof (mac);
    204 
    205 	status = crypto_digest_init(&crypto_mech, &crypto_ctx, 0);
    206 	if (status != CRYPTO_SUCCESS)
    207 		goto error;
    208 
    209 	/*
    210 	 * Put the sequence number into the first 4 bytes
    211 	 * of the signature field in little endian format.
    212 	 * We are using a buffer to represent the signature
    213 	 * rather than modifying the SMB message.
    214 	 */
    215 #ifdef __sparc
    216 	{
    217 		uint32_t temp;
    218 		((uint8_t *)&temp)[0] = ((uint8_t *)&seqnum)[3];
    219 		((uint8_t *)&temp)[1] = ((uint8_t *)&seqnum)[2];
    220 		((uint8_t *)&temp)[2] = ((uint8_t *)&seqnum)[1];
    221 		((uint8_t *)&temp)[3] = ((uint8_t *)&seqnum)[0];
    222 
    223 		seq_buf[0] = temp;
    224 	}
    225 #else
    226 	seq_buf[0] = seqnum;
    227 #endif
    228 
    229 	/* Digest the MACKey */
    230 	data.cd_raw.iov_base = (char *)sign->mackey;
    231 	data.cd_raw.iov_len = sign->mackey_len;
    232 	data.cd_length = sign->mackey_len;
    233 	status = crypto_digest_update(crypto_ctx, &data, 0);
    234 	if (status != CRYPTO_SUCCESS)
    235 		goto error;
    236 
    237 	/* Find start of data in chain */
    238 	while (offset >= mbuf->m_len) {
    239 		offset -= mbuf->m_len;
    240 		mbuf = mbuf->m_next;
    241 	}
    242 
    243 	/* Digest the SMB packet up to the signature field */
    244 	size = SMB_SIG_OFFS;
    245 	while (size >= mbuf->m_len - offset) {
    246 		data.cd_raw.iov_base = &mbuf->m_data[offset];
    247 		data.cd_raw.iov_len = mbuf->m_len - offset;
    248 		data.cd_length = mbuf->m_len - offset;
    249 		status = crypto_digest_update(crypto_ctx, &data, 0);
    250 		if (status != CRYPTO_SUCCESS)
    251 			goto error;
    252 
    253 		size -= mbuf->m_len - offset;
    254 		mbuf = mbuf->m_next;
    255 		offset = 0;
    256 	}
    257 	if (size > 0) {
    258 		data.cd_raw.iov_base = &mbuf->m_data[offset];
    259 		data.cd_raw.iov_len = size;
    260 		data.cd_length = size;
    261 		status = crypto_digest_update(crypto_ctx, &data, 0);
    262 		if (status != CRYPTO_SUCCESS)
    263 			goto error;
    264 		offset += size;
    265 	}
    266 
    267 	/*
    268 	 * Digest in the seq_buf instead of the signature
    269 	 * which has the sequence number
    270 	 */
    271 
    272 	data.cd_raw.iov_base = (char *)seq_buf;
    273 	data.cd_raw.iov_len = SMB_SIG_SIZE;
    274 	data.cd_length = SMB_SIG_SIZE;
    275 	status = crypto_digest_update(crypto_ctx, &data, 0);
    276 	if (status != CRYPTO_SUCCESS)
    277 		goto error;
    278 
    279 	/* Find the end of the signature field  */
    280 	offset += SMB_SIG_SIZE;
    281 	while (offset >= mbuf->m_len) {
    282 		offset -= mbuf->m_len;
    283 		mbuf = mbuf->m_next;
    284 	}
    285 	/* Digest the rest of the SMB packet */
    286 	while (mbuf) {
    287 		data.cd_raw.iov_base = &mbuf->m_data[offset];
    288 		data.cd_raw.iov_len = mbuf->m_len - offset;
    289 		data.cd_length = mbuf->m_len - offset;
    290 		status = crypto_digest_update(crypto_ctx, &data, 0);
    291 		if (status != CRYPTO_SUCCESS)
    292 			goto error;
    293 		mbuf = mbuf->m_next;
    294 		offset = 0;
    295 	}
    296 	digest.cd_length = SMBAUTH_SESSION_KEY_SZ;
    297 	status = crypto_digest_final(crypto_ctx, &digest, 0);
    298 	if (status != CRYPTO_SUCCESS)
    299 		goto error;
    300 	bcopy(mac, mac_sign, SMB_SIG_SIZE);
    301 	return (0);
    302 error:
    303 	cmn_err(CE_WARN, "SmbSignCalc: crypto error %d", status);
    304 	return (-1);
    305 
    306 }
    307 
    308 
    309 /*
    310  * smb_sign_check_request
    311  *
    312  * Calculates MAC signature for the request mbuf chain
    313  * using the next expected sequence number and compares
    314  * it to the given signature.
    315  *
    316  * Note it does not check the signature for secondary transactions
    317  * as their sequence number is the same as the original request.
    318  *
    319  * Return 0 if the signature verifies, otherwise, returns -1;
    320  *
    321  */
    322 int
    323 smb_sign_check_request(smb_request_t *sr)
    324 {
    325 	struct mbuf_chain command = sr->command;
    326 	unsigned char mac_sig[SMB_SIG_SIZE];
    327 	struct smb_sign *sign = &sr->session->signing;
    328 	int rtn = 0;
    329 	boolean_t found = B_TRUE;
    330 	/*
    331 	 * Don't check secondary transactions - we dont know the sequence
    332 	 * number.
    333 	 */
    334 	if (sr->smb_com == SMB_COM_TRANSACTION_SECONDARY ||
    335 	    sr->smb_com == SMB_COM_TRANSACTION2_SECONDARY ||
    336 	    sr->smb_com == SMB_COM_NT_TRANSACT_SECONDARY)
    337 		return (0);
    338 
    339 	/* Reset the offset to begining of header */
    340 	command.chain_offset = sr->orig_request_hdr;
    341 
    342 	/* calculate mac signature */
    343 	if (smb_sign_calc(&command, sign, sign->seqnum, mac_sig) != 0)
    344 		return (-1);
    345 
    346 	/* compare the signatures */
    347 	if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
    348 		DTRACE_PROBE2(smb__signing__req, smb_request_t, sr,
    349 		    smb_sign_t *, sr->smb_sig);
    350 		cmn_err(CE_NOTE, "message signing: bad signature");
    351 		/*
    352 		 * check nearby sequence numbers in debug mode
    353 		 */
    354 		SMB_CHECK_SEQNUM(sign, &command, mac_sig, sr->smb_sig, &found);
    355 		if (found == B_FALSE)
    356 			rtn = -1;
    357 	}
    358 	/*
    359 	 * Increment the sequence number for the reply, save the reply
    360 	 * and set it for the next expect command.
    361 	 * There is no reply for NT Cancel so just increment it for the
    362 	 * next expected command.
    363 	 */
    364 	sign->seqnum++;
    365 
    366 	if (sr->smb_com == SMB_COM_NT_CANCEL)
    367 		sr->reply_seqnum = 0;
    368 	else
    369 		sr->reply_seqnum = sign->seqnum++;
    370 
    371 	return (rtn);
    372 }
    373 
    374 /*
    375  * smb_sign_check_secondary
    376  *
    377  * Calculates MAC signature for the secondary transaction mbuf chain
    378  * and compares it to the given signature.
    379  * Return 0 if the signature verifies, otherwise, returns -1;
    380  *
    381  */
    382 int
    383 smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum)
    384 {
    385 	struct mbuf_chain command = sr->command;
    386 	unsigned char mac_sig[SMB_SIG_SIZE];
    387 	struct smb_sign *sign = &sr->session->signing;
    388 	int rtn = 0;
    389 
    390 	/* Reset the offset to begining of header */
    391 	command.chain_offset = sr->orig_request_hdr;
    392 
    393 	/* calculate mac signature */
    394 	if (smb_sign_calc(&command, sign, reply_seqnum - 1,
    395 	    mac_sig) != 0)
    396 		return (-1);
    397 
    398 
    399 	/* compare the signatures */
    400 	if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) {
    401 		cmn_err(CE_WARN, "SmbSignCheckSecond: bad signature");
    402 		rtn = -1;
    403 	}
    404 	/* Save the reply sequence number */
    405 	sr->reply_seqnum = reply_seqnum;
    406 
    407 	return (rtn);
    408 }
    409 
    410 
    411 
    412 
    413 /*
    414  * smb_sign_reply
    415  *
    416  * Calculates MAC signature for the given mbuf chain,
    417  * and write it to the signature field in the mbuf.
    418  *
    419  */
    420 void
    421 smb_sign_reply(smb_request_t *sr, struct mbuf_chain *reply)
    422 {
    423 	struct mbuf_chain resp;
    424 	struct smb_sign *sign = &sr->session->signing;
    425 	unsigned char signature[SMB_SIG_SIZE];
    426 	struct mbuf *mbuf;
    427 	int size = SMB_SIG_SIZE;
    428 	unsigned char *sig_ptr = signature;
    429 	int offset = 0;
    430 
    431 	if (reply)
    432 		resp = *reply;
    433 	else
    434 		resp = sr->reply;
    435 
    436 	/* Reset offset to start of reply */
    437 	resp.chain_offset = 0;
    438 	mbuf = resp.chain;
    439 
    440 	/*
    441 	 * Calculate MAC signature
    442 	 */
    443 	if (smb_sign_calc(&resp, sign, sr->reply_seqnum, signature) != 0)
    444 		return;
    445 
    446 	/*
    447 	 * Put signature in the response
    448 	 *
    449 	 * First find start of signature in chain (offset + signature offset)
    450 	 */
    451 	offset += SMB_SIG_OFFS;
    452 	while (offset >= mbuf->m_len) {
    453 		offset -= mbuf->m_len;
    454 		mbuf = mbuf->m_next;
    455 	}
    456 
    457 	while (size >= mbuf->m_len - offset) {
    458 		(void) memcpy(&mbuf->m_data[offset],
    459 		    sig_ptr, mbuf->m_len - offset);
    460 		offset = 0;
    461 		sig_ptr += mbuf->m_len - offset;
    462 		size -= mbuf->m_len - offset;
    463 		mbuf = mbuf->m_next;
    464 	}
    465 	if (size > 0) {
    466 		(void) memcpy(&mbuf->m_data[offset], sig_ptr, size);
    467 	}
    468 }
    469