Commit a77e0e02 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

ksmbd: add support for supplementary groups

Even though system user has a supplementary group, It gets
NT_STATUS_ACCESS_DENIED when attempting to create file or directory.
This patch add KSMBD_EVENT_LOGIN_REQUEST_EXT/RESPONSE_EXT netlink events
to get supplementary groups list. The new netlink event doesn't break
backward compatibility when using old ksmbd-tools.
Co-developed-by: default avatarAtte Heikkilä <atteh.mailbox@gmail.com>
Signed-off-by: default avatarAtte Heikkilä <atteh.mailbox@gmail.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 7aa8804c
...@@ -512,6 +512,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, ...@@ -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, ...@@ -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;
......
...@@ -51,6 +51,9 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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.
......
...@@ -12,6 +12,7 @@ ...@@ -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) ...@@ -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) ...@@ -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;
kfree(user->name); user->sgid = NULL;
kfree(user->passkey);
kfree(user); if (!user->name || !user->passkey)
user = NULL; 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; return user;
err_free:
kfree(user->name);
kfree(user->passkey);
kfree(user);
return NULL;
} }
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);
......
...@@ -18,6 +18,8 @@ struct ksmbd_user { ...@@ -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) ...@@ -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);
......
...@@ -736,13 +736,15 @@ int __ksmbd_override_fsids(struct ksmbd_work *work, ...@@ -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, ...@@ -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);
......
...@@ -120,6 +120,12 @@ static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX + 1] = { ...@@ -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[] = { ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
{ {
......
...@@ -12,6 +12,8 @@ ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment