mirror of
https://github.com/torvalds/linux.git
synced 2026-10-07 02:56:02 +02:00
ksmbd: expire SMB sessions when Kerberos tickets expire
Store the expiry time from the Kerberos authentication response in the session and reject requests after that time with STATUS_NETWORK_SESSION_EXPIRED. Allow an expired Kerberos session to be reauthenticated. Keep the old SMB signing key until its SESSION_SETUP response has been signed, then install the new session key and regenerate the SMB3 keys. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
84c41b731b
commit
a47634cd72
|
|
@ -463,6 +463,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
|
|||
memcpy(out_blob, resp->payload + resp->session_key_len,
|
||||
resp->spnego_blob_len);
|
||||
*out_len = resp->spnego_blob_len;
|
||||
sess->kerberos_expiry = resp->session_expiry;
|
||||
retval = 0;
|
||||
out:
|
||||
kvfree(resp);
|
||||
|
|
@ -717,8 +718,21 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
|
|||
|
||||
if (enc)
|
||||
sess = work->sess;
|
||||
else
|
||||
sess = ksmbd_session_lookup_all(work->conn, ses_id);
|
||||
else {
|
||||
/*
|
||||
* An encrypted SESSION_SETUP request may reauthenticate an expired
|
||||
* Kerberos session. Keep using the established decryption key so
|
||||
* that the command can reach the session setup handler. Other
|
||||
* commands are rejected there with STATUS_NETWORK_SESSION_EXPIRED.
|
||||
*/
|
||||
sess = ksmbd_session_lookup(work->conn, ses_id);
|
||||
if (sess && sess->state != SMB2_SESSION_VALID &&
|
||||
(sess->state != SMB2_SESSION_EXPIRED ||
|
||||
!sess->kerberos_expiry)) {
|
||||
ksmbd_user_session_put(sess);
|
||||
sess = NULL;
|
||||
}
|
||||
}
|
||||
if (!sess)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -286,6 +286,7 @@ struct ksmbd_spnego_authen_response {
|
|||
* stored in SecurityBuffer of SMB2 SESSION
|
||||
* SETUP response
|
||||
*/
|
||||
__u64 session_expiry; /* Kerberos ticket expiry time */
|
||||
__u8 payload[]; /* session key + AP_REP */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ struct ksmbd_work {
|
|||
bool owns_conn_ref:1;
|
||||
bool need_invalidate_rkey:1;
|
||||
bool request_open_chseq_tracked:1;
|
||||
bool session_setup_reauth:1;
|
||||
|
||||
unsigned int remote_key;
|
||||
/* cancel works */
|
||||
|
|
|
|||
|
|
@ -547,8 +547,8 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
|
|||
return sess;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
||||
unsigned long long id)
|
||||
struct ksmbd_session *ksmbd_session_lookup_all_states(struct ksmbd_conn *conn,
|
||||
unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
|
|
@ -560,6 +560,15 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
|||
sess = NULL;
|
||||
}
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
||||
unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
sess = ksmbd_session_lookup_all_states(conn, id);
|
||||
if (sess && sess->state != SMB2_SESSION_VALID) {
|
||||
ksmbd_user_session_put(sess);
|
||||
sess = NULL;
|
||||
|
|
@ -639,6 +648,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
|
|||
}
|
||||
|
||||
ksmbd_destroy_file_table(prev_sess);
|
||||
prev_sess->kerberos_expiry = 0;
|
||||
prev_sess->state = SMB2_SESSION_EXPIRED;
|
||||
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
|
||||
ksmbd_launch_ksmbd_durable_scavenger();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ struct ksmbd_session {
|
|||
__u8 *Preauth_HashValue;
|
||||
|
||||
char sess_key[CIFS_KEY_SIZE];
|
||||
u64 kerberos_expiry;
|
||||
|
||||
struct hlist_node hlist;
|
||||
struct rw_semaphore chann_lock;
|
||||
|
|
@ -100,6 +101,8 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
|
|||
struct ksmbd_session *__session_lookup(unsigned long long id);
|
||||
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
||||
unsigned long long id);
|
||||
struct ksmbd_session *ksmbd_session_lookup_all_states(struct ksmbd_conn *conn,
|
||||
unsigned long long id);
|
||||
void destroy_previous_session(struct ksmbd_conn *conn,
|
||||
struct ksmbd_user *user, u64 id);
|
||||
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
|
||||
|
|
|
|||
|
|
@ -208,6 +208,9 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
|
|||
if (rc == -EINVAL)
|
||||
conn->ops->set_rsp_status(work,
|
||||
STATUS_INVALID_PARAMETER);
|
||||
else if (rc == -EKEYEXPIRED)
|
||||
conn->ops->set_rsp_status(work,
|
||||
STATUS_NETWORK_SESSION_EXPIRED);
|
||||
else
|
||||
conn->ops->set_rsp_status(work,
|
||||
STATUS_USER_SESSION_DELETED);
|
||||
|
|
@ -215,7 +218,11 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
|
|||
struct smb2_hdr *rsp_hdr;
|
||||
|
||||
rsp_hdr = ksmbd_resp_buf_curr(work);
|
||||
rsp_hdr->Flags |= SMB2_FLAGS_SIGNED;
|
||||
if (rc == -EKEYEXPIRED && work->sess &&
|
||||
conn->ops->set_sign_rsp)
|
||||
conn->ops->set_sign_rsp(work);
|
||||
else
|
||||
rsp_hdr->Flags |= SMB2_FLAGS_SIGNED;
|
||||
}
|
||||
goto send;
|
||||
} else if (rc > 0) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/mount.h>
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include "glob.h"
|
||||
#include "../common/smbfsctl.h"
|
||||
|
|
@ -55,6 +56,10 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp)
|
|||
}
|
||||
}
|
||||
|
||||
static struct ksmbd_work *smb2_notify_cancel_claim(void **argv);
|
||||
static void smb2_notify_cancel_fn(void **argv);
|
||||
static void smb2_complete_notify_cancel(struct ksmbd_work *in_work);
|
||||
|
||||
#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs))
|
||||
|
||||
#define SMB2_CREATE_FILE_ATTRIBUTE_MASK \
|
||||
|
|
@ -67,29 +72,6 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp)
|
|||
/* MAXFILESIZE in [MS-FSA] 2.1.5.3 Server Requests a Write. */
|
||||
#define SMB2_MAX_FILE_SIZE 0xfffffff0000ULL
|
||||
|
||||
/**
|
||||
* check_session_id() - check for valid session id in smb header
|
||||
* @conn: connection instance
|
||||
* @id: session id from smb header
|
||||
*
|
||||
* Return: 1 if valid session id, otherwise 0
|
||||
*/
|
||||
static inline bool check_session_id(struct ksmbd_conn *conn, u64 id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
if (id == 0 || id == -1)
|
||||
return false;
|
||||
|
||||
sess = ksmbd_session_lookup_all(conn, id);
|
||||
if (sess) {
|
||||
ksmbd_user_session_put(sess);
|
||||
return true;
|
||||
}
|
||||
pr_err("Invalid user session id: %llu\n", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn)
|
||||
{
|
||||
struct channel *chann;
|
||||
|
|
@ -899,6 +881,47 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool smb2_session_expired_cmd_allowed(struct ksmbd_work *work,
|
||||
unsigned int cmd)
|
||||
{
|
||||
struct smb2_lock_req *req;
|
||||
unsigned int len, lock_count, i;
|
||||
|
||||
if (cmd == SMB2_CANCEL_HE || cmd == SMB2_CLOSE_HE ||
|
||||
cmd == SMB2_LOGOFF_HE)
|
||||
return true;
|
||||
if (cmd != SMB2_LOCK_HE)
|
||||
return false;
|
||||
|
||||
req = ksmbd_req_buf_next(work);
|
||||
if (req->hdr.NextCommand)
|
||||
len = le32_to_cpu(req->hdr.NextCommand);
|
||||
else {
|
||||
len = get_rfc1002_len(work->request_buf);
|
||||
if (len < work->next_smb2_rcv_hdr_off)
|
||||
return false;
|
||||
len -= work->next_smb2_rcv_hdr_off;
|
||||
}
|
||||
|
||||
lock_count = le16_to_cpu(req->LockCount);
|
||||
if (!lock_count || len < offsetof(struct smb2_lock_req, locks) ||
|
||||
lock_count > (len - offsetof(struct smb2_lock_req, locks)) /
|
||||
sizeof(struct smb2_lock_element))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < lock_count; i++) {
|
||||
if (le32_to_cpu(req->locks[i].Flags) != SMB2_LOCKFLAG_UNLOCK)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool smb2_session_kerberos_expired(struct ksmbd_session *sess)
|
||||
{
|
||||
return sess->kerberos_expiry &&
|
||||
ktime_get_real_seconds() >= sess->kerberos_expiry;
|
||||
}
|
||||
|
||||
/**
|
||||
* smb2_check_user_session() - check for valid session for a user
|
||||
* @work: smb work containing smb request buffer
|
||||
|
|
@ -913,19 +936,37 @@ int smb2_check_user_session(struct ksmbd_work *work)
|
|||
unsigned long long sess_id;
|
||||
|
||||
/*
|
||||
* SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not
|
||||
* require a session id, so no need to validate user session's for
|
||||
* these commands.
|
||||
* SMB2_NEGOTIATE and SMB2_SESSION_SETUP do not require a session id.
|
||||
* SMB2_ECHO may omit it, but an echo carrying a session id still needs
|
||||
* the session attached to work so that its signature can be checked and
|
||||
* the response can be signed, including after Kerberos expiry.
|
||||
*/
|
||||
if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE ||
|
||||
cmd == SMB2_SESSION_SETUP_HE)
|
||||
if (cmd == SMB2_NEGOTIATE_HE || cmd == SMB2_SESSION_SETUP_HE)
|
||||
return 0;
|
||||
|
||||
sess_id = le64_to_cpu(req_hdr->SessionId);
|
||||
if (cmd == SMB2_ECHO_HE) {
|
||||
/*
|
||||
* ECHO remains valid without a live session, including after
|
||||
* LOGOFF. Attach an existing session only to authenticate a signed
|
||||
* ECHO and sign its response; a stale SessionId is not an error.
|
||||
*/
|
||||
if (!work->next_smb2_rcv_hdr_off && sess_id)
|
||||
work->sess = ksmbd_session_lookup_all_states(conn, sess_id);
|
||||
if (work->sess) {
|
||||
if (smb2_session_kerberos_expired(work->sess)) {
|
||||
work->sess->state = SMB2_SESSION_EXPIRED;
|
||||
} else if (work->sess->state != SMB2_SESSION_VALID) {
|
||||
ksmbd_user_session_put(work->sess);
|
||||
work->sess = NULL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksmbd_conn_good(conn))
|
||||
return -EIO;
|
||||
|
||||
sess_id = le64_to_cpu(req_hdr->SessionId);
|
||||
|
||||
/*
|
||||
* If request is not the first in Compound request,
|
||||
* Just validate session id in header with work->sess->id.
|
||||
|
|
@ -940,18 +981,35 @@ int smb2_check_user_session(struct ksmbd_work *work)
|
|||
sess_id, work->sess->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (smb2_session_kerberos_expired(work->sess))
|
||||
work->sess->state = SMB2_SESSION_EXPIRED;
|
||||
if (work->sess->state != SMB2_SESSION_VALID) {
|
||||
pr_err("compound request on a non-valid session (state %d)\n",
|
||||
work->sess->state);
|
||||
return -EINVAL;
|
||||
if (smb2_session_kerberos_expired(work->sess) &&
|
||||
smb2_session_expired_cmd_allowed(work, cmd))
|
||||
return 1;
|
||||
return smb2_session_kerberos_expired(work->sess) ?
|
||||
-EKEYEXPIRED : -EINVAL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check for validity of user session */
|
||||
work->sess = ksmbd_session_lookup_all(conn, sess_id);
|
||||
if (work->sess)
|
||||
work->sess = ksmbd_session_lookup_all_states(conn, sess_id);
|
||||
if (work->sess) {
|
||||
if (smb2_session_kerberos_expired(work->sess)) {
|
||||
work->sess->state = SMB2_SESSION_EXPIRED;
|
||||
return smb2_session_expired_cmd_allowed(work, cmd) ?
|
||||
1 : -EKEYEXPIRED;
|
||||
}
|
||||
if (work->sess->state != SMB2_SESSION_VALID) {
|
||||
ksmbd_user_session_put(work->sess);
|
||||
work->sess = NULL;
|
||||
return -ENOENT;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
@ -2082,7 +2140,9 @@ static int krb5_authenticate(struct ksmbd_work *work,
|
|||
struct ksmbd_session *sess = work->sess;
|
||||
char *in_blob, *out_blob;
|
||||
char channel_key[CIFS_KEY_SIZE] = {};
|
||||
char *auth_key = conn->binding ? channel_key : sess->sess_key;
|
||||
char reauth_key[CIFS_KEY_SIZE] = {};
|
||||
char *auth_key = conn->binding ? channel_key :
|
||||
(work->session_setup_reauth ? reauth_key : sess->sess_key);
|
||||
u64 prev_sess_id;
|
||||
bool binding = conn->binding;
|
||||
int in_len, out_len;
|
||||
|
|
@ -2101,7 +2161,7 @@ static int krb5_authenticate(struct ksmbd_work *work,
|
|||
if (retval) {
|
||||
ksmbd_debug(SMB, "krb5 authentication failed\n");
|
||||
if (retval != -EKEYREJECTED)
|
||||
retval = -EINVAL;
|
||||
retval = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -2117,12 +2177,21 @@ static int krb5_authenticate(struct ksmbd_work *work,
|
|||
* that it is reauthentication. And the user/password
|
||||
* has been verified, so return it here.
|
||||
*/
|
||||
if (sess->state == SMB2_SESSION_VALID) {
|
||||
if (sess->state == SMB2_SESSION_VALID && !work->session_setup_reauth) {
|
||||
if (conn->binding)
|
||||
goto binding_session;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reauthentication verifies the new Kerberos credentials but keeps
|
||||
* the established SMB session keys.
|
||||
*/
|
||||
if (work->session_setup_reauth) {
|
||||
retval = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE &&
|
||||
(conn->sign || server_conf.enforced_signing)) ||
|
||||
(req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED))
|
||||
|
|
@ -2160,6 +2229,7 @@ static int krb5_authenticate(struct ksmbd_work *work,
|
|||
}
|
||||
retval = 0;
|
||||
out:
|
||||
memzero_explicit(reauth_key, sizeof(reauth_key));
|
||||
if (binding)
|
||||
memzero_explicit(channel_key, sizeof(channel_key));
|
||||
return retval;
|
||||
|
|
@ -2318,8 +2388,13 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
|||
}
|
||||
|
||||
if (sess->state == SMB2_SESSION_EXPIRED) {
|
||||
rc = -EFAULT;
|
||||
goto out_err;
|
||||
if (sess->kerberos_expiry &&
|
||||
ktime_get_real_seconds() >= sess->kerberos_expiry) {
|
||||
work->session_setup_reauth = true;
|
||||
} else {
|
||||
rc = -EFAULT;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (ksmbd_conn_need_reconnect(conn)) {
|
||||
|
|
@ -2363,10 +2438,8 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
|||
if (conn->preferred_auth_mech &
|
||||
(KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) {
|
||||
rc = krb5_authenticate(work, req, rsp);
|
||||
if (rc) {
|
||||
rc = -EINVAL;
|
||||
if (rc)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (!ksmbd_conn_need_reconnect(conn)) {
|
||||
ksmbd_conn_set_good(conn);
|
||||
|
|
@ -2477,6 +2550,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
|||
*/
|
||||
if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
|
||||
sess->last_active = jiffies;
|
||||
sess->kerberos_expiry = 0;
|
||||
sess->state = SMB2_SESSION_EXPIRED;
|
||||
}
|
||||
/*
|
||||
|
|
@ -2824,6 +2898,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
|||
}
|
||||
|
||||
down_write(&conn->session_lock);
|
||||
sess->kerberos_expiry = 0;
|
||||
sess->state = SMB2_SESSION_EXPIRED;
|
||||
up_write(&conn->session_lock);
|
||||
|
||||
|
|
@ -7404,7 +7479,6 @@ int smb2_close(struct ksmbd_work *work)
|
|||
u64 sess_id;
|
||||
struct smb2_close_req *req;
|
||||
struct smb2_close_rsp *rsp;
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct ksmbd_file *fp;
|
||||
u64 time;
|
||||
int err = 0;
|
||||
|
|
@ -7427,7 +7501,7 @@ int smb2_close(struct ksmbd_work *work)
|
|||
sess_id = work->compound_sid;
|
||||
|
||||
work->compound_sid = 0;
|
||||
if (check_session_id(conn, sess_id)) {
|
||||
if (work->sess && work->sess->id == sess_id) {
|
||||
work->compound_sid = sess_id;
|
||||
} else {
|
||||
rsp->hdr.Status = STATUS_USER_SESSION_DELETED;
|
||||
|
|
@ -8914,6 +8988,7 @@ int smb2_cancel(struct ksmbd_work *work)
|
|||
struct smb2_hdr *hdr = smb_get_msg(work->request_buf);
|
||||
struct smb2_hdr *chdr;
|
||||
struct ksmbd_work *iter;
|
||||
struct ksmbd_work *cancelled_notify = NULL;
|
||||
struct list_head *command_list;
|
||||
|
||||
if (work->next_smb2_rcv_hdr_off)
|
||||
|
|
@ -8951,11 +9026,23 @@ int smb2_cancel(struct ksmbd_work *work)
|
|||
le64_to_cpu(hdr->Id.AsyncId),
|
||||
le16_to_cpu(chdr->Command));
|
||||
iter->state = KSMBD_WORK_CANCELLED;
|
||||
if (iter->cancel_fn)
|
||||
if (iter->cancel_fn == smb2_notify_cancel_fn)
|
||||
cancelled_notify =
|
||||
smb2_notify_cancel_claim(iter->cancel_argv);
|
||||
else if (iter->cancel_fn)
|
||||
iter->cancel_fn(iter->cancel_argv);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&conn->request_lock);
|
||||
|
||||
/*
|
||||
* Complete a cancelled notify before this CANCEL handler returns.
|
||||
* Deferring it to the system workqueue lets a following request and
|
||||
* its response overtake STATUS_CANCELLED, leaving clients waiting
|
||||
* for the original notify even though the cancellation was accepted.
|
||||
*/
|
||||
if (cancelled_notify)
|
||||
smb2_complete_notify_cancel(cancelled_notify);
|
||||
} else {
|
||||
command_list = &conn->requests;
|
||||
|
||||
|
|
@ -10905,37 +10992,54 @@ int smb2_oplock_break(struct ksmbd_work *work)
|
|||
* it from here would self-deadlock the very thread processing the
|
||||
* client's CANCEL command. ksmbd_conn_write() can also sleep (it takes
|
||||
* conn's write mutex). So: do only the non-sleeping, no-relock cleanup
|
||||
* inline here (the async_requests removal itself is safe without
|
||||
* re-locking, since the caller already holds that lock), and defer the
|
||||
* actual response send + work-struct free to a workqueue, matching the
|
||||
* minimal, non-blocking style of the existing smb2_remove_blocked_lock()
|
||||
* cancel_fn (which only wakes a waiter, never sends network data itself).
|
||||
* inline here. smb2_cancel() sends and frees the claimed notify after it
|
||||
* drops request_lock, preserving response order for a client CANCEL. The
|
||||
* connection teardown caller has no such post-unlock path, so its wrapper
|
||||
* defers the send and free to a workqueue.
|
||||
*/
|
||||
struct notify_cancel_ctx {
|
||||
struct work_struct work;
|
||||
struct ksmbd_work *in_work;
|
||||
};
|
||||
|
||||
static void smb2_send_notify_cancelled(struct ksmbd_work *work)
|
||||
{
|
||||
struct smb2_hdr *hdr = smb_get_msg(work->response_buf);
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
sess = ksmbd_session_lookup(conn, le64_to_cpu(hdr->SessionId));
|
||||
if (sess) {
|
||||
work->sess = sess;
|
||||
if (work->encrypted && sess->enc && conn->ops->encrypt_resp) {
|
||||
conn->ops->encrypt_resp(work);
|
||||
} else if (conn->ops->is_sign_req && conn->ops->set_sign_rsp &&
|
||||
conn->ops->is_sign_req(work,
|
||||
conn->ops->get_cmd_val(work))) {
|
||||
conn->ops->set_sign_rsp(work);
|
||||
}
|
||||
}
|
||||
|
||||
ksmbd_conn_write(work);
|
||||
if (sess) {
|
||||
ksmbd_user_session_put(sess);
|
||||
work->sess = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void smb2_notify_cancel_deferred(struct work_struct *w)
|
||||
{
|
||||
struct notify_cancel_ctx *ctx =
|
||||
container_of(w, struct notify_cancel_ctx, work);
|
||||
struct ksmbd_work *in_work = ctx->in_work;
|
||||
struct smb2_hdr *in_hdr;
|
||||
|
||||
in_hdr = smb_get_msg(in_work->response_buf);
|
||||
in_hdr->Status = STATUS_CANCELLED;
|
||||
ksmbd_conn_write(in_work);
|
||||
ksmbd_free_work_struct(in_work);
|
||||
smb2_complete_notify_cancel(ctx->in_work);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
static void smb2_notify_cancel_fn(void **argv)
|
||||
static struct ksmbd_work *smb2_notify_cancel_claim(void **argv)
|
||||
{
|
||||
struct ksmbd_work *in_work = (struct ksmbd_work *)argv[0];
|
||||
struct ksmbd_file *fp = (struct ksmbd_file *)argv[1];
|
||||
struct ksmbd_conn *conn = in_work->conn;
|
||||
struct notify_cancel_ctx *ctx;
|
||||
bool claimed;
|
||||
|
||||
spin_lock(&fp->f_lock);
|
||||
|
|
@ -10945,22 +11049,44 @@ static void smb2_notify_cancel_fn(void **argv)
|
|||
spin_unlock(&fp->f_lock);
|
||||
|
||||
if (!claimed)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
/* conn->request_lock is already held by the caller (smb2_cancel()). */
|
||||
list_del_init(&in_work->async_request_entry);
|
||||
in_work->asynchronous = false;
|
||||
/* conn->request_lock is held by smb2_cancel() or connection teardown. */
|
||||
in_work->cancel_fn = NULL;
|
||||
kfree(in_work->cancel_argv);
|
||||
in_work->cancel_argv = NULL;
|
||||
if (in_work->async_id) {
|
||||
ksmbd_release_id(&conn->async_ida, in_work->async_id);
|
||||
in_work->async_id = 0;
|
||||
}
|
||||
return in_work;
|
||||
}
|
||||
|
||||
static void smb2_complete_notify_cancel(struct ksmbd_work *in_work)
|
||||
{
|
||||
struct smb2_hdr *in_hdr = smb_get_msg(in_work->response_buf);
|
||||
|
||||
in_hdr->Status = STATUS_CANCELLED;
|
||||
smb2_send_notify_cancelled(in_work);
|
||||
release_async_work(in_work);
|
||||
ksmbd_free_work_struct(in_work);
|
||||
}
|
||||
|
||||
static void smb2_notify_cancel_fn(void **argv)
|
||||
{
|
||||
struct ksmbd_work *in_work = smb2_notify_cancel_claim(argv);
|
||||
struct ksmbd_conn *conn;
|
||||
struct notify_cancel_ctx *ctx;
|
||||
|
||||
if (!in_work)
|
||||
return;
|
||||
conn = in_work->conn;
|
||||
|
||||
ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
|
||||
if (!ctx) {
|
||||
/* Can't defer the response -- free without sending one. */
|
||||
list_del_init(&in_work->async_request_entry);
|
||||
in_work->asynchronous = false;
|
||||
if (in_work->async_id) {
|
||||
ksmbd_release_id(&conn->async_ida, in_work->async_id);
|
||||
in_work->async_id = 0;
|
||||
}
|
||||
ksmbd_free_work_struct(in_work);
|
||||
return;
|
||||
}
|
||||
|
|
@ -11081,6 +11207,8 @@ int smb2_notify(struct ksmbd_work *work)
|
|||
smb2_set_err_rsp(work);
|
||||
return 0;
|
||||
}
|
||||
memcpy(smb_get_msg(in_work->request_buf), req,
|
||||
__SMB2_HEADER_STRUCTURE_SIZE);
|
||||
|
||||
if (setup_async_work(work, NULL, NULL)) {
|
||||
ksmbd_free_work_struct(in_work);
|
||||
|
|
@ -11095,6 +11223,7 @@ int smb2_notify(struct ksmbd_work *work)
|
|||
/* Keep the async IDA alive until the deferred work is released. */
|
||||
in_work->conn = ksmbd_conn_get(work->conn);
|
||||
in_work->owns_conn_ref = true;
|
||||
in_work->encrypted = work->encrypted;
|
||||
in_hdr = smb_get_msg(in_work->response_buf);
|
||||
memcpy(in_hdr, ksmbd_resp_buf_next(work), __SMB2_HEADER_STRUCTURE_SIZE);
|
||||
in_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND;
|
||||
|
|
@ -11460,7 +11589,6 @@ bool smb3_is_transform_hdr(void *buf)
|
|||
|
||||
int smb3_decrypt_req(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
char *buf = work->request_buf;
|
||||
unsigned int pdu_length = get_rfc1002_len(buf);
|
||||
struct kvec iov[2];
|
||||
|
|
@ -11480,14 +11608,6 @@ int smb3_decrypt_req(struct ksmbd_work *work)
|
|||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
sess = ksmbd_session_lookup_all(work->conn, le64_to_cpu(tr_hdr->SessionId));
|
||||
if (!sess) {
|
||||
pr_err("invalid session id(%llx) in transform header\n",
|
||||
le64_to_cpu(tr_hdr->SessionId));
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
ksmbd_user_session_put(sess);
|
||||
|
||||
iov[0].iov_base = buf;
|
||||
iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4;
|
||||
iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user