Skip to content

Commit 1bb5681

Browse files
aaptelsmfrench
authored andcommitted
cifs: change format of CIFS_FULL_KEY_DUMP ioctl
Make CIFS_FULL_KEY_DUMP ioctl able to return variable-length keys. * userspace needs to pass the struct size along with optional session_id and some space at the end to store keys * if there is enough space kernel returns keys in the extra space and sets the length of each key via xyz_key_length fields This also fixes the build error for get_user() on ARM. Sample program: #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <sys/fcntl.h> #include <sys/ioctl.h> struct smb3_full_key_debug_info { uint32_t in_size; uint64_t session_id; uint16_t cipher_type; uint8_t session_key_length; uint8_t server_in_key_length; uint8_t server_out_key_length; uint8_t data[]; /* * return this struct with the keys appended at the end: * uint8_t session_key[session_key_length]; * uint8_t server_in_key[server_in_key_length]; * uint8_t server_out_key[server_out_key_length]; */ } __attribute__((packed)); #define CIFS_IOCTL_MAGIC 0xCF #define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info) void dump(const void *p, size_t len) { const char *hex = "0123456789ABCDEF"; const uint8_t *b = p; for (int i = 0; i < len; i++) printf("%c%c ", hex[(b[i]>>4)&0xf], hex[b[i]&0xf]); putchar('\n'); } int main(int argc, char **argv) { struct smb3_full_key_debug_info *keys; uint8_t buf[sizeof(*keys)+1024] = {0}; size_t off = 0; int fd, rc; keys = (struct smb3_full_key_debug_info *)&buf; keys->in_size = sizeof(buf); fd = open(argv[1], O_RDONLY); if (fd < 0) perror("open"), exit(1); rc = ioctl(fd, CIFS_DUMP_FULL_KEY, keys); if (rc < 0) perror("ioctl"), exit(1); printf("SessionId "); dump(&keys->session_id, 8); printf("Cipher %04x\n", keys->cipher_type); printf("SessionKey "); dump(keys->data+off, keys->session_key_length); off += keys->session_key_length; printf("ServerIn Key "); dump(keys->data+off, keys->server_in_key_length); off += keys->server_in_key_length; printf("ServerOut Key "); dump(keys->data+off, keys->server_out_key_length); return 0; } Usage: $ gcc -o dumpkeys dumpkeys.c Against Windows Server 2020 preview (with AES-256-GCM support): # mount.cifs //$ip/test /mnt -o "username=administrator,password=foo,vers=3.0,seal" # ./dumpkeys /mnt/somefile SessionId 0D 00 00 00 00 0C 00 00 Cipher 0002 SessionKey AB CD CC 0D E4 15 05 0C 6F 3C 92 90 19 F3 0D 25 ServerIn Key 73 C6 6A C8 6B 08 CF A2 CB 8E A5 7D 10 D1 5B DC ServerOut Key 6D 7E 2B A1 71 9D D7 2B 94 7B BA C4 F0 A5 A4 F8 # umount /mnt With 256 bit keys: # echo 1 > /sys/module/cifs/parameters/require_gcm_256 # mount.cifs //$ip/test /mnt -o "username=administrator,password=foo,vers=3.11,seal" # ./dumpkeys /mnt/somefile SessionId 09 00 00 00 00 0C 00 00 Cipher 0004 SessionKey 93 F5 82 3B 2F B7 2A 50 0B B9 BA 26 FB 8C 8B 03 ServerIn Key 6C 6A 89 B2 CB 7B 78 E8 04 93 37 DA 22 53 47 DF B3 2C 5F 02 26 70 43 DB 8D 33 7B DC 66 D3 75 A9 ServerOut Key 04 11 AA D7 52 C7 A8 0F ED E3 93 3A 65 FE 03 AD 3F 63 03 01 2B C0 1B D7 D7 E5 52 19 7F CC 46 B4 Signed-off-by: Aurelien Aptel <[email protected]> Reviewed-by: Ronnie Sahlberg <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent eb06881 commit 1bb5681

File tree

3 files changed

+126
-45
lines changed

3 files changed

+126
-45
lines changed

fs/cifs/cifs_ioctl.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,28 @@ struct smb3_key_debug_info {
7272
} __packed;
7373

