diff options
author | Steve French <sfrench@us.ibm.com> | 2007-11-16 18:37:35 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2007-11-16 18:37:35 -0500 |
commit | 2442421b176420eca7cb68c575fc221332f488d8 (patch) | |
tree | aaf7a89594c9ae116e4a1768a97dcaeb83841c07 /fs/cifs/sess.c | |
parent | 8840dee9dc53883883c321d2811e9f87700d9350 (diff) |
[CIFS] Have CIFS_SessSetup build correct SPNEGO SessionSetup request
Have CIFS_SessSetup call cifs_get_spnego_key when Kerberos is
negotiated. Use the info in the key payload to build a session
setup request packet. Also clean up how the request buffer in
the function is freed on error.
With appropriate user space helper (in samba/source/client). Kerberos
support (secure session establishment can be done now via Kerberos,
previously users would have to use NTLMv2 instead for more secure
session setup).
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/sess.c')
-rw-r--r-- | fs/cifs/sess.c | 91 |
1 files changed, 74 insertions, 17 deletions
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index ed01ef382aa9..d0cb469daab7 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "ntlmssp.h" | 29 | #include "ntlmssp.h" |
30 | #include "nterr.h" | 30 | #include "nterr.h" |
31 | #include <linux/utsname.h> | 31 | #include <linux/utsname.h> |
32 | #include "cifs_spnego.h" | ||
32 | 33 | ||
33 | extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, | 34 | extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, |
34 | unsigned char *p24); | 35 | unsigned char *p24); |
@@ -340,11 +341,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
340 | SESSION_SETUP_ANDX *pSMB; | 341 | SESSION_SETUP_ANDX *pSMB; |
341 | __u32 capabilities; | 342 | __u32 capabilities; |
342 | int count; | 343 | int count; |
343 | int resp_buf_type = 0; | 344 | int resp_buf_type; |
344 | struct kvec iov[2]; | 345 | struct kvec iov[3]; |
345 | enum securityEnum type; | 346 | enum securityEnum type; |
346 | __u16 action; | 347 | __u16 action; |
347 | int bytes_remaining; | 348 | int bytes_remaining; |
349 | struct key *spnego_key = NULL; | ||
348 | 350 | ||
349 | if (ses == NULL) | 351 | if (ses == NULL) |
350 | return -EINVAL; | 352 | return -EINVAL; |
@@ -377,24 +379,32 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
377 | 379 | ||
378 | capabilities = cifs_ssetup_hdr(ses, pSMB); | 380 | capabilities = cifs_ssetup_hdr(ses, pSMB); |
379 | 381 | ||
380 | /* we will send the SMB in two pieces, | 382 | /* we will send the SMB in three pieces: |
381 | a fixed length beginning part, and a | 383 | a fixed length beginning part, an optional |
382 | second part which will include the strings | 384 | SPNEGO blob (which can be zero length), and a |
383 | and rest of bcc area, in order to avoid having | 385 | last part which will include the strings |
384 | to do a large buffer 17K allocation */ | 386 | and rest of bcc area. This allows us to avoid |
387 | a large buffer 17K allocation */ | ||
385 | iov[0].iov_base = (char *)pSMB; | 388 | iov[0].iov_base = (char *)pSMB; |
386 | iov[0].iov_len = smb_buf->smb_buf_length + 4; | 389 | iov[0].iov_len = smb_buf->smb_buf_length + 4; |
387 | 390 | ||
391 | /* setting this here allows the code at the end of the function | ||
392 | to free the request buffer if there's an error */ | ||
393 | resp_buf_type = CIFS_SMALL_BUFFER; | ||
394 | |||
388 | /* 2000 big enough to fit max user, domain, NOS name etc. */ | 395 | /* 2000 big enough to fit max user, domain, NOS name etc. */ |
389 | str_area = kmalloc(2000, GFP_KERNEL); | 396 | str_area = kmalloc(2000, GFP_KERNEL); |
390 | if (str_area == NULL) { | 397 | if (str_area == NULL) { |
391 | cifs_small_buf_release(smb_buf); | 398 | rc = -ENOMEM; |
392 | return -ENOMEM; | 399 | goto ssetup_exit; |
393 | } | 400 | } |
394 | bcc_ptr = str_area; | 401 | bcc_ptr = str_area; |
395 | 402 | ||
396 | ses->flags &= ~CIFS_SES_LANMAN; | 403 | ses->flags &= ~CIFS_SES_LANMAN; |
397 | 404 | ||
405 | iov[1].iov_base = NULL; | ||
406 | iov[1].iov_len = 0; | ||
407 | |||
398 | if (type == LANMAN) { | 408 | if (type == LANMAN) { |
399 | #ifdef CONFIG_CIFS_WEAK_PW_HASH | 409 | #ifdef CONFIG_CIFS_WEAK_PW_HASH |
400 | char lnm_session_key[CIFS_SESS_KEY_SIZE]; | 410 | char lnm_session_key[CIFS_SESS_KEY_SIZE]; |
@@ -463,8 +473,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
463 | struct ntlmv2_resp */ | 473 | struct ntlmv2_resp */ |
464 | 474 | ||
465 | if (v2_sess_key == NULL) { | 475 | if (v2_sess_key == NULL) { |
466 | cifs_small_buf_release(smb_buf); | 476 | rc = -ENOMEM; |
467 | return -ENOMEM; | 477 | goto ssetup_exit; |
468 | } | 478 | } |
469 | 479 | ||
470 | pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); | 480 | pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); |
@@ -499,21 +509,66 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
499 | unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); | 509 | unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); |
500 | } else | 510 | } else |
501 | ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); | 511 | ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); |
502 | } else /* NTLMSSP or SPNEGO */ { | 512 | } else if (type == Kerberos) { |
513 | #ifdef CONFIG_CIFS_UPCALL | ||
514 | struct cifs_spnego_msg *msg; | ||
515 | spnego_key = cifs_get_spnego_key(ses); | ||
516 | if (IS_ERR(spnego_key)) { | ||
517 | rc = PTR_ERR(spnego_key); | ||
518 | spnego_key = NULL; | ||
519 | goto ssetup_exit; | ||
520 | } | ||
521 | |||
522 | msg = spnego_key->payload.data; | ||
523 | /* bail out if key is too long */ | ||
524 | if (msg->sesskey_len > | ||
525 | sizeof(ses->server->mac_signing_key.data.krb5)) { | ||
526 | cERROR(1, ("Kerberos signing key too long (%u bytes)", | ||
527 | msg->sesskey_len)); | ||
528 | rc = -EOVERFLOW; | ||
529 | goto ssetup_exit; | ||
530 | } | ||
531 | ses->server->mac_signing_key.len = msg->sesskey_len; | ||
532 | memcpy(ses->server->mac_signing_key.data.krb5, msg->data, | ||
533 | msg->sesskey_len); | ||
503 | pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; | 534 | pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; |
504 | capabilities |= CAP_EXTENDED_SECURITY; | 535 | capabilities |= CAP_EXTENDED_SECURITY; |
505 | pSMB->req.Capabilities = cpu_to_le32(capabilities); | 536 | pSMB->req.Capabilities = cpu_to_le32(capabilities); |
506 | /* BB set password lengths */ | 537 | iov[1].iov_base = msg->data + msg->sesskey_len; |
538 | iov[1].iov_len = msg->secblob_len; | ||
539 | pSMB->req.SecurityBlobLength = cpu_to_le16(iov[1].iov_len); | ||
540 | |||
541 | if (ses->capabilities & CAP_UNICODE) { | ||
542 | /* unicode strings must be word aligned */ | ||
543 | if (iov[0].iov_len % 2) { | ||
544 | *bcc_ptr = 0; | ||
545 | bcc_ptr++; | ||
546 | } | ||
547 | unicode_oslm_strings(&bcc_ptr, nls_cp); | ||
548 | unicode_domain_string(&bcc_ptr, ses, nls_cp); | ||
549 | } else | ||
550 | /* BB: is this right? */ | ||
551 | ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); | ||
552 | #else /* ! CONFIG_CIFS_UPCALL */ | ||
553 | cERROR(1, ("Kerberos negotiated but upcall support disabled!")); | ||
554 | rc = -ENOSYS; | ||
555 | goto ssetup_exit; | ||
556 | #endif /* CONFIG_CIFS_UPCALL */ | ||
557 | } else { | ||
558 | cERROR(1, ("secType %d not supported!", type)); | ||
559 | rc = -ENOSYS; | ||
560 | goto ssetup_exit; | ||
507 | } | 561 | } |
508 | 562 | ||
509 | count = (long) bcc_ptr - (long) str_area; | 563 | iov[2].iov_base = str_area; |
564 | iov[2].iov_len = (long) bcc_ptr - (long) str_area; | ||
565 | |||
566 | count = iov[1].iov_len + iov[2].iov_len; | ||
510 | smb_buf->smb_buf_length += count; | 567 | smb_buf->smb_buf_length += count; |
511 | 568 | ||
512 | BCC_LE(smb_buf) = cpu_to_le16(count); | 569 | BCC_LE(smb_buf) = cpu_to_le16(count); |
513 | 570 | ||
514 | iov[1].iov_base = str_area; | 571 | rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, |
515 | iov[1].iov_len = count; | ||
516 | rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type, | ||
517 | CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); | 572 | CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); |
518 | /* SMB request buf freed in SendReceive2 */ | 573 | /* SMB request buf freed in SendReceive2 */ |
519 | 574 | ||
@@ -560,6 +615,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, | |||
560 | ses, nls_cp); | 615 | ses, nls_cp); |
561 | 616 | ||
562 | ssetup_exit: | 617 | ssetup_exit: |
618 | if (spnego_key) | ||
619 | key_put(spnego_key); | ||
563 | kfree(str_area); | 620 | kfree(str_area); |
564 | if (resp_buf_type == CIFS_SMALL_BUFFER) { | 621 | if (resp_buf_type == CIFS_SMALL_BUFFER) { |
565 | cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base)); | 622 | cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base)); |