Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cifs: try opening channels after mounting

After doing mount() successfully we call cifs_try_adding_channels()
which will open as many channels as it can.

Channels are closed when the master session is closed.

The master connection becomes the first channel.

,-------------> global cifs_tcp_ses_list <-------------------------.
| |
'- TCP_Server_Info <--> TCP_Server_Info <--> TCP_Server_Info <-'
(master con) (chan#1 con) (chan#2 con)
| ^ ^ ^
v '--------------------|--------------------'
cifs_ses |
- chan_count = 3 |
- chans[] ---------------------'
- smb3signingkey[]
(master signing key)

Note how channel connections don't have sessions. That's because
cifs_ses can only be part of one linked list (list_head are internal
to the elements).

For signing keys, each channel has its own signing key which must be
used only after the channel has been bound. While it's binding it must
use the master session signing key.

For encryption keys, since channel connections do not have sessions
attached we must now find matching session by looping over all sessions
in smb2_get_enc_key().

Each channel is opened like a regular server connection but at the
session setup request step it must set the
SMB2_SESSION_REQ_FLAG_BINDING flag and use the session id to bind to.

Finally, while sending in compound_send_recv() for requests that
aren't negprot, ses-setup or binding related, use a channel by cycling
through the available ones (round-robin).

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Aurelien Aptel and committed by
Steve French
d70e9fa5 b8f7442b

+478 -88
+16
fs/cifs/cifsglob.h
··· 1001 1001 __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; 1002 1002 __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; 1003 1003 1004 + __u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; 1005 + 1004 1006 /* 1005 1007 * Network interfaces available on the server this session is 1006 1008 * connected to. ··· 1023 1021 size_t chan_max; 1024 1022 atomic_t chan_seq; /* round robin state */ 1025 1023 }; 1024 + 1025 + /* 1026 + * When binding a new channel, we need to access the channel which isn't fully 1027 + * established yet (one past the established count) 1028 + */ 1029 + 1030 + static inline 1031 + struct cifs_chan *cifs_ses_binding_channel(struct cifs_ses *ses) 1032 + { 1033 + if (ses->binding) 1034 + return &ses->chans[ses->chan_count]; 1035 + else 1036 + return NULL; 1037 + } 1026 1038 1027 1039 static inline 1028 1040 struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
+7
fs/cifs/cifsproto.h
··· 243 243 struct tcon_link *tlink, 244 244 struct cifs_pending_open *open); 245 245 extern void cifs_del_pending_open(struct cifs_pending_open *open); 246 + extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol); 246 247 extern void cifs_put_tcp_session(struct TCP_Server_Info *server, 247 248 int from_reconnect); 248 249 extern void cifs_put_tcon(struct cifs_tcon *tcon); ··· 586 585 587 586 extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page, 588 587 unsigned int *len, unsigned int *offset); 588 + int cifs_try_adding_channels(struct cifs_ses *ses); 589 + int cifs_ses_add_channel(struct cifs_ses *ses, 590 + struct cifs_server_iface *iface); 591 + bool is_server_using_iface(struct TCP_Server_Info *server, 592 + struct cifs_server_iface *iface); 593 + bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); 589 594 590 595 void extract_unc_hostname(const char *unc, const char **h, size_t *len); 591 596 int copy_path_name(char *dst, const char *src);
+35 -13
fs/cifs/connect.c
··· 2745 2745 send_sig(SIGKILL, task, 1); 2746 2746 } 2747 2747 2748 - static struct TCP_Server_Info * 2748 + struct TCP_Server_Info * 2749 2749 cifs_get_tcp_session(struct smb_vol *volume_info) 2750 2750 { 2751 2751 struct TCP_Server_Info *tcp_ses = NULL; ··· 3074 3074 list_del_init(&ses->smb_ses_list); 3075 3075 spin_unlock(&cifs_tcp_ses_lock); 3076 3076 3077 + /* close any extra channels */ 3078 + if (ses->chan_count > 1) { 3079 + int i; 3080 + 3081 + for (i = 1; i < ses->chan_count; i++) 3082 + cifs_put_tcp_session(ses->chans[i].server, 0); 3083 + } 3084 + 3077 3085 sesInfoFree(ses); 3078 3086 cifs_put_tcp_session(server, 0); 3079 3087 } ··· 3328 3320 ses->sectype = volume_info->sectype; 3329 3321 ses->sign = volume_info->sign; 3330 3322 mutex_lock(&ses->session_mutex); 3323 + 3324 + /* add server as first channel */ 3325 + ses->chans[0].server = server; 3326 + ses->chan_count = 1; 3327 + ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1; 3328 + 3331 3329 rc = cifs_negotiate_protocol(xid, ses); 3332 3330 if (!rc) 3333 3331 rc = cifs_setup_session(xid, ses, volume_info->local_nls); 3332 + 3333 + /* each channel uses a different signing key */ 3334 + memcpy(ses->chans[0].signkey, ses->smb3signingkey, 3335 + sizeof(ses->smb3signingkey)); 3336 + 3334 3337 mutex_unlock(&ses->session_mutex); 3335 3338 if (rc) 3336 3339 goto get_ses_fail; 3337 3340 3338 - /* success, put it on the list */ 3341 + /* success, put it on the list and add it as first channel */ 3339 3342 spin_lock(&cifs_tcp_ses_lock); 3340 3343 list_add(&ses->smb_ses_list, &server->smb_ses_list); 3341 3344 spin_unlock(&cifs_tcp_ses_lock); ··· 4959 4940 cifs_autodisable_serverino(cifs_sb); 4960 4941 out: 4961 4942 free_xid(xid); 4943 + cifs_try_adding_channels(ses); 4962 4944 return mount_setup_tlink(cifs_sb, ses, tcon); 4963 4945 4964 4946 error: ··· 5234 5214 int rc = -ENOSYS; 5235 5215 struct TCP_Server_Info *server = cifs_ses_server(ses); 5236 5216 5237 - ses->capabilities = server->capabilities; 5238 - if (linuxExtEnabled == 0) 5239 - ses->capabilities &= (~server->vals->cap_unix); 5217 + if (!ses->binding) { 5218 + ses->capabilities = server->capabilities; 5219 + if (linuxExtEnabled == 0) 5220 + ses->capabilities &= (~server->vals->cap_unix); 5221 + 5222 + if (ses->auth_key.response) { 5223 + cifs_dbg(FYI, "Free previous auth_key.response = %p\n", 5224 + ses->auth_key.response); 5225 + kfree(ses->auth_key.response); 5226 + ses->auth_key.response = NULL; 5227 + ses->auth_key.len = 0; 5228 + } 5229 + } 5240 5230 5241 5231 cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", 5242 5232 server->sec_mode, server->capabilities, server->timeAdj); 5243 - 5244 - if (ses->auth_key.response) { 5245 - cifs_dbg(FYI, "Free previous auth_key.response = %p\n", 5246 - ses->auth_key.response); 5247 - kfree(ses->auth_key.response); 5248 - ses->auth_key.response = NULL; 5249 - ses->auth_key.len = 0; 5250 - } 5251 5233 5252 5234 if (server->ops->sess_setup) 5253 5235 rc = server->ops->sess_setup(xid, ses, nls_info);
+213
fs/cifs/sess.c
··· 31 31 #include <linux/utsname.h> 32 32 #include <linux/slab.h> 33 33 #include "cifs_spnego.h" 34 + #include "smb2proto.h" 35 + 36 + bool 37 + is_server_using_iface(struct TCP_Server_Info *server, 38 + struct cifs_server_iface *iface) 39 + { 40 + struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr; 41 + struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr; 42 + struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr; 43 + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr; 44 + 45 + if (server->dstaddr.ss_family != iface->sockaddr.ss_family) 46 + return false; 47 + if (server->dstaddr.ss_family == AF_INET) { 48 + if (s4->sin_addr.s_addr != i4->sin_addr.s_addr) 49 + return false; 50 + } else if (server->dstaddr.ss_family == AF_INET6) { 51 + if (memcmp(&s6->sin6_addr, &i6->sin6_addr, 52 + sizeof(i6->sin6_addr)) != 0) 53 + return false; 54 + } else { 55 + /* unknown family.. */ 56 + return false; 57 + } 58 + return true; 59 + } 60 + 61 + bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) 62 + { 63 + int i; 64 + 65 + for (i = 0; i < ses->chan_count; i++) { 66 + if (is_server_using_iface(ses->chans[i].server, iface)) 67 + return true; 68 + } 69 + return false; 70 + } 71 + 72 + /* returns number of channels added */ 73 + int cifs_try_adding_channels(struct cifs_ses *ses) 74 + { 75 + int old_chan_count = ses->chan_count; 76 + int left = ses->chan_max - ses->chan_count; 77 + int i = 0; 78 + int rc = 0; 79 + 80 + if (left <= 0) { 81 + cifs_dbg(FYI, 82 + "ses already at max_channels (%zu), nothing to open\n", 83 + ses->chan_max); 84 + return 0; 85 + } 86 + 87 + if (ses->server->dialect < SMB30_PROT_ID) { 88 + cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n"); 89 + return 0; 90 + } 91 + 92 + /* ifaces are sorted by speed, try them in order */ 93 + for (i = 0; left > 0 && i < ses->iface_count; i++) { 94 + struct cifs_server_iface *iface; 95 + 96 + iface = &ses->iface_list[i]; 97 + if (is_ses_using_iface(ses, iface) && !iface->rss_capable) 98 + continue; 99 + 100 + rc = cifs_ses_add_channel(ses, iface); 101 + if (rc) { 102 + cifs_dbg(FYI, "failed to open extra channel\n"); 103 + continue; 104 + } 105 + 106 + cifs_dbg(FYI, "successfully opened new channel\n"); 107 + left--; 108 + } 109 + 110 + /* 111 + * TODO: if we still have channels left to open try to connect 112 + * to same RSS-capable iface multiple times 113 + */ 114 + 115 + return ses->chan_count - old_chan_count; 116 + } 117 + 118 + int 119 + cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface) 120 + { 121 + struct cifs_chan *chan; 122 + struct smb_vol vol = {NULL}; 123 + static const char unc_fmt[] = "\\%s\\foo"; 124 + char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0}; 125 + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; 126 + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; 127 + int rc; 128 + unsigned int xid = get_xid(); 129 + 130 + cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ", 131 + ses, iface->speed, iface->rdma_capable ? "yes" : "no"); 132 + if (iface->sockaddr.ss_family == AF_INET) 133 + cifs_dbg(FYI, "ip:%pI4)\n", &ipv4->sin_addr); 134 + else 135 + cifs_dbg(FYI, "ip:%pI6)\n", &ipv6->sin6_addr); 136 + 137 + /* 138 + * Setup a smb_vol with mostly the same info as the existing 139 + * session and overwrite it with the requested iface data. 140 + * 141 + * We need to setup at least the fields used for negprot and 142 + * sesssetup. 143 + * 144 + * We only need the volume here, so we can reuse memory from 145 + * the session and server without caring about memory 146 + * management. 147 + */ 148 + 149 + /* Always make new connection for now (TODO?) */ 150 + vol.nosharesock = true; 151 + 152 + /* Auth */ 153 + vol.domainauto = ses->domainAuto; 154 + vol.domainname = ses->domainName; 155 + vol.username = ses->user_name; 156 + vol.password = ses->password; 157 + vol.sectype = ses->sectype; 158 + vol.sign = ses->sign; 159 + 160 + /* UNC and paths */ 161 + /* XXX: Use ses->server->hostname? */ 162 + sprintf(unc, unc_fmt, ses->serverName); 163 + vol.UNC = unc; 164 + vol.prepath = ""; 165 + 166 + /* Re-use same version as master connection */ 167 + vol.vals = ses->server->vals; 168 + vol.ops = ses->server->ops; 169 + 170 + vol.noblocksnd = ses->server->noblocksnd; 171 + vol.noautotune = ses->server->noautotune; 172 + vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay; 173 + vol.echo_interval = ses->server->echo_interval / HZ; 174 + 175 + /* 176 + * This will be used for encoding/decoding user/domain/pw 177 + * during sess setup auth. 178 + * 179 + * XXX: We use the default for simplicity but the proper way 180 + * would be to use the one that ses used, which is not 181 + * stored. This might break when dealing with non-ascii 182 + * strings. 183 + */ 184 + vol.local_nls = load_nls_default(); 185 + 186 + /* Use RDMA if possible */ 187 + vol.rdma = iface->rdma_capable; 188 + memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage)); 189 + 190 + /* reuse master con client guid */ 191 + memcpy(&vol.client_guid, ses->server->client_guid, 192 + SMB2_CLIENT_GUID_SIZE); 193 + vol.use_client_guid = true; 194 + 195 + mutex_lock(&ses->session_mutex); 196 + 197 + chan = &ses->chans[ses->chan_count]; 198 + chan->server = cifs_get_tcp_session(&vol); 199 + if (IS_ERR(chan->server)) { 200 + rc = PTR_ERR(chan->server); 201 + chan->server = NULL; 202 + goto out; 203 + } 204 + 205 + /* 206 + * We need to allocate the server crypto now as we will need 207 + * to sign packets before we generate the channel signing key 208 + * (we sign with the session key) 209 + */ 210 + rc = smb311_crypto_shash_allocate(chan->server); 211 + if (rc) { 212 + cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); 213 + goto out; 214 + } 215 + 216 + ses->binding = true; 217 + rc = cifs_negotiate_protocol(xid, ses); 218 + if (rc) 219 + goto out; 220 + 221 + rc = cifs_setup_session(xid, ses, vol.local_nls); 222 + if (rc) 223 + goto out; 224 + 225 + /* success, put it on the list 226 + * XXX: sharing ses between 2 tcp server is not possible, the 227 + * way "internal" linked lists works in linux makes element 228 + * only able to belong to one list 229 + * 230 + * the binding session is already established so the rest of 231 + * the code should be able to look it up, no need to add the 232 + * ses to the new server. 233 + */ 234 + 235 + ses->chan_count++; 236 + atomic_set(&ses->chan_seq, 0); 237 + out: 238 + ses->binding = false; 239 + mutex_unlock(&ses->session_mutex); 240 + 241 + if (rc && chan->server) 242 + cifs_put_tcp_session(chan->server, 0); 243 + unload_nls(vol.local_nls); 244 + 245 + return rc; 246 + } 34 247 35 248 static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) 36 249 {
+26 -11
fs/cifs/smb2misc.c
··· 29 29 #include "cifs_unicode.h" 30 30 #include "smb2status.h" 31 31 #include "smb2glob.h" 32 + #include "nterr.h" 32 33 33 34 static int 34 35 check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid) ··· 835 834 int i, rc; 836 835 struct sdesc *d; 837 836 struct smb2_sync_hdr *hdr; 837 + struct TCP_Server_Info *server = cifs_ses_server(ses); 838 838 839 - if (ses->server->tcpStatus == CifsGood) { 840 - /* skip non smb311 connections */ 841 - if (ses->server->dialect != SMB311_PROT_ID) 842 - return 0; 839 + hdr = (struct smb2_sync_hdr *)iov[0].iov_base; 840 + /* neg prot are always taken */ 841 + if (hdr->Command == SMB2_NEGOTIATE) 842 + goto ok; 843 843 844 - /* skip last sess setup response */ 845 - hdr = (struct smb2_sync_hdr *)iov[0].iov_base; 846 - if (hdr->Flags & SMB2_FLAGS_SIGNED) 847 - return 0; 848 - } 844 + /* 845 + * If we process a command which wasn't a negprot it means the 846 + * neg prot was already done, so the server dialect was set 847 + * and we can test it. Preauth requires 3.1.1 for now. 848 + */ 849 + if (server->dialect != SMB311_PROT_ID) 850 + return 0; 849 851 850 - rc = smb311_crypto_shash_allocate(ses->server); 852 + if (hdr->Command != SMB2_SESSION_SETUP) 853 + return 0; 854 + 855 + /* skip last sess setup response */ 856 + if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) 857 + && (hdr->Status == NT_STATUS_OK 858 + || (hdr->Status != 859 + cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)))) 860 + return 0; 861 + 862 + ok: 863 + rc = smb311_crypto_shash_allocate(server); 851 864 if (rc) 852 865 return rc; 853 866 854 - d = ses->server->secmech.sdescsha512; 867 + d = server->secmech.sdescsha512; 855 868 rc = crypto_shash_init(&d->shash); 856 869 if (rc) { 857 870 cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
+10 -8
fs/cifs/smb2ops.c
··· 3599 3599 u8 *ses_enc_key; 3600 3600 3601 3601 spin_lock(&cifs_tcp_ses_lock); 3602 - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 3603 - if (ses->Suid != ses_id) 3604 - continue; 3605 - ses_enc_key = enc ? ses->smb3encryptionkey : 3606 - ses->smb3decryptionkey; 3607 - memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE); 3608 - spin_unlock(&cifs_tcp_ses_lock); 3609 - return 0; 3602 + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { 3603 + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 3604 + if (ses->Suid == ses_id) { 3605 + ses_enc_key = enc ? ses->smb3encryptionkey : 3606 + ses->smb3decryptionkey; 3607 + memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE); 3608 + spin_unlock(&cifs_tcp_ses_lock); 3609 + return 0; 3610 + } 3611 + } 3610 3612 } 3611 3613 spin_unlock(&cifs_tcp_ses_lock); 3612 3614
+50 -28
fs/cifs/smb2pdu.c
··· 1179 1179 if (rc) 1180 1180 return rc; 1181 1181 1182 - /* First session, not a reauthenticate */ 1183 - req->sync_hdr.SessionId = 0; 1184 - 1185 - /* if reconnect, we need to send previous sess id, otherwise it is 0 */ 1186 - req->PreviousSessionId = sess_data->previous_session; 1187 - 1188 - req->Flags = 0; /* MBZ */ 1182 + if (sess_data->ses->binding) { 1183 + req->sync_hdr.SessionId = sess_data->ses->Suid; 1184 + req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; 1185 + req->PreviousSessionId = 0; 1186 + req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; 1187 + } else { 1188 + /* First session, not a reauthenticate */ 1189 + req->sync_hdr.SessionId = 0; 1190 + /* 1191 + * if reconnect, we need to send previous sess id 1192 + * otherwise it is 0 1193 + */ 1194 + req->PreviousSessionId = sess_data->previous_session; 1195 + req->Flags = 0; /* MBZ */ 1196 + } 1189 1197 1190 1198 /* enough to enable echos and oplocks and one max size write */ 1191 1199 req->sync_hdr.CreditRequest = cpu_to_le16(130); ··· 1285 1277 mutex_unlock(&server->srv_mutex); 1286 1278 1287 1279 cifs_dbg(FYI, "SMB2/3 session established successfully\n"); 1288 - spin_lock(&GlobalMid_Lock); 1289 - ses->status = CifsGood; 1290 - ses->need_reconnect = false; 1291 - spin_unlock(&GlobalMid_Lock); 1280 + /* keep existing ses state if binding */ 1281 + if (!ses->binding) { 1282 + spin_lock(&GlobalMid_Lock); 1283 + ses->status = CifsGood; 1284 + ses->need_reconnect = false; 1285 + spin_unlock(&GlobalMid_Lock); 1286 + } 1287 + 1292 1288 return rc; 1293 1289 } 1294 1290 ··· 1330 1318 goto out_put_spnego_key; 1331 1319 } 1332 1320 1333 - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, 1334 - GFP_KERNEL); 1335 - if (!ses->auth_key.response) { 1336 - cifs_dbg(VFS, 1337 - "Kerberos can't allocate (%u bytes) memory", 1338 - msg->sesskey_len); 1339 - rc = -ENOMEM; 1340 - goto out_put_spnego_key; 1321 + /* keep session key if binding */ 1322 + if (!ses->binding) { 1323 + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, 1324 + GFP_KERNEL); 1325 + if (!ses->auth_key.response) { 1326 + cifs_dbg(VFS, 1327 + "Kerberos can't allocate (%u bytes) memory", 1328 + msg->sesskey_len); 1329 + rc = -ENOMEM; 1330 + goto out_put_spnego_key; 1331 + } 1332 + ses->auth_key.len = msg->sesskey_len; 1341 1333 } 1342 - ses->auth_key.len = msg->sesskey_len; 1343 1334 1344 1335 sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; 1345 1336 sess_data->iov[1].iov_len = msg->secblob_len; ··· 1352 1337 goto out_put_spnego_key; 1353 1338 1354 1339 rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 1355 - ses->Suid = rsp->sync_hdr.SessionId; 1356 - 1357 - ses->session_flags = le16_to_cpu(rsp->SessionFlags); 1340 + /* keep session id and flags if binding */ 1341 + if (!ses->binding) { 1342 + ses->Suid = rsp->sync_hdr.SessionId; 1343 + ses->session_flags = le16_to_cpu(rsp->SessionFlags); 1344 + } 1358 1345 1359 1346 rc = SMB2_sess_establish_session(sess_data); 1360 1347 out_put_spnego_key: ··· 1450 1433 1451 1434 cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); 1452 1435 1453 - 1454 - ses->Suid = rsp->sync_hdr.SessionId; 1455 - ses->session_flags = le16_to_cpu(rsp->SessionFlags); 1436 + /* keep existing ses id and flags if binding */ 1437 + if (!ses->binding) { 1438 + ses->Suid = rsp->sync_hdr.SessionId; 1439 + ses->session_flags = le16_to_cpu(rsp->SessionFlags); 1440 + } 1456 1441 1457 1442 out: 1458 1443 kfree(ntlmssp_blob); ··· 1511 1492 1512 1493 rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 1513 1494 1514 - ses->Suid = rsp->sync_hdr.SessionId; 1515 - ses->session_flags = le16_to_cpu(rsp->SessionFlags); 1495 + /* keep existing ses id and flags if binding */ 1496 + if (!ses->binding) { 1497 + ses->Suid = rsp->sync_hdr.SessionId; 1498 + ses->session_flags = le16_to_cpu(rsp->SessionFlags); 1499 + } 1516 1500 1517 1501 rc = SMB2_sess_establish_session(sess_data); 1518 1502 out:
+109 -27
fs/cifs/smb2transport.c
··· 98 98 return rc; 99 99 } 100 100 101 + 102 + static 103 + int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) 104 + { 105 + struct cifs_chan *chan; 106 + struct cifs_ses *ses = NULL; 107 + int i; 108 + int rc = 0; 109 + 110 + spin_lock(&cifs_tcp_ses_lock); 111 + 112 + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { 113 + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 114 + if (ses->Suid == ses_id) 115 + goto found; 116 + } 117 + } 118 + cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n", 119 + __func__, ses_id); 120 + rc = -ENOENT; 121 + goto out; 122 + 123 + found: 124 + if (ses->binding) { 125 + /* 126 + * If we are in the process of binding a new channel 127 + * to an existing session, use the master connection 128 + * session key 129 + */ 130 + memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE); 131 + goto out; 132 + } 133 + 134 + /* 135 + * Otherwise, use the channel key. 136 + */ 137 + 138 + for (i = 0; i < ses->chan_count; i++) { 139 + chan = ses->chans + i; 140 + if (chan->server == server) { 141 + memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE); 142 + goto out; 143 + } 144 + } 145 + 146 + cifs_dbg(VFS, 147 + "%s: Could not find channel signing key for session 0x%llx\n", 148 + __func__, ses_id); 149 + rc = -ENOENT; 150 + 151 + out: 152 + spin_unlock(&cifs_tcp_ses_lock); 153 + return rc; 154 + } 155 + 101 156 static struct cifs_ses * 102 157 smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) 103 158 { ··· 383 328 { 384 329 int rc; 385 330 386 - rc = generate_key(ses, ptriplet->signing.label, 387 - ptriplet->signing.context, ses->smb3signingkey, 388 - SMB3_SIGN_KEY_SIZE); 389 - if (rc) 390 - return rc; 331 + /* 332 + * All channels use the same encryption/decryption keys but 333 + * they have their own signing key. 334 + * 335 + * When we generate the keys, check if it is for a new channel 336 + * (binding) in which case we only need to generate a signing 337 + * key and store it in the channel as to not overwrite the 338 + * master connection signing key stored in the session 339 + */ 391 340 392 - rc = generate_key(ses, ptriplet->encryption.label, 393 - ptriplet->encryption.context, ses->smb3encryptionkey, 394 - SMB3_SIGN_KEY_SIZE); 395 - if (rc) 396 - return rc; 397 - 398 - rc = generate_key(ses, ptriplet->decryption.label, 399 - ptriplet->decryption.context, 400 - ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); 341 + if (ses->binding) { 342 + rc = generate_key(ses, ptriplet->signing.label, 343 + ptriplet->signing.context, 344 + cifs_ses_binding_channel(ses)->signkey, 345 + SMB3_SIGN_KEY_SIZE); 346 + if (rc) 347 + return rc; 348 + } else { 349 + rc = generate_key(ses, ptriplet->signing.label, 350 + ptriplet->signing.context, 351 + ses->smb3signingkey, 352 + SMB3_SIGN_KEY_SIZE); 353 + if (rc) 354 + return rc; 355 + rc = generate_key(ses, ptriplet->encryption.label, 356 + ptriplet->encryption.context, 357 + ses->smb3encryptionkey, 358 + SMB3_SIGN_KEY_SIZE); 359 + rc = generate_key(ses, ptriplet->decryption.label, 360 + ptriplet->decryption.context, 361 + ses->smb3decryptionkey, 362 + SMB3_SIGN_KEY_SIZE); 363 + if (rc) 364 + return rc; 365 + } 401 366 402 367 if (rc) 403 368 return rc; ··· 506 431 unsigned char *sigptr = smb3_signature; 507 432 struct kvec *iov = rqst->rq_iov; 508 433 struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; 509 - struct cifs_ses *ses; 510 434 struct shash_desc *shash = &server->secmech.sdesccmacaes->shash; 511 435 struct smb_rqst drqst; 436 + u8 key[SMB3_SIGN_KEY_SIZE]; 512 437 513 - ses = smb2_find_smb_ses(server, shdr->SessionId); 514 - if (!ses) { 515 - cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); 438 + rc = smb2_get_sign_key(shdr->SessionId, server, key); 439 + if (rc) 516 440 return 0; 517 - } 518 441 519 442 memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); 520 443 memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); 521 444 522 445 rc = crypto_shash_setkey(server->secmech.cmacaes, 523 - ses->smb3signingkey, SMB2_CMACAES_SIZE); 446 + key, SMB2_CMACAES_SIZE); 524 447 if (rc) { 525 448 cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); 526 449 return rc; ··· 567 494 smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) 568 495 { 569 496 int rc = 0; 570 - struct smb2_sync_hdr *shdr = 571 - (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; 497 + struct smb2_sync_hdr *shdr; 498 + struct smb2_sess_setup_req *ssr; 499 + bool is_binding; 500 + bool is_signed; 572 501 573 - if (!(shdr->Flags & SMB2_FLAGS_SIGNED) || 574 - server->tcpStatus == CifsNeedNegotiate) 575 - return rc; 502 + shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; 503 + ssr = (struct smb2_sess_setup_req *)shdr; 576 504 577 - if (!server->session_estab) { 505 + is_binding = shdr->Command == SMB2_SESSION_SETUP && 506 + (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING); 507 + is_signed = shdr->Flags & SMB2_FLAGS_SIGNED; 508 + 509 + if (!is_signed) 510 + return 0; 511 + if (server->tcpStatus == CifsNeedNegotiate) 512 + return 0; 513 + if (!is_binding && !server->session_estab) { 578 514 strncpy(shdr->Signature, "BSRSPYL", 8); 579 - return rc; 515 + return 0; 580 516 } 581 517 582 518 rc = server->ops->calc_signature(rqst, server);
+12 -1
fs/cifs/transport.c
··· 1009 1009 return -EIO; 1010 1010 } 1011 1011 1012 - server = ses->server; 1012 + if (!ses->binding) { 1013 + uint index = 0; 1014 + 1015 + if (ses->chan_count > 1) { 1016 + index = (uint)atomic_inc_return(&ses->chan_seq); 1017 + index %= ses->chan_count; 1018 + } 1019 + server = ses->chans[index].server; 1020 + } else { 1021 + server = cifs_ses_server(ses); 1022 + } 1023 + 1013 1024 if (server->tcpStatus == CifsExiting) 1014 1025 return -ENOENT; 1015 1026