diff options
author | Shirish Pargaonkar <shirishpargaonkar@gmail.com> | 2010-10-21 15:25:17 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2010-10-26 14:38:06 -0400 |
commit | 307fbd31b61623ad1b5388b452118f8aea99f9d0 (patch) | |
tree | 71231b840aac77531d5dc26021ecb0451feab7ae /fs/cifs | |
parent | d2b915210b5ec01409f581421d633eca6c38d444 (diff) |
NTLM auth and sign - Use kernel crypto apis to calculate hashes and smb signatures
Use kernel crypto sync hash apis insetead of cifs crypto functions.
The calls typically corrospond one to one except that insead of
key init, setkey is used.
Use crypto apis to generate smb signagtures also.
Use hmac-md5 to genereate ntlmv2 hash, ntlmv2 response, and HMAC (CR1 of
ntlmv2 auth blob.
User crypto apis to genereate signature and to verify signature.
md5 hash is used to calculate signature.
Use secondary key to calculate signature in case of ntlmssp.
For ntlmv2 within ntlmssp, during signature calculation, only 16 bytes key
(a nonce) stored within session key is used. during smb signature calculation.
For ntlm and ntlmv2 without extended security, 16 bytes key
as well as entire response (24 bytes in case of ntlm and variable length
in case of ntlmv2) is used for smb signature calculation.
For kerberos, there is no distinction between key and response.
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsencrypt.c | 195 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 1 |
2 files changed, 136 insertions, 60 deletions
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index eaa2327ee7af..96908874a45c 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c | |||
@@ -45,17 +45,30 @@ extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8, | |||
45 | static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, | 45 | static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, |
46 | struct TCP_Server_Info *server, char *signature) | 46 | struct TCP_Server_Info *server, char *signature) |
47 | { | 47 | { |
48 | struct MD5Context context; | 48 | int rc; |
49 | 49 | ||
50 | if (cifs_pdu == NULL || signature == NULL || server == NULL) | 50 | if (cifs_pdu == NULL || signature == NULL || server == NULL) |
51 | return -EINVAL; | 51 | return -EINVAL; |
52 | 52 | ||
53 | cifs_MD5_init(&context); | 53 | if (!server->secmech.sdescmd5) { |
54 | cifs_MD5_update(&context, server->session_key.response, | 54 | cERROR(1, "%s: Can't generate signature\n", __func__); |
55 | server->session_key.len); | 55 | return -1; |
56 | cifs_MD5_update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length); | 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); | ||
57 | 71 | ||
58 | cifs_MD5_final(signature, &context); | ||
59 | return 0; | 72 | return 0; |
60 | } | 73 | } |
61 | 74 | ||
@@ -92,15 +105,26 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, | |||
92 | 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, |
93 | struct TCP_Server_Info *server, char *signature) | 106 | struct TCP_Server_Info *server, char *signature) |
94 | { | 107 | { |
95 | struct MD5Context context; | ||
96 | int i; | 108 | int i; |
109 | int rc; | ||
97 | 110 | ||
98 | if (iov == NULL || signature == NULL || server == NULL) | 111 | if (iov == NULL || signature == NULL || server == NULL) |
99 | return -EINVAL; | 112 | return -EINVAL; |
100 | 113 | ||
101 | cifs_MD5_init(&context); | 114 | if (!server->secmech.sdescmd5) { |
102 | cifs_MD5_update(&context, server->session_key.response, | 115 | cERROR(1, "%s: Can't generate signature\n", __func__); |
103 | server->session_key.len); | 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 | |||
104 | for (i = 0; i < n_vec; i++) { | 128 | for (i = 0; i < n_vec; i++) { |
105 | if (iov[i].iov_len == 0) | 129 | if (iov[i].iov_len == 0) |
106 | continue; | 130 | continue; |
@@ -113,18 +137,18 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, | |||
113 | if (i == 0) { | 137 | if (i == 0) { |
114 | if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ | 138 | if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ |
115 | break; /* nothing to sign or corrupt header */ | 139 | break; /* nothing to sign or corrupt header */ |
116 | cifs_MD5_update(&context, iov[0].iov_base+4, | 140 | crypto_shash_update(&server->secmech.sdescmd5->shash, |
117 | iov[0].iov_len-4); | 141 | iov[i].iov_base + 4, iov[i].iov_len - 4); |
118 | } else | 142 | } else |
119 | 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); | ||
120 | } | 145 | } |
121 | 146 | ||
122 | cifs_MD5_final(signature, &context); | 147 | rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); |
123 | 148 | ||
124 | return 0; | 149 | return rc; |
125 | } | 150 | } |
126 | 151 | ||
127 | |||
128 | 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, |
129 | __u32 *pexpected_response_sequence_number) | 153 | __u32 *pexpected_response_sequence_number) |
130 | { | 154 | { |
@@ -420,67 +444,120 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses, | |||
420 | { | 444 | { |
421 | int rc = 0; | 445 | int rc = 0; |
422 | int len; | 446 | int len; |
423 | char nt_hash[16]; | 447 | char nt_hash[CIFS_NTHASH_SIZE]; |
424 | struct HMACMD5Context *pctxt; | ||
425 | wchar_t *user; | 448 | wchar_t *user; |
426 | wchar_t *domain; | 449 | wchar_t *domain; |
450 | wchar_t *server; | ||
427 | 451 | ||
428 | pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL); | 452 | if (!ses->server->secmech.sdeschmacmd5) { |
429 | 453 | cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); | |
430 | if (pctxt == NULL) | 454 | return -1; |
431 | return -ENOMEM; | 455 | } |
432 | 456 | ||
433 | /* calculate md4 hash of password */ | 457 | /* calculate md4 hash of password */ |
434 | E_md4hash(ses->password, nt_hash); | 458 | E_md4hash(ses->password, nt_hash); |
435 | 459 | ||
436 | /* convert Domainname to unicode and uppercase */ | 460 | crypto_shash_setkey(ses->server->secmech.hmacmd5, nt_hash, |
437 | hmac_md5_init_limK_to_64(nt_hash, 16, pctxt); | 461 | CIFS_NTHASH_SIZE); |
462 | |||
463 | rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); | ||
464 | if (rc) { | ||
465 | cERROR(1, "calc_ntlmv2_hash: could not init hmacmd5\n"); | ||
466 | return rc; | ||
467 | } | ||
438 | 468 | ||
439 | /* convert ses->userName to unicode and uppercase */ | 469 | /* convert ses->userName to unicode and uppercase */ |
440 | len = strlen(ses->userName); | 470 | len = strlen(ses->userName); |
441 | user = kmalloc(2 + (len * 2), GFP_KERNEL); | 471 | user = kmalloc(2 + (len * 2), GFP_KERNEL); |
442 | if (user == NULL) | 472 | if (user == NULL) { |
473 | cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n"); | ||
474 | rc = -ENOMEM; | ||
443 | goto calc_exit_2; | 475 | goto calc_exit_2; |
476 | } | ||
444 | len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); | 477 | len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp); |
445 | UniStrupr(user); | 478 | UniStrupr(user); |
446 | hmac_md5_update((char *)user, 2*len, pctxt); | 479 | |
480 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
481 | (char *)user, 2 * len); | ||
447 | 482 | ||
448 | /* convert ses->domainName to unicode and uppercase */ | 483 | /* convert ses->domainName to unicode and uppercase */ |
449 | if (ses->domainName) { | 484 | if (ses->domainName) { |
450 | len = strlen(ses->domainName); | 485 | len = strlen(ses->domainName); |
451 | 486 | ||
452 | domain = kmalloc(2 + (len * 2), GFP_KERNEL); | 487 | domain = kmalloc(2 + (len * 2), GFP_KERNEL); |
453 | if (domain == NULL) | 488 | if (domain == NULL) { |
489 | cERROR(1, "calc_ntlmv2_hash: domain mem alloc failure"); | ||
490 | rc = -ENOMEM; | ||
454 | goto calc_exit_1; | 491 | goto calc_exit_1; |
492 | } | ||
455 | len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, | 493 | len = cifs_strtoUCS((__le16 *)domain, ses->domainName, len, |
456 | nls_cp); | 494 | nls_cp); |
457 | /* the following line was removed since it didn't work well | 495 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, |
458 | with lower cased domain name that passed as an option. | 496 | (char *)domain, 2 * len); |
459 | Maybe converting the domain name earlier makes sense */ | ||
460 | /* UniStrupr(domain); */ | ||
461 | |||
462 | hmac_md5_update((char *)domain, 2*len, pctxt); | ||
463 | |||
464 | kfree(domain); | 497 | kfree(domain); |
498 | } else if (ses->serverName) { | ||
499 | len = strlen(ses->serverName); | ||
500 | |||
501 | server = kmalloc(2 + (len * 2), GFP_KERNEL); | ||
502 | if (server == NULL) { | ||
503 | cERROR(1, "calc_ntlmv2_hash: server mem alloc failure"); | ||
504 | rc = -ENOMEM; | ||
505 | goto calc_exit_1; | ||
506 | } | ||
507 | len = cifs_strtoUCS((__le16 *)server, ses->serverName, len, | ||
508 | nls_cp); | ||
509 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
510 | (char *)server, 2 * len); | ||
511 | kfree(server); | ||
465 | } | 512 | } |
513 | |||
514 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, | ||
515 | ses->ntlmv2_hash); | ||
516 | |||
466 | calc_exit_1: | 517 | calc_exit_1: |
467 | kfree(user); | 518 | kfree(user); |
468 | calc_exit_2: | 519 | calc_exit_2: |
469 | /* BB FIXME what about bytes 24 through 40 of the signing key? | 520 | return rc; |
470 | compare with the NTLM example */ | 521 | } |
471 | hmac_md5_final(ses->ntlmv2_hash, pctxt); | 522 | |
523 | static int | ||
524 | CalcNTLMv2_response(const struct cifsSesInfo *ses) | ||
525 | { | ||
526 | int rc; | ||
527 | unsigned int offset = CIFS_SESS_KEY_SIZE + 8; | ||
528 | |||
529 | if (!ses->server->secmech.sdeschmacmd5) { | ||
530 | cERROR(1, "calc_ntlmv2_hash: can't generate ntlmv2 hash\n"); | ||
531 | return -1; | ||
532 | } | ||
533 | |||
534 | crypto_shash_setkey(ses->server->secmech.hmacmd5, | ||
535 | ses->ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); | ||
536 | |||
537 | rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); | ||
538 | if (rc) { | ||
539 | cERROR(1, "CalcNTLMv2_response: could not init hmacmd5"); | ||
540 | return rc; | ||
541 | } | ||
542 | |||
543 | memcpy(ses->auth_key.response + offset, | ||
544 | ses->cryptKey, CIFS_SERVER_CHALLENGE_SIZE); | ||
545 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
546 | ses->auth_key.response + offset, ses->auth_key.len - offset); | ||
547 | |||
548 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, | ||
549 | ses->auth_key.response + CIFS_SESS_KEY_SIZE); | ||
472 | 550 | ||
473 | kfree(pctxt); | ||
474 | return rc; | 551 | return rc; |
475 | } | 552 | } |
476 | 553 | ||
554 | |||
477 | int | 555 | int |
478 | setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp) | 556 | setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp) |
479 | { | 557 | { |
480 | int rc; | 558 | int rc; |
481 | int baselen; | 559 | int baselen; |
482 | struct ntlmv2_resp *buf; | 560 | struct ntlmv2_resp *buf; |
483 | struct HMACMD5Context context; | ||
484 | 561 | ||
485 | if (ses->server->secType == RawNTLMSSP) { | 562 | if (ses->server->secType == RawNTLMSSP) { |
486 | if (!ses->domainName) { | 563 | if (!ses->domainName) { |
@@ -523,13 +600,28 @@ setup_ntlmv2_rsp(struct cifsSesInfo *ses, const struct nls_table *nls_cp) | |||
523 | cERROR(1, "could not get v2 hash rc %d", rc); | 600 | cERROR(1, "could not get v2 hash rc %d", rc); |
524 | goto setup_ntlmv2_rsp_ret; | 601 | goto setup_ntlmv2_rsp_ret; |
525 | } | 602 | } |
526 | CalcNTLMv2_response(ses); | 603 | rc = CalcNTLMv2_response(ses); |
604 | if (rc) { | ||
605 | cERROR(1, "Could not calculate CR1 rc: %d", rc); | ||
606 | goto setup_ntlmv2_rsp_ret; | ||
607 | } | ||
527 | 608 | ||
528 | /* now calculate the session key for NTLMv2 */ | 609 | /* now calculate the session key for NTLMv2 */ |
529 | hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context); | 610 | crypto_shash_setkey(ses->server->secmech.hmacmd5, |
530 | hmac_md5_update(ses->auth_key.response + CIFS_SESS_KEY_SIZE, | 611 | ses->ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); |
531 | 16, &context); | 612 | |
532 | hmac_md5_final(ses->auth_key.response, &context); | 613 | rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); |
614 | if (rc) { | ||
615 | cERROR(1, "%s: Could not init hmacmd5\n", __func__); | ||
616 | goto setup_ntlmv2_rsp_ret; | ||
617 | } | ||
618 | |||
619 | crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, | ||
620 | ses->auth_key.response + CIFS_SESS_KEY_SIZE, | ||
621 | CIFS_HMAC_MD5_HASH_SIZE); | ||
622 | |||
623 | rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, | ||
624 | ses->auth_key.response); | ||
533 | 625 | ||
534 | return 0; | 626 | return 0; |
535 | 627 | ||
@@ -653,18 +745,3 @@ crypto_allocate_md5_fail: | |||
653 | 745 | ||
654 | return rc; | 746 | return rc; |
655 | } | 747 | } |
656 | |||
657 | void CalcNTLMv2_response(const struct cifsSesInfo *ses) | ||
658 | { | ||
659 | unsigned int offset = CIFS_SESS_KEY_SIZE + 8; | ||
660 | struct HMACMD5Context context; | ||
661 | |||
662 | /* rest of v2 struct already generated */ | ||
663 | memcpy(ses->auth_key.response + offset, ses->cryptKey, 8); | ||
664 | hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context); | ||
665 | |||
666 | hmac_md5_update(ses->auth_key.response + offset, | ||
667 | ses->auth_key.len - offset, &context); | ||
668 | |||
669 | hmac_md5_final(ses->auth_key.response + CIFS_SESS_KEY_SIZE, &context); | ||
670 | } | ||
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1e4728bcf065..edb6d90efdf2 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -366,7 +366,6 @@ extern int cifs_verify_signature(struct smb_hdr *, | |||
366 | __u32 expected_sequence_number); | 366 | __u32 expected_sequence_number); |
367 | extern void SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *); | 367 | extern void SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *); |
368 | extern int setup_ntlm_response(struct cifsSesInfo *); | 368 | extern int setup_ntlm_response(struct cifsSesInfo *); |
369 | extern void CalcNTLMv2_response(const struct cifsSesInfo *); | ||
370 | extern int setup_ntlmv2_rsp(struct cifsSesInfo *, const struct nls_table *); | 369 | extern int setup_ntlmv2_rsp(struct cifsSesInfo *, const struct nls_table *); |
371 | extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); | 370 | extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); |
372 | extern void cifs_crypto_shash_release(struct TCP_Server_Info *); | 371 | extern void cifs_crypto_shash_release(struct TCP_Server_Info *); |