cifsencrypt.c 11.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *   fs/cifs/cifsencrypt.c
 *
4
 *   Copyright (C) International Business Machines  Corp., 2005,2006
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <linux/fs.h>
#include "cifspdu.h"
#include "cifsglob.h" 
#include "cifs_debug.h"
#include "md5.h"
#include "cifs_unicode.h"
#include "cifsproto.h"
29
#include <linux/ctype.h>
S
Steve French 已提交
30
#include <linux/random.h>
L
Linus Torvalds 已提交
31 32 33 34 35 36 37 38 39

/* Calculate and return the CIFS signature based on the mac key and the smb pdu */
/* the 16 byte signature must be allocated by the caller  */
/* Note we only use the 1st eight bytes */
/* Note that the smb header signature field on input contains the  
	sequence number before this function is called */

extern void mdfour(unsigned char *out, unsigned char *in, int n);
extern void E_md4hash(const unsigned char *passwd, unsigned char *p16);
40 41
extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
                       unsigned char *p24);
L
Linus Torvalds 已提交
42
	
43 44
static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu, 
				    const char * key, char * signature)
L
Linus Torvalds 已提交
45 46 47 48 49 50 51
{
	struct	MD5Context context;

	if((cifs_pdu == NULL) || (signature == NULL))
		return -EINVAL;

	MD5Init(&context);
52
	MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16);
L
Linus Torvalds 已提交
53 54 55 56 57
	MD5Update(&context,cifs_pdu->Protocol,cifs_pdu->smb_buf_length);
	MD5Final(signature,&context);
	return 0;
}

58
int cifs_sign_smb(struct smb_hdr * cifs_pdu, struct TCP_Server_Info * server,
L
Linus Torvalds 已提交
59 60 61 62 63
	__u32 * pexpected_response_sequence_number)
{
	int rc = 0;
	char smb_signature[20];

64
	if((cifs_pdu == NULL) || (server == NULL))
L
Linus Torvalds 已提交
65 66 67 68 69 70
		return -EINVAL;

	if((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) 
		return rc;

	spin_lock(&GlobalMid_Lock);
71
	cifs_pdu->Signature.Sequence.SequenceNumber = cpu_to_le32(server->sequence_number);
L
Linus Torvalds 已提交
72 73
	cifs_pdu->Signature.Sequence.Reserved = 0;
	
74 75
	*pexpected_response_sequence_number = server->sequence_number++;
	server->sequence_number++;
L
Linus Torvalds 已提交
76 77
	spin_unlock(&GlobalMid_Lock);

78
	rc = cifs_calculate_signature(cifs_pdu, server->mac_signing_key,smb_signature);
L
Linus Torvalds 已提交
79 80 81 82 83 84 85 86
	if(rc)
		memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
	else
		memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);

	return rc;
}

87 88 89
static int cifs_calc_signature2(const struct kvec * iov, int n_vec,
				const char * key, char * signature)
{
90 91
	struct  MD5Context context;
	int i;
92

93 94
	if((iov == NULL) || (signature == NULL))
		return -EINVAL;
95

96
	MD5Init(&context);
97
	MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16);
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	for(i=0;i<n_vec;i++) {
		if(iov[i].iov_base == NULL) {
			cERROR(1,("null iovec entry"));
			return -EIO;
		} else if(iov[i].iov_len == 0)
			break; /* bail out if we are sent nothing to sign */
		/* The first entry includes a length field (which does not get 
		   signed that occupies the first 4 bytes before the header */
		if(i==0) {
			if (iov[0].iov_len <= 8 ) /* cmd field at offset 9 */
				break; /* nothing to sign or corrupt header */
			MD5Update(&context,iov[0].iov_base+4, iov[0].iov_len-4);
		} else
			MD5Update(&context,iov[i].iov_base, iov[i].iov_len);
	}
113

114
	MD5Final(signature,&context);
115

116
	return 0;
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
}


