cifsencrypt.c 12.4 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
 *   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"
S
Steve French 已提交
24
#include "cifsglob.h"
L
Linus Torvalds 已提交
25 26 27 28
#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

S
Steve French 已提交
32
/* Calculate and return the CIFS signature based on the mac key and SMB PDU */
L
Linus Torvalds 已提交
33 34
/* the 16 byte signature must be allocated by the caller  */
/* Note we only use the 1st eight bytes */
S
Steve French 已提交
35
/* Note that the smb header signature field on input contains the
L
Linus Torvalds 已提交
36 37 38 39
	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
extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
S
Steve French 已提交
41
		       unsigned char *p24);
L
Linus Torvalds 已提交
42
	
S
Steve French 已提交
43
static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
44
				    const struct mac_key *key, char *signature)
L
Linus Torvalds 已提交
45 46 47
{
	struct	MD5Context context;

48
	if ((cifs_pdu == NULL) || (signature == NULL) || (key == NULL))
L
Linus Torvalds 已提交
49 50 51
		return -EINVAL;

	MD5Init(&context);
52
	MD5Update(&context, (char *)&key->data, key->len);
S
Steve French 已提交
53
	MD5Update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length);
54

S
Steve French 已提交
55
	MD5Final(signature, &context);
L
Linus Torvalds 已提交
56 57 58
	return 0;
}

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

S
Steve French 已提交
65
	if ((cifs_pdu == NULL) || (server == NULL))
L
Linus Torvalds 已提交
66 67
		return -EINVAL;

S
Steve French 已提交
68
	if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
L
Linus Torvalds 已提交
69 70 71
		return rc;

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

80
	rc = cifs_calculate_signature(cifs_pdu, &server->mac_signing_key,
S
Steve French 已提交
81 82
				      smb_signature);
	if (rc)
L
Linus Torvalds 已提交
83 84 85 86 87 88 89
		memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
	else
		memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);

	return rc;
}

S
Steve French 已提交
90
static int cifs_calc_signature2(const struct kvec *iov, int n_vec,
91
				const struct mac_key *key, char *signature)
92
{
93 94
	struct  MD5Context context;
	int i;
95

96
	if ((iov == NULL) || (signature == NULL) || (key == NULL))
97
		return -EINVAL;
98

99
	MD5Init(&context);
100
	MD5Update(&context, (char *)&key->data, key->len);
S
Steve French 已提交
101 102 103
	for (i=0;i<n_vec;i++) {
		if (iov[i].iov_base == NULL) {
			cERROR(1 ,("null iovec entry"));
104
			return -EIO;
S
Steve French 已提交
105
		} else if (iov[i].iov_len == 0)
106
			break; /* bail out if we are sent nothing to sign */
S
Steve French 已提交
107
		/* The first entry includes a length field (which does not get
108
		   signed that occupies the first 4 bytes before the header */
S
Steve French 已提交
109
		if (i == 0) {
110 111 112 113
			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
S
Steve French 已提交
114
			MD5Update(&context, iov[i].iov_base, iov[i].iov_len);
115
	}
116

S
Steve French 已提交
117
	MD5Final(signature, &context);
118

119
	return 0;
120 121 122
}


S
Steve French 已提交
123
int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
124 125 126 127
		   __u32 * pexpected_response_sequence_number)
{
	int rc = 0;
	char smb_signature[20];
S
Steve French 已提交
128
	struct smb_hdr *cifs_pdu = iov[0].iov_base;
129

S
Steve French 已提交
130
	if ((cifs_pdu == NULL) || (server == NULL))
131 132
		return -EINVAL;

S
Steve French 已提交
133
	if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0)
134 135
		return rc;

S
Steve French 已提交
136 137
	spin_lock(&GlobalMid_Lock);
	cifs_pdu->Signature.Sequence.SequenceNumber =
138
				cpu_to_le32(server->sequence_number);
S
Steve French 已提交
139
	cifs_pdu->Signature.Sequence.Reserved = 0;
140

S
Steve French 已提交
141 142 143
	*pexpected_response_sequence_number = server->sequence_number++;
	server->sequence_number++;
	spin_unlock(&GlobalMid_Lock);
144

145
	rc = cifs_calc_signature2(iov, n_vec, &server->mac_signing_key,
146
				      smb_signature);
S
Steve French 已提交
147 148 149 150
	if (rc)
		memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
	else
		memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
151

S
Steve French 已提交
152
	return rc;
153 154
}

155 156
int cifs_verify_signature(struct smb_hdr *cifs_pdu,
			  const struct mac_key *mac_key,
S
Steve French 已提交
157
			  __u32 expected_sequence_number)
L
Linus Torvalds 已提交
158 159 160 161 162
{
	unsigned int rc;
	char server_response_sig[8];
	char what_we_think_sig_should_be[20];

S
Steve French 已提交
163
	if ((cifs_pdu == NULL) || (mac_key == NULL))
L
Linus Torvalds 已提交
164 165 166 167 168 169
		return -EINVAL;

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

	if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) {
S
Steve French 已提交
170 171 172
		struct smb_com_lock_req * pSMB =
			(struct smb_com_lock_req *)cifs_pdu;
	    if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)
L
Linus Torvalds 已提交
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
			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 */
207 208
int cifs_calculate_mac_key(struct mac_key *key, const char *rn,
			   const char *password)
L
Linus Torvalds 已提交
209 210 211 212 213 214
{
	char temp_key[16];
	if ((key == NULL) || (rn == NULL))
		return -EINVAL;

	E_md4hash(password, temp_key);
215 216 217
	mdfour(key->data.ntlm, temp_key, 16);
	memcpy(key->data.ntlm+16, rn, CIFS_SESS_KEY_SIZE);
	key->len = 40;
L
Linus Torvalds 已提交
218 219 220
	return 0;
}

