Prepare for encryption support (first part). Add decryption and encryption key generation. Thanks to Metze for helping with this.
Reviewed-by: Stefan Metzmacher <metze@samba.org> Signed-off-by: Steve French <steve.french@primarydata.com>
This commit is contained in:
parent
882137c4d6
commit
373512ec5c
10 changed files with 156 additions and 33 deletions
|
@ -227,7 +227,7 @@ struct smb_version_operations {
|
|||
void (*print_stats)(struct seq_file *m, struct cifs_tcon *);
|
||||
void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *);
|
||||
/* verify the message */
|
||||
int (*check_message)(char *, unsigned int);
|
||||
int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
|
||||
bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
|
||||
void (*downgrade_oplock)(struct TCP_Server_Info *,
|
||||
struct cifsInodeInfo *, bool);
|
||||
|
@ -630,6 +630,7 @@ struct TCP_Server_Info {
|
|||
#ifdef CONFIG_CIFS_SMB2
|
||||
unsigned int max_read;
|
||||
unsigned int max_write;
|
||||
__u8 preauth_hash[512];
|
||||
#endif /* CONFIG_CIFS_SMB2 */
|
||||
unsigned long echo_interval;
|
||||
};
|
||||
|
@ -813,7 +814,10 @@ struct cifs_ses {
|
|||
bool need_reconnect:1; /* connection reset, uid now invalid */
|
||||
#ifdef CONFIG_CIFS_SMB2
|
||||
__u16 session_flags;
|
||||
char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */
|
||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 preauth_hash[512];
|
||||
#endif /* CONFIG_CIFS_SMB2 */
|
||||
};
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
|
|||
struct smb_hdr *out_buf,
|
||||
int *bytes_returned);
|
||||
extern int cifs_reconnect(struct TCP_Server_Info *server);
|
||||
extern int checkSMB(char *buf, unsigned int length);
|
||||
extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
|
||||
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
|
||||
extern bool backup_cred(struct cifs_sb_info *);
|
||||
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
|
||||
|
@ -439,7 +439,8 @@ extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
|
|||
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
|
||||
extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
|
||||
extern int calc_seckey(struct cifs_ses *);
|
||||
extern int generate_smb3signingkey(struct cifs_ses *);
|
||||
extern int generate_smb30signingkey(struct cifs_ses *);
|
||||
extern int generate_smb311signingkey(struct cifs_ses *);
|
||||
|
||||
#ifdef CONFIG_CIFS_WEAK_PW_HASH
|
||||
extern int calc_lanman_hash(const char *password, const char *cryptkey,
|
||||
|
|
|
@ -831,7 +831,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
|||
* 48 bytes is enough to display the header and a little bit
|
||||
* into the payload for debugging purposes.
|
||||
*/
|
||||
length = server->ops->check_message(buf, server->total_read);
|
||||
length = server->ops->check_message(buf, server->total_read, server);
|
||||
if (length != 0)
|
||||
cifs_dump_mem("Bad SMB: ", buf,
|
||||
min_t(unsigned int, server->total_read, 48));
|
||||
|
|
|
@ -310,7 +310,7 @@ check_smb_hdr(struct smb_hdr *smb)
|
|||
}
|
||||
|
||||
int
|
||||
checkSMB(char *buf, unsigned int total_read)
|
||||
checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
|
||||
{
|
||||
struct smb_hdr *smb = (struct smb_hdr *)buf;
|
||||
__u32 rfclen = be32_to_cpu(smb->smb_buf_length);
|
||||
|
|
|
@ -38,7 +38,7 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
|
|||
* Make sure that this really is an SMB, that it is a response,
|
||||
* and that the message ids match.
|
||||
*/
|
||||
if ((*(__le32 *)hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
|
||||
if ((hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
|
||||
(mid == wire_mid)) {
|
||||
if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
|
||||
return 0;
|
||||
|
@ -50,9 +50,9 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
|
|||
cifs_dbg(VFS, "Received Request not response\n");
|
||||
}
|
||||
} else { /* bad signature or mid */
|
||||
if (*(__le32 *)hdr->ProtocolId != SMB2_PROTO_NUMBER)
|
||||
if (hdr->ProtocolId != SMB2_PROTO_NUMBER)
|
||||
cifs_dbg(VFS, "Bad protocol string signature header %x\n",
|
||||
*(unsigned int *) hdr->ProtocolId);
|
||||
le32_to_cpu(hdr->ProtocolId));
|
||||
if (mid != wire_mid)
|
||||
cifs_dbg(VFS, "Mids do not match: %llu and %llu\n",
|
||||
mid, wire_mid);
|
||||
|
@ -93,11 +93,11 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
|
|||
};
|
||||
|
||||
int
|
||||
smb2_check_message(char *buf, unsigned int length)
|
||||
smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
|
||||
{
|
||||
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
|
||||
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
|
||||
__u64 mid = le64_to_cpu(hdr->MessageId);
|
||||
__u64 mid;
|
||||
__u32 len = get_rfc1002_length(buf);
|
||||
__u32 clc_len; /* calculated length */
|
||||
int command;
|
||||
|
@ -111,6 +111,30 @@ smb2_check_message(char *buf, unsigned int length)
|
|||
* ie Validate the wct via smb2_struct_sizes table above
|
||||
*/
|
||||
|
||||
if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
|
||||
struct smb2_transform_hdr *thdr =
|
||||
(struct smb2_transform_hdr *)buf;
|
||||
struct cifs_ses *ses = NULL;
|
||||
struct list_head *tmp;
|
||||
|
||||
/* decrypt frame now that it is completely read in */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp, &srvr->smb_ses_list) {
|
||||
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
|
||||
if (ses->Suid == thdr->SessionId)
|
||||
break;
|
||||
|
||||
ses = NULL;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
if (ses == NULL) {
|
||||
cifs_dbg(VFS, "no decryption - session id not found\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mid = le64_to_cpu(hdr->MessageId);
|
||||
if (length < sizeof(struct smb2_pdu)) {
|
||||
if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) {
|
||||
pdu->StructureSize2 = 0;
|
||||
|
@ -322,7 +346,7 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
|
|||
|
||||
/* return pointer to beginning of data area, ie offset from SMB start */
|
||||
if ((*off != 0) && (*len != 0))
|
||||
return (char *)(&hdr->ProtocolId[0]) + *off;
|
||||
return (char *)(&hdr->ProtocolId) + *off;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -182,6 +182,11 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
|
|||
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
|
||||
__u64 wire_mid = le64_to_cpu(hdr->MessageId);
|
||||
|
||||
if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
|
||||
cifs_dbg(VFS, "encrypted frame parsing not supported yet");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
|
||||
if ((mid->mid == wire_mid) &&
|
||||
|
@ -1692,7 +1697,7 @@ struct smb_version_operations smb30_operations = {
|
|||
.get_lease_key = smb2_get_lease_key,
|
||||
.set_lease_key = smb2_set_lease_key,
|
||||
.new_lease_key = smb2_new_lease_key,
|
||||
.generate_signingkey = generate_smb3signingkey,
|
||||
.generate_signingkey = generate_smb30signingkey,
|
||||
.calc_signature = smb3_calc_signature,
|
||||
.set_integrity = smb3_set_integrity,
|
||||
.is_read_op = smb21_is_read_op,
|
||||
|
@ -1779,7 +1784,7 @@ struct smb_version_operations smb311_operations = {
|
|||
.get_lease_key = smb2_get_lease_key,
|
||||
.set_lease_key = smb2_set_lease_key,
|
||||
.new_lease_key = smb2_new_lease_key,
|
||||
.generate_signingkey = generate_smb3signingkey,
|
||||
.generate_signingkey = generate_smb311signingkey,
|
||||
.calc_signature = smb3_calc_signature,
|
||||
.set_integrity = smb3_set_integrity,
|
||||
.is_read_op = smb21_is_read_op,
|
||||
|
@ -1838,7 +1843,7 @@ struct smb_version_values smb21_values = {
|
|||
struct smb_version_values smb30_values = {
|
||||
.version_string = SMB30_VERSION_STRING,
|
||||
.protocol_id = SMB30_PROT_ID,
|
||||
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES,
|
||||
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
|
||||
.large_lock_type = 0,
|
||||
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
||||
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
||||
|
@ -1858,7 +1863,7 @@ struct smb_version_values smb30_values = {
|
|||
struct smb_version_values smb302_values = {
|
||||
.version_string = SMB302_VERSION_STRING,
|
||||
.protocol_id = SMB302_PROT_ID,
|
||||
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES,
|
||||
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
|
||||
.large_lock_type = 0,
|
||||
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
||||
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
||||
|
|
|
@ -97,10 +97,7 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
|
|||
hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr)
|
||||
- 4 /* RFC 1001 length field itself not counted */);
|
||||
|
||||
hdr->ProtocolId[0] = 0xFE;
|
||||
hdr->ProtocolId[1] = 'S';
|
||||
hdr->ProtocolId[2] = 'M';
|
||||
hdr->ProtocolId[3] = 'B';
|
||||
hdr->ProtocolId = SMB2_PROTO_NUMBER;
|
||||
hdr->StructureSize = cpu_to_le16(64);
|
||||
hdr->Command = smb2_cmd;
|
||||
hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
|
||||
|
@ -1573,7 +1570,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
|||
goto ioctl_exit;
|
||||
}
|
||||
|
||||
memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
|
||||
memcpy(*out_data,
|
||||
(char *)&rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
|
||||
*plen);
|
||||
ioctl_exit:
|
||||
free_rsp_buf(resp_buftype, rsp);
|
||||
|
@ -2093,7 +2091,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
|
|||
}
|
||||
|
||||
if (*buf) {
|
||||
memcpy(*buf, (char *)rsp->hdr.ProtocolId + rsp->DataOffset,
|
||||
memcpy(*buf, (char *)&rsp->hdr.ProtocolId + rsp->DataOffset,
|
||||
*nbytes);
|
||||
free_rsp_buf(resp_buftype, iov[0].iov_base);
|
||||
} else if (resp_buftype != CIFS_NO_BUFFER) {
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */
|
||||
|
||||
#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
|
||||
#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
|
||||
|
||||
/*
|
||||
* SMB2 Header Definition
|
||||
|
@ -102,7 +103,7 @@ struct smb2_hdr {
|
|||
__be32 smb2_buf_length; /* big endian on wire */
|
||||
/* length is only two or three bytes - with
|
||||
one or two byte type preceding it that MBZ */
|
||||
__u8 ProtocolId[4]; /* 0xFE 'S' 'M' 'B' */
|
||||
__le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */
|
||||
__le16 StructureSize; /* 64 */
|
||||
__le16 CreditCharge; /* MBZ */
|
||||
__le32 Status; /* Error from server */
|
||||
|
@ -128,11 +129,10 @@ struct smb2_transform_hdr {
|
|||
one or two byte type preceding it that MBZ */
|
||||
__u8 ProtocolId[4]; /* 0xFD 'S' 'M' 'B' */
|
||||
__u8 Signature[16];
|
||||
__u8 Nonce[11];
|
||||
__u8 Reserved[5];
|
||||
__u8 Nonce[16];
|
||||
__le32 OriginalMessageSize;
|
||||
__u16 Reserved1;
|
||||
__le16 EncryptionAlgorithm;
|
||||
__le16 Flags; /* EncryptionAlgorithm */
|
||||
__u64 SessionId;
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ struct smb_rqst;
|
|||
*****************************************************************
|
||||
*/
|
||||
extern int map_smb2_to_linux_error(char *buf, bool log_err);
|
||||
extern int smb2_check_message(char *buf, unsigned int length);
|
||||
extern int smb2_check_message(char *buf, unsigned int length,
|
||||
struct TCP_Server_Info *server);
|
||||
extern unsigned int smb2_calc_size(void *buf);
|
||||
extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
|
||||
extern __le16 *cifs_convert_path_to_utf16(const char *from,
|
||||
|
|
|
@ -222,8 +222,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
|||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
generate_smb3signingkey(struct cifs_ses *ses)
|
||||
static int generate_key(struct cifs_ses *ses, struct kvec label,
|
||||
struct kvec context, __u8 *key, unsigned int key_size)
|
||||
{
|
||||
unsigned char zero = 0x0;
|
||||
__u8 i[4] = {0, 0, 0, 1};
|
||||
|
@ -233,7 +233,7 @@ generate_smb3signingkey(struct cifs_ses *ses)
|
|||
unsigned char *hashptr = prfhash;
|
||||
|
||||
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
|
||||
memset(ses->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
|
||||
memset(key, 0x0, key_size);
|
||||
|
||||
rc = smb3_crypto_shash_allocate(ses->server);
|
||||
if (rc) {
|
||||
|
@ -262,7 +262,7 @@ generate_smb3signingkey(struct cifs_ses *ses)
|
|||
}
|
||||
|
||||
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
|
||||
"SMB2AESCMAC", 12);
|
||||
label.iov_base, label.iov_len);
|
||||
if (rc) {
|
||||
cifs_dbg(VFS, "%s: Could not update with label\n", __func__);
|
||||
goto smb3signkey_ret;
|
||||
|
@ -276,7 +276,7 @@ generate_smb3signingkey(struct cifs_ses *ses)
|
|||
}
|
||||
|
||||
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
|
||||
"SmbSign", 8);
|
||||
context.iov_base, context.iov_len);
|
||||
if (rc) {
|
||||
cifs_dbg(VFS, "%s: Could not update with context\n", __func__);
|
||||
goto smb3signkey_ret;
|
||||
|
@ -296,12 +296,102 @@ generate_smb3signingkey(struct cifs_ses *ses)
|
|||
goto smb3signkey_ret;
|
||||
}
|
||||
|
||||
memcpy(ses->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE);
|
||||
memcpy(key, hashptr, key_size);
|
||||
|
||||
smb3signkey_ret:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct derivation {
|
||||
struct kvec label;
|
||||
struct kvec context;
|
||||
};
|
||||
|
||||
struct derivation_triplet {
|
||||
struct derivation signing;
|
||||
struct derivation encryption;
|
||||
struct derivation decryption;
|
||||
};
|
||||
|
||||
static int
|
||||
generate_smb3signingkey(struct cifs_ses *ses,
|
||||
const struct derivation_triplet *ptriplet)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = generate_key(ses, ptriplet->signing.label,
|
||||
ptriplet->signing.context, ses->smb3signingkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = generate_key(ses, ptriplet->encryption.label,
|
||||
ptriplet->encryption.context, ses->smb3encryptionkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return generate_key(ses, ptriplet->decryption.label,
|
||||
ptriplet->decryption.context,
|
||||
ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
|
||||
}
|
||||
|
||||
int
|
||||
generate_smb30signingkey(struct cifs_ses *ses)
|
||||
|
||||
{
|
||||
struct derivation_triplet triplet;
|
||||
struct derivation *d;
|
||||
|
||||
d = &triplet.signing;
|
||||
d->label.iov_base = "SMB2AESCMAC";
|
||||
d->label.iov_len = 12;
|
||||
d->context.iov_base = "SmbSign";
|
||||
d->context.iov_len = 8;
|
||||
|
||||
d = &triplet.encryption;
|
||||
d->label.iov_base = "SMB2AESCCM";
|
||||
d->label.iov_len = 11;
|
||||
d->context.iov_base = "ServerIn ";
|
||||
d->context.iov_len = 10;
|
||||
|
||||
d = &triplet.decryption;
|
||||
d->label.iov_base = "SMB2AESCCM";
|
||||
d->label.iov_len = 11;
|
||||
d->context.iov_base = "ServerOut";
|
||||
d->context.iov_len = 10;
|
||||
|
||||
return generate_smb3signingkey(ses, &triplet);
|
||||
}
|
||||
|
||||
int
|
||||
generate_smb311signingkey(struct cifs_ses *ses)
|
||||
|
||||
{
|
||||
struct derivation_triplet triplet;
|
||||
struct derivation *d;
|
||||
|
||||
d = &triplet.signing;
|
||||
d->label.iov_base = "SMB2AESCMAC";
|
||||
d->label.iov_len = 12;
|
||||
d->context.iov_base = "SmbSign";
|
||||
d->context.iov_len = 8;
|
||||
|
||||
d = &triplet.encryption;
|
||||
d->label.iov_base = "SMB2AESCCM";
|
||||
d->label.iov_len = 11;
|
||||
d->context.iov_base = "ServerIn ";
|
||||
d->context.iov_len = 10;
|
||||
|
||||
d = &triplet.decryption;
|
||||
d->label.iov_base = "SMB2AESCCM";
|
||||
d->label.iov_len = 11;
|
||||
d->context.iov_base = "ServerOut";
|
||||
d->context.iov_len = 10;
|
||||
|
||||
return generate_smb3signingkey(ses, &triplet);
|
||||
}
|
||||
|
||||
int
|
||||
smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue