// 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));
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.