mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-21 06:27:58 -04:00
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
This commit is contained in:
@@ -133,16 +133,17 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
|
||||
* @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 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
|
||||
* @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 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
|
||||
}
|
||||
#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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,12 +2013,35 @@ 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 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
||||
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 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
||||
}
|
||||
|
||||
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 @@ int smb2_sess_setup(struct ksmbd_work *work)
|
||||
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 @@ int smb2_open(struct ksmbd_work *work)
|
||||
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 @@ int smb2_set_info(struct ksmbd_work *work)
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user