two ksmbd server fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmcPUXEACgkQiiy9cAdy
 T1Gm7QwAlPW5//Cb4B0gpjzRcUws51IZ4yFhp4IQWmsd0RqdjZ4TxSCOPF3u3HR3
 0OPxyLdbUn6h5g0S2ayzqomHx2VBOQTjgyuMtaTWzokToMNu8kqvxK1MTslkBior
 9YEHUz9+5f0OJ+JBGNUzjfy4Plygr5y09udaLfqIknuY8+SeuooxNNUNfkIvrP7C
 JsSAWJznN9VMpKJmszYc4ntyTiz1XVXyyjJmjhRQ27ah8LUghqZ0mamgigTS5UFa
 U7eYBDfs6+9i5Lvkd4bJPdGyov9g/EPViLURZMfNaz3+p0TfosN8s2UZuhHC+zuv
 BDQ+wHGRqzmteZspLanrGBt9y9svHXp1CD7MwqWeGR3GhKsfsxCMJpE931fBhsxM
 vlJdd/xCs128fv48AvNyHA9abN0U1FpskOJhOzjDgvhKqDoIQ4TCC7QFDEttsPRv
 ZiQmyOCPyZZY28EmfoltU4CFcMIwKQ81nPUSOJFgKmHBbSpc+Qtnv5QgRHZCzj7n
 StJfaIMv
 =WhJj
 -----END PGP SIGNATURE-----

Merge tag 'v6.12-rc3-ksmbd-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:

 - fix race between session setup and session logoff

 - add supplementary group support

* tag 'v6.12-rc3-ksmbd-fixes' of git://git.samba.org/ksmbd:
  ksmbd: add support for supplementary groups
  ksmbd: fix user-after-free from session log off
This commit is contained in:
Linus Torvalds 2024-10-16 09:15:43 -07:00
commit 9f635d44d7
11 changed files with 171 additions and 23 deletions

View File

