diff options
author | Pavel Shilovsky <piastry@etersoft.ru> | 2011-12-27 07:22:00 -0500 |
---|---|---|
committer | Pavel Shilovsky <pshilovsky@samba.org> | 2012-07-24 13:54:57 -0400 |
commit | 5478f9ba9a34d660eb3227dcd16314689c51f946 (patch) | |
tree | 6cb95588d403d5a962f6450b68d2daaf23566250 /fs/cifs | |
parent | ec2e4523fdba88317e06d0c7a88af3a0860447fc (diff) |
CIFS: Add session setup/logoff capability for SMB2
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsglob.h | 3 | ||||
-rw-r--r-- | fs/cifs/ntlmssp.h | 10 | ||||
-rw-r--r-- | fs/cifs/sess.c | 6 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 5 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 2 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 221 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 37 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 3 |
8 files changed, 284 insertions, 3 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2d48f880b130..0d78bc410cb3 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -504,6 +504,9 @@ struct cifs_ses { | |||
504 | struct session_key auth_key; | 504 | struct session_key auth_key; |
505 | struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ | 505 | struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ |
506 | bool need_reconnect:1; /* connection reset, uid now invalid */ | 506 | bool need_reconnect:1; /* connection reset, uid now invalid */ |
507 | #ifdef CONFIG_CIFS_SMB2 | ||
508 | __u16 session_flags; | ||
509 | #endif /* CONFIG_CIFS_SMB2 */ | ||
507 | }; | 510 | }; |
508 | /* no more than one of the following three session flags may be set */ | 511 | /* no more than one of the following three session flags may be set */ |
509 | #define CIFS_SES_NT4 1 | 512 | #define CIFS_SES_NT4 1 |
diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index 5d52e4a3b1ed..848249fa120f 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h | |||
@@ -126,3 +126,13 @@ typedef struct _AUTHENTICATE_MESSAGE { | |||
126 | do not set the version is present flag */ | 126 | do not set the version is present flag */ |
127 | char UserString[0]; | 127 | char UserString[0]; |
128 | } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; | 128 | } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; |
129 | |||
130 | /* | ||
131 | * Size of the session key (crypto key encrypted with the password | ||
132 | */ | ||
133 | |||
134 | int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); | ||
135 | void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); | ||
136 | int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen, | ||
137 | struct cifs_ses *ses, | ||
138 | const struct nls_table *nls_cp); | ||
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 08efc3c8efef..382c06d01b38 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
@@ -364,7 +364,7 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, | |||
364 | return rc; | 364 | return rc; |
365 | } | 365 | } |
366 | 366 | ||
367 | static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, | 367 | int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, |
368 | struct cifs_ses *ses) | 368 | struct cifs_ses *ses) |
369 | { | 369 | { |
370 | unsigned int tioffset; /* challenge message target info area */ | 370 | unsigned int tioffset; /* challenge message target info area */ |
@@ -415,7 +415,7 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, | |||
415 | 415 | ||
416 | /* We do not malloc the blob, it is passed in pbuffer, because | 416 | /* We do not malloc the blob, it is passed in pbuffer, because |
417 | it is fixed size, and small, making this approach cleaner */ | 417 | it is fixed size, and small, making this approach cleaner */ |
418 | static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, | 418 | void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, |
419 | struct cifs_ses *ses) | 419 | struct cifs_ses *ses) |
420 | { | 420 | { |
421 | NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer; | 421 | NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer; |
@@ -451,7 +451,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, | |||
451 | /* We do not malloc the blob, it is passed in pbuffer, because its | 451 | /* We do not malloc the blob, it is passed in pbuffer, because its |
452 | maximum possible size is fixed and small, making this approach cleaner. | 452 | maximum possible size is fixed and small, making this approach cleaner. |
453 | This function returns the length of the data in the blob */ | 453 | This function returns the length of the data in the blob */ |
454 | static int build_ntlmssp_auth_blob(unsigned char *pbuffer, | 454 | int build_ntlmssp_auth_blob(unsigned char *pbuffer, |
455 | u16 *buflen, | 455 | u16 *buflen, |
456 | struct cifs_ses *ses, | 456 | struct cifs_ses *ses, |
457 | const struct nls_table *nls_cp) | 457 | const struct nls_table *nls_cp) |
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index e4dede4ae058..10729a74da27 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c | |||
@@ -224,6 +224,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) | |||
224 | ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength); | 224 | ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength); |
225 | break; | 225 | break; |
226 | case SMB2_SESSION_SETUP: | 226 | case SMB2_SESSION_SETUP: |
227 | *off = le16_to_cpu( | ||
228 | ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferOffset); | ||
229 | *len = le16_to_cpu( | ||
230 | ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength); | ||
231 | break; | ||
227 | case SMB2_CREATE: | 232 | case SMB2_CREATE: |
228 | case SMB2_READ: | 233 | case SMB2_READ: |
229 | case SMB2_QUERY_INFO: | 234 | case SMB2_QUERY_INFO: |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 2b5232b4f7e7..0057861ce19d 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -170,6 +170,8 @@ struct smb_version_operations smb21_operations = { | |||
170 | .dump_detail = smb2_dump_detail, | 170 | .dump_detail = smb2_dump_detail, |
171 | .need_neg = smb2_need_neg, | 171 | .need_neg = smb2_need_neg, |
172 | .negotiate = smb2_negotiate, | 172 | .negotiate = smb2_negotiate, |
173 | .sess_setup = SMB2_sess_setup, | ||
174 | .logoff = SMB2_logoff, | ||
173 | }; | 175 | }; |
174 | 176 | ||
175 | struct smb_version_values smb21_values = { | 177 | struct smb_version_values smb21_values = { |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 719e4c4f0307..2165f0d15963 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -328,3 +328,224 @@ neg_exit: | |||
328 | free_rsp_buf(resp_buftype, rsp); | 328 | free_rsp_buf(resp_buftype, rsp); |
329 | return rc; | 329 | return rc; |
330 | } | 330 | } |
331 | |||
332 | int | ||
333 | SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, | ||
334 | const struct nls_table *nls_cp) | ||
335 | { | ||
336 | struct smb2_sess_setup_req *req; | ||
337 | struct smb2_sess_setup_rsp *rsp = NULL; | ||
338 | struct kvec iov[2]; | ||
339 | int rc = 0; | ||
340 | int resp_buftype; | ||
341 | __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ | ||
342 | struct TCP_Server_Info *server; | ||
343 | unsigned int sec_flags; | ||
344 | u8 temp = 0; | ||
345 | u16 blob_length = 0; | ||
346 | char *security_blob; | ||
347 | char *ntlmssp_blob = NULL; | ||
348 | bool use_spnego = false; /* else use raw ntlmssp */ | ||
349 | |||
350 | cFYI(1, "Session Setup"); | ||
351 | |||
352 | if (ses->server) | ||
353 | server = ses->server; | ||
354 | else { | ||
355 | rc = -EIO; | ||
356 | return rc; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * If memory allocation is successful, caller of this function | ||
361 | * frees it. | ||
362 | */ | ||
363 | ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); | ||
364 | if (!ses->ntlmssp) | ||
365 | return -ENOMEM; | ||
366 | |||
367 | ses->server->secType = RawNTLMSSP; | ||
368 | |||
369 | ssetup_ntlmssp_authenticate: | ||
370 | if (phase == NtLmChallenge) | ||
371 | phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ | ||
372 | |||
373 | rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); | ||
374 | if (rc) | ||
375 | return rc; | ||
376 | |||
377 | /* if any of auth flags (ie not sign or seal) are overriden use them */ | ||
378 | if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) | ||
379 | sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/ | ||
380 | else /* if override flags set only sign/seal OR them with global auth */ | ||
381 | sec_flags = global_secflags | ses->overrideSecFlg; | ||
382 | |||
383 | cFYI(1, "sec_flags 0x%x", sec_flags); | ||
384 | |||
385 | req->hdr.SessionId = 0; /* First session, not a reauthenticate */ | ||
386 | req->VcNumber = 0; /* MBZ */ | ||
387 | /* to enable echos and oplocks */ | ||
388 | req->hdr.CreditRequest = cpu_to_le16(3); | ||
389 | |||
390 | /* only one of SMB2 signing flags may be set in SMB2 request */ | ||
391 | if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) | ||
392 | temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; | ||
393 | else if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) | ||
394 | temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; | ||
395 | else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */ | ||
396 | temp = SMB2_NEGOTIATE_SIGNING_ENABLED; | ||
397 | |||
398 | req->SecurityMode = temp; | ||
399 | req->Capabilities = 0; | ||
400 | req->Channel = 0; /* MBZ */ | ||
401 | |||
402 | iov[0].iov_base = (char *)req; | ||
403 | /* 4 for rfc1002 length field and 1 for pad */ | ||
404 | iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; | ||
405 | if (phase == NtLmNegotiate) { | ||
406 | ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), | ||
407 | GFP_KERNEL); | ||
408 | if (ntlmssp_blob == NULL) { | ||
409 | rc = -ENOMEM; | ||
410 | goto ssetup_exit; | ||
411 | } | ||
412 | build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); | ||
413 | if (use_spnego) { | ||
414 | /* blob_length = build_spnego_ntlmssp_blob( | ||
415 | &security_blob, | ||
416 | sizeof(struct _NEGOTIATE_MESSAGE), | ||
417 | ntlmssp_blob); */ | ||
418 | /* BB eventually need to add this */ | ||
419 | cERROR(1, "spnego not supported for SMB2 yet"); | ||
420 | rc = -EOPNOTSUPP; | ||
421 | kfree(ntlmssp_blob); | ||
422 | goto ssetup_exit; | ||
423 | } else { | ||
424 | blob_length = sizeof(struct _NEGOTIATE_MESSAGE); | ||
425 | /* with raw NTLMSSP we don't encapsulate in SPNEGO */ | ||
426 | security_blob = ntlmssp_blob; | ||
427 | } | ||
428 | } else if (phase == NtLmAuthenticate) { | ||
429 | req->hdr.SessionId = ses->Suid; | ||
430 | ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, | ||
431 | GFP_KERNEL); | ||
432 | if (ntlmssp_blob == NULL) { | ||
433 | cERROR(1, "failed to malloc ntlmssp blob"); | ||
434 | rc = -ENOMEM; | ||
435 | goto ssetup_exit; | ||
436 | } | ||
437 | rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses, | ||
438 | nls_cp); | ||
439 | if (rc) { | ||
440 | cFYI(1, "build_ntlmssp_auth_blob failed %d", rc); | ||
441 | goto ssetup_exit; /* BB double check error handling */ | ||
442 | } | ||
443 | if (use_spnego) { | ||
444 | /* blob_length = build_spnego_ntlmssp_blob( | ||
445 | &security_blob, | ||
446 | blob_length, | ||
447 | ntlmssp_blob); */ | ||
448 | cERROR(1, "spnego not supported for SMB2 yet"); | ||
449 | rc = -EOPNOTSUPP; | ||
450 | kfree(ntlmssp_blob); | ||
451 | goto ssetup_exit; | ||
452 | } else { | ||
453 | security_blob = ntlmssp_blob; | ||
454 | } | ||
455 | } else { | ||
456 | cERROR(1, "illegal ntlmssp phase"); | ||
457 | rc = -EIO; | ||
458 | goto ssetup_exit; | ||
459 | } | ||
460 | |||
461 | /* Testing shows that buffer offset must be at location of Buffer[0] */ | ||
462 | req->SecurityBufferOffset = | ||
463 | cpu_to_le16(sizeof(struct smb2_sess_setup_req) - | ||
464 | 1 /* pad */ - 4 /* rfc1001 len */); | ||
465 | req->SecurityBufferLength = cpu_to_le16(blob_length); | ||
466 | iov[1].iov_base = security_blob; | ||
467 | iov[1].iov_len = blob_length; | ||
468 | |||
469 | inc_rfc1001_len(req, blob_length - 1 /* pad */); | ||
470 | |||
471 | /* BB add code to build os and lm fields */ | ||
472 | |||
473 | rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, CIFS_LOG_ERROR); | ||
474 | |||
475 | kfree(security_blob); | ||
476 | rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base; | ||
477 | if (rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) { | ||
478 | if (phase != NtLmNegotiate) { | ||
479 | cERROR(1, "Unexpected more processing error"); | ||
480 | goto ssetup_exit; | ||
481 | } | ||
482 | if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != | ||
483 | le16_to_cpu(rsp->SecurityBufferOffset)) { | ||
484 | cERROR(1, "Invalid security buffer offset %d", | ||
485 | le16_to_cpu(rsp->SecurityBufferOffset)); | ||
486 | rc = -EIO; | ||
487 | goto ssetup_exit; | ||
488 | } | ||
489 | |||
490 | /* NTLMSSP Negotiate sent now processing challenge (response) */ | ||
491 | phase = NtLmChallenge; /* process ntlmssp challenge */ | ||
492 | rc = 0; /* MORE_PROCESSING is not an error here but expected */ | ||
493 | ses->Suid = rsp->hdr.SessionId; | ||
494 | rc = decode_ntlmssp_challenge(rsp->Buffer, | ||
495 | le16_to_cpu(rsp->SecurityBufferLength), ses); | ||
496 | } | ||
497 | |||
498 | /* | ||
499 | * BB eventually add code for SPNEGO decoding of NtlmChallenge blob, | ||
500 | * but at least the raw NTLMSSP case works. | ||
501 | */ | ||
502 | /* | ||
503 | * No tcon so can't do | ||
504 | * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); | ||
505 | */ | ||
506 | if (rc != 0) | ||
507 | goto ssetup_exit; | ||
508 | |||
509 | if (rsp == NULL) { | ||
510 | rc = -EIO; | ||
511 | goto ssetup_exit; | ||
512 | } | ||
513 | |||
514 | ses->session_flags = le16_to_cpu(rsp->SessionFlags); | ||
515 | ssetup_exit: | ||
516 | free_rsp_buf(resp_buftype, rsp); | ||
517 | |||
518 | /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ | ||
519 | if ((phase == NtLmChallenge) && (rc == 0)) | ||
520 | goto ssetup_ntlmssp_authenticate; | ||
521 | return rc; | ||
522 | } | ||
523 | |||
524 | int | ||
525 | SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) | ||
526 | { | ||
527 | struct smb2_logoff_req *req; /* response is also trivial struct */ | ||
528 | int rc = 0; | ||
529 | struct TCP_Server_Info *server; | ||
530 | |||
531 | cFYI(1, "disconnect session %p", ses); | ||
532 | |||
533 | if (ses && (ses->server)) | ||
534 | server = ses->server; | ||
535 | else | ||
536 | return -EIO; | ||
537 | |||
538 | rc = small_smb2_init(SMB2_LOGOFF, NULL, (void **) &req); | ||
539 | if (rc) | ||
540 | return rc; | ||
541 | |||
542 | /* since no tcon, smb2_init can not do this, so do here */ | ||
543 | req->hdr.SessionId = ses->Suid; | ||
544 | |||
545 | rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0); | ||
546 | /* | ||
547 | * No tcon so can't do | ||
548 | * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); | ||
549 | */ | ||
550 | return rc; | ||
551 | } | ||
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index ef8dae213f60..26af68b2955a 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -187,4 +187,41 @@ struct smb2_negotiate_rsp { | |||
187 | __u8 Buffer[1]; /* variable length GSS security buffer */ | 187 | __u8 Buffer[1]; /* variable length GSS security buffer */ |
188 | } __packed; | 188 | } __packed; |
189 | 189 | ||
190 | struct smb2_sess_setup_req { | ||
191 | struct smb2_hdr hdr; | ||
192 | __le16 StructureSize; /* Must be 25 */ | ||
193 | __u8 VcNumber; | ||
194 | __u8 SecurityMode; | ||
195 | __le32 Capabilities; | ||
196 | __le32 Channel; | ||
197 | __le16 SecurityBufferOffset; | ||
198 | __le16 SecurityBufferLength; | ||
199 | __le64 PreviousSessionId; | ||
200 | __u8 Buffer[1]; /* variable length GSS security buffer */ | ||
201 | } __packed; | ||
202 | |||
203 | /* Currently defined SessionFlags */ | ||
204 | #define SMB2_SESSION_FLAG_IS_GUEST 0x0001 | ||
205 | #define SMB2_SESSION_FLAG_IS_NULL 0x0002 | ||
206 | struct smb2_sess_setup_rsp { | ||
207 | struct smb2_hdr hdr; | ||
208 | __le16 StructureSize; /* Must be 9 */ | ||
209 | __le16 SessionFlags; | ||
210 | __le16 SecurityBufferOffset; | ||
211 | __le16 SecurityBufferLength; | ||
212 | __u8 Buffer[1]; /* variable length GSS security buffer */ | ||
213 | } __packed; | ||
214 | |||
215 | struct smb2_logoff_req { | ||
216 | struct smb2_hdr hdr; | ||
217 | __le16 StructureSize; /* Must be 4 */ | ||
218 | __le16 Reserved; | ||
219 | } __packed; | ||
220 | |||
221 | struct smb2_logoff_rsp { | ||
222 | struct smb2_hdr hdr; | ||
223 | __le16 StructureSize; /* Must be 4 */ | ||
224 | __le16 Reserved; | ||
225 | } __packed; | ||
226 | |||
190 | #endif /* _SMB2PDU_H */ | 227 | #endif /* _SMB2PDU_H */ |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 881767002807..9364fbcb90c6 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -47,5 +47,8 @@ extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov, | |||
47 | * are contained within these calls. | 47 | * are contained within these calls. |
48 | */ | 48 | */ |
49 | extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses); | 49 | extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses); |
50 | extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, | ||
51 | const struct nls_table *nls_cp); | ||
52 | extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); | ||
50 | 53 | ||
51 | #endif /* _SMB2PROTO_H */ | 54 | #endif /* _SMB2PROTO_H */ |