Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Atte Heikkilä <atteh.mailbox@gmail.com>
Signed-off-by: Atte Heikkilä <atteh.mailbox@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Namjae Jeon and committed by
Steve French
a77e0e02 7aa8804c

+137 -17
+5 -1
fs/smb/server/auth.c
··· 512 512 int in_len, char *out_blob, int *out_len) 513 513 { 514 514 struct ksmbd_spnego_authen_response *resp; 515 + struct ksmbd_login_response_ext *resp_ext = NULL; 515 516 struct ksmbd_user *user = NULL; 516 517 int retval; 517 518 ··· 541 540 goto out; 542 541 } 543 542 544 - user = ksmbd_alloc_user(&resp->login_response); 543 + if (resp->login_response.status & KSMBD_USER_FLAG_EXTENSION) 544 + resp_ext = ksmbd_ipc_login_request_ext(resp->login_response.account); 545 + 546 + user = ksmbd_alloc_user(&resp->login_response, resp_ext); 545 547 if (!user) { 546 548 ksmbd_debug(AUTH, "login failure\n"); 547 549 retval = -ENOMEM;
+17
fs/smb/server/ksmbd_netlink.h
··· 51 51 * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) 52 52 * This event is to make kerberos authentication to be processed in 53 53 * userspace. 54 + * 55 + * - KSMBD_EVENT_LOGIN_REQUEST_EXT/RESPONSE_EXT(ksmbd_login_request_ext/response_ext) 56 + * This event is to get user account extension info to user IPC daemon. 54 57 */ 55 58 56 59 #define KSMBD_GENL_NAME "SMBD_GENL" ··· 146 143 __u16 hash_sz; /* hash size */ 147 144 __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ 148 145 __u32 reserved[16]; /* Reserved room */ 146 + }; 147 + 148 + /* 149 + * IPC user login response extension. 150 + */ 151 + struct ksmbd_login_response_ext { 152 + __u32 handle; 153 + __s32 ngroups; /* supplementary group count */ 154 + __s8 reserved[128]; /* Reserved room */ 155 + __s8 ____payload[]; 149 156 }; 150 157 151 158 /* ··· 319 306 KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, 320 307 KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, 321 308 309 + KSMBD_EVENT_LOGIN_REQUEST_EXT, 310 + KSMBD_EVENT_LOGIN_RESPONSE_EXT, 311 + 322 312 __KSMBD_EVENT_MAX, 323 313 KSMBD_EVENT_MAX = __KSMBD_EVENT_MAX - 1 324 314 }; ··· 352 336 #define KSMBD_USER_FLAG_BAD_USER BIT(3) 353 337 #define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) 354 338 #define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) 339 + #define KSMBD_USER_FLAG_EXTENSION BIT(6) 355 340 356 341 /* 357 342 * Share config flags.
+37 -8
fs/smb/server/mgmt/user_config.c
··· 12 12 struct ksmbd_user *ksmbd_login_user(const char *account) 13 13 { 14 14 struct ksmbd_login_response *resp; 15 + struct ksmbd_login_response_ext *resp_ext = NULL; 15 16 struct ksmbd_user *user = NULL; 16 17 17 18 resp = ksmbd_ipc_login_request(account); ··· 22 21 if (!(resp->status & KSMBD_USER_FLAG_OK)) 23 22 goto out; 24 23 25 - user = ksmbd_alloc_user(resp); 24 + if (resp->status & KSMBD_USER_FLAG_EXTENSION) 25 + resp_ext = ksmbd_ipc_login_request_ext(account); 26 + 27 + user = ksmbd_alloc_user(resp, resp_ext); 26 28 out: 27 29 kvfree(resp); 28 30 return user; 29 31 } 30 32 31 - struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) 33 + struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, 34 + struct ksmbd_login_response_ext *resp_ext) 32 35 { 33 - struct ksmbd_user *user = NULL; 36 + struct ksmbd_user *user; 34 37 35 38 user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); 36 39 if (!user) ··· 49 44 if (user->passkey) 50 45 memcpy(user->passkey, resp->hash, resp->hash_sz); 51 46 52 - if (!user->name || !user->passkey) { 53 - kfree(user->name); 54 - kfree(user->passkey); 55 - kfree(user); 56 - user = NULL; 47 + user->ngroups = 0; 48 + user->sgid = NULL; 49 + 50 + if (!user->name || !user->passkey) 51 + goto err_free; 52 + 53 + if (resp_ext) { 54 + if (resp_ext->ngroups > NGROUPS_MAX) { 55 + pr_err("ngroups(%u) from login response exceeds max groups(%d)\n", 56 + resp_ext->ngroups, NGROUPS_MAX); 57 + goto err_free; 58 + } 59 + 60 + user->sgid = kmemdup(resp_ext->____payload, 61 + resp_ext->ngroups * sizeof(gid_t), 62 + GFP_KERNEL); 63 + if (!user->sgid) 64 + goto err_free; 65 + 66 + user->ngroups = resp_ext->ngroups; 67 + ksmbd_debug(SMB, "supplementary groups : %d\n", user->ngroups); 57 68 } 69 + 58 70 return user; 71 + 72 + err_free: 73 + kfree(user->name); 74 + kfree(user->passkey); 75 + kfree(user); 76 + return NULL; 59 77 } 60 78 61 79 void ksmbd_free_user(struct ksmbd_user *user) 62 80 { 63 81 ksmbd_ipc_logout_request(user->name, user->flags); 82 + kfree(user->sgid); 64 83 kfree(user->name); 65 84 kfree(user->passkey); 66 85 kfree(user);
+4 -1
fs/smb/server/mgmt/user_config.h
··· 18 18 19 19 size_t passkey_sz; 20 20 char *passkey; 21 + int ngroups; 22 + gid_t *sgid; 21 23 }; 22 24 23 25 static inline bool user_guest(struct ksmbd_user *user) ··· 62 60 } 63 61 64 62 struct ksmbd_user *ksmbd_login_user(const char *account); 65 - struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); 63 + struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, 64 + struct ksmbd_login_response_ext *resp_ext); 66 65 void ksmbd_free_user(struct ksmbd_user *user); 67 66 int ksmbd_anonymous_user(struct ksmbd_user *user); 68 67 bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2);
+12 -3
fs/smb/server/smb_common.c
··· 736 736 struct ksmbd_share_config *share) 737 737 { 738 738 struct ksmbd_session *sess = work->sess; 739 + struct ksmbd_user *user = sess->user; 739 740 struct cred *cred; 740 741 struct group_info *gi; 741 742 unsigned int uid; 742 743 unsigned int gid; 744 + int i; 743 745 744 - uid = user_uid(sess->user); 745 - gid = user_gid(sess->user); 746 + uid = user_uid(user); 747 + gid = user_gid(user); 746 748 if (share->force_uid != KSMBD_SHARE_INVALID_UID) 747 749 uid = share->force_uid; 748 750 if (share->force_gid != KSMBD_SHARE_INVALID_GID) ··· 757 755 cred->fsuid = make_kuid(&init_user_ns, uid); 758 756 cred->fsgid = make_kgid(&init_user_ns, gid); 759 757 760 - gi = groups_alloc(0); 758 + gi = groups_alloc(user->ngroups); 761 759 if (!gi) { 762 760 abort_creds(cred); 763 761 return -ENOMEM; 764 762 } 763 + 764 + for (i = 0; i < user->ngroups; i++) 765 + gi->gid[i] = make_kgid(&init_user_ns, user->sgid[i]); 766 + 767 + if (user->ngroups) 768 + groups_sort(gi); 769 + 765 770 set_groups(cred, gi); 766 771 put_group_info(gi); 767 772
+60 -4
fs/smb/server/transport_ipc.c
··· 120 120 }, 121 121 [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { 122 122 }, 123 + [KSMBD_EVENT_LOGIN_REQUEST_EXT] = { 124 + .len = sizeof(struct ksmbd_login_request), 125 + }, 126 + [KSMBD_EVENT_LOGIN_RESPONSE_EXT] = { 127 + .len = sizeof(struct ksmbd_login_response_ext), 128 + }, 123 129 }; 124 130 125 131 static struct genl_ops ksmbd_genl_ops[] = { ··· 193 187 .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, 194 188 .doit = handle_generic_event, 195 189 }, 190 + { 191 + .cmd = KSMBD_EVENT_LOGIN_REQUEST_EXT, 192 + .doit = handle_unsupported_event, 193 + }, 194 + { 195 + .cmd = KSMBD_EVENT_LOGIN_RESPONSE_EXT, 196 + .doit = handle_generic_event, 197 + }, 196 198 }; 197 199 198 200 static struct genl_family ksmbd_genl_family = { ··· 212 198 .module = THIS_MODULE, 213 199 .ops = ksmbd_genl_ops, 214 200 .n_ops = ARRAY_SIZE(ksmbd_genl_ops), 215 - .resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, 201 + .resv_start_op = KSMBD_EVENT_LOGIN_RESPONSE_EXT + 1, 216 202 }; 217 203 218 204 static void ksmbd_nl_init_fixup(void) ··· 473 459 { 474 460 unsigned int msg_sz = entry->msg_sz; 475 461 476 - if (entry->type == KSMBD_EVENT_RPC_REQUEST) { 462 + switch (entry->type) { 463 + case KSMBD_EVENT_RPC_REQUEST: 464 + { 477 465 struct ksmbd_rpc_command *resp = entry->response; 478 466 479 467 msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz; 480 - } else if (entry->type == KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST) { 468 + break; 469 + } 470 + case KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST: 471 + { 481 472 struct ksmbd_spnego_authen_response *resp = entry->response; 482 473 483 474 msg_sz = sizeof(struct ksmbd_spnego_authen_response) + 484 475 resp->session_key_len + resp->spnego_blob_len; 485 - } else if (entry->type == KSMBD_EVENT_SHARE_CONFIG_REQUEST) { 476 + break; 477 + } 478 + case KSMBD_EVENT_SHARE_CONFIG_REQUEST: 479 + { 486 480 struct ksmbd_share_config_response *resp = entry->response; 487 481 488 482 if (resp->payload_sz) { ··· 500 478 msg_sz = sizeof(struct ksmbd_share_config_response) + 501 479 resp->payload_sz; 502 480 } 481 + break; 482 + } 483 + case KSMBD_EVENT_LOGIN_REQUEST_EXT: 484 + { 485 + struct ksmbd_login_response_ext *resp = entry->response; 486 + 487 + if (resp->ngroups) { 488 + msg_sz = sizeof(struct ksmbd_login_response_ext) + 489 + resp->ngroups * sizeof(gid_t); 490 + } 491 + } 503 492 } 504 493 505 494 return entry->msg_sz != msg_sz ? -EINVAL : 0; ··· 587 554 req->handle = ksmbd_acquire_id(&ipc_ida); 588 555 strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); 589 556 557 + resp = ipc_msg_send_request(msg, req->handle); 558 + ipc_msg_handle_free(req->handle); 559 + ipc_msg_free(msg); 560 + return resp; 561 + } 562 + 563 + struct ksmbd_login_response_ext *ksmbd_ipc_login_request_ext(const char *account) 564 + { 565 + struct ksmbd_ipc_msg *msg; 566 + struct ksmbd_login_request *req; 567 + struct ksmbd_login_response_ext *resp; 568 + 569 + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) 570 + return NULL; 571 + 572 + msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); 573 + if (!msg) 574 + return NULL; 575 + 576 + msg->type = KSMBD_EVENT_LOGIN_REQUEST_EXT; 577 + req = (struct ksmbd_login_request *)msg->payload; 578 + req->handle = ksmbd_acquire_id(&ipc_ida); 579 + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); 590 580 resp = ipc_msg_send_request(msg, req->handle); 591 581 ipc_msg_handle_free(req->handle); 592 582 ipc_msg_free(msg);
+2
fs/smb/server/transport_ipc.h
··· 12 12 13 13 struct ksmbd_login_response * 14 14 ksmbd_ipc_login_request(const char *account); 15 + struct ksmbd_login_response_ext * 16 + ksmbd_ipc_login_request_ext(const char *account); 15 17 16 18 struct ksmbd_session; 17 19 struct ksmbd_share_config;