@ -512,6 +512,7 @@ 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)
{ {
struct ksmbd_spnego_authen_response *resp; struct ksmbd_spnego_authen_response *resp;
struct ksmbd_login_response_ext *resp_ext = NULL;
struct ksmbd_user *user = NULL; struct ksmbd_user *user = NULL;
int retval; int retval;
@ -540,7 +541,10 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
goto out; goto out;
} }
user = ksmbd_alloc_user(&resp->login_response); if (resp->login_response.status & KSMBD_USER_FLAG_EXTENSION)
resp_ext = ksmbd_ipc_login_request_ext(resp->login_response.account);
user = ksmbd_alloc_user(&resp->login_response, resp_ext);
if (!user) { if (!user) {
ksmbd_debug(AUTH, "login failure\n"); ksmbd_debug(AUTH, "login failure\n");
retval = -ENOMEM; retval = -ENOMEM;

View File

@ -51,6 +51,9 @@
* - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response)
* This event is to make kerberos authentication to be processed in * This event is to make kerberos authentication to be processed in
* userspace. * userspace.
*
* - KSMBD_EVENT_LOGIN_REQUEST_EXT/RESPONSE_EXT(ksmbd_login_request_ext/response_ext)
* This event is to get user account extension info to user IPC daemon.
*/ */
#define KSMBD_GENL_NAME "SMBD_GENL" #define KSMBD_GENL_NAME "SMBD_GENL"
@ -145,6 +148,16 @@ struct ksmbd_login_response {
__u32 reserved[16]; /* Reserved room */ __u32 reserved[16]; /* Reserved room */
}; };
/*
* IPC user login response extension.
*/
struct ksmbd_login_response_ext {
__u32 handle;
__s32 ngroups; /* supplementary group count */
__s8 reserved[128]; /* Reserved room */
__s8 ____payload[];
};
/* /*
* IPC request to fetch net share config. * IPC request to fetch net share config.
*/ */
@ -306,6 +319,9 @@ enum ksmbd_event {
KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15,
KSMBD_EVENT_LOGIN_REQUEST_EXT,
KSMBD_EVENT_LOGIN_RESPONSE_EXT,
__KSMBD_EVENT_MAX, __KSMBD_EVENT_MAX,
KSMBD_EVENT_MAX = __KSMBD_EVENT_MAX - 1 KSMBD_EVENT_MAX = __KSMBD_EVENT_MAX - 1
}; };
@ -336,6 +352,7 @@ enum KSMBD_TREE_CONN_STATUS {
#define KSMBD_USER_FLAG_BAD_USER BIT(3) #define KSMBD_USER_FLAG_BAD_USER BIT(3)
#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) #define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4)
#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) #define KSMBD_USER_FLAG_DELAY_SESSION BIT(5)
#define KSMBD_USER_FLAG_EXTENSION BIT(6)
/* /*
* Share config flags. * Share config flags.

View File

@ -12,6 +12,7 @@
struct ksmbd_user *ksmbd_login_user(const char *account) struct ksmbd_user *ksmbd_login_user(const char *account)
{ {
struct ksmbd_login_response *resp; struct ksmbd_login_response *resp;
struct ksmbd_login_response_ext *resp_ext = NULL;
struct ksmbd_user *user = NULL; struct ksmbd_user *user = NULL;
resp = ksmbd_ipc_login_request(account); resp = ksmbd_ipc_login_request(account);
@ -21,15 +22,19 @@ struct ksmbd_user *ksmbd_login_user(const char *account)
if (!(resp->status & KSMBD_USER_FLAG_OK)) if (!(resp->status & KSMBD_USER_FLAG_OK))
goto out; goto out;
user = ksmbd_alloc_user(resp); if (resp->status & KSMBD_USER_FLAG_EXTENSION)
resp_ext = ksmbd_ipc_login_request_ext(account);
user = ksmbd_alloc_user(resp, resp_ext);
out: out:
kvfree(resp); kvfree(resp);
return user; return user;
} }
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
struct ksmbd_login_response_ext *resp_ext)
{ {
struct ksmbd_user *user = NULL; struct ksmbd_user *user;
user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL);
if (!user) if (!user)
@ -44,18 +49,42 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
if (user->passkey) if (user->passkey)
memcpy(user->passkey, resp->hash, resp->hash_sz); memcpy(user->passkey, resp->hash, resp->hash_sz);
if (!user->name || !user->passkey) { user->ngroups = 0;
user->sgid = NULL;
if (!user->name || !user->passkey)
goto err_free;
if (resp_ext) {
if (resp_ext->ngroups > NGROUPS_MAX) {
pr_err("ngroups(%u) from login response exceeds max groups(%d)\n",
resp_ext->ngroups, NGROUPS_MAX);
goto err_free;
}
user->sgid = kmemdup(resp_ext->____payload,
resp_ext->ngroups * sizeof(gid_t),
GFP_KERNEL);
if (!user->sgid)
goto err_free;
user->ngroups = resp_ext->ngroups;
ksmbd_debug(SMB, "supplementary groups : %d\n", user->ngroups);
}
return user;
err_free:
kfree(user->name); kfree(user->name);
kfree(user->passkey); kfree(user->passkey);
kfree(user); kfree(user);
user = NULL; return NULL;
}
return user;
} }
void ksmbd_free_user(struct ksmbd_user *user) void ksmbd_free_user(struct ksmbd_user *user)
{ {
ksmbd_ipc_logout_request(user->name, user->flags); ksmbd_ipc_logout_request(user->name, user->flags);
kfree(user->sgid);
kfree(user->name); kfree(user->name);
kfree(user->passkey); kfree(user->passkey);
kfree(user); kfree(user);

View File

@ -18,6 +18,8 @@ struct ksmbd_user {
size_t passkey_sz; size_t passkey_sz;
char *passkey; char *passkey;
int ngroups;
gid_t *sgid;
}; };
static inline bool user_guest(struct ksmbd_user *user) static inline bool user_guest(struct ksmbd_user *user)
@ -60,7 +62,8 @@ static inline unsigned int user_gid(struct ksmbd_user *user)
} }
struct ksmbd_user *ksmbd_login_user(const char *account); struct ksmbd_user *ksmbd_login_user(const char *account);
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
struct ksmbd_login_response_ext *resp_ext);
void ksmbd_free_user(struct ksmbd_user *user); void ksmbd_free_user(struct ksmbd_user *user);
int ksmbd_anonymous_user(struct ksmbd_user *user); int ksmbd_anonymous_user(struct ksmbd_user *user);
bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2);

View File

@ -177,9 +177,10 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
down_write(&conn->session_lock); down_write(&conn->session_lock);
xa_for_each(&conn->sessions, id, sess) { xa_for_each(&conn->sessions, id, sess) {
if (sess->state != SMB2_SESSION_VALID || if (atomic_read(&sess->refcnt) == 0 &&
(sess->state != SMB2_SESSION_VALID ||
time_after(jiffies, time_after(jiffies,
sess->last_active + SMB2_SESSION_TIMEOUT)) { sess->last_active + SMB2_SESSION_TIMEOUT))) {
xa_erase(&conn->sessions, sess->id); xa_erase(&conn->sessions, sess->id);
hash_del(&sess->hlist); hash_del(&sess->hlist);
ksmbd_session_destroy(sess); ksmbd_session_destroy(sess);
@ -269,8 +270,6 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
down_read(&sessions_table_lock); down_read(&sessions_table_lock);
sess = __session_lookup(id); sess = __session_lookup(id);
if (sess)
sess->last_active = jiffies;
up_read(&sessions_table_lock); up_read(&sessions_table_lock);
return sess; return sess;
@ -289,6 +288,22 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
return sess; return sess;
} }
void ksmbd_user_session_get(struct ksmbd_session *sess)
{
atomic_inc(&sess->refcnt);
}
void ksmbd_user_session_put(struct ksmbd_session *sess)
{
if (!sess)
return;
if (atomic_read(&sess->refcnt) <= 0)
WARN_ON(1);
else
atomic_dec(&sess->refcnt);
}
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
u64 sess_id) u64 sess_id)
{ {
@ -393,6 +408,7 @@ static struct ksmbd_session *__session_create(int protocol)
xa_init(&sess->rpc_handle_list); xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1; sess->sequence_number = 1;
rwlock_init(&sess->tree_conns_lock); rwlock_init(&sess->tree_conns_lock);
atomic_set(&sess->refcnt, 1);
ret = __init_smb2_session(sess); ret = __init_smb2_session(sess);
if (ret) if (ret)

View File

@ -61,6 +61,8 @@ struct ksmbd_session {
struct ksmbd_file_table file_table; struct ksmbd_file_table file_table;
unsigned long last_active; unsigned long last_active;
rwlock_t tree_conns_lock; rwlock_t tree_conns_lock;
atomic_t refcnt;
}; };
static inline int test_session_flag(struct ksmbd_session *sess, int bit) static inline int test_session_flag(struct ksmbd_session *sess, int bit)
@ -104,4 +106,6 @@ void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id);
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name);
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
void ksmbd_user_session_get(struct ksmbd_session *sess);
void ksmbd_user_session_put(struct ksmbd_session *sess);
#endif /* __USER_SESSION_MANAGEMENT_H__ */ #endif /* __USER_SESSION_MANAGEMENT_H__ */

View File

@ -238,6 +238,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
} while (is_chained == true); } while (is_chained == true);
send: send:
if (work->sess)
ksmbd_user_session_put(work->sess);
if (work->tcon) if (work->tcon)
ksmbd_tree_connect_put(work->tcon); ksmbd_tree_connect_put(work->tcon);
smb3_preauth_hash_rsp(work); smb3_preauth_hash_rsp(work);

View File

@ -605,8 +605,10 @@ int smb2_check_user_session(struct ksmbd_work *work)
/* Check for validity of user session */ /* Check for validity of user session */
work->sess = ksmbd_session_lookup_all(conn, sess_id); work->sess = ksmbd_session_lookup_all(conn, sess_id);
if (work->sess) if (work->sess) {
ksmbd_user_session_get(work->sess);
return 1; return 1;
}
ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
return -ENOENT; return -ENOENT;
} }
@ -1740,6 +1742,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
} }
conn->binding = true; conn->binding = true;
ksmbd_user_session_get(sess);
} else if ((conn->dialect < SMB30_PROT_ID || } else if ((conn->dialect < SMB30_PROT_ID ||
server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
@ -1766,6 +1769,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
} }
conn->binding = false; conn->binding = false;
ksmbd_user_session_get(sess);
} }
work->sess = sess; work->sess = sess;
@ -2228,7 +2232,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
} }
ksmbd_destroy_file_table(&sess->file_table); ksmbd_destroy_file_table(&sess->file_table);
down_write(&conn->session_lock);
sess->state = SMB2_SESSION_EXPIRED; sess->state = SMB2_SESSION_EXPIRED;
up_write(&conn->session_lock);
ksmbd_free_user(sess->user); ksmbd_free_user(sess->user);
sess->user = NULL; sess->user = NULL;

View File

@ -736,13 +736,15 @@ int __ksmbd_override_fsids(struct ksmbd_work *work,
struct ksmbd_share_config *share) struct ksmbd_share_config *share)
{ {
struct ksmbd_session *sess = work->sess; struct ksmbd_session *sess = work->sess;
struct ksmbd_user *user = sess->user;
struct cred *cred; struct cred *cred;
struct group_info *gi; struct group_info *gi;
unsigned int uid; unsigned int uid;
unsigned int gid; unsigned int gid;
int i;
uid = user_uid(sess->user); uid = user_uid(user);
gid = user_gid(sess->user); gid = user_gid(user);
if (share->force_uid != KSMBD_SHARE_INVALID_UID) if (share->force_uid != KSMBD_SHARE_INVALID_UID)
uid = share->force_uid; uid = share->force_uid;
if (share->force_gid != KSMBD_SHARE_INVALID_GID) if (share->force_gid != KSMBD_SHARE_INVALID_GID)
@ -755,11 +757,18 @@ int __ksmbd_override_fsids(struct ksmbd_work *work,
cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsuid = make_kuid(&init_user_ns, uid);
cred->fsgid = make_kgid(&init_user_ns, gid); cred->fsgid = make_kgid(&init_user_ns, gid);
gi = groups_alloc(0); gi = groups_alloc(user->ngroups);
if (!gi) { if (!gi) {
abort_creds(cred); abort_creds(cred);
return -ENOMEM; return -ENOMEM;
} }
for (i = 0; i < user->ngroups; i++)
gi->gid[i] = make_kgid(&init_user_ns, user->sgid[i]);
if (user->ngroups)
groups_sort(gi);
set_groups(cred, gi); set_groups(cred, gi);
put_group_info(gi); put_group_info(gi);

View File

@ -120,6 +120,12 @@ static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX + 1] = {
}, },
[KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = {
}, },
[KSMBD_EVENT_LOGIN_REQUEST_EXT] = {
.len = sizeof(struct ksmbd_login_request),
},
[KSMBD_EVENT_LOGIN_RESPONSE_EXT] = {
.len = sizeof(struct ksmbd_login_response_ext),
},
}; };
static struct genl_ops ksmbd_genl_ops[] = { static struct genl_ops ksmbd_genl_ops[] = {
@ -187,6 +193,14 @@ static struct genl_ops ksmbd_genl_ops[] = {
.cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE,
.doit = handle_generic_event, .doit = handle_generic_event,
}, },
{
.cmd = KSMBD_EVENT_LOGIN_REQUEST_EXT,
.doit = handle_unsupported_event,
},
{
.cmd = KSMBD_EVENT_LOGIN_RESPONSE_EXT,
.doit = handle_generic_event,
},
}; };
static struct genl_family ksmbd_genl_family = { static struct genl_family ksmbd_genl_family = {
@ -198,7 +212,7 @@ static struct genl_family ksmbd_genl_family = {
.module = THIS_MODULE, .module = THIS_MODULE,
.ops = ksmbd_genl_ops, .ops = ksmbd_genl_ops,
.n_ops = ARRAY_SIZE(ksmbd_genl_ops), .n_ops = ARRAY_SIZE(ksmbd_genl_ops),
.resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, .resv_start_op = KSMBD_EVENT_LOGIN_RESPONSE_EXT + 1,
}; };
static void ksmbd_nl_init_fixup(void) static void ksmbd_nl_init_fixup(void)
@ -459,16 +473,24 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{ {
unsigned int msg_sz = entry->msg_sz; unsigned int msg_sz = entry->msg_sz;
if (entry->type == KSMBD_EVENT_RPC_REQUEST) { switch (entry->type) {
case KSMBD_EVENT_RPC_REQUEST:
{
struct ksmbd_rpc_command *resp = entry->response; struct ksmbd_rpc_command *resp = entry->response;
msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz; msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz;
} else if (entry->type == KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST) { break;
}
case KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST:
{
struct ksmbd_spnego_authen_response *resp = entry->response; struct ksmbd_spnego_authen_response *resp = entry->response;
msg_sz = sizeof(struct ksmbd_spnego_authen_response) + msg_sz = sizeof(struct ksmbd_spnego_authen_response) +
resp->session_key_len + resp->spnego_blob_len; resp->session_key_len + resp->spnego_blob_len;
} else if (entry->type == KSMBD_EVENT_SHARE_CONFIG_REQUEST) { break;
}
case KSMBD_EVENT_SHARE_CONFIG_REQUEST:
{
struct ksmbd_share_config_response *resp = entry->response; struct ksmbd_share_config_response *resp = entry->response;
if (resp->payload_sz) { if (resp->payload_sz) {
@ -478,6 +500,17 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
msg_sz = sizeof(struct ksmbd_share_config_response) + msg_sz = sizeof(struct ksmbd_share_config_response) +
resp->payload_sz; resp->payload_sz;
} }
break;
}
case KSMBD_EVENT_LOGIN_REQUEST_EXT:
{
struct ksmbd_login_response_ext *resp = entry->response;
if (resp->ngroups) {
msg_sz = sizeof(struct ksmbd_login_response_ext) +
resp->ngroups * sizeof(gid_t);
}
}
} }
return entry->msg_sz != msg_sz ? -EINVAL : 0; return entry->msg_sz != msg_sz ? -EINVAL : 0;
@ -560,6 +593,29 @@ struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
return resp; return resp;
} }
struct ksmbd_login_response_ext *ksmbd_ipc_login_request_ext(const char *account)
{
struct ksmbd_ipc_msg *msg;
struct ksmbd_login_request *req;
struct ksmbd_login_response_ext *resp;
if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
return NULL;
msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request));
if (!msg)
return NULL;
msg->type = KSMBD_EVENT_LOGIN_REQUEST_EXT;
req = (struct ksmbd_login_request *)msg->payload;
req->handle = ksmbd_acquire_id(&ipc_ida);
strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
resp = ipc_msg_send_request(msg, req->handle);
ipc_msg_handle_free(req->handle);
ipc_msg_free(msg);
return resp;
}
struct ksmbd_spnego_authen_response * struct ksmbd_spnego_authen_response *
ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len)
{ {

View File

@ -12,6 +12,8 @@
struct ksmbd_login_response * struct ksmbd_login_response *
ksmbd_ipc_login_request(const char *account); ksmbd_ipc_login_request(const char *account);
struct ksmbd_login_response_ext *
ksmbd_ipc_login_request_ext(const char *account);
struct ksmbd_session; struct ksmbd_session;
struct ksmbd_share_config; struct ksmbd_share_config;