int cifs_sign_smb2(struct kvec * iov, int n_vec, struct TCP_Server_Info *server,
		   __u32 * pexpected_response_sequence_number)
{
	int rc = 0;
	char smb_signature[20];
	struct smb_hdr * cifs_pdu = iov[0].iov_base;

	if((cifs_pdu == NULL) || (server == NULL))
		return -EINVAL;

	if((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
		return rc;

        spin_lock(&GlobalMid_Lock);
        cifs_pdu->Signature.Sequence.SequenceNumber = 
				cpu_to_le32(server->sequence_number);
        cifs_pdu->Signature.Sequence.Reserved = 0;

        *pexpected_response_sequence_number = server->sequence_number++;
        server->sequence_number++;
        spin_unlock(&GlobalMid_Lock);

        rc = cifs_calc_signature2(iov, n_vec, server->mac_signing_key,
				      smb_signature);
        if(rc)
                memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
        else
                memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);

        return rc;

}

L
Linus Torvalds 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
int cifs_verify_signature(struct smb_hdr * cifs_pdu, const char * mac_key,
	__u32 expected_sequence_number)
{
	unsigned int rc;
	char server_response_sig[8];
	char what_we_think_sig_should_be[20];

	if((cifs_pdu == NULL) || (mac_key == NULL))
		return -EINVAL;

	if (cifs_pdu->Command == SMB_COM_NEGOTIATE)
		return 0;

	if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) {
		struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)cifs_pdu;
	    if(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)
			return 0;
	}

	/* BB what if signatures are supposed to be on for session but server does not
		send one? BB */
	
	/* Do not need to verify session setups with signature "BSRSPYL "  */
	if(memcmp(cifs_pdu->Signature.SecuritySignature,"BSRSPYL ",8)==0)
		cFYI(1,("dummy signature received for smb command 0x%x",cifs_pdu->Command));

	/* save off the origiginal signature so we can modify the smb and check
		its signature against what the server sent */
	memcpy(server_response_sig,cifs_pdu->Signature.SecuritySignature,8);

	cifs_pdu->Signature.Sequence.SequenceNumber = cpu_to_le32(expected_sequence_number);
	cifs_pdu->Signature.Sequence.Reserved = 0;

	rc = cifs_calculate_signature(cifs_pdu, mac_key,
		what_we_think_sig_should_be);

	if(rc)
		return rc;

	
/*	cifs_dump_mem("what we think it should be: ",what_we_think_sig_should_be,16); */

	if(memcmp(server_response_sig, what_we_think_sig_should_be, 8))
		return -EACCES;
	else
		return 0;

}

/* We fill in key by putting in 40 byte array which was allocated by caller */
int cifs_calculate_mac_key(char * key, const char * rn, const char * password)
{
	char temp_key[16];
	if ((key == NULL) || (rn == NULL))
		return -EINVAL;

	E_md4hash(password, temp_key);
	mdfour(key,temp_key,16);
211
	memcpy(key+16,rn, CIFS_SESS_KEY_SIZE);
L
Linus Torvalds 已提交
212 213 214
	return 0;
}

S
Steve French 已提交
215 216
int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, 
				const struct nls_table * nls_info)
L
Linus Torvalds 已提交
217 218 219 220
{
	char temp_hash[16];
	struct HMACMD5Context ctx;
	char * ucase_buf;
221
	__le16 * unicode_buf;
L
Linus Torvalds 已提交
222 223 224 225 226 227 228 229 230 231 232
	unsigned int i,user_name_len,dom_name_len;

	if(ses == NULL)
		return -EINVAL;

	E_md4hash(ses->password, temp_hash);

	hmac_md5_init_limK_to_64(temp_hash, 16, &ctx);
	user_name_len = strlen(ses->userName);
	if(user_name_len > MAX_USERNAME_SIZE)
		return -EINVAL;
233 234
	if(ses->domainName == NULL)
		return -EINVAL; /* BB should we use CIFS_LINUX_DOM */
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
	dom_name_len = strlen(ses->domainName);
	if(dom_name_len > MAX_USERNAME_SIZE)
		return -EINVAL;
  
	ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL);
	if(ucase_buf == NULL)
		return -ENOMEM;
	unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL);
	if(unicode_buf == NULL) {
		kfree(ucase_buf);
		return -ENOMEM;
	}
   
	for(i=0;i<user_name_len;i++)
		ucase_buf[i] = nls_info->charset2upper[(int)ses->userName[i]];
	ucase_buf[i] = 0;
	user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf, MAX_USERNAME_SIZE*2, nls_info);
	unicode_buf[user_name_len] = 0;
	user_name_len++;

	for(i=0;i<dom_name_len;i++)
		ucase_buf[i] = nls_info->charset2upper[(int)ses->domainName[i]];
	ucase_buf[i] = 0;
	dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf, MAX_USERNAME_SIZE*2, nls_info);

	unicode_buf[user_name_len + dom_name_len] = 0;
	hmac_md5_update((const unsigned char *) unicode_buf,
		(user_name_len+dom_name_len)*2,&ctx);

264
	hmac_md5_final(ses->server->mac_signing_key,&ctx);
L
Linus Torvalds 已提交
265 266 267 268
	kfree(ucase_buf);
	kfree(unicode_buf);
	return 0;
}
269 270 271 272 273 274 275

