CIFS: Add session setup/logoff capability for SMB2
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
parent
ec2e4523fd
commit
5478f9ba9a
8 changed files with 284 additions and 3 deletions
|
@ -504,6 +504,9 @@ struct cifs_ses {
|
||||||
struct session_key auth_key;
|
struct session_key auth_key;
|
||||||
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
|
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
|
||||||
bool need_reconnect:1; /* connection reset, uid now invalid */
|
bool need_reconnect:1; /* connection reset, uid now invalid */
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
__u16 session_flags;
|
||||||
|
#endif /* CONFIG_CIFS_SMB2 */
|
||||||
};
|
};
|
||||||
/* no more than one of the following three session flags may be set */
|
/* no more than one of the following three session flags may be set */
|
||||||
#define CIFS_SES_NT4 1
|
#define CIFS_SES_NT4 1
|
||||||
|
|
|
@ -126,3 +126,13 @@ typedef struct _AUTHENTICATE_MESSAGE {
|
||||||
do not set the version is present flag */
|
do not set the version is present flag */
|
||||||
char UserString[0];
|
char UserString[0];
|
||||||
} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
|
} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Size of the session key (crypto key encrypted with the password
|
||||||
|
*/
|
||||||
|
|
||||||
|
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
|
||||||
|
void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses);
|
||||||
|
int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen,
|
||||||
|
struct cifs_ses *ses,
|
||||||
|
const struct nls_table *nls_cp);
|
||||||
|
|
|
@ -364,7 +364,7 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
||||||
struct cifs_ses *ses)
|
struct cifs_ses *ses)
|
||||||
{
|
{
|
||||||
unsigned int tioffset; /* challenge message target info area */
|
unsigned int tioffset; /* challenge message target info area */
|
||||||
|
@ -415,7 +415,7 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
||||||
|
|
||||||
/* We do not malloc the blob, it is passed in pbuffer, because
|
/* We do not malloc the blob, it is passed in pbuffer, because
|
||||||
it is fixed size, and small, making this approach cleaner */
|
it is fixed size, and small, making this approach cleaner */
|
||||||
static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
||||||
struct cifs_ses *ses)
|
struct cifs_ses *ses)
|
||||||
{
|
{
|
||||||
NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
|
NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
|
||||||
|
@ -451,7 +451,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
||||||
/* We do not malloc the blob, it is passed in pbuffer, because its
|
/* We do not malloc the blob, it is passed in pbuffer, because its
|
||||||
maximum possible size is fixed and small, making this approach cleaner.
|
maximum possible size is fixed and small, making this approach cleaner.
|
||||||
This function returns the length of the data in the blob */
|
This function returns the length of the data in the blob */
|
||||||
static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
|
int build_ntlmssp_auth_blob(unsigned char *pbuffer,
|
||||||
u16 *buflen,
|
u16 *buflen,
|
||||||
struct cifs_ses *ses,
|
struct cifs_ses *ses,
|
||||||
const struct nls_table *nls_cp)
|
const struct nls_table *nls_cp)
|
||||||
|
|
|
@ -224,6 +224,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
|
||||||
((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength);
|
((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength);
|
||||||
break;
|
break;
|
||||||
case SMB2_SESSION_SETUP:
|
case SMB2_SESSION_SETUP:
|
||||||
|
*off = le16_to_cpu(
|
||||||
|
((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferOffset);
|
||||||
|
*len = le16_to_cpu(
|
||||||
|
((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength);
|
||||||
|
break;
|
||||||
case SMB2_CREATE:
|
case SMB2_CREATE:
|
||||||
case SMB2_READ:
|
case SMB2_READ:
|
||||||
case SMB2_QUERY_INFO:
|
case SMB2_QUERY_INFO:
|
||||||
|
|
|
@ -170,6 +170,8 @@ struct smb_version_operations smb21_operations = {
|
||||||
.dump_detail = smb2_dump_detail,
|
.dump_detail = smb2_dump_detail,
|
||||||
.need_neg = smb2_need_neg,
|
.need_neg = smb2_need_neg,
|
||||||
.negotiate = smb2_negotiate,
|
.negotiate = smb2_negotiate,
|
||||||
|
.sess_setup = SMB2_sess_setup,
|
||||||
|
.logoff = SMB2_logoff,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb21_values = {
|
struct smb_version_values smb21_values = {
|
||||||
|
|
|
@ -328,3 +328,224 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||||
free_rsp_buf(resp_buftype, rsp);
|
free_rsp_buf(resp_buftype, rsp);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
const struct nls_table *nls_cp)
|
||||||
|
{
|
||||||
|
struct smb2_sess_setup_req *req;
|
||||||
|
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||||
|
struct kvec iov[2];
|
||||||
|
int rc = 0;
|
||||||
|
int resp_buftype;
|
||||||
|
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
|
unsigned int sec_flags;
|
||||||
|
u8 temp = 0;
|
||||||
|
u16 blob_length = 0;
|
||||||
|
char *security_blob;
|
||||||
|
char *ntlmssp_blob = NULL;
|
||||||
|
bool use_spnego = false; /* else use raw ntlmssp */
|
||||||
|
|
||||||
|
cFYI(1, "Session Setup");
|
||||||
|
|
||||||
|
if (ses->server)
|
||||||
|
server = ses->server;
|
||||||
|
else {
|
||||||
|
rc = -EIO;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If memory allocation is successful, caller of this function
|
||||||
|
* frees it.
|
||||||
|
*/
|
||||||
|
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
|
||||||
|
if (!ses->ntlmssp)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ses->server->secType = RawNTLMSSP;
|
||||||
|
|
||||||
|
ssetup_ntlmssp_authenticate:
|
||||||
|
if (phase == NtLmChallenge)
|
||||||
|
phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
|
||||||
|
|
||||||
|
rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* if any of auth flags (ie not sign or seal) are overriden use them */
|
||||||
|
if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
|
||||||
|
sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/
|
||||||
|
else /* if override flags set only sign/seal OR them with global auth */
|
||||||
|
sec_flags = global_secflags | ses->overrideSecFlg;
|
||||||
|
|
||||||
|
cFYI(1, "sec_flags 0x%x", sec_flags);
|
||||||
|
|
||||||
|
req->hdr.SessionId = 0; /* First session, not a reauthenticate */
|
||||||
|
req->VcNumber = 0; /* MBZ */
|
||||||
|
/* to enable echos and oplocks */
|
||||||
|
req->hdr.CreditRequest = cpu_to_le16(3);
|
||||||
|
|
||||||
|
/* only one of SMB2 signing flags may be set in SMB2 request */
|
||||||
|
if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN)
|
||||||
|
temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
|
||||||
|
else if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
|
||||||
|
temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
|
||||||
|
else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */
|
||||||
|
temp = SMB2_NEGOTIATE_SIGNING_ENABLED;
|
||||||
|
|
||||||
|
req->SecurityMode = temp;
|
||||||
|
req->Capabilities = 0;
|
||||||
|
req->Channel = 0; /* MBZ */
|
||||||
|
|
||||||
|
iov[0].iov_base = (char *)req;
|
||||||
|
/* 4 for rfc1002 length field and 1 for pad */
|
||||||
|
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
|
||||||
|
if (phase == NtLmNegotiate) {
|
||||||
|
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (ntlmssp_blob == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto ssetup_exit;
|
||||||
|
}
|
||||||
|
build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
|
||||||
|
if (use_spnego) {
|
||||||
|
/* blob_length = build_spnego_ntlmssp_blob(
|
||||||
|
&security_blob,
|
||||||
|
sizeof(struct _NEGOTIATE_MESSAGE),
|
||||||
|
ntlmssp_blob); */
|
||||||
|
/* BB eventually need to add this */
|
||||||
|
cERROR(1, "spnego not supported for SMB2 yet");
|
||||||
|
rc = -EOPNOTSUPP;
|
||||||
|
kfree(ntlmssp_blob);
|
||||||
|
goto ssetup_exit;
|
||||||
|
} else {
|
||||||
|
blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
|
||||||
|
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
|
||||||
|
security_blob = ntlmssp_blob;
|
||||||
|
}
|
||||||
|
} else if (phase == NtLmAuthenticate) {
|
||||||
|
req->hdr.SessionId = ses->Suid;
|
||||||
|
ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (ntlmssp_blob == NULL) {
|
||||||
|
cERROR(1, "failed to malloc ntlmssp blob");
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto ssetup_exit;
|
||||||
|
}
|
||||||
|
rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses,
|
||||||
|
nls_cp);
|
||||||
|
if (rc) {
|
||||||
|
cFYI(1, "build_ntlmssp_auth_blob failed %d", rc);
|
||||||
|
goto ssetup_exit; /* BB double check error handling */
|
||||||
|
}
|
||||||
|
if (use_spnego) {
|
||||||
|
/* blob_length = build_spnego_ntlmssp_blob(
|
||||||
|
&security_blob,
|
||||||
|
blob_length,
|
||||||
|
ntlmssp_blob); */
|
||||||
|
cERROR(1, "spnego not supported for SMB2 yet");
|
||||||
|
rc = -EOPNOTSUPP;
|
||||||
|
kfree(ntlmssp_blob);
|
||||||
|
goto ssetup_exit;
|
||||||
|
} else {
|
||||||
|
security_blob = ntlmssp_blob;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cERROR(1, "illegal ntlmssp phase");
|
||||||
|
rc = -EIO;
|
||||||
|
goto ssetup_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Testing shows that buffer offset must be at location of Buffer[0] */
|
||||||
|
req->SecurityBufferOffset =
|
||||||
|
cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
|
||||||
|
1 /* pad */ - 4 /* rfc1001 len */);
|
||||||
|
req->SecurityBufferLength = cpu_to_le16(blob_length);
|
||||||
|
iov[1].iov_base = security_blob;
|
||||||
|
iov[1].iov_len = blob_length;
|
||||||
|
|
||||||
|
inc_rfc1001_len(req, blob_length - 1 /* pad */);
|
||||||
|
|
||||||
|
/* BB add code to build os and lm fields */
|
||||||
|
|
||||||
|
rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, CIFS_LOG_ERROR);
|
||||||
|
|
||||||
|
kfree(security_blob);
|
||||||
|
rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
|
||||||
|
if (rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||||||
|
if (phase != NtLmNegotiate) {
|
||||||
|
cERROR(1, "Unexpected more processing error");
|
||||||
|
goto ssetup_exit;
|
||||||
|
}
|
||||||
|
if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
|
||||||
|
le16_to_cpu(rsp->SecurityBufferOffset)) {
|
||||||
|
cERROR(1, "Invalid security buffer offset %d",
|
||||||
|
le16_to_cpu(rsp->SecurityBufferOffset));
|
||||||
|
rc = -EIO;
|
||||||
|
goto ssetup_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NTLMSSP Negotiate sent now processing challenge (response) */
|
||||||
|
phase = NtLmChallenge; /* process ntlmssp challenge */
|
||||||
|
rc = 0; /* MORE_PROCESSING is not an error here but expected */
|
||||||
|
ses->Suid = rsp->hdr.SessionId;
|
||||||
|
rc = decode_ntlmssp_challenge(rsp->Buffer,
|
||||||
|
le16_to_cpu(rsp->SecurityBufferLength), ses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
|
||||||
|
* but at least the raw NTLMSSP case works.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* No tcon so can't do
|
||||||
|
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
|
||||||
|
*/
|
||||||
|
if (rc != 0)
|
||||||
|
goto ssetup_exit;
|
||||||
|
|
||||||
|
if (rsp == NULL) {
|
||||||
|
rc = -EIO;
|
||||||
|
goto ssetup_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||||
|
ssetup_exit:
|
||||||
|
free_rsp_buf(resp_buftype, rsp);
|
||||||
|
|
||||||
|
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
|
||||||
|
if ((phase == NtLmChallenge) && (rc == 0))
|
||||||
|
goto ssetup_ntlmssp_authenticate;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
|
||||||
|
{
|
||||||
|
struct smb2_logoff_req *req; /* response is also trivial struct */
|
||||||
|
int rc = 0;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
|
|
||||||
|
cFYI(1, "disconnect session %p", ses);
|
||||||
|
|
||||||
|
if (ses && (ses->server))
|
||||||
|
server = ses->server;
|
||||||
|
else
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
rc = small_smb2_init(SMB2_LOGOFF, NULL, (void **) &req);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* since no tcon, smb2_init can not do this, so do here */
|
||||||
|
req->hdr.SessionId = ses->Suid;
|
||||||
|
|
||||||
|
rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
|
||||||
|
/*
|
||||||
|
* No tcon so can't do
|
||||||
|
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
|
||||||
|
*/
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
|
@ -187,4 +187,41 @@ struct smb2_negotiate_rsp {
|
||||||
__u8 Buffer[1]; /* variable length GSS security buffer */
|
__u8 Buffer[1]; /* variable length GSS security buffer */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_sess_setup_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 25 */
|
||||||
|
__u8 VcNumber;
|
||||||
|
__u8 SecurityMode;
|
||||||
|
__le32 Capabilities;
|
||||||
|
__le32 Channel;
|
||||||
|
__le16 SecurityBufferOffset;
|
||||||
|
__le16 SecurityBufferLength;
|
||||||
|
__le64 PreviousSessionId;
|
||||||
|
__u8 Buffer[1]; /* variable length GSS security buffer */
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/* Currently defined SessionFlags */
|
||||||
|
#define SMB2_SESSION_FLAG_IS_GUEST 0x0001
|
||||||
|
#define SMB2_SESSION_FLAG_IS_NULL 0x0002
|
||||||
|
struct smb2_sess_setup_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 9 */
|
||||||
|
__le16 SessionFlags;
|
||||||
|
__le16 SecurityBufferOffset;
|
||||||
|
__le16 SecurityBufferLength;
|
||||||
|
__u8 Buffer[1]; /* variable length GSS security buffer */
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_logoff_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 4 */
|
||||||
|
__le16 Reserved;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_logoff_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 4 */
|
||||||
|
__le16 Reserved;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#endif /* _SMB2PDU_H */
|
#endif /* _SMB2PDU_H */
|
||||||
|
|
|
@ -47,5 +47,8 @@ extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
|
||||||
* are contained within these calls.
|
* are contained within these calls.
|
||||||
*/
|
*/
|
||||||
extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses);
|
extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses);
|
||||||
|
extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
const struct nls_table *nls_cp);
|
||||||
|
extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses);
|
||||||
|
|
||||||
#endif /* _SMB2PROTO_H */
|
#endif /* _SMB2PROTO_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue