aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorPavel Shilovsky <piastry@etersoft.ru>2011-12-27 07:22:00 -0500
committerPavel Shilovsky <pshilovsky@samba.org>2012-07-24 13:54:57 -0400
commit5478f9ba9a34d660eb3227dcd16314689c51f946 (patch)
tree6cb95588d403d5a962f6450b68d2daaf23566250 /fs/cifs
parentec2e4523fdba88317e06d0c7a88af3a0860447fc (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.h3
-rw-r--r--fs/cifs/ntlmssp.h10
-rw-r--r--fs/cifs/sess.c6
-rw-r--r--fs/cifs/smb2misc.c5
-rw-r--r--fs/cifs/smb2ops.c2
-rw-r--r--fs/cifs/smb2pdu.c221
-rw-r--r--fs/cifs/smb2pdu.h37
-rw-r--r--fs/cifs/smb2proto.h3
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
134int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
135void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses);
136int 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
367static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, 367int 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 */
418static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, 418void 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 */
454static int build_ntlmssp_auth_blob(unsigned char *pbuffer, 454int 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
175struct smb_version_values smb21_values = { 177struct 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
332int
333SMB2_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
369ssetup_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);
515ssetup_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
524int
525SMB2_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
190struct 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
206struct 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
215struct smb2_logoff_req {
216 struct smb2_hdr hdr;
217 __le16 StructureSize; /* Must be 4 */
218 __le16 Reserved;
219} __packed;
220
221struct 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 */
49extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses); 49extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses);
50extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
51 const struct nls_table *nls_cp);
52extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses);
50 53
51#endif /* _SMB2PROTO_H */ 54#endif /* _SMB2PROTO_H */