S
Steve French 已提交
221 222
int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, 
				const struct nls_table * nls_info)
L
Linus Torvalds 已提交
223 224 225 226
{
	char temp_hash[16];
	struct HMACMD5Context ctx;
	char * ucase_buf;
227
	__le16 * unicode_buf;
L
Linus Torvalds 已提交
228 229 230 231 232 233 234 235 236 237 238
	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;
239 240
	if(ses->domainName == NULL)
		return -EINVAL; /* BB should we use CIFS_LINUX_DOM */
L
Linus Torvalds 已提交
241
	dom_name_len = strlen(ses->domainName);
242
	if (dom_name_len > MAX_USERNAME_SIZE)
L
Linus Torvalds 已提交
243 244 245
		return -EINVAL;
  
	ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL);
246
	if (ucase_buf == NULL)
L
Linus Torvalds 已提交
247 248
		return -ENOMEM;
	unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL);
249
	if (unicode_buf == NULL) {
L
Linus Torvalds 已提交
250 251 252 253
		kfree(ucase_buf);
		return -ENOMEM;
	}
   
254
	for (i = 0;i < user_name_len; i++)
L
Linus Torvalds 已提交
255 256 257 258 259 260
		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++;

261
	for (i = 0; i < dom_name_len; i++)
L
Linus Torvalds 已提交
262 263 264 265 266 267
		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,
268
		(user_name_len+dom_name_len)*2, &ctx);
L
Linus Torvalds 已提交
269

270
	hmac_md5_final(ses->server->ntlmv2_hash, &ctx);
L
Linus Torvalds 已提交
271 272 273 274
	kfree(ucase_buf);
	kfree(unicode_buf);
	return 0;
}
275 276 277 278 279 280 281

#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];

282 283 284
	if(ses->server == NULL)
		return;

285
	memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
286 287
	if(ses->password)
		strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
288

289 290 291 292 293 294
	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;
		}

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
	/* 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 已提交
316 317
static int calc_ntlmv2_hash(struct cifsSesInfo *ses, 
			    const struct nls_table * nls_cp)
S
Steve French 已提交
318 319 320 321 322
{
	int rc = 0;
	int len;
	char nt_hash[16];
	struct HMACMD5Context * pctxt;
S
Steve French 已提交
323 324
	wchar_t * user;
	wchar_t * domain;
S
Steve French 已提交
325 326 327 328 329 330 331 332 333

	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 已提交
334
	/* convert Domainname to unicode and uppercase */
S
Steve French 已提交
335 336 337
	hmac_md5_init_limK_to_64(nt_hash, 16, pctxt);

	/* convert ses->userName to unicode and uppercase */
S
Steve French 已提交
338 339 340 341 342 343 344
	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 已提交
345 346

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

S
Steve French 已提交
350 351 352 353
        	domain = kmalloc(2 + (len * 2), GFP_KERNEL);
		if(domain == NULL)
			goto calc_exit_1;
		len = cifs_strtoUCS(domain, ses->domainName, len, nls_cp);
354 355 356 357
		/* the following line was removed since it didn't work well
		   with lower cased domain name that passed as an option.
		   Maybe converting the domain name earlier makes sense */
		/* UniStrupr(domain); */
S
Steve French 已提交
358

S
Steve French 已提交
359 360 361 362 363 364 365 366 367
		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 */
368
	hmac_md5_final(ses->server->ntlmv2_hash, pctxt);
S
Steve French 已提交
369 370 371 372

	return rc;
}

S
Steve French 已提交
373 374
void setup_ntlmv2_rsp(struct cifsSesInfo * ses, char * resp_buf, 
		      const struct nls_table * nls_cp)
S
Steve French 已提交
375
{
S
Steve French 已提交
376
	int rc;
S
Steve French 已提交
377
	struct ntlmv2_resp * buf = (struct ntlmv2_resp *)resp_buf;
378
	struct HMACMD5Context context;
S
Steve French 已提交
379 380 381 382

	buf->blob_signature = cpu_to_le32(0x00000101);
	buf->reserved = 0;
	buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
S
Steve French 已提交
383
	get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
S
Steve French 已提交
384
	buf->reserved2 = 0;
385
	buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE);
S
Steve French 已提交
386
	buf->names[0].length = 0;
387 388
	buf->names[1].type = 0;
	buf->names[1].length = 0;
S
Steve French 已提交
389

S
Steve French 已提交
390
	/* calculate buf->ntlmv2_hash */
S
Steve French 已提交
391
	rc = calc_ntlmv2_hash(ses, nls_cp);
S
Steve French 已提交
392 393
	if(rc)
		cERROR(1,("could not get v2 hash rc %d",rc));
S
Steve French 已提交
394
	CalcNTLMv2_response(ses, resp_buf);
395 396 397 398 399 400 401 402 403

	/* now calculate the MAC key for NTLMv2 */
	hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
	hmac_md5_update(resp_buf, 16, &context);
	hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context);

	memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf,
	       sizeof(struct ntlmv2_resp));
	ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp);
S
Steve French 已提交
404 405
}

S
Steve French 已提交
406
void CalcNTLMv2_response(const struct cifsSesInfo * ses, char * v2_session_response)
L
Linus Torvalds 已提交
407 408
{
	struct HMACMD5Context context;
S
Steve French 已提交
409
	/* rest of v2 struct already generated */
L
Linus Torvalds 已提交
410
	memcpy(v2_session_response + 8, ses->server->cryptKey,8);
411
	hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
L
Linus Torvalds 已提交
412

413
	hmac_md5_update(v2_session_response+8,
S
Steve French 已提交
414
			sizeof(struct ntlmv2_resp) - 8, &context);
L
Linus Torvalds 已提交
415 416

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