diff options
Diffstat (limited to 'fs/cifs/cifsencrypt.c')
-rw-r--r-- | fs/cifs/cifsencrypt.c | 555 |
1 files changed, 469 insertions, 86 deletions
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 35042d8f7338..f856732161ab 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "md5.h" | 27 | #include "md5.h" |
28 | #include "cifs_unicode.h" | 28 | #include "cifs_unicode.h" |
29 | #include "cifsproto.h" | 29 | #include "cifsproto.h" |
30 | #include "ntlmssp.h" | ||
30 | #include <linux/ctype.h> | 31 | #include <linux/ctype.h> |
31 | #include <linux/random.h> | 32 | #include <linux/random.h> |
32 | 33 | ||
@@ -42,18 +43,32 @@ extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8, | |||
42 | unsigned char *p24); | 43 | unsigned char *p24); |
43 | 44 | ||
44 | static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, | 45 | static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, |
45 | const struct mac_key *key, char *signature) | 46 | struct TCP_Server_Info *server, char *signature) |
46 | { | 47 | { |
47 | struct MD5Context context; | 48 | int rc; |
48 | 49 | ||
49 | if ((cifs_pdu == NULL) || (signature == NULL) || (key == NULL)) | 50 | if (cifs_pdu == NULL || signature == NULL || server == NULL) |
50 | return -EINVAL; | 51 | return -EINVAL; |
51 | 52 | ||
52 | cifs_MD5_init(&context); | 53 | if (!server->secmech.sdescmd5) { |
53 | cifs_MD5_update(&context, (char *)&key->data, key->len); | 54 | cERROR(1, "%s: Can't generate signature\n", __func__); |
54 | cifs_MD5_update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length); | 55 | return -1; |
56 | } | ||
57 | |||
58 | rc = crypto_shash_init(&server->secmech.sdescmd5->shash); | ||
59 | if (rc) { | ||
60 | cERROR(1, "%s: Oould not init md5\n", __func__); | ||
61 | return rc; | ||
62 | } | ||
63 | |||
64 | crypto_shash_update(&server->secmech.sdescmd5->shash, | ||
65 | server->session_key.response, server->session_key.len); | ||
66 | |||
67 | crypto_shash_update(&server->secmech.sdescmd5->shash, | ||
68 | cifs_pdu->Protocol, cifs_pdu->smb_buf_length); | ||
69 | |||
70 | rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); | ||
55 | 71 | ||
56 | cifs_MD5_final(signature, &context); | ||
57 | return 0; | 72 | return 0; |
58 | } | 73 | } |
59 | 74 | ||
@@ -78,8 +93,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, | |||
78 | server->sequence_number++; | 93 | server->sequence_number++; |
79 | spin_unlock(&GlobalMid_Lock); | 94 | spin_unlock(&GlobalMid_Lock); |
80 | 95 | ||
81 | rc = cifs_calculate_signature(cifs_pdu, &server->mac_signing_key, | 96 | rc = cifs_calculate_signature(cifs_pdu, server, smb_signature); |
82 | smb_signature); | ||
83 | if (rc) | 97 | if (rc) |
84 | memset(cifs_pdu->Signature.SecuritySignature, 0, 8); | 98 | memset(cifs_pdu->Signature.SecuritySignature, 0, 8); |
85 | else | 99 | else |
@@ -89,16 +103,28 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, | |||
89 | } | 103 | } |
90 | 104 | ||
91 | static int cifs_calc_signature2(const struct kvec *iov, int n_vec, | 105 | static int cifs_calc_signature2(const struct kvec *iov, int n_vec, |
92 | const struct mac_key *key, char *signature) | 106 | struct TCP_Server_Info *server, char *signature) |
93 | { | 107 | { |
94 | struct MD5Context context; | ||
95 | int i; | 108 | int i; |
109 | int rc; | ||
96 | 110 | ||
97 | if ((iov == NULL) || (signature == NULL) || (key == NULL)) | 111 | if (iov == NULL || signature == NULL || server == NULL) |
98 | return -EINVAL; | 112 | return -EINVAL; |
99 | 113 | ||
100 | cifs_MD5_init(&context); | 114 | if (!server->secmech.sdescmd5) { |
101 | cifs_MD5_update(&context, (char *)&key->data, key->len); | 115 | cERROR(1, "%s: Can't generate signature\n", __func__); |
116 | return -1; | ||
117 | } | ||
118 | |||
119 | rc = crypto_shash_init(&server->secmech.sdescmd5->shash); | ||
120 | if (rc) { | ||
121 | cERROR(1, "%s: Oould not init md5\n", __func__); | ||
122 | return rc; | ||
123 | } | ||
124 | |||
125 | crypto_shash_update(&server->secmech.sdescmd5->shash, | ||
126 | server->session_key.response, server->session_key.len); | ||
127 | |||
102 | for (i = 0; i < n_vec; i++) { | 128 | for (i = 0; i < n_vec; i++) { |
103 | if (iov[i].iov_len == 0) | 129 | if (iov[i].iov_len == 0) |
104 | continue; | 130 | continue; |
@@ -111,18 +137,18 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, | |||
111 | if (i == 0) { | 137 | if (i == 0) { |
112 | if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ | 138 | if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ |
113 | break; /* nothing to sign or corrupt header */ | 139 | break; /* nothing to sign or corrupt header */ |
114 | cifs_MD5_update(&context, iov[0].iov_base+4, | 140 | crypto_shash_update(&server->secmech.sdescmd5->shash, |
115 | iov[0].iov_len-4); | 141 | iov[i].iov_base + 4, iov[i].iov_len - 4); |
116 | } else | 142 | } else |
117 | cifs_MD5_update(&context, iov[i].iov_base, iov[i].iov_len); | 143 | crypto_shash_update(&server->secmech.sdescmd5->shash, |
144 | iov[i].iov_base, iov[i].iov_len); | ||
118 | } | 145 | } |
119 | 146 | ||
120 | cifs_MD5_final(signature, &context); | 147 | rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); |
121 | 148 | ||
122 | return 0; | 149 | return rc; |
123 | } | 150 | } |
124 | 151 | ||
125 | |||
126 | int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, | 152 | int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, |
127 | __u32 *pexpected_response_sequence_number) | 153 | __u32 *pexpected_response_sequence_number) |
128 | { | 154 | { |
@@ -145,8 +171,7 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, | |||
145 | server->sequence_number++; | 171 | server->sequence_number++; |
146 | spin_unlock(&GlobalMid_Lock); | 172 | spin_unlock(&GlobalMid_Lock); |
147 | 173 | ||
148 | rc = cifs_calc_signature2(iov, n_vec, &server->mac_signing_key, | 174 | rc = cifs_calc_signature2(iov, n_vec, server, smb_signature); |
149 | smb_signature); | ||
150 | if (rc) | 175 | if (rc) |
151 | memset(cifs_pdu->Signature.SecuritySignature, 0, 8); | 176 | memset(cifs_pdu->Signature.SecuritySignature, 0, 8); |
152 | else | 177 | else |
@@ -156,14 +181,14 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, | |||
156 | } | 181 | } |
157 | 182 | ||
158 | int cifs_verify_signature(struct smb_hdr *cifs_pdu, | 183 | int cifs_verify_signature(struct smb_hdr *cifs_pdu, |
159 | const struct mac_key *mac_key, | 184 | struct TCP_Server_Info *server, |
160 | __u32 expected_sequence_number) | 185 | __u32 expected_sequence_number) |
161 | { | 186 | { |
162 | unsigned int rc; | 187 | unsigned int rc; |
163 | char server_response_sig[8]; | 188 | char server_response_sig[8]; |
164 | char what_we_think_sig_should_be[20]; | 189 | char what_we_think_sig_should_be[20]; |
165 | 190 | ||
166 | if ((cifs_pdu == NULL) || (mac_key == NULL)) | 191 | if (cifs_pdu == NULL || server == NULL) |
167 | return -EINVAL; | 192 | return -EINVAL; |
168 | 193 | ||
169 | if (cifs_pdu->Command == SMB_COM_NEGOTIATE) | 194 | if (cifs_pdu->Command == SMB_COM_NEGOTIATE) |
@@ -192,7 +217,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, | |||
192 | cpu_to_le32(expected_sequence_number); | 217 | cpu_to_le32(expected_sequence_number); |
193 | cifs_pdu->Signature.Sequence.Reserved = 0; | 218 | cifs_pdu->Signature.Sequence.Reserved = 0; |
194 | 219 | ||
195 | rc = cifs_calculate_signature(cifs_pdu, mac_key, | 220 | rc = cifs_calculate_signature(cifs_pdu, server, |
196 | what_we_think_sig_should_be); | 221 | what_we_think_sig_should_be); |
197 | 222 | ||
198 | if (rc) | 223 | if (rc) |
@@ -208,18 +233,28 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, | |||
208 | 233 | ||
209 | } | 234 | } |
210 | 235 | ||
211 | /* We fill in key by putting in 40 byte array which was allocated by caller */ | 236 | /* first calculate 24 bytes ntlm response and then 16 byte session key */ |
212 | int cifs_calculate_mac_key(struct mac_key *key, const char *rn, | 237 | int setup_ntlm_response(struct cifsSesInfo *ses) |
213 | const char *password) | ||
214 | { | 238 | { |
215 | char temp_key[16]; | 239 | unsigned int temp_len = CIFS_SESS_KEY_SIZE + CIFS_AUTH_RESP_SIZE; |
216 | if ((key == NULL) || (rn == NULL)) | 240 | char temp_key[CIFS_SESS_KEY_SIZE]; |
241 | |||
242 | if (!ses) | ||
217 | return -EINVAL; | 243 | return -EINVAL; |
218 | 244 | ||
219 | E_md4hash(password, temp_key); | 245 | ses->auth_key.response = kmalloc(temp_len, GFP_KERNEL); |
220 | mdfour(key->data.ntlm, temp_key, 16); | 246 | if (!ses->auth_key.response) { |
221 | memcpy(key->data.ntlm+16, rn, CIFS_SESS_KEY_SIZE); | 247 | cERROR(1, "NTLM can't allocate (%u bytes) memory", temp_len); |
222 | key->len = 40; | 248 | return -ENOMEM; |
249 | } | ||
250 | ses->auth_key.len = temp_len; | ||
251 | |||
252 | SMBNTencrypt(ses->password, ses->server->cryptkey, | ||
253 | ses->auth_key.response + CIFS_SESS_KEY_SIZE); | ||
254 | |||
255 | E_md4hash(ses->password, temp_key); | ||
256 | mdfour(ses->auth_key.response, temp_key, CIFS_SESS_KEY_SIZE); | ||
257 | |||
223 | return 0; | 258 | return 0; |
224 | } | 259 | } |
225 | 260 | ||
@@ -262,109 +297,457 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, | |||
262 | } | 297 | } |
263 | #endif /* CIFS_WEAK_PW_HASH */ | 298 | #endif /* CIFS_WEAK_PW_HASH */ |
264 | 299 | ||
265 | static int calc_ntlmv2_hash(struct cifsSesInfo *ses, | 300 | /* Build a proper attribute value/target info pairs blob. |
301 | * Fill in netbios and dns domain name and workstation name | ||
302 | * and client time (total five av pairs and + one end of fields indicator. | ||
303 | * Allocate domain name which gets freed when session struct is deallocated. | ||
304 | */ | ||
305 | static int | ||
306 | build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp) | ||
307 | { | ||
308 | unsigned int dlen; | ||
309 | unsigned int wlen; | ||
310 | unsigned int size = 6 * sizeof(struct ntlmssp2_name); | ||
311 | __le64 curtime; | ||
312 | char *defdmname = "WORKGROUP"; | ||
313 | unsigned char *blobptr; | ||
314 | struct ntlmssp2_name *attrptr; | ||
315 | |||
316 | if (!ses->domainName) { | ||
317 | ses->domainName = kstrdup(defdmname, GFP_KERNEL); | ||
318 | if (!ses->domainName) | ||
319 | return -ENOMEM; | ||
320 | } | ||
321 | |||
322 | dlen = strlen(ses->domainName); | ||
323 | wlen = strlen(ses->server->hostname); | ||
324 | |||
325 | /* The length of this blob is a size which is | ||
326 | * six times the size of a structure which holds name/size + | ||
327 | * two times the unicode length of a domain name + | ||
328 | * two times the unicode length of a server name + | ||
329 | * size of a timestamp (which is 8 bytes). | ||
330 | */ | ||
331 | ses->auth_key.len = size + 2 * (2 * dlen) + 2 * (2 * wlen) + 8; | ||
332 | ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); | ||
333 | if (!ses->auth_key.response) { | ||
334 | ses->auth_key.len = 0; | ||
335 | cERROR(1, "Challenge target info allocation failure"); | ||
336 | return -ENOMEM; | ||
337 | } | ||
338 | |||
339 | blobptr = ses->auth_key.response; | ||
340 | attrptr = (struct ntlmssp2_name *) blobptr; | ||
341 | |||
342 | attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); | ||
343 | attrptr->length = cpu_to_le16(2 * dlen); | ||
344 | blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); | ||
345 | cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); | ||
346 | |||
347 | blobptr += 2 * dlen; | ||
348 | attrptr = (struct ntlmssp2_name *) blobptr; | ||
349 | |||
350 | attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_COMPUTER_NAME); | ||
351 | attrptr->length = cpu_to_le16(2 * wlen); | ||
352 | blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); | ||
353 | cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); | ||
354 | |||
355 | blobptr += 2 * wlen; | ||
356 | attrptr = (struct ntlmssp2_name *) blobptr; | ||
357 | |||
358 | attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_DOMAIN_NAME); | ||
359 | attrptr->length = cpu_to_le16(2 * dlen); | ||
360 | blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); | ||
361 | cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); | ||
362 | |||
363 | blobptr += 2 * dlen; | ||
364 | attrptr = (struct ntlmssp2_name *) blobptr; | ||
365 | |||
366 | attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_COMPUTER_NAME); | ||
367 | attrptr->length = cpu_to_le16(2 * wlen); | ||
368 | blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); | ||
369 | cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); | ||
370 | |||
371 | blobptr += 2 * wlen; | ||
372 | attrptr = (struct ntlmssp2_name *) blobptr; | ||
373 | |||
374 | attrptr->type = cpu_to_le16(NTLMSSP_AV_TIMESTAMP); | ||
375 | attrptr->length = cpu_to_le16(sizeof(__le64)); | ||
376 | blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); | ||
377 | curtime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
378 | memcpy(blobptr, &curtime, sizeof(__le64)); | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | /* Server has provided av pairs/target info in the type 2 challenge | ||
384 | * packet and we have plucked it and stored within smb session. | ||
385 | * We parse that blob here to find netbios domain name to be used | ||
386 | * as part of ntlmv2 authentication (in Target String), if not already | ||
387 | * specified on the command line. | ||
388 | * If this function returns without any error but without fetching | ||
389 | * domain name, authentication may fail against some server but | ||
390 | * may not fail against other (those who are not very particular | ||
391 | * about target string i.e. for some, just user name might suffice. | ||
392 | */ | ||
393 | static int | ||
394 | find_domain_name(struct cifsSesInfo *ses, const struct nls_table *nls_cp) | ||
395 | { | ||
396 | unsigned int attrsize; | ||
397 | unsigned int type; | ||
398 | unsigned int onesize = sizeof(struct ntlmssp2_name); | ||
399 | unsigned char *blobptr; | ||
400 | unsigned char *blobend; | ||
401 | struct ntlmssp2_name *attrptr; | ||
402 | |||
403 | if (!ses->auth_key.len || !ses->auth_key.response) | ||
404 | return 0; | ||
405 | |||
406 | blobptr = ses->auth_key.response; | ||
407 | blobend = blobptr + ses->auth_key.len; | ||
408 | |||
409 | while (blobptr + onesize < blobend) { | ||
410 | attrptr = (struct ntlmssp2_name *) blobptr; | ||
411 | type = le16_to_cpu(attrptr->type); | ||
412 | if (type == NTLMSSP_AV_EOL) | ||
413 | break; | ||
414 | blobptr += 2; /* advance attr type */ | ||
415 | attrsize = le16_to_cpu(attrptr->length); | ||
416 | blobptr += 2; /* advance attr size */ | ||
417 | if (blobptr + attrsize > blobend) | ||
418 | break; | ||
419 | if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { | ||
420 | if (!attrsize) | ||
421 | break; | ||
422 | if (!ses->domainName) { | ||
423 | ses->domainName = | ||
424 | kmalloc(attrsize + 1, GFP_KERNEL); | ||
425 | if (!ses->domainName) | ||
426 | return -ENOMEM; | ||
427 | cifs_from_ucs2(ses->domainName, | ||
428 | (__le16 *)blobptr, attrsize, attrsize, | ||
429 | nls_cp, false); | ||
430 | break; | ||
431 | } | ||
432 | } | ||
433 | blobptr += attrsize; /* advance attr value */ | ||
434 | } | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | static int calc_ntlmv2_hash(struct cifsSesInfo *ses, char *ntlmv2_hash, | ||
266 | const struct nls_table *nls_cp) | 440 | const struct nls_table *nls_cp) |
267 | { | 441 | { |
268 | int rc = 0; | 442 | int rc = 0; |
269 | int len; | 443 | int len; |
270 | char nt_hash[16]; | 444 | char nt_hash[CIFS_NTHASH_SIZE]; |
271 | struct HMACMD5Context *pctxt; | ||
272 | wchar_t *user; | 445 | wchar_t *user; |
273 | wchar_t *domain; | 446 | wchar_t *domain; |
447 | wchar_t *server; | ||
274 | 448 | ||
275 | pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL); | 449 | if (!ses->server->secmech.sdeschmacmd5) { |
276 | 450 | cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); | |
277 | if (pctxt == NULL) | 451 | return -1; |
278 | return -ENOMEM; | 452 | } |
279 | 453 | ||
280 | /* calculate md4 hash of password */ | 454 | /* calculate md4 hash of password */ |
281 | E_md4hash(ses->password, nt_hash); | 455 | E_md4hash(ses->password, nt_hash); |
282 | 456 | ||
283 | /* convert Domainname to unicode and uppercase */ | 457 | crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash, |
284 | hmac_md5_init_limK_to_64(nt_hash, 16, pctxt); | 458 | CIFS_NTHASH_SIZE); |
459 | |||
460 | rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); | ||
461 | if (rc) { | ||
462 | cERROR(1, "calc_ntlmv2_hash: could not init hmacmd5\n"); | ||
463 | return rc; | ||
464 | } | ||
285 | 465 | ||
286 | /* convert ses->userName to unicode and uppercase */ | 466 | /* convert ses->userName to unicode and uppercase */ |
287 | len = strlen(ses->userName); | 467 | len = strlen(ses->userName); |
288 | user = kmalloc(2 + (len * 2), GFP_KERNEL); | 468 | user = kmalloc(2 + (len * 2), GFP_KERNEL); |
289 | if (user == NULL) | 469 | if (user == NULL) { |
470 | cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n"); | ||
471 | rc = -ENOMEM; | ||
290 | goto calc_exit_2; | 472 | goto calc_exit_2; |
473 | } | ||
291 | len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); | 474 | len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); |
292 | UniStrupr(user); | 475 | UniStrupr(user); |
293 | hmac_md5_update((char *)user, 2*len, pctxt); | 476 | |
477 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
478 | (char *)user, 2 * len); | ||
294 | 479 | ||
295 | /* convert ses->domainName to unicode and uppercase */ | 480 | /* convert ses->domainName to unicode and uppercase */ |
296 | if (ses->domainName) { | 481 | if (ses->domainName) { |
297 | len = strlen(ses->domainName); | 482 | len = strlen(ses->domainName); |
298 | 483 | ||
299 | domain = kmalloc(2 + (len * 2), GFP_KERNEL); | 484 | domain = kmalloc(2 + (len * 2), GFP_KERNEL); |
300 | if (domain == NULL) | 485 | if (domain == NULL) { |
486 | cERROR(1, "calc_ntlmv2_hash: domain mem alloc failure"); | ||
487 | rc = -ENOMEM; | ||
301 | goto calc_exit_1; | 488 | goto calc_exit_1; |
489 | } | ||
302 | len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, | 490 | len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, |
303 | nls_cp); | 491 | nls_cp); |
304 | /* the following line was removed since it didn't work well | 492 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, |
305 | with lower cased domain name that passed as an option. | 493 | (char *)domain, 2 * len); |
306 | Maybe converting the domain name earlier makes sense */ | ||
307 | /* UniStrupr(domain); */ | ||
308 | |||
309 | hmac_md5_update((char *)domain, 2*len, pctxt); | ||
310 | |||
311 | kfree(domain); | 494 | kfree(domain); |
495 | } else if (ses->serverName) { | ||
496 | len = strlen(ses->serverName); | ||
497 | |||
498 | server = kmalloc(2 + (len * 2), GFP_KERNEL); | ||
499 | if (server == NULL) { | ||
500 | cERROR(1, "calc_ntlmv2_hash: server mem alloc failure"); | ||
501 | rc = -ENOMEM; | ||
502 | goto calc_exit_1; | ||
503 | } | ||
504 | len = cifs_strtoUCS((__le16 *)server, ses->serverName, len, | ||
505 | nls_cp); | ||
506 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
507 | (char *)server, 2 * len); | ||
508 | kfree(server); | ||
312 | } | 509 | } |
510 | |||
511 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, | ||
512 | ntlmv2_hash); | ||
513 | |||
313 | calc_exit_1: | 514 | calc_exit_1: |
314 | kfree(user); | 515 | kfree(user); |
315 | calc_exit_2: | 516 | calc_exit_2: |
316 | /* BB FIXME what about bytes 24 through 40 of the signing key? | 517 | return rc; |
317 | compare with the NTLM example */ | 518 | } |
318 | hmac_md5_final(ses->server->ntlmv2_hash, pctxt); | 519 | |
520 | static int | ||
521 | CalcNTLMv2_response(const struct cifsSesInfo *ses, char *ntlmv2_hash) | ||
522 | { | ||
523 | int rc; | ||
524 | unsigned int offset = CIFS_SESS_KEY_SIZE + 8; | ||
525 | |||
526 | if (!ses->server->secmech.sdeschmacmd5) { | ||
527 | cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); | ||
528 | return -1; | ||
529 | } | ||
530 | |||
531 | crypto_shash_setkey(ses->server->secmech.hmacmd5, | ||
532 | ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); | ||
533 | |||
534 | rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); | ||
535 | if (rc) { | ||
536 | cERROR(1, "CalcNTLMv2_response: could not init hmacmd5"); | ||
537 | return rc; | ||
538 | } | ||
539 | |||
540 | if (ses->server->secType == RawNTLMSSP) | ||
541 | memcpy(ses->auth_key.response + offset, | ||
542 | ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); | ||
543 | else | ||
544 | memcpy(ses->auth_key.response + offset, | ||
545 | ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); | ||
546 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
547 | ses->auth_key.response + offset, ses->auth_key.len - offset); | ||
548 | |||
549 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, | ||
550 | ses->auth_key.response + CIFS_SESS_KEY_SIZE); | ||
319 | 551 | ||
320 | kfree(pctxt); | ||
321 | return rc; | 552 | return rc; |
322 | } | 553 | } |
323 | 554 | ||
324 | void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, | 555 | |
325 | const struct nls_table *nls_cp) | 556 | int |
557 | setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp) | ||
326 | { | 558 | { |
327 | int rc; | 559 | int rc; |
328 | struct ntlmv2_resp *buf = (struct ntlmv2_resp *)resp_buf; | 560 | int baselen; |
329 | struct HMACMD5Context context; | 561 | unsigned int tilen; |
562 | struct ntlmv2_resp *buf; | ||
563 | char ntlmv2_hash[16]; | ||
564 | unsigned char *tiblob = NULL; /* target info blob */ | ||
565 | |||
566 | if (ses->server->secType == RawNTLMSSP) { | ||
567 | if (!ses->domainName) { | ||
568 | rc = find_domain_name(ses, nls_cp); | ||
569 | if (rc) { | ||
570 | cERROR(1, "error %d finding domain name", rc); | ||
571 | goto setup_ntlmv2_rsp_ret; | ||
572 | } | ||
573 | } | ||
574 | } else { | ||
575 | rc = build_avpair_blob(ses, nls_cp); | ||
576 | if (rc) { | ||
577 | cERROR(1, "error %d building av pair blob", rc); | ||
578 | goto setup_ntlmv2_rsp_ret; | ||
579 | } | ||
580 | } | ||
330 | 581 | ||
582 | baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp); | ||
583 | tilen = ses->auth_key.len; | ||
584 | tiblob = ses->auth_key.response; | ||
585 | |||
586 | ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL); | ||
587 | if (!ses->auth_key.response) { | ||
588 | rc = ENOMEM; | ||
589 | ses->auth_key.len = 0; | ||
590 | cERROR(1, "%s: Can't allocate auth blob", __func__); | ||
591 | goto setup_ntlmv2_rsp_ret; | ||
592 | } | ||
593 | ses->auth_key.len += baselen; | ||
594 | |||
595 | buf = (struct ntlmv2_resp *) | ||
596 | (ses->auth_key.response + CIFS_SESS_KEY_SIZE); | ||
331 | buf->blob_signature = cpu_to_le32(0x00000101); | 597 | buf->blob_signature = cpu_to_le32(0x00000101); |
332 | buf->reserved = 0; | 598 | buf->reserved = 0; |
333 | buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | 599 | buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); |
334 | get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); | 600 | get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); |
335 | buf->reserved2 = 0; | 601 | buf->reserved2 = 0; |
336 | buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); | ||
337 | buf->names[0].length = 0; | ||
338 | buf->names[1].type = 0; | ||
339 | buf->names[1].length = 0; | ||
340 | 602 | ||
341 | /* calculate buf->ntlmv2_hash */ | 603 | memcpy(ses->auth_key.response + baselen, tiblob, tilen); |
342 | rc = calc_ntlmv2_hash(ses, nls_cp); | 604 | |
343 | if (rc) | 605 | /* calculate ntlmv2_hash */ |
606 | rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); | ||
607 | if (rc) { | ||
344 | cERROR(1, "could not get v2 hash rc %d", rc); | 608 | cERROR(1, "could not get v2 hash rc %d", rc); |
345 | CalcNTLMv2_response(ses, resp_buf); | 609 | goto setup_ntlmv2_rsp_ret; |
610 | } | ||
611 | |||
612 | /* calculate first part of the client response (CR1) */ | ||
613 | rc = CalcNTLMv2_response(ses, ntlmv2_hash); | ||
614 | if (rc) { | ||
615 | cERROR(1, "Could not calculate CR1 rc: %d", rc); | ||
616 | goto setup_ntlmv2_rsp_ret; | ||
617 | } | ||
618 | |||
619 | /* now calculate the session key for NTLMv2 */ | ||
620 | crypto_shash_setkey(ses->server->secmech.hmacmd5, | ||
621 | ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); | ||
622 | |||
623 | rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); | ||
624 | if (rc) { | ||
625 | cERROR(1, "%s: Could not init hmacmd5\n", __func__); | ||
626 | goto setup_ntlmv2_rsp_ret; | ||
627 | } | ||
628 | |||
629 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
630 | ses->auth_key.response + CIFS_SESS_KEY_SIZE, | ||
631 | CIFS_HMAC_MD5_HASH_SIZE); | ||
632 | |||
633 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, | ||
634 | ses->auth_key.response); | ||
635 | |||
636 | setup_ntlmv2_rsp_ret: | ||
637 | kfree(tiblob); | ||
638 | |||
639 | return rc; | ||
640 | } | ||
346 | 641 | ||
347 | /* now calculate the MAC key for NTLMv2 */ | 642 | int |
348 | hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); | 643 | calc_seckey(struct cifsSesInfo *ses) |
349 | hmac_md5_update(resp_buf, 16, &context); | 644 | { |
350 | hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context); | 645 | int rc; |
646 | struct crypto_blkcipher *tfm_arc4; | ||
647 | struct scatterlist sgin, sgout; | ||
648 | struct blkcipher_desc desc; | ||
649 | unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ | ||
650 | |||
651 | get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); | ||
652 | |||
653 | tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); | ||
654 | if (!tfm_arc4 || IS_ERR(tfm_arc4)) { | ||
655 | cERROR(1, "could not allocate crypto API arc4\n"); | ||
656 | return PTR_ERR(tfm_arc4); | ||
657 | } | ||
658 | |||
659 | desc.tfm = tfm_arc4; | ||
660 | |||
661 | crypto_blkcipher_setkey(tfm_arc4, ses->auth_key.response, | ||
662 | CIFS_SESS_KEY_SIZE); | ||
663 | |||
664 | sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE); | ||
665 | sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); | ||
666 | |||
667 | rc = crypto_blkcipher_encrypt(&desc, &sgout, &sgin, CIFS_CPHTXT_SIZE); | ||
668 | if (rc) { | ||
669 | cERROR(1, "could not encrypt session key rc: %d\n", rc); | ||
670 | crypto_free_blkcipher(tfm_arc4); | ||
671 | return rc; | ||
672 | } | ||
673 | |||
674 | /* make secondary_key/nonce as session key */ | ||
675 | memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); | ||
676 | /* and make len as that of session key only */ | ||
677 | ses->auth_key.len = CIFS_SESS_KEY_SIZE; | ||
351 | 678 | ||
352 | memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf, | 679 | crypto_free_blkcipher(tfm_arc4); |
353 | sizeof(struct ntlmv2_resp)); | 680 | |
354 | ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp); | 681 | return 0; |
355 | } | 682 | } |
356 | 683 | ||
357 | void CalcNTLMv2_response(const struct cifsSesInfo *ses, | 684 | void |
358 | char *v2_session_response) | 685 | cifs_crypto_shash_release(struct TCP_Server_Info *server) |
359 | { | 686 | { |
360 | struct HMACMD5Context context; | 687 | if (server->secmech.md5) |
361 | /* rest of v2 struct already generated */ | 688 | crypto_free_shash(server->secmech.md5); |
362 | memcpy(v2_session_response + 8, ses->server->cryptKey, 8); | ||
363 | hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); | ||
364 | 689 | ||
365 | hmac_md5_update(v2_session_response+8, | 690 | if (server->secmech.hmacmd5) |
366 | sizeof(struct ntlmv2_resp) - 8, &context); | 691 | crypto_free_shash(server->secmech.hmacmd5); |
367 | 692 | ||
368 | hmac_md5_final(v2_session_response, &context); | 693 | kfree(server->secmech.sdeschmacmd5); |
369 | /* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */ | 694 | |
695 | kfree(server->secmech.sdescmd5); | ||
696 | } | ||
697 | |||
698 | int | ||
699 | cifs_crypto_shash_allocate(struct TCP_Server_Info *server) | ||
700 | { | ||
701 | int rc; | ||
702 | unsigned int size; | ||
703 | |||
704 | server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0); | ||
705 | if (!server->secmech.hmacmd5 || | ||
706 | IS_ERR(server->secmech.hmacmd5)) { | ||
707 | cERROR(1, "could not allocate crypto hmacmd5\n"); | ||
708 | return PTR_ERR(server->secmech.hmacmd5); | ||
709 | } | ||
710 | |||
711 | server->secmech.md5 = crypto_alloc_shash("md5", 0, 0); | ||
712 | if (!server->secmech.md5 || IS_ERR(server->secmech.md5)) { | ||
713 | cERROR(1, "could not allocate crypto md5\n"); | ||
714 | rc = PTR_ERR(server->secmech.md5); | ||
715 | goto crypto_allocate_md5_fail; | ||
716 | } | ||
717 | |||
718 | size = sizeof(struct shash_desc) + | ||
719 | crypto_shash_descsize(server->secmech.hmacmd5); | ||
720 | server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL); | ||
721 | if (!server->secmech.sdeschmacmd5) { | ||
722 | cERROR(1, "cifs_crypto_shash_allocate: can't alloc hmacmd5\n"); | ||
723 | rc = -ENOMEM; | ||
724 | goto crypto_allocate_hmacmd5_sdesc_fail; | ||
725 | } | ||
726 | server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5; | ||
727 | server->secmech.sdeschmacmd5->shash.flags = 0x0; | ||
728 | |||
729 | |||
730 | size = sizeof(struct shash_desc) + | ||
731 | crypto_shash_descsize(server->secmech.md5); | ||
732 | server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL); | ||
733 | if (!server->secmech.sdescmd5) { | ||
734 | cERROR(1, "cifs_crypto_shash_allocate: can't alloc md5\n"); | ||
735 | rc = -ENOMEM; | ||
736 | goto crypto_allocate_md5_sdesc_fail; | ||
737 | } | ||
738 | server->secmech.sdescmd5->shash.tfm = server->secmech.md5; | ||
739 | server->secmech.sdescmd5->shash.flags = 0x0; | ||
740 | |||
741 | return 0; | ||
742 | |||
743 | crypto_allocate_md5_sdesc_fail: | ||
744 | kfree(server->secmech.sdeschmacmd5); | ||
745 | |||
746 | crypto_allocate_hmacmd5_sdesc_fail: | ||
747 | crypto_free_shash(server->secmech.md5); | ||
748 | |||
749 | crypto_allocate_md5_fail: | ||
750 | crypto_free_shash(server->secmech.hmacmd5); | ||
751 | |||
752 | return rc; | ||
370 | } | 753 | } |