7474
/*
75-
* Dump full key (32 byte encrypt/decrypt keys instead of 16 bytes)
76-
* is needed if GCM256 (stronger encryption) negotiated
75+
* Dump variable-sized keys
7776
*/
7877
struct smb3_full_key_debug_info {
79-
__u64 Suid;
78+
/* INPUT: size of userspace buffer */
79+
__u32 in_size;
80+
81+
/*
82+
* INPUT: 0 for current user, otherwise session to dump
83+
* OUTPUT: session id that was dumped
84+
*/
85+
__u64 session_id;
8086
__u16 cipher_type;
81-
__u8 auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */
82-
__u8 smb3encryptionkey[32]; /* SMB3_ENC_DEC_KEY_SIZE */
83-
__u8 smb3decryptionkey[32]; /* SMB3_ENC_DEC_KEY_SIZE */
87+
__u8 session_key_length;
88+
__u8 server_in_key_length;
89+
__u8 server_out_key_length;
90+
__u8 data[];
91+
/*
92+
* return this struct with the keys appended at the end:
93+
* __u8 session_key[session_key_length];
94+
* __u8 server_in_key[server_in_key_length];
95+
* __u8 server_out_key[server_out_key_length];
96+
*/
8497
} __packed;
8598

8699
struct smb3_notify {

fs/cifs/cifspdu.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@
148148
#define SMB3_SIGN_KEY_SIZE (16)
149149

150150
/*
151-
* Size of the smb3 encryption/decryption keys
151+
* Size of the smb3 encryption/decryption key storage.
152+
* This size is big enough to store any cipher key types.
152153
*/
153154
#define SMB3_ENC_DEC_KEY_SIZE (32)
154155

fs/cifs/ioctl.c

Lines changed: 105 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "cifsfs.h"
3434
#include "cifs_ioctl.h"
3535
#include "smb2proto.h"
36+
#include "smb2glob.h"
3637
#include <linux/btrfs.h>
3738

3839
static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
@@ -214,48 +215,112 @@ static int cifs_shutdown(struct super_block *sb, unsigned long arg)
214215
return 0;
215216
}
216217

