// SPDX-License-Identifier: LGPL-2.1 /* * * Copyright (C) International Business Machines Corp., 2009, 2013 * Etersoft, 2012 * Author(s): Steve French (sfrench@us.ibm.com) * Pavel Shilovsky (pshilovsky@samba.org) 2012 * * Contains the routines for constructing the SMB2 PDUs themselves *
*/
/* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ /* Note that there are handle based routines which must be */ /* treated slightly differently for reconnection purposes since we never */ /* want to reuse a stale file handle and only the caller knows the file info */
if (server) { /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ if (server->dialect >= SMB30_PROT_ID) {
smb3_hdr = (struct smb3_hdr_req *)shdr; /* * if primary channel is not set yet, use default * channel for chan sequence num
*/ if (SERVER_IS_CHAN(server))
smb3_hdr->ChannelSequence =
cpu_to_le16(server->primary_server->channel_sequence_num); else
smb3_hdr->ChannelSequence =
cpu_to_le16(server->channel_sequence_num);
}
spin_lock(&server->req_lock); /* Request up to 10 credits but don't go over the limit. */ if (server->credits >= server->max_credits)
shdr->CreditRequest = cpu_to_le16(0); else
shdr->CreditRequest = cpu_to_le16(
min_t(int, server->max_credits -
server->credits, 10));
spin_unlock(&server->req_lock);
} else {
shdr->CreditRequest = cpu_to_le16(2);
}
shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid);
if (!tcon) goto out;
/* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
shdr->CreditCharge = cpu_to_le16(1); /* else CreditCharge MBZ */
shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); /* Uid is not converted */ if (tcon->ses)
shdr->SessionId = cpu_to_le64(tcon->ses->Suid);
/* * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have * to pass the path on the Open SMB prefixed by \\server\share. * Not sure when we would need to do the augmented path (if ever) and * setting this flag breaks the SMB2 open operation since it is * illegal to send an empty path name (without \\server\share prefix) * when the DFS flag is set in the SMB open header. We could * consider setting the flag on all operations other than open * but it is safer to net set it for now.
*/ /* if (tcon->share_flags & SHI1005_FLAGS_DFS)
shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
/* * the above reference of server by channel * needs to be dropped without holding chan_lock * as cifs_put_tcp_session takes a higher lock * i.e. cifs_tcp_ses_lock
*/
cifs_put_tcp_session(server, from_reconnect);
cifs_signal_cifsd_for_reconnect(server, false);
/* mark primary server as needing reconnect */
pserver = server->primary_server;
cifs_signal_cifsd_for_reconnect(pserver, false);
skip_terminate: return -EHOSTDOWN;
}
cifs_server_dbg(VFS, "server does not support multichannel anymore. Disable all other channels\n");
cifs_disable_secondary_channels(ses);
/* * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so * check for tcp and smb session status done differently * for those three - in the calling routine.
*/ if (tcon == NULL) return 0;
if (smb2_command == SMB2_TREE_CONNECT) return 0;
spin_lock(&tcon->tc_lock); if (tcon->status == TID_EXITING) { /* * only tree disconnect allowed when disconnecting ...
*/ if (smb2_command != SMB2_TREE_DISCONNECT) {
spin_unlock(&tcon->tc_lock);
cifs_dbg(FYI, "can not send cmd %d while umounting\n",
smb2_command); return -ENODEV;
}
}
spin_unlock(&tcon->tc_lock);
ses = tcon->ses; if (!ses) return -EIO;
spin_lock(&ses->ses_lock); if (ses->ses_status == SES_EXITING) {
spin_unlock(&ses->ses_lock); return -EIO;
}
spin_unlock(&ses->ses_lock); if (!ses->server || !server) return -EIO;
spin_lock(&server->srv_lock); if (server->tcpStatus == CifsNeedReconnect) { /* * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE * here since they are implicitly done when session drops.
*/ switch (smb2_command) { /* * BB Should we keep oplock break and add flush to exceptions?
*/ case SMB2_TREE_DISCONNECT: case SMB2_CANCEL: case SMB2_CLOSE: case SMB2_OPLOCK_BREAK:
spin_unlock(&server->srv_lock); return -EAGAIN;
}
}
/* if server is marked for termination, cifsd will cleanup */ if (server->terminate) {
spin_unlock(&server->srv_lock); return -EHOSTDOWN;
}
spin_unlock(&server->srv_lock);
again:
rc = cifs_wait_for_server_reconnect(server, tcon->retry); if (rc) return rc;
mutex_lock(&ses->session_mutex); /* * Handle the case where a concurrent thread failed to negotiate or * killed a channel.
*/
spin_lock(&server->srv_lock); switch (server->tcpStatus) { case CifsExiting:
spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex); return -EHOSTDOWN; case CifsNeedReconnect:
spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex); if (!tcon->retry) return -EHOSTDOWN; goto again; default: break;
}
spin_unlock(&server->srv_lock);
/* * need to prevent multiple threads trying to simultaneously * reconnect the same SMB session
*/
spin_lock(&ses->ses_lock);
spin_lock(&ses->chan_lock); if (!cifs_chan_needs_reconnect(ses, server) &&
ses->ses_status == SES_GOOD) {
spin_unlock(&ses->chan_lock);
spin_unlock(&ses->ses_lock); /* this means that we only need to tree connect */ if (tcon->need_reconnect) goto skip_sess_setup;
rc = cifs_negotiate_protocol(0, ses, server); if (rc) {
mutex_unlock(&ses->session_mutex); if (!tcon->retry) return -EHOSTDOWN; goto again;
} /* * if server stopped supporting multichannel * and the first channel reconnected, disable all the others.
*/ if (ses->chan_count > 1 &&
!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
rc = cifs_chan_skip_or_disable(ses, server,
from_reconnect); if (rc) {
mutex_unlock(&ses->session_mutex); goto out;
}
}
rc = cifs_setup_session(0, ses, server, ses->local_nls); if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) { /* * Try alternate password for next reconnect (key rotation * could be enabled on the server e.g.) if an alternate * password is available and the current password is expired, * but do not swap on non pwd related errors like host down
*/ if (ses->password2)
swap(ses->password2, ses->password);
} if (rc) {
mutex_unlock(&ses->session_mutex); if (rc == -EACCES && !tcon->retry) return -EHOSTDOWN; goto out;
}
skip_sess_setup: if (!tcon->need_reconnect) {
mutex_unlock(&ses->session_mutex); goto out;
}
cifs_mark_open_files_invalid(tcon); if (tcon->use_persistent)
tcon->need_reopen_files = true;
if (!rc &&
(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL) &&
server->ops->query_server_interfaces) { /* * query server network interfaces, in case they change. * Also mark the session as pending this update while the query * is in progress. This will be used to avoid calling * smb2_reconnect recursively.
*/
ses->flags |= CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES;
xid = get_xid();
rc = server->ops->query_server_interfaces(xid, tcon, false);
free_xid(xid);
ses->flags &= ~CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES;
if (!tcon->ipc && !tcon->dummy)
queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
(SMB_INTERFACE_POLL_INTERVAL * HZ));
mutex_unlock(&ses->session_mutex);
if (rc == -EOPNOTSUPP && ses->chan_count > 1) { /* * some servers like Azure SMB server do not advertise * that multichannel has been disabled with server * capabilities, rather return STATUS_NOT_IMPLEMENTED. * treat this as server not supporting multichannel
*/
rc = cifs_chan_skip_or_disable(ses, server,
from_reconnect); goto skip_add_channels;
} elseif (rc)
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
__func__, rc);
if (ses->chan_max > ses->chan_count &&
ses->iface_count &&
!SERVER_IS_CHAN(server)) { if (ses->chan_count == 1)
cifs_server_dbg(VFS, "supports multichannel now\n");
if (smb2_command != SMB2_INTERNAL_CMD)
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
atomic_inc(&tconInfoReconnectCount);
out: /* * Check if handle based operation so we know whether we can continue * or not without returning to caller to reset file handle.
*/ /* * BB Is flush done by server on drop of tcp session? Should we special * case it and skip above?
*/ switch (smb2_command) { case SMB2_FLUSH: case SMB2_READ: case SMB2_WRITE: case SMB2_LOCK: case SMB2_QUERY_DIRECTORY: case SMB2_CHANGE_NOTIFY: case SMB2_QUERY_INFO: case SMB2_SET_INFO: case SMB2_IOCTL:
rc = -EAGAIN;
} return rc;
}
/* * Allocate and return pointer to an SMB request hdr, and set basic * SMB information in the SMB header. If the return code is zero, this * function must have filled in request_buf pointer.
*/ staticint __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, struct TCP_Server_Info *server, void **request_buf, unsignedint *total_len)
{ /* BB eventually switch this to SMB2 specific small buf size */ switch (smb2_command) { case SMB2_SET_INFO: case SMB2_QUERY_INFO:
*request_buf = cifs_buf_get(); break; default:
*request_buf = cifs_small_buf_get(); break;
} if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM;
}
staticint smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, struct TCP_Server_Info *server, void **request_buf, unsignedint *total_len)
{ /* * Skip reconnect in one of the following cases: * 1. For FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs * 2. For FSCTL_QUERY_NETWORK_INTERFACE_INFO IOCTL when called from * smb2_reconnect (indicated by CIFS_SES_FLAG_SCALE_CHANNELS ses flag)
*/ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO ||
(opcode == FSCTL_QUERY_NETWORK_INTERFACE_INFO &&
(tcon->ses->flags & CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES))) return __smb2_plain_req_init(SMB2_IOCTL, tcon, server,
request_buf, total_len);
/* copy up to max of first 100 bytes of server name to NetName field */
pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); /* context size is DataLength + minimal smb2_neg_context */ return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8);
}
if (*total_len > 200) { /* In case length corrupted don't want to overrun smb buffer */
cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); return;
}
/* * round up total_len of fixed part of SMB3 negotiate request to 8 * byte boundary before adding negotiate contexts
*/
*total_len = ALIGN(*total_len, 8);
/* check for and add transport_capabilities and signing capabilities */
req->NegotiateContextCount = cpu_to_le16(neg_context_count);
}
/* If invalid preauth context warn but use what we requested, SHA-512 */ staticvoid decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
{ unsignedint len = le16_to_cpu(ctxt->DataLength);
/* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one HashAlgorithms member is accounted for.
*/ if (len < MIN_PREAUTH_CTXT_DATA_LEN) {
pr_warn_once("server sent bad preauth context\n"); return;
} elseif (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) {
pr_warn_once("server sent invalid SaltLength\n"); return;
} if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1)
pr_warn_once("Invalid SMB3 hash algorithm count\n"); if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512)
pr_warn_once("unknown SMB3 hash algorithm\n");
}
/* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one CompressionAlgorithms member is accounted * for.
*/ if (len < 10) {
pr_warn_once("server sent bad compression cntxt\n"); return;
}
cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); /* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one Cipher flexible array member is accounted * for.
*/ if (len < MIN_ENCRYPT_CTXT_DATA_LEN) {
pr_warn_once("server sent bad crypto ctxt len\n"); return -EINVAL;
}
if (le16_to_cpu(ctxt->CipherCount) != 1) {
pr_warn_once("Invalid SMB3.11 cipher count\n"); return -EINVAL;
}
cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); if (require_gcm_256) { if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) {
cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); return -EOPNOTSUPP;
}
} elseif (ctxt->Ciphers[0] == 0) { /* * e.g. if server only supported AES256_CCM (very unlikely) * or server supported no encryption types or had all disabled. * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case * in which mount requested encryption ("seal") checks later * on during tree connection will return proper rc, but if * seal not requested by client, since server is allowed to * return 0 to indicate no supported cipher, we can't fail here
*/
server->cipher_type = 0;
server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION;
pr_warn_once("Server does not support requested encryption types\n"); return 0;
} elseif ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) &&
(ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) &&
(ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { /* server returned a cipher we didn't ask for */
pr_warn_once("Invalid SMB3.11 cipher returned\n"); return -EINVAL;
}
server->cipher_type = ctxt->Ciphers[0];
server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; return 0;
}
/* * Caller checked that DataLength remains within SMB boundary. We still * need to confirm that one SigningAlgorithms flexible array member is * accounted for.
*/ if ((len < 4) || (len > 16)) {
pr_warn_once("server sent bad signing negcontext\n"); return;
} if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) {
pr_warn_once("Invalid signing algorithm count\n"); return;
} if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) {
pr_warn_once("unknown signing algorithm\n"); return;
}
iov[num].iov_base = create_posix_buf(mode); if (mode == ACL_NO_MODE)
cifs_dbg(FYI, "%s: no mode\n", __func__); if (iov[num].iov_base == NULL) return -ENOMEM;
iov[num].iov_len = sizeof(struct create_posix);
*num_iovec = num + 1; return 0;
}
/* * * SMB2 Worker functions follow: * * The general structure of the worker functions is: * 1) Call smb2_init (assembles SMB2 header) * 2) Initialize SMB2 command specific fields in fixed length area of SMB * 3) Call smb_sendrcv2 (sends request on socket and waits for response) * 4) Decode SMB2 command specific fields in the fixed length area * 5) Decode variable length data area (if any for this SMB2 command type) * 6) Call free smb buffer * 7) return *
*/
int
SMB2_negotiate(constunsignedint xid, struct cifs_ses *ses, struct TCP_Server_Info *server)
{ struct smb_rqst rqst; struct smb2_negotiate_req *req; struct smb2_negotiate_rsp *rsp; struct kvec iov[1]; struct kvec rsp_iov; int rc; int resp_buftype; int blob_offset, blob_length; char *security_blob; int flags = CIFS_NEG_OP; unsignedint total_len;
cifs_dbg(FYI, "Negotiate protocol\n");
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__); return -EIO;
}
/* only one of SMB2 signing flags may be set in SMB2 request */ if (ses->sign)
req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); elseif (global_secflags & CIFSSEC_MAY_SIGN)
req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); else
req->SecurityMode = 0;
req->Capabilities = cpu_to_le32(server->vals->req_capabilities); if (ses->chan_max > 1)
req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
/* ClientGUID must be zero for SMB2.02 dialect */ if (server->vals->protocol_id == SMB20_PROT_ID)
memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); else {
memcpy(req->ClientGUID, server->client_guid,
SMB2_CLIENT_GUID_SIZE); if ((server->vals->protocol_id == SMB311_PROT_ID) ||
(strcmp(server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) ||
(strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0))
assemble_neg_contexts(req, server, &total_len);
}
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
/* * Keep a copy of the hash after negprot. This hash will be * the starting hash value for all sessions made from this * server.
*/
memcpy(server->preauth_sha_hash, ses->preauth_sha_hash,
SMB2_PREAUTH_HASH_SIZE);
/* SMB2 only has an extended negflavor */
server->negflavor = CIFS_NEGFLAVOR_EXTENDED; /* set it to the maximum buffer size value we can send with 1 credit */
server->maxBuf = min_t(unsignedint, le32_to_cpu(rsp->MaxTransactSize),
SMB2_MAX_BUFFER_SIZE);
server->max_read = le32_to_cpu(rsp->MaxReadSize);
server->max_write = le32_to_cpu(rsp->MaxWriteSize);
server->sec_mode = le16_to_cpu(rsp->SecurityMode); if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode)
cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n",
server->sec_mode);
server->capabilities = le32_to_cpu(rsp->Capabilities); /* Internal types */
server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES;
/* * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context * Set the cipher type manually.
*/ if ((server->dialect == SMB30_PROT_ID ||
server->dialect == SMB302_PROT_ID) &&
(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
server->cipher_type = SMB2_ENCRYPTION_AES128_CCM;
security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
(struct smb2_hdr *)rsp); /* * See MS-SMB2 section 2.2.4: if no blob, client picks default which * for us will be * ses->sectype = RawNTLMSSP; * but for time being this is our only auth choice so doesn't matter. * We just found a server which sets blob length to zero expecting raw.
*/ if (blob_length == 0) {
cifs_dbg(FYI, "missing security blob on negprot\n");
server->sec_ntlmssp = true;
}
int smb3_validate_negotiate(constunsignedint xid, struct cifs_tcon *tcon)
{ int rc; struct validate_negotiate_info_req *pneg_inbuf; struct validate_negotiate_info_rsp *pneg_rsp = NULL;
u32 rsplen;
u32 inbuflen; /* max of 4 dialects */ struct TCP_Server_Info *server = tcon->ses->server;
cifs_dbg(FYI, "validate negotiate\n");
/* In SMB3.11 preauth integrity supersedes validate negotiate */ if (server->dialect == SMB311_PROT_ID) return 0;
/* * validation ioctl must be signed, so no point sending this if we * can not sign it (ie are not known user). Even if signing is not * required (enabled but not negotiated), in those cases we selectively * sign just this, the first and only signed request on a connection. * Having validation of negotiate info helps reduce attack vectors.
*/ if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) return 0; /* validation requires signing */
if (tcon->ses->user_name == NULL) {
cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); return 0; /* validation requires signing */
}
if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); if (!pneg_inbuf) return -ENOMEM;
pneg_inbuf->Capabilities =
cpu_to_le32(server->vals->req_capabilities); if (tcon->ses->chan_max > 1)
pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
/* we will send the SMB in three pieces: * a fixed length beginning part, an optional * SPNEGO blob (which can be zero length), and a * last part which will include the strings * and rest of bcc area. This allows us to avoid * a large buffer 17K allocation
*/ int buf0_type; struct kvec iov[2];
};
if (is_binding) {
req->hdr.SessionId = cpu_to_le64(ses->Suid);
req->hdr.Flags |= SMB2_FLAGS_SIGNED;
req->PreviousSessionId = 0;
req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid);
} else { /* First session, not a reauthenticate */
req->hdr.SessionId = 0; /* * if reconnect, we need to send previous sess id * otherwise it is 0
*/
req->PreviousSessionId = cpu_to_le64(sess_data->previous_session);
req->Flags = 0; /* MBZ */
cifs_dbg(FYI, "Fresh session. Previous: %llx\n",
sess_data->previous_session);
}
/* enough to enable echos and oplocks and one max size write */ if (server->credits >= server->max_credits)
req->hdr.CreditRequest = cpu_to_le16(0); else
req->hdr.CreditRequest = cpu_to_le16(
min_t(int, server->max_credits -
server->credits, 130));
/* only one of SMB2 signing flags may be set in SMB2 request */ if (server->sign)
req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; elseif (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; else
req->SecurityMode = 0;
sess_data->iov[0].iov_base = (char *)req; /* 1 for pad */
sess_data->iov[0].iov_len = total_len - 1; /* * This variable will be used to clear the buffer * allocated above in case of any error in the calling function.
*/
sess_data->buf0_type = CIFS_SMALL_BUFFER;
/* Testing shows that buffer offset must be at location of Buffer[0] */
req->SecurityBufferOffset =
cpu_to_le16(sizeof(struct smb2_sess_setup_req));
req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
rc = SMB2_sess_alloc_buffer(sess_data); if (rc) goto out;
spnego_key = cifs_get_spnego_key(ses, server); if (IS_ERR(spnego_key)) {
rc = PTR_ERR(spnego_key); if (rc == -ENOKEY)
cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n");
spnego_key = NULL; goto out;
}
msg = spnego_key->payload.data[0]; /* * check version field to make sure that cifs.upcall is * sending us a response in an expected form
*/ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n",
CIFS_SPNEGO_UPCALL_VERSION, msg->version);
rc = -EKEYREJECTED; goto out_put_spnego_key;
}
/* * If memory allocation is successful, caller of this function * frees it.
*/
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); if (!ses->ntlmssp) {
rc = -ENOMEM; goto out_err;
}
ses->ntlmssp->sesskey_per_smbsess = true;
rc = SMB2_sess_alloc_buffer(sess_data); if (rc) goto out_err;
if (use_spnego) { /* BB eventually need to add this */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP; goto out;
}
sess_data->iov[1].iov_base = ntlmssp_blob;
sess_data->iov[1].iov_len = blob_length;
/* If true, rc here is expected and not an error */ if (sess_data->buf0_type != CIFS_NO_BUFFER &&
rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
rc = 0;
/* keep existing ses id and flags if binding */ if (!is_binding) {
ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
}
if (use_spnego) { /* BB eventually need to add this */
cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
rc = -EOPNOTSUPP; goto out;
}
sess_data->iov[1].iov_base = ntlmssp_blob;
sess_data->iov[1].iov_len = blob_length;
rc = SMB2_sess_sendreceive(sess_data); if (rc) goto out;
/* keep existing ses id and flags if binding */ if (!is_binding) {
ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
}
rc = SMB2_sess_establish_session(sess_data); #ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS if (ses->server->dialect < SMB30_PROT_ID) {
cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); /* * The session id is opaque in terms of endianness, so we can't * print it as a long long. we dump it as we got it on the wire
*/
cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid),
&ses->Suid);
cifs_dbg(VFS, "Session Key %*ph\n",
SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
cifs_dbg(VFS, "Signing Key %*ph\n",
SMB3_SIGN_KEY_SIZE, ses->auth_key.response);
} #endif
out:
kfree_sensitive(ntlmssp_blob);
SMB2_sess_free_buffer(sess_data);
kfree_sensitive(ses->ntlmssp);
ses->ntlmssp = NULL;
sess_data->result = rc;
sess_data->func = NULL;
}
rc = SMB2_select_sec(sess_data); if (rc) goto out;
/* * Initialize the session hash with the server one.
*/
memcpy(ses->preauth_sha_hash, server->preauth_sha_hash,
SMB2_PREAUTH_HASH_SIZE);
while (sess_data->func)
sess_data->func(sess_data);
if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign))
cifs_server_dbg(VFS, "signing requested but authenticated as guest\n");
rc = sess_data->result;
out:
kfree_sensitive(sess_data); return rc;
}
int
SMB2_logoff(constunsignedint xid, struct cifs_ses *ses)
{ struct smb_rqst rqst; struct smb2_logoff_req *req; /* response is also trivial struct */ int rc = 0; struct TCP_Server_Info *server; int flags = 0; unsignedint total_len; struct kvec iov[1]; struct kvec rsp_iov; int resp_buf_type;
cifs_dbg(FYI, "disconnect session %p\n", ses);
if (ses && (ses->server))
server = ses->server; else return -EIO;
/* no need to send SMB logoff if uid already closed due to reconnect */
spin_lock(&ses->chan_lock); if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
spin_unlock(&ses->chan_lock); goto smb2_session_already_dead;
}
spin_unlock(&ses->chan_lock);
/* These are similar values to what Windows uses */ staticinlinevoid init_copy_chunk_defaults(struct cifs_tcon *tcon)
{
tcon->max_chunks = 256;
tcon->max_bytes_chunk = 1048576;
tcon->max_bytes_copy = 16777216;
}
/* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
tcon->tid = 0;
atomic_set(&tcon->num_remote_opens, 0);
rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server,
(void **) &req, &total_len); if (rc) {
kfree(unc_path); return rc;
}
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
iov[0].iov_base = (char *)req; /* 1 for pad */
iov[0].iov_len = total_len - 1;
/* Testing shows that buffer offset must be at location of Buffer[0] */
req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req));
req->PathLength = cpu_to_le16(unc_path_len);
iov[1].iov_base = unc_path;
iov[1].iov_len = unc_path_len;
/* * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 * (Samba servers don't always set the flag so also check if null user)
*/ if ((server->dialect == SMB311_PROT_ID) &&
!smb3_encryption_required(tcon) &&
!(ses->session_flags &
(SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) &&
((ses->user_name != NULL) || (ses->sectype == Kerberos)))
req->hdr.Flags |= SMB2_FLAGS_SIGNED;
/* Need 64 for max size write so ask for more in case not there yet */ if (server->credits >= server->max_credits)
req->hdr.CreditRequest = cpu_to_le16(0); else
req->hdr.CreditRequest = cpu_to_le16(
min_t(int, server->max_credits -
server->credits, 64));
off = le32_to_cpu(rsp->CreateContextsOffset);
rem = le32_to_cpu(rsp->CreateContextsLength); if (check_add_overflow(off, rem, &len) || len > rsp_iov->iov_len) return -EINVAL;
cc = (struct create_context *)((u8 *)rsp + off);
/* Initialize inode number to 0 in case no valid data in qfid context */ if (buf)
buf->IndexNumber = 0;
while (rem >= sizeof(*cc)) {
doff = le16_to_cpu(cc->DataOffset);
dlen = le32_to_cpu(cc->DataLength); if (check_add_overflow(doff, dlen, &len) || len > rem) return -EINVAL;
/* * NB: Handle timeout defaults to 0, which allows server to choose * (most servers default to 120 seconds) and most clients default to 0. * This can be overridden at mount ("handletimeout=") if the user wants * a different persistent (or resilient) handle timeout for all opens * on a particular SMB3 mount.
*/
buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout);
buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
/* for replay, we should not overwrite the existing create guid */ if (!oparms->replay) {
generate_random_uuid(buf->dcontext.CreateGuid);
memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
} else
memcpy(buf->dcontext.CreateGuid, pfid->create_guid, 16);
buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd));
buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name));
buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */
buf->Name[0] = 'S';
buf->Name[1] = 'e';
buf->Name[2] = 'c';
buf->Name[3] = 'D';
buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */
/* * ACL is "self relative" ie ACL is stored in contiguous block of memory * and "DP" ie the DACL is present
*/
buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP);
/* offset owner, group and Sbz1 and SACL are all zero */
buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); /* Ship the ACL for now. we will copy it into buf later. */
aclptr = ptr;
ptr += sizeof(struct smb3_acl);
/* create one ACE to hold the mode embedded in reserved special SID */
acelen = setup_special_mode_ACE((struct smb_ace *)ptr, false, (__u64)mode);
ptr += acelen;
acl_size = acelen + sizeof(struct smb3_acl);
ace_count = 1;
if (set_owner) { /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */
acelen = setup_special_user_owner_ACE((struct smb_ace *)ptr);
ptr += acelen;
acl_size += acelen;
ace_count += 1;
}
/* and one more ACE to allow access for authenticated users */
acelen = setup_authusers_ACE((struct smb_ace *)ptr);
ptr += acelen;
acl_size += acelen;
ace_count += 1;
acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */
acl.AclSize = cpu_to_le16(acl_size);
acl.AceCount = cpu_to_le16(ace_count); /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */
memcpy(aclptr, &acl, sizeof(struct smb3_acl));
/* Do not append the separator if the path is empty */ if (path[0] != cpu_to_le16(0x0000)) {
UniStrcat((wchar_t *)*out_path, (wchar_t *)sep);
UniStrcat((wchar_t *)*out_path, (wchar_t *)path);
}
unload_nls(cp);
return 0;
}
int smb311_posix_mkdir(constunsignedint xid, struct inode *inode,
umode_t mode, struct cifs_tcon *tcon, constchar *full_path, struct cifs_sb_info *cifs_sb)
{ struct smb_rqst rqst; struct smb2_create_req *req; struct smb2_create_rsp *rsp = NULL; struct cifs_ses *ses = tcon->ses; struct kvec iov[3]; /* make sure at least one for each open context */ struct kvec rsp_iov = {NULL, 0}; int resp_buftype; int uni_path_len;
__le16 *copy_path = NULL; int copy_size; int rc = 0; unsignedint n_iov = 2;
__u32 file_attributes = 0; char *pc_buf = NULL; int flags = 0; unsignedint total_len;
__le16 *utf16_path = NULL; struct TCP_Server_Info *server; int retries = 0, cur_sleep = 1;
replay_again: /* reinitialize for possible replay */
flags = 0;
n_iov = 2;
server = cifs_pick_channel(ses);
/* [MS-SMB2] 2.2.13 NameOffset: * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of * the SMB2 header, the file name includes a prefix that will * be processed during DFS name normalization as specified in * section 3.3.5.9. Otherwise, the file name is relative to * the share that is identified by the TreeId in the SMB2 * header.
*/ if (tcon->share_flags & SHI1005_FLAGS_DFS) { int name_len;
/* no need to inc num_remote_opens because we close it just below */
trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE,
FILE_WRITE_ATTRIBUTES);
/* * Although unlikely to be possible for rsp to be null and rc not set, * adding check below is slightly safer long term (and quiets Coverity * warning)
*/
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; if (rsp == NULL) {
rc = -EIO;
kfree(pc_buf); goto err_free_req;
}
/* [MS-SMB2] 2.2.13 NameOffset: * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of * the SMB2 header, the file name includes a prefix that will * be processed during DFS name normalization as specified in * section 3.3.5.9. Otherwise, the file name is relative to * the share that is identified by the TreeId in the SMB2 * header.
*/ if (tcon->share_flags & SHI1005_FLAGS_DFS) { int name_len;
if (n_iov > 2) { /* * We have create contexts behind iov[1] (the file * name), point at them from the main create request
*/
req->CreateContextsOffset = cpu_to_le32( sizeof(struct smb2_create_req) +
iov[1].iov_len);
req->CreateContextsLength = 0;
for (unsignedint i = 2; i < (n_iov-1); i++) { struct kvec *v = &iov[i];
size_t len = v->iov_len; struct create_context *cctx =
(struct create_context *)v->iov_base;
/* rq_iov[0] is the request and is released by cifs_small_buf_release(). * All other vectors are freed by kfree().
*/ void
SMB2_open_free(struct smb_rqst *rqst)
{ int i;
if (rqst && rqst->rq_iov) {
cifs_small_buf_release(rqst->rq_iov[0].iov_base); for (i = 1; i < rqst->rq_nvec; i++) if (rqst->rq_iov[i].iov_base != smb2_padding)
kfree(rqst->rq_iov[i].iov_base);
}
}
if (indatalen) { /* * indatalen is usually small at a couple of bytes max, so * just allocate through generic pool
*/
in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); if (!in_data_buf) {
cifs_small_buf_release(req); return -ENOMEM;
}
}
iov[0].iov_base = (char *)req; /* * If no input data, the size of ioctl struct in * protocol spec still includes a 1 byte data buffer, * but if input data passed to ioctl, we do not * want to double count this, so we do not send * the dummy one byte of data in iovec[0] if sending * input data (in iovec[1]).
*/ if (indatalen) {
req->InputCount = cpu_to_le32(indatalen); /* do not set InputOffset if no input data */
req->InputOffset =
cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer));
rqst->rq_nvec = 2;
iov[0].iov_len = total_len - 1;
iov[1].iov_base = in_data_buf;
iov[1].iov_len = indatalen;
} else {
rqst->rq_nvec = 1;
iov[0].iov_len = total_len;
}
/* * In most cases max_response_size is set to 16K (CIFSMaxBufSize) * We Could increase default MaxOutputResponse, but that could require * more credits. Windows typically sets this smaller, but for some * ioctls it may be useful to allow server to send more. No point * limiting what the server can send as long as fits in one credit * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want * to increase this limit up in the future. * Note that for snapshot queries that servers like Azure expect that * the first query be minimal size (and just used to get the number/size * of previous versions) so response size must be specified as EXACTLY * sizeof(struct snapshot_array) which is 16 when rounded up to multiple * of eight bytes. Currently that is the only case where we set max * response size smaller.
*/
req->MaxOutputResponse = cpu_to_le32(max_response_size);
req->hdr.CreditCharge =
cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size),
SMB2_MAX_BUFFER_SIZE)); /* always an FSCTL (for now) */
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
/* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
req->hdr.Flags |= SMB2_FLAGS_SIGNED;
return 0;
}
void
SMB2_ioctl_free(struct smb_rqst *rqst)
{ int i;
if (rqst && rqst->rq_iov) {
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ for (i = 1; i < rqst->rq_nvec; i++) if (rqst->rq_iov[i].iov_base != smb2_padding)
kfree(rqst->rq_iov[i].iov_base);
}
}
/* * SMB2 IOCTL is used for both IOCTLs and FSCTLs
*/ int
SMB2_ioctl(constunsignedint xid, struct cifs_tcon *tcon, u64 persistent_fid,
u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen,
u32 max_out_data_len, char **out_data,
u32 *plen /* returned data len */)
{ struct smb_rqst rqst; struct smb2_ioctl_rsp *rsp = NULL; struct cifs_ses *ses; struct TCP_Server_Info *server; struct kvec iov[SMB2_IOCTL_IOV_SIZE]; struct kvec rsp_iov = {NULL, 0}; int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; int retries = 0, cur_sleep = 1;
if (!tcon) return -EIO;
ses = tcon->ses; if (!ses) return -EIO;
replay_again: /* reinitialize for possible replay */
flags = 0;
server = cifs_pick_channel(ses);
if (!server) return -EIO;
cifs_dbg(FYI, "SMB2 IOCTL\n");
if (out_data != NULL)
*out_data = NULL;
/* zero out returned data len, in case of error */ if (plen)
*plen = 0;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
/* check if caller wants to look at return data or just return rc */ if ((plen == NULL) || (out_data == NULL)) goto ioctl_exit;
/* * Although unlikely to be possible for rsp to be null and rc not set, * adding check below is slightly safer long term (and quiets Coverity * warning)
*/ if (rsp == NULL) {
rc = -EIO; goto ioctl_exit;
}
*plen = le32_to_cpu(rsp->OutputCount);
/* We check for obvious errors in the output buffer length and offset */ if (*plen == 0) goto ioctl_exit; /* server returned no data */ elseif (*plen > rsp_iov.iov_len || *plen > 0xFF00) {
cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
*plen = 0;
rc = -EIO; goto ioctl_exit;
}
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
FSCTL_SET_COMPRESSION,
(char *)&fsctl_input /* data input */,
2 /* in data len */, CIFSMaxBufSize /* max out data */,
&ret_data /* out data */, NULL);
req->OutputBufferLength = cpu_to_le32(output_len); if (input_len) {
req->InputBufferLength = cpu_to_le32(input_len); /* total_len for smb query request never close to le16 max */
req->InputBufferOffset = cpu_to_le16(total_len - 1);
memcpy(req->Buffer, input, input_len);
}
cnotify_exit: if (rqst.rq_iov)
cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */
free_rsp_buf(resp_buftype, rsp_iov.iov_base);
if (is_replayable_error(rc) &&
smb2_should_replay(tcon, &retries, &cur_sleep)) goto replay_again;
return rc;
}
/* * This is a no-op for now. We're not really interested in the reply, but * rather in the fact that the server sent one and that server->lstrp * gets updated. * * FIXME: maybe we should consider checking that the reply matches request?
*/ staticvoid
smb2_echo_callback(struct mid_q_entry *mid)
{ struct TCP_Server_Info *server = mid->callback_data; struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; struct cifs_credits credits = { .value = 0, .instance = 0 };
/* first check if ref count has reached 0, if not inc ref count */
spin_lock(&cifs_tcp_ses_lock); if (!server->srv_count) {
spin_unlock(&cifs_tcp_ses_lock); return;
}
server->srv_count++;
spin_unlock(&cifs_tcp_ses_lock);
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
mutex_lock(&pserver->reconnect_mutex);
/* if the server is marked for termination, drop the ref count here */ if (server->terminate) {
cifs_put_tcp_session(server, true);
mutex_unlock(&pserver->reconnect_mutex); return;
}
INIT_LIST_HEAD(&tmp_list);
INIT_LIST_HEAD(&tmp_ses_list);
cifs_dbg(FYI, "Reconnecting tcons and channels\n");
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { if (tcon->need_reconnect || tcon->need_reopen_files) {
tcon->tc_count++;
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
netfs_trace_tcon_ref_get_reconnect_server);
list_add_tail(&tcon->rlist, &tmp_list);
tcon_selected = true;
}
} /* * IPC has the same lifetime as its session and uses its * refcount.
*/ if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
tcon_selected = true;
cifs_smb_ses_inc_refcount(ses);
} /* * handle the case where channel needs to reconnect * binding session, but tcon is healthy (some other channel * is active)
*/
spin_lock(&ses->chan_lock); if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) {
list_add_tail(&ses->rlist, &tmp_ses_list);
ses_exist = true;
cifs_smb_ses_inc_refcount(ses);
}
spin_unlock(&ses->chan_lock);
}
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id);
spin_lock(&server->srv_lock); if (server->ops->need_neg &&
server->ops->need_neg(server)) {
spin_unlock(&server->srv_lock); /* No need to send echo on newly established connections */
mod_delayed_work(cifsiod_wq, &server->reconnect, 0); return rc;
}
spin_unlock(&server->srv_lock);
/* we can only offload if we're connected */ if (!server || !tcon) returnfalse;
/* we can only offload on an rdma connection */ if (!server->rdma || !server->smbd_conn) returnfalse;
/* we don't support signed offload yet */ if (server->sign) returnfalse;
/* we don't support encrypted offload yet */ if (smb3_encryption_required(tcon)) returnfalse;
/* offload also has its overhead, so only do it if desired */ if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) returnfalse;
returntrue;
} #endif/* CONFIG_CIFS_SMB_DIRECT */
/* * To form a chain of read requests, any read requests after the first should * have the end_of_chain boolean set to true.
*/ staticint
smb2_new_read_req(void **buf, unsignedint *total_len, struct cifs_io_parms *io_parms, struct cifs_io_subrequest *rdata, unsignedint remaining_bytes, int request_type)
{ int rc = -EACCES; struct smb2_read_req *req = NULL; struct smb2_hdr *shdr; struct TCP_Server_Info *server = io_parms->server;
trace_smb3_read_enter(rdata ? rdata->rreq->debug_id : 0,
rdata ? rdata->subreq.debug_index : 0,
rdata ? rdata->xid : 0,
io_parms->persistent_fid,
io_parms->tcon->tid, io_parms->tcon->ses->Suid,
io_parms->offset, io_parms->length); #ifdef CONFIG_CIFS_SMB_DIRECT /* * If we want to do a RDMA write, fill in and append * smbdirect_buffer_descriptor_v1 to the end of read request
*/ if (rdata && smb3_use_rdma_offload(io_parms)) { struct smbdirect_buffer_descriptor_v1 *v1; bool need_invalidate = server->dialect == SMB30_PROT_ID;
rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter, true, need_invalidate); if (!rdata->mr) return -EAGAIN;
switch (mid->mid_state) { case MID_RESPONSE_RECEIVED:
credits.value = le16_to_cpu(shdr->CreditRequest);
credits.instance = server->reconnect_instance; /* result already set, check signature */ if (server->sign && !mid->decrypted) { int rc;
iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes);
rc = smb2_verify_signature(&rqst, server); if (rc)
cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n",
rc);
} /* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_REQUEST_SUBMITTED:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_req_submitted); goto do_retry; case MID_RETRY_NEEDED:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
do_retry:
__set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
rdata->result = -EAGAIN; if (server->sign && rdata->got_bytes) /* reset bytes number since we can not check a sign */
rdata->got_bytes = 0; /* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_RESPONSE_MALFORMED:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_malformed);
credits.value = le16_to_cpu(shdr->CreditRequest);
credits.instance = server->reconnect_instance;
rdata->result = -EIO; break; default:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_unknown);
rdata->result = -EIO; break;
} #ifdef CONFIG_CIFS_SMB_DIRECT /* * If this rdata has a memory registered, the MR can be freed * MR needs to be freed as soon as I/O finishes to prevent deadlock * because they have limited number and are used for future I/Os
*/ if (rdata->mr) {
smbd_deregister_mr(rdata->mr);
rdata->mr = NULL;
} #endif if (rdata->result && rdata->result != -ENODATA) {
cifs_stats_fail_inc(tcon, SMB2_READ_HE);
trace_smb3_read_err(rdata->rreq->debug_id,
rdata->subreq.debug_index,
rdata->xid,
rdata->req->cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid,
rdata->subreq.start + rdata->subreq.transferred,
rdata->subreq.len - rdata->subreq.transferred,
rdata->result);
} else
trace_smb3_read_done(rdata->rreq->debug_id,
rdata->subreq.debug_index,
rdata->xid,
rdata->req->cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid,
rdata->subreq.start + rdata->subreq.transferred,
rdata->got_bytes);
WARN_ONCE(wdata->server != mid->server, "wdata server %p != mid server %p",
wdata->server, mid->server);
switch (mid->mid_state) { case MID_RESPONSE_RECEIVED:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress);
credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
credits.instance = server->reconnect_instance;
result = smb2_check_receive(mid, server, 0); if (result != 0) {
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_bad); break;
}
written = le32_to_cpu(rsp->DataLength); /* * Mask off high 16 bits when bytes written as returned * by the server is greater than bytes requested by the * client. OS/2 servers are known to set incorrect * CountHigh values.
*/ if (written > wdata->subreq.len)
written &= 0xFFFF;
cifs_stats_bytes_written(tcon, written);
if (written < wdata->subreq.len) {
wdata->result = -ENOSPC;
} elseif (written > 0) {
wdata->subreq.len = written;
__set_bit(NETFS_SREQ_MADE_PROGRESS, &wdata->subreq.flags);
} break; case MID_REQUEST_SUBMITTED:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_req_submitted);
__set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
result = -EAGAIN; break; case MID_RETRY_NEEDED:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed);
__set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
result = -EAGAIN; break; case MID_RESPONSE_MALFORMED:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_malformed);
credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
credits.instance = server->reconnect_instance;
result = -EIO; break; default:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_unknown);
result = -EIO; break;
} #ifdef CONFIG_CIFS_SMB_DIRECT /* * If this wdata has a memory registered, the MR can be freed * The number of MRs available is limited, it's important to recover * used MR as soon as I/O is finished. Hold MR longer in the later * I/O process can possibly result in I/O deadlock due to lack of MR * to send request on I/O retry
*/ if (wdata->mr) {
smbd_deregister_mr(wdata->mr);
wdata->mr = NULL;
} #endif if (result) {
cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
trace_smb3_write_err(wdata->rreq->debug_id,
wdata->subreq.debug_index,
wdata->xid,
wdata->req->cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, wdata->subreq.start,
wdata->subreq.len, wdata->result); if (wdata->result == -ENOSPC)
pr_warn_once("Out of space writing to %s\n",
tcon->tree_name);
} else
trace_smb3_write_done(wdata->rreq->debug_id,
wdata->subreq.debug_index,
wdata->xid,
wdata->req->cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid,
wdata->subreq.start, wdata->subreq.len);
/* smb2_async_writev - send an async write, and set up mid to handle result */ void
smb2_async_writev(struct cifs_io_subrequest *wdata)
{ int rc = -EACCES, flags = 0; struct smb2_write_req *req = NULL; struct smb2_hdr *shdr; struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink); struct TCP_Server_Info *server = wdata->server; struct kvec iov[1]; struct smb_rqst rqst = { }; unsignedint total_len, xid = wdata->xid; struct cifs_io_parms _io_parms; struct cifs_io_parms *io_parms = NULL; int credit_request;
/* * in future we may get cifs_io_parms passed in from the caller, * but for now we construct it here...
*/
_io_parms = (struct cifs_io_parms) {
.tcon = tcon,
.server = server,
.offset = wdata->subreq.start,
.length = wdata->subreq.len,
.persistent_fid = wdata->req->cfile->fid.persistent_fid,
.volatile_fid = wdata->req->cfile->fid.volatile_fid,
.pid = wdata->req->pid,
};
io_parms = &_io_parms;
#ifdef CONFIG_CIFS_SMB_DIRECT /* * If we want to do a server RDMA read, fill in and append * smbdirect_buffer_descriptor_v1 to the end of write request
*/ if (smb3_use_rdma_offload(io_parms)) { struct smbdirect_buffer_descriptor_v1 *v1; bool need_invalidate = server->dialect == SMB30_PROT_ID;
wdata->mr = smbd_register_mr(server->smbd_conn, &wdata->subreq.io_iter, false, need_invalidate); if (!wdata->mr) {
rc = -EAGAIN; goto async_writev_out;
} /* For RDMA read, I/O size is in RemainingBytes not in Length */
req->RemainingBytes = req->Length;
req->Length = 0;
req->DataOffset = 0;
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; if (need_invalidate)
req->Channel = SMB2_CHANNEL_RDMA_V1;
req->WriteChannelInfoOffset =
cpu_to_le16(offsetof(struct smb2_write_req, Buffer));
req->WriteChannelInfoLength =
cpu_to_le16(sizeof(struct smbdirect_buffer_descriptor_v1));
v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0];
v1->offset = cpu_to_le64(wdata->mr->mr->iova);
v1->token = cpu_to_le32(wdata->mr->mr->rkey);
v1->length = cpu_to_le32(wdata->mr->mr->length);
rqst.rq_iov[0].iov_len += sizeof(*v1);
/* * We keep wdata->subreq.io_iter, * but we have to truncate rqst.rq_iter
*/
iov_iter_truncate(&rqst.rq_iter, 0);
} #endif
if (wdata->subreq.retry_count > 0)
smb2_set_replay(server, &rqst);
/* * SMB2_write function gets iov pointer to kvec array with n_vec as a length. * The length field from io_parms must be at least 1 and indicates a number of * elements with data to write that begins with position 1 in iov array. All * data length is specified by count.
*/ int
SMB2_write(constunsignedint xid, struct cifs_io_parms *io_parms, unsignedint *nbytes, struct kvec *iov, int n_vec)
{ struct smb_rqst rqst; int rc = 0; struct smb2_write_req *req = NULL; struct smb2_write_rsp *rsp = NULL; int resp_buftype; struct kvec rsp_iov; int flags = 0; unsignedint total_len; struct TCP_Server_Info *server; int retries = 0, cur_sleep = 1;
replay_again: /* reinitialize for possible replay */
flags = 0;
*nbytes = 0; if (!io_parms->server)
io_parms->server = cifs_pick_channel(io_parms->tcon->ses);
server = io_parms->server; if (server == NULL) return -ECONNABORTED;
if (infotype == SMB_FIND_FILE_POSIX_INFO)
len = posix_info_extra_size(entryptr, end_of_buf); else
len = le32_to_cpu(dir_info->FileNameLength);
if (len < 0 ||
entryptr + len < entryptr ||
entryptr + len > end_of_buf ||
entryptr + len + size > end_of_buf) {
cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n",
end_of_buf); break;
}
*lastentry = entryptr;
entrycount++;
next_offset = le32_to_cpu(dir_info->NextEntryOffset); if (!next_offset) break;
}
len = 0x2;
bufptr = req->Buffer;
memcpy(bufptr, &asteriks, len);
req->FileNameOffset =
cpu_to_le16(sizeof(struct smb2_query_directory_req));
req->FileNameLength = cpu_to_le16(len); /* * BB could be 30 bytes or so longer if we used SMB2 specific * buffer lengths, but this is safe and close enough.
*/
output_size = min_t(unsignedint, output_size, server->maxBuf);
output_size = min_t(unsignedint, output_size, 2 << 15);
req->OutputBufferLength = cpu_to_le32(output_size);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.114Bemerkung:
(vorverarbeitet am 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.