summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-07-09 16:30:56 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-07-09 16:30:56 -0700
commita635d6748234582ea287c5ffeae28b9b23f91c7e (patch)
treef350db99de06fb2d32fe2093b07efcef73c6e2ee
parent2c7c88a412aa6d09cd04b414211b4ef8553b5309 (diff)
parentf49bca41c12f9f79d39dbf0779d0c672d74b09fe (diff)
downloadlinux-a635d6748234582ea287c5ffeae28b9b23f91c7e.tar.gz
linux-a635d6748234582ea287c5ffeae28b9b23f91c7e.zip
Merge tag 'v7.2-rc2-smb3-server-fixes' of git://git.samba.org/ksmbd
Pull smb server fixes from Steve French: "This contains a set of SMB server fixes mostly around session setup, multichannel/session binding, and protocol-compatible error reporting: - Fix SID-to-id mapping so only SIDs with a valid local Unix representation are translated, while preserving other Windows SIDs in NT ACL xattrs - Fix SMB3 multichannel binding across multi-round authentication, keep the derived channel key separate from the established session key, and enforce the 32-channel session limit - Match Windows-compatible close timestamp behavior by coalescing automatic write time updates smaller than 15ms - Return STATUS_DISK_FULL for SET_INFO allocation failures caused by ENOSPC or EFBIG - Fix several signed SESSION_SETUP error paths so clients see the intended server status instead of replacing it with STATUS_ACCESS_DENIED - Fix reauthentication on bound channels and reject different-user channel binding with STATUS_ACCESS_DENIED - Use the referenced session dialect/signing algorithm when validating and signing rejected cross-dialect binding requests" * tag 'v7.2-rc2-smb3-server-fixes' of git://git.samba.org/ksmbd: ksmbd: use the session dialect for rejected binding signatures ksmbd: mark rejected cross-dialect bindings as signed ksmbd: sign rejected SMB2.1 session binding responses ksmbd: handle channel binding with a different user ksmbd: find bound sessions during reauthentication ksmbd: mark invalid session responses as signed smb/server: map SET_INFO ENOSPC to disk full ksmbd: coalesce sub-15ms write time updates on close ksmbd: fix multichannel binding and enforce channel limit ksmbd: validate SID namespace before mapping IDs
-rw-r--r--fs/smb/server/auth.c49
-rw-r--r--fs/smb/server/auth.h7
-rw-r--r--fs/smb/server/mgmt/user_session.c4
-rw-r--r--fs/smb/server/mgmt/user_session.h1
-rw-r--r--fs/smb/server/server.c16
-rw-r--r--fs/smb/server/smb2pdu.c205
-rw-r--r--fs/smb/server/smbacl.c21
-rw-r--r--fs/smb/server/vfs_cache.h1
8 files changed, 218 insertions, 86 deletions
diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
index 86f521e849d5..4e7b6f0e6b8c 100644
--- a/fs/smb/server/auth.c
+++ b/fs/smb/server/auth.c
@@ -133,16 +133,17 @@ out:
* @blen: NTLMv2 blob length
* @domain_name: domain name
* @cryptkey: session crypto key
+ * @sess_key: derived session key output buffer
*
* Return: 0 on success, error number on error
*/
int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
struct ntlmv2_resp *ntlmv2, int blen, char *domain_name,
- char *cryptkey)
+ char *cryptkey, char *sess_key)
{
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
- char sess_key[SMB2_NTLMV2_SESSKEY_SIZE];
+ char base_key[SMB2_NTLMV2_SESSKEY_SIZE];
struct hmac_md5_ctx ctx;
int rc;
@@ -165,7 +166,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
/* Generate the session key */
hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE,
- sess_key);
+ base_key);
if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp,
CIFS_HMAC_MD5_HASH_SIZE)) {
@@ -173,12 +174,12 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
goto out;
}
- memcpy(sess->sess_key, sess_key, sizeof(sess_key));
+ memcpy(sess_key, base_key, sizeof(base_key));
rc = 0;
out:
memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash));
memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp));
- memzero_explicit(sess_key, sizeof(sess_key));
+ memzero_explicit(base_key, sizeof(base_key));
return rc;
}
@@ -189,12 +190,13 @@ out:
* @blob_len: length of the @authblob message
* @conn: connection
* @sess: session of connection
+ * @sess_key: derived session key output buffer
*
* Return: 0 on success, error number on error
*/
int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
int blob_len, struct ksmbd_conn *conn,
- struct ksmbd_session *sess)
+ struct ksmbd_session *sess, char *sess_key)
{
char *domain_name;
unsigned int nt_off, dn_off;
@@ -234,7 +236,7 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
ret = ksmbd_auth_ntlmv2(conn, sess,
(struct ntlmv2_resp *)((char *)authblob + nt_off),
nt_len - CIFS_ENCPWD_SIZE,
- domain_name, conn->ntlmssp.cryptkey);
+ domain_name, conn->ntlmssp.cryptkey, sess_key);
kfree(domain_name);
if (ret)
return ret;
@@ -257,8 +259,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
if (!ctx_arc4)
return -ENOMEM;
- arc4_setkey(ctx_arc4, sess->sess_key, SMB2_NTLMV2_SESSKEY_SIZE);
- arc4_crypt(ctx_arc4, sess->sess_key,
+ arc4_setkey(ctx_arc4, sess_key, SMB2_NTLMV2_SESSKEY_SIZE);
+ arc4_crypt(ctx_arc4, sess_key,
(char *)authblob + sess_key_off, sess_key_len);
kfree_sensitive(ctx_arc4);
}
@@ -400,7 +402,8 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
#ifdef CONFIG_SMB_SERVER_KERBEROS5
int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
- int in_len, char *out_blob, int *out_len)
+ int in_len, char *out_blob, int *out_len,
+ char *sess_key)
{
struct ksmbd_spnego_authen_response *resp;
struct ksmbd_login_response_ext *resp_ext = NULL;
@@ -448,14 +451,14 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
} else {
if (!ksmbd_compare_user(sess->user, user)) {
ksmbd_debug(AUTH, "different user tried to reuse session\n");
- retval = -EPERM;
+ retval = -EKEYREJECTED;
ksmbd_free_user(user);
goto out;
}
ksmbd_free_user(user);
}
- memcpy(sess->sess_key, resp->payload, resp->session_key_len);
+ memcpy(sess_key, resp->payload, resp->session_key_len);
memcpy(out_blob, resp->payload + resp->session_key_len,
resp->spnego_blob_len);
*out_len = resp->spnego_blob_len;
@@ -466,7 +469,8 @@ out:
}
#else
int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
- int in_len, char *out_blob, int *out_len)
+ int in_len, char *out_blob, int *out_len,
+ char *sess_key)
{
return -EOPNOTSUPP;
}
@@ -525,7 +529,7 @@ struct derivation {
bool binding;
};
-static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+static void generate_key(struct ksmbd_conn *conn, const char *sess_key,
struct kvec label, struct kvec context, __u8 *key,
unsigned int key_size)
{
@@ -536,7 +540,7 @@ static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
unsigned char prfhash[SMB2_HMACSHA256_SIZE];
struct hmac_sha256_ctx ctx;
- hmac_sha256_init_usingrawkey(&ctx, sess->sess_key,
+ hmac_sha256_init_usingrawkey(&ctx, sess_key,
SMB2_NTLMV2_SESSKEY_SIZE);
hmac_sha256_update(&ctx, i, 4);
hmac_sha256_update(&ctx, label.iov_base, label.iov_len);
@@ -559,18 +563,21 @@ static int generate_smb3signingkey(struct ksmbd_session *sess,
const struct derivation *signing)
{
struct channel *chann;
- char *key;
+ char *key, *sess_key;
chann = lookup_chann_list(sess, conn);
if (!chann)
return 0;
- if (conn->dialect >= SMB30_PROT_ID && signing->binding)
+ if (conn->dialect >= SMB30_PROT_ID && signing->binding) {
key = chann->smb3signingkey;
- else
+ sess_key = chann->sess_key;
+ } else {
key = sess->smb3signingkey;
+ sess_key = sess->sess_key;
+ }
- generate_key(conn, sess, signing->label, signing->context, key,
+ generate_key(conn, sess_key, signing->label, signing->context, key,
SMB3_SIGN_KEY_SIZE);
if (!(conn->dialect >= SMB30_PROT_ID && signing->binding))
@@ -627,11 +634,11 @@ static void generate_smb3encryptionkey(struct ksmbd_conn *conn,
struct ksmbd_session *sess,
const struct derivation_twin *ptwin)
{
- generate_key(conn, sess, ptwin->encryption.label,
+ generate_key(conn, sess->sess_key, ptwin->encryption.label,
ptwin->encryption.context, sess->smb3encryptionkey,
SMB3_ENC_DEC_KEY_SIZE);
- generate_key(conn, sess, ptwin->decryption.label,
+ generate_key(conn, sess->sess_key, ptwin->decryption.label,
ptwin->decryption.context,
sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h
index 5767aabc63c9..f14b7c033264 100644
--- a/fs/smb/server/auth.h
+++ b/fs/smb/server/auth.h
@@ -41,17 +41,18 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov,
void ksmbd_copy_gss_neg_header(void *buf);
int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
struct ntlmv2_resp *ntlmv2, int blen, char *domain_name,
- char *cryptkey);
+ char *cryptkey, char *sess_key);
int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
int blob_len, struct ksmbd_conn *conn,
- struct ksmbd_session *sess);
+ struct ksmbd_session *sess, char *sess_key);
int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
int blob_len, struct ksmbd_conn *conn);
unsigned int
ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
struct ksmbd_conn *conn);
int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
- int in_len, char *out_blob, int *out_len);
+ int in_len, char *out_blob, int *out_len,
+ char *sess_key);
void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig);
void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
index de58aed76cb4..d6331184ebfc 100644
--- a/fs/smb/server/mgmt/user_session.c
+++ b/fs/smb/server/mgmt/user_session.c
@@ -255,7 +255,7 @@ static void free_channel_list(struct ksmbd_session *sess)
down_write(&sess->chann_lock);
xa_for_each(&sess->ksmbd_chann_list, index, chann) {
xa_erase(&sess->ksmbd_chann_list, index);
- kfree(chann);
+ kfree_sensitive(chann);
}
xa_destroy(&sess->ksmbd_chann_list);
@@ -449,7 +449,7 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
if (!chann)
return -ENOENT;
- kfree(chann);
+ kfree_sensitive(chann);
return 0;
}
diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
index 6aebd385be84..8893a9aaede7 100644
--- a/fs/smb/server/mgmt/user_session.h
+++ b/fs/smb/server/mgmt/user_session.h
@@ -19,6 +19,7 @@
struct ksmbd_file_table;
struct channel {
+ char sess_key[SMB2_NTLMV2_SESSKEY_SIZE];
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
struct ksmbd_conn *conn;
};
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 36a5ea4828ad..f5baba934840 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -199,6 +199,12 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
else
conn->ops->set_rsp_status(work,
STATUS_USER_SESSION_DELETED);
+ if (conn->ops->is_sign_req(work, conn->ops->get_cmd_val(work))) {
+ struct smb2_hdr *rsp_hdr;
+
+ rsp_hdr = ksmbd_resp_buf_curr(work);
+ rsp_hdr->Flags |= SMB2_FLAGS_SIGNED;
+ }
goto send;
} else if (rc > 0) {
rc = conn->ops->get_ksmbd_tcon(work);
@@ -237,8 +243,14 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
if (work->sess &&
(work->sess->sign || smb3_11_final_sess_setup_resp(work) ||
- conn->ops->is_sign_req(work, command)))
- conn->ops->set_sign_rsp(work);
+ conn->ops->is_sign_req(work, command))) {
+ if (command == SMB2_SESSION_SETUP_HE &&
+ work->sess->dialect >= SMB30_PROT_ID &&
+ conn->dialect < SMB30_PROT_ID)
+ smb3_set_sign_rsp(work);
+ else
+ conn->ops->set_sign_rsp(work);
+ }
} while (is_chained == true);
send:
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 097f51fc7ed6..b73167785e87 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -61,6 +61,9 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp)
(FILE_ATTRIBUTE_MASK & ~(FILE_ATTRIBUTE_INTEGRITY_STREAM | \
FILE_ATTRIBUTE_NO_SCRUB_DATA))
+/* Windows reports automatic write-time updates at roughly 15 ms resolution. */
+#define KSMBD_WRITE_TIME_RESOLUTION (15ULL * 10000)
+
/**
* check_session_id() - check for valid session id in smb header
* @conn: connection instance
@@ -95,6 +98,47 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn
return chann;
}
+#define KSMBD_MAX_CHANNELS 32
+
+static int register_session_channel(struct ksmbd_session *sess,
+ struct ksmbd_conn *conn,
+ const char *sess_key)
+{
+ struct channel *chann, *old;
+ unsigned long index;
+ unsigned int count = 0;
+ int rc = 0;
+
+ down_write(&sess->chann_lock);
+ if (xa_load(&sess->ksmbd_chann_list, (long)conn))
+ goto out;
+
+ xa_for_each(&sess->ksmbd_chann_list, index, chann)
+ count++;
+ if (count >= KSMBD_MAX_CHANNELS) {
+ rc = -ENOSPC;
+ goto out;
+ }
+
+ chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP);
+ if (!chann) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ chann->conn = conn;
+ memcpy(chann->sess_key, sess_key, sizeof(chann->sess_key));
+ old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
+ KSMBD_DEFAULT_GFP);
+ if (xa_is_err(old)) {
+ kfree_sensitive(chann);
+ rc = xa_err(old);
+ }
+out:
+ up_write(&sess->chann_lock);
+ return rc;
+}
+
/**
* smb2_get_ksmbd_tcon() - get tree connection information using a tree id.
* @work: smb work
@@ -1644,9 +1688,11 @@ static int ntlm_authenticate(struct ksmbd_work *work,
{
struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
- struct channel *chann = NULL, *old;
struct ksmbd_user *user;
+ char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {};
+ char *auth_key = conn->binding ? channel_key : sess->sess_key;
u64 prev_id;
+ bool binding = conn->binding;
int sz, rc;
ksmbd_debug(SMB, "authenticate phase\n");
@@ -1688,7 +1734,7 @@ static int ntlm_authenticate(struct ksmbd_work *work,
if (!ksmbd_compare_user(sess->user, user)) {
ksmbd_free_user(user);
- return -EPERM;
+ return -EKEYREJECTED;
}
ksmbd_free_user(user);
} else {
@@ -1705,11 +1751,13 @@ static int ntlm_authenticate(struct ksmbd_work *work,
sz = conn->mechTokenLen;
else
sz = le16_to_cpu(req->SecurityBufferLength);
- rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess);
+ rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess,
+ auth_key);
if (rc) {
set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD);
ksmbd_debug(SMB, "authentication failed\n");
- return -EPERM;
+ rc = -EPERM;
+ goto out;
}
}
@@ -1744,37 +1792,30 @@ static int ntlm_authenticate(struct ksmbd_work *work,
binding_session:
if (conn->dialect >= SMB30_PROT_ID) {
- chann = lookup_chann_list(sess, conn);
- if (!chann) {
- chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP);
- if (!chann)
- return -ENOMEM;
-
- chann->conn = conn;
- down_write(&sess->chann_lock);
- old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
- KSMBD_DEFAULT_GFP);
- up_write(&sess->chann_lock);
- if (xa_is_err(old)) {
- kfree(chann);
- return xa_err(old);
- }
- }
+ rc = register_session_channel(sess, conn, auth_key);
+ if (rc)
+ goto out;
}
if (conn->ops->generate_signingkey) {
rc = conn->ops->generate_signingkey(sess, conn);
if (rc) {
ksmbd_debug(SMB, "SMB3 signing key generation failed\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
}
if (!ksmbd_conn_lookup_dialect(conn)) {
pr_err("fail to verify the dialect\n");
- return -ENOENT;
+ rc = -ENOENT;
+ goto out;
}
- return 0;
+ rc = 0;
+out:
+ if (binding)
+ memzero_explicit(channel_key, sizeof(channel_key));
+ return rc;
}
#ifdef CONFIG_SMB_SERVER_KERBEROS5
@@ -1785,8 +1826,10 @@ static int krb5_authenticate(struct ksmbd_work *work,
struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
char *in_blob, *out_blob;
- struct channel *chann = NULL, *old;
+ char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {};
+ char *auth_key = conn->binding ? channel_key : sess->sess_key;
u64 prev_sess_id;
+ bool binding = conn->binding;
int in_len, out_len;
int retval;
@@ -1799,10 +1842,12 @@ static int krb5_authenticate(struct ksmbd_work *work,
(le16_to_cpu(rsp->SecurityBufferOffset) + 4);
retval = ksmbd_krb5_authenticate(sess, in_blob, in_len,
- out_blob, &out_len);
+ out_blob, &out_len, auth_key);
if (retval) {
ksmbd_debug(SMB, "krb5 authentication failed\n");
- return -EINVAL;
+ if (retval != -EKEYREJECTED)
+ retval = -EINVAL;
+ goto out;
}
/* Check previous session */
@@ -1839,37 +1884,30 @@ static int krb5_authenticate(struct ksmbd_work *work,
binding_session:
if (conn->dialect >= SMB30_PROT_ID) {
- chann = lookup_chann_list(sess, conn);
- if (!chann) {
- chann = kmalloc_obj(struct channel, KSMBD_DEFAULT_GFP);
- if (!chann)
- return -ENOMEM;
-
- chann->conn = conn;
- down_write(&sess->chann_lock);
- old = xa_store(&sess->ksmbd_chann_list, (long)conn,
- chann, KSMBD_DEFAULT_GFP);
- up_write(&sess->chann_lock);
- if (xa_is_err(old)) {
- kfree(chann);
- return xa_err(old);
- }
- }
+ retval = register_session_channel(sess, conn, auth_key);
+ if (retval)
+ goto out;
}
if (conn->ops->generate_signingkey) {
retval = conn->ops->generate_signingkey(sess, conn);
if (retval) {
ksmbd_debug(SMB, "SMB3 signing key generation failed\n");
- return -EINVAL;
+ retval = -EINVAL;
+ goto out;
}
}
if (!ksmbd_conn_lookup_dialect(conn)) {
pr_err("fail to verify the dialect\n");
- return -ENOENT;
+ retval = -ENOENT;
+ goto out;
}
- return 0;
+ retval = 0;
+out:
+ if (binding)
+ memzero_explicit(channel_key, sizeof(channel_key));
+ return retval;
}
#else
static int krb5_authenticate(struct ksmbd_work *work,
@@ -1975,13 +2013,36 @@ int smb2_sess_setup(struct ksmbd_work *work)
} else if ((conn->dialect < SMB30_PROT_ID ||
server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
- sess = NULL;
+ sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId));
+ if (sess) {
+ int sign_ret;
+
+ work->sess = sess;
+ if (sess->dialect >= SMB30_PROT_ID)
+ sign_ret = smb3_check_sign_req(work);
+ else
+ sign_ret = smb2_check_sign_req(work);
+ if (sess->state != SMB2_SESSION_VALID ||
+ !(req->hdr.Flags & SMB2_FLAGS_SIGNED) ||
+ !sign_ret) {
+ ksmbd_user_session_put(sess);
+ work->sess = NULL;
+ sess = NULL;
+ }
+ }
rc = -EACCES;
goto out_err;
} else {
sess = ksmbd_session_lookup(conn,
le64_to_cpu(req->hdr.SessionId));
if (!sess) {
+ sess = ksmbd_session_lookup_slowpath(le64_to_cpu(req->hdr.SessionId));
+ if (sess && !lookup_chann_list(sess, conn)) {
+ ksmbd_user_session_put(sess);
+ sess = NULL;
+ }
+ }
+ if (!sess) {
rc = -ENOENT;
goto out_err;
}
@@ -2091,12 +2152,19 @@ out_err:
rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED;
else if (rc == -EFAULT)
rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED;
- else if (rc == -ENOMEM)
+ else if (rc == -ENOMEM || rc == -ENOSPC)
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
else if (rc == -EOPNOTSUPP)
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
+ else if (rc == -EKEYREJECTED)
+ rsp->hdr.Status = STATUS_ACCESS_DENIED;
else if (rc)
rsp->hdr.Status = STATUS_LOGON_FAILURE;
+ if ((rsp->hdr.Status == STATUS_USER_SESSION_DELETED ||
+ (rsp->hdr.Status == STATUS_INVALID_PARAMETER &&
+ (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING))) &&
+ (req->hdr.Flags & SMB2_FLAGS_SIGNED))
+ rsp->hdr.Flags |= SMB2_FLAGS_SIGNED;
if (conn->mechToken) {
kfree(conn->mechToken);
@@ -2104,6 +2172,17 @@ out_err:
}
if (rc < 0) {
+ if (sess && conn->dialect == SMB311_PROT_ID &&
+ (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
+ struct preauth_session *preauth_sess;
+
+ preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id);
+ if (preauth_sess) {
+ list_del(&preauth_sess->preauth_entry);
+ kfree(preauth_sess);
+ }
+ }
+
/*
* SecurityBufferOffset should be set to zero
* in session setup error response.
@@ -2130,8 +2209,16 @@ out_err:
sess->last_active = jiffies;
sess->state = SMB2_SESSION_EXPIRED;
}
- ksmbd_user_session_put(sess);
- work->sess = NULL;
+ /*
+ * Keep the binding session reference until the response is
+ * signed and sent. Error responses for a signed binding
+ * request are signed with the existing session signing key.
+ */
+ if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) ||
+ work->sess != sess) {
+ ksmbd_user_session_put(sess);
+ work->sess = NULL;
+ }
if (try_delay) {
ksmbd_conn_set_need_reconnect(conn);
ssleep(5);
@@ -3943,6 +4030,7 @@ reconnected_fp:
time = ksmbd_UnixTimeToNT(stat.atime);
rsp->LastAccessTime = cpu_to_le64(time);
time = ksmbd_UnixTimeToNT(stat.mtime);
+ fp->open_mtime = time;
rsp->LastWriteTime = cpu_to_le64(time);
rsp->ChangeTime = cpu_to_le64(fp->change_time);
/*
@@ -6409,6 +6497,9 @@ int smb2_close(struct ksmbd_work *work)
time = ksmbd_UnixTimeToNT(stat.atime);
rsp->LastAccessTime = cpu_to_le64(time);
time = ksmbd_UnixTimeToNT(stat.mtime);
+ if (time > fp->open_mtime &&
+ time - fp->open_mtime < KSMBD_WRITE_TIME_RESOLUTION)
+ time = fp->open_mtime;
rsp->LastWriteTime = cpu_to_le64(time);
rsp->ChangeTime = cpu_to_le64(fp->change_time);
ksmbd_fd_put(work, fp);
@@ -7080,6 +7171,8 @@ err_out:
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
else if (rc == -EMSGSIZE)
rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
+ else if (rc == -ENOSPC || rc == -EFBIG)
+ rsp->hdr.Status = STATUS_DISK_FULL;
else if (rc == -ESHARE)
rsp->hdr.Status = STATUS_SHARING_VIOLATION;
else if (rc == -ENOENT)
@@ -9483,7 +9576,6 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command)
if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) &&
command != SMB2_NEGOTIATE_HE &&
- command != SMB2_SESSION_SETUP_HE &&
command != SMB2_OPLOCK_BREAK_HE)
return true;
@@ -9595,9 +9687,13 @@ int smb3_check_sign_req(struct ksmbd_work *work)
} else {
chann = lookup_chann_list(work->sess, conn);
if (!chann) {
- return 0;
+ if (le16_to_cpu(hdr->Command) != SMB2_SESSION_SETUP_HE ||
+ !(hdr->Flags & SMB2_FLAGS_SIGNED))
+ return 0;
+ signing_key = work->sess->smb3signingkey;
+ } else {
+ signing_key = chann->smb3signingkey;
}
- signing_key = chann->smb3signingkey;
}
if (!signing_key) {
@@ -9632,13 +9728,14 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
struct channel *chann;
char signature[SMB2_CMACAES_SIZE];
struct kvec *iov;
+ u16 command = conn->ops->get_cmd_val(work);
int n_vec = 1;
char *signing_key;
hdr = ksmbd_resp_buf_curr(work);
- if (conn->binding == false &&
- le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) {
+ if (command == SMB2_SESSION_SETUP_HE &&
+ (!conn->binding || hdr->Status != STATUS_SUCCESS)) {
signing_key = work->sess->smb3signingkey;
} else {
chann = lookup_chann_list(work->sess, work->conn);
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 9c59c8f73b66..67b39b4d218c 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -258,6 +258,7 @@ static int sid_to_id(struct mnt_idmap *idmap,
struct smb_sid *psid, uint sidtype,
struct smb_fattr *fattr)
{
+ const struct smb_sid *sid_prefix;
int rc = -EINVAL;
/*
@@ -279,6 +280,12 @@ static int sid_to_id(struct mnt_idmap *idmap,
kuid_t uid;
uid_t id;
+ /* Only the server domain RID has a local uid representation. */
+ sid_prefix = &server_conf.domain_sid;
+ if (psid->num_subauth != sid_prefix->num_subauth + 1 ||
+ compare_sids(psid, sid_prefix))
+ return -EINVAL;
+
id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]);
uid = KUIDT_INIT(id);
uid = from_vfsuid(idmap, &init_user_ns, VFSUIDT_INIT(uid));
@@ -290,6 +297,12 @@ static int sid_to_id(struct mnt_idmap *idmap,
kgid_t gid;
gid_t id;
+ /* Local gids are represented by S-1-22-2-<gid>. */
+ sid_prefix = &sid_unix_groups;
+ if (psid->num_subauth != sid_prefix->num_subauth + 1 ||
+ compare_sids(psid, sid_prefix))
+ return -EINVAL;
+
id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]);
gid = KGIDT_INIT(id);
gid = from_vfsgid(idmap, &init_user_ns, VFSGIDT_INIT(gid));
@@ -900,9 +913,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
rc = sid_to_id(idmap, owner_sid_ptr, SIDOWNER, fattr);
if (rc) {
- pr_err("%s: Error %d mapping Owner SID to uid\n",
- __func__, rc);
+ ksmbd_debug(SMB, "Owner SID has no Unix uid mapping\n");
owner_sid_ptr = NULL;
+ rc = 0;
}
}
@@ -918,9 +931,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
}
rc = sid_to_id(idmap, group_sid_ptr, SIDUNIX_GROUP, fattr);
if (rc) {
- pr_err("%s: Error %d mapping Group SID to gid\n",
- __func__, rc);
+ ksmbd_debug(SMB, "Group SID has no Unix gid mapping\n");
group_sid_ptr = NULL;
+ rc = 0;
}
}
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index 287f3e675cd3..b9e27307a26c 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -105,6 +105,7 @@ struct ksmbd_file {
__u64 change_time;
__u64 allocation_size;
__u64 itime;
+ __u64 open_mtime;
bool is_nt_open;
bool attrib_only;