217-
static int cifs_dump_full_key(struct cifs_tcon *tcon, unsigned long arg)
218+
static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in)
218219
{
219-
struct smb3_full_key_debug_info pfull_key_inf;
220-
__u64 suid;
221-
struct list_head *tmp;
220+
struct smb3_full_key_debug_info out;
222221
struct cifs_ses *ses;
222+
int rc = 0;
223223
bool found = false;
224+
u8 __user *end;
224225

225-
if (!smb3_encryption_required(tcon))
226-
return -EOPNOTSUPP;
226+
if (!smb3_encryption_required(tcon)) {
227+
rc = -EOPNOTSUPP;
228+
goto out;
229+
}
230+
231+
/* copy user input into our output buffer */
232+
if (copy_from_user(&out, in, sizeof(out))) {
233+
rc = -EINVAL;
234+
goto out;
235+
}
236+
237+
if (!out.session_id) {
238+
/* if ses id is 0, use current user session */
239+
ses = tcon->ses;
240+
} else {
241+
/* otherwise if a session id is given, look for it in all our sessions */
242+
struct cifs_ses *ses_it = NULL;
243+
struct TCP_Server_Info *server_it = NULL;
227244

228-
ses = tcon->ses; /* default to user id for current user */
229-
if (get_user(suid, (__u64 __user *)arg))
230-
suid = 0;
231-
if (suid) {
232-
/* search to see if there is a session with a matching SMB UID */
233245
spin_lock(&cifs_tcp_ses_lock);
234-
list_for_each(tmp, &tcon->ses->server->smb_ses_list) {
235-
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
236-
if (ses->Suid == suid) {
237-
found = true;
238-
break;
246+
list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) {
247+
list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) {
248+
if (ses_it->Suid == out.session_id) {
249+
ses = ses_it;
250+
/*
251+
* since we are using the session outside the crit
252+
* section, we need to make sure it won't be released
253+
* so increment its refcount
254+
*/
255+
ses->ses_count++;
256+
found = true;
257+
goto search_end;
258+
}
239259
}
240260
}
261+
search_end:
241262
spin_unlock(&cifs_tcp_ses_lock);
242-
if (found == false)
243-
return -EINVAL;
244-
} /* else uses default user's SMB UID (ie current user) */
245-
246-
pfull_key_inf.cipher_type = le16_to_cpu(ses->server->cipher_type);
247-
pfull_key_inf.Suid = ses->Suid;
248-
memcpy(pfull_key_inf.auth_key, ses->auth_key.response,
249-
16 /* SMB2_NTLMV2_SESSKEY_SIZE */);
250-
memcpy(pfull_key_inf.smb3decryptionkey, ses->smb3decryptionkey,
251-
32 /* SMB3_ENC_DEC_KEY_SIZE */);
252-
memcpy(pfull_key_inf.smb3encryptionkey,
253-
ses->smb3encryptionkey, 32 /* SMB3_ENC_DEC_KEY_SIZE */);
254-
if (copy_to_user((void __user *)arg, &pfull_key_inf,
255-
sizeof(struct smb3_full_key_debug_info)))
256-
return -EFAULT;
263+
if (!found) {
264+
rc = -ENOENT;
265+
goto out;
266+
}
267+
}
257268

258-
return 0;
269+
switch (ses->server->cipher_type) {
270+
case SMB2_ENCRYPTION_AES128_CCM:
271+
case SMB2_ENCRYPTION_AES128_GCM:
272+
out.session_key_length = CIFS_SESS_KEY_SIZE;
273+
out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE;
274+
break;
275+
case SMB2_ENCRYPTION_AES256_CCM:
276+
case SMB2_ENCRYPTION_AES256_GCM:
277+
out.session_key_length = CIFS_SESS_KEY_SIZE;
278+
out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE;
279+
break;
280+
default:
281+
rc = -EOPNOTSUPP;
282+
goto out;
283+
}
284+
285+
/* check if user buffer is big enough to store all the keys */
286+
if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length
287+
+ out.server_out_key_length) {
288+
rc = -ENOBUFS;
289+
goto out;
290+
}
291+
292+
out.session_id = ses->Suid;
293+
out.cipher_type = le16_to_cpu(ses->server->cipher_type);
294+
295+
/* overwrite user input with our output */
296+
if (copy_to_user(in, &out, sizeof(out))) {
297+
rc = -EINVAL;
298+
goto out;
299+
}
300+
301+
/* append all the keys at the end of the user buffer */
302+
end = in->data;
303+
if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) {
304+
rc = -EINVAL;
305+
goto out;
306+
}
307+
end += out.session_key_length;
308+
309+
if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) {
310+
rc = -EINVAL;
311+
goto out;
312+
}
313+
end += out.server_in_key_length;
314+
315+
if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) {
316+
rc = -EINVAL;
317+
goto out;
318+
}
319+
320+
out:
321+
if (found)
322+
cifs_put_smb_ses(ses);
323+
return rc;
259324
}
260325

261326
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
@@ -371,6 +436,10 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
371436
rc = -EOPNOTSUPP;
372437
break;
373438
case CIFS_DUMP_KEY:
439+
/*
440+
* Dump encryption keys. This is an old ioctl that only
441+
* handles AES-128-{CCM,GCM}.
442+
*/
374443
if (pSMBFile == NULL)
375444
break;
376445
if (!capable(CAP_SYS_ADMIN)) {
@@ -398,20 +467,18 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
398467
else
399468
rc = 0;
400469
break;
401-
/*
402-
* Dump full key (32 bytes instead of 16 bytes) is
403-
* needed if GCM256 (stronger encryption) negotiated
404-
*/
405470
case CIFS_DUMP_FULL_KEY:
471+
/*
472+
* Dump encryption keys (handles any key sizes)
473+
*/
406474
if (pSMBFile == NULL)
407475
break;
408476
if (!capable(CAP_SYS_ADMIN)) {
409477
rc = -EACCES;
410478
break;
411479
}
412480
tcon = tlink_tcon(pSMBFile->tlink);
413-
rc = cifs_dump_full_key(tcon, arg);
414-
481+
rc = cifs_dump_full_key(tcon, (void __user *)arg);
415482
break;
416483
case CIFS_IOC_NOTIFY:
417484
if (!S_ISDIR(inode->i_mode)) {

0 commit comments

Comments
 (0)