#ifdef CONFIG_CIFS_WEAK_PW_HASH
void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key)
{
	int i;
	char password_with_pad[CIFS_ENCPWD_SIZE];

276 277 278
	if(ses->server == NULL)
		return;

279
	memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
280 281
	if(ses->password)
		strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
282

283 284 285 286 287 288
	if((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0)
		if(extended_security & CIFSSEC_MAY_PLNTXT) {
			memcpy(lnm_session_key, password_with_pad, CIFS_ENCPWD_SIZE); 
			return;
		}

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	/* calculate old style session key */
	/* calling toupper is less broken than repeatedly
	calling nls_toupper would be since that will never
	work for UTF8, but neither handles multibyte code pages
	but the only alternative would be converting to UCS-16 (Unicode)
	(using a routine something like UniStrupr) then
	uppercasing and then converting back from Unicode - which
	would only worth doing it if we knew it were utf8. Basically
	utf8 and other multibyte codepages each need their own strupper
	function since a byte at a time will ont work. */

	for(i = 0; i < CIFS_ENCPWD_SIZE; i++) {
		password_with_pad[i] = toupper(password_with_pad[i]);
	}

	SMBencrypt(password_with_pad, ses->server->cryptKey, lnm_session_key);
	/* clear password before we return/free memory */
	memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
}
#endif /* CIFS_WEAK_PW_HASH */

S
Steve French 已提交
310 311
static int calc_ntlmv2_hash(struct cifsSesInfo *ses, 
			    const struct nls_table * nls_cp)
S
Steve French 已提交
312 313 314 315 316
{
	int rc = 0;
	int len;
	char nt_hash[16];
	struct HMACMD5Context * pctxt;
S
Steve French 已提交
317 318
	wchar_t * user;
	wchar_t * domain;
S
Steve French 已提交
319 320 321 322 323 324 325 326 327

	pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL);

	if(pctxt == NULL)
		return -ENOMEM;

	/* calculate md4 hash of password */
	E_md4hash(ses->password, nt_hash);

S
Steve French 已提交
328
	/* convert Domainname to unicode and uppercase */
S
Steve French 已提交
329 330 331
	hmac_md5_init_limK_to_64(nt_hash, 16, pctxt);

	/* convert ses->userName to unicode and uppercase */
S
Steve French 已提交
332 333 334 335 336 337 338
	len = strlen(ses->userName);
	user = kmalloc(2 + (len * 2), GFP_KERNEL);
	if(user == NULL)
		goto calc_exit_2;
	len = cifs_strtoUCS(user, ses->userName, len, nls_cp);
	UniStrupr(user);
	hmac_md5_update((char *)user, 2*len, pctxt);
S
Steve French 已提交
339 340

	/* convert ses->domainName to unicode and uppercase */
S
Steve French 已提交
341 342
	if(ses->domainName) {
		len = strlen(ses->domainName);
S
Steve French 已提交
343

S
Steve French 已提交
344 345 346 347 348
        	domain = kmalloc(2 + (len * 2), GFP_KERNEL);
		if(domain == NULL)
			goto calc_exit_1;
		len = cifs_strtoUCS(domain, ses->domainName, len, nls_cp);
		UniStrupr(domain);
S
Steve French 已提交
349

S
Steve French 已提交
350 351 352 353 354 355 356 357 358 359
		hmac_md5_update((char *)domain, 2*len, pctxt);
	
		kfree(domain);
	}
calc_exit_1:
	kfree(user);
calc_exit_2:
	/* BB FIXME what about bytes 24 through 40 of the signing key? 
	   compare with the NTLM example */
	hmac_md5_final(ses->server->mac_signing_key, pctxt);
S
Steve French 已提交
360 361 362 363

	return rc;
}

S
Steve French 已提交
364 365
void setup_ntlmv2_rsp(struct cifsSesInfo * ses, char * resp_buf, 
		      const struct nls_table * nls_cp)
S
Steve French 已提交
366
{
S
Steve French 已提交
367
	int rc;
S
Steve French 已提交
368 369 370 371 372
	struct ntlmv2_resp * buf = (struct ntlmv2_resp *)resp_buf;

	buf->blob_signature = cpu_to_le32(0x00000101);
	buf->reserved = 0;
	buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
S
Steve French 已提交
373
	get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
S
Steve French 已提交
374
	buf->reserved2 = 0;
375
	buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE);
S
Steve French 已提交
376
	buf->names[0].length = 0;
377 378
	buf->names[1].type = 0;
	buf->names[1].length = 0;
S
Steve French 已提交
379

S
Steve French 已提交
380
	/* calculate buf->ntlmv2_hash */
S
Steve French 已提交
381
	rc = calc_ntlmv2_hash(ses, nls_cp);
S
Steve French 已提交
382 383
	if(rc)
		cERROR(1,("could not get v2 hash rc %d",rc));
S
Steve French 已提交
384
	CalcNTLMv2_response(ses, resp_buf);
S
Steve French 已提交
385 386
}

S
Steve French 已提交
387
void CalcNTLMv2_response(const struct cifsSesInfo * ses, char * v2_session_response)
L
Linus Torvalds 已提交
388 389
{
	struct HMACMD5Context context;
S
Steve French 已提交
390
	/* rest of v2 struct already generated */
L
Linus Torvalds 已提交
391
	memcpy(v2_session_response + 8, ses->server->cryptKey,8);
392
	hmac_md5_init_limK_to_64(ses->server->mac_signing_key, 16, &context);
L
Linus Torvalds 已提交
393

S
Steve French 已提交
394 395
	hmac_md5_update(v2_session_response+8, 
			sizeof(struct ntlmv2_resp) - 8, &context);
L
Linus Torvalds 已提交
396 397

	hmac_md5_final(v2_session_response,&context);
S
Steve French 已提交
398
/*	cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */
L
Linus Torvalds 已提交
399
}