From: Zebediah Figura Subject: [PATCH 3/4] ntdll: Implement NtFilterToken. Message-Id: <20200922223116.2955353-3-z.figura12@gmail.com> Date: Tue, 22 Sep 2020 17:31:15 -0500 In-Reply-To: <20200922223116.2955353-1-z.figura12@gmail.com> References: <20200922223116.2955353-1-z.figura12@gmail.com> From: Michael Müller Signed-off-by: Zebediah Figura --- dlls/ntdll/ntdll.spec | 2 +- dlls/ntdll/unix/security.c | 63 +++++++++++++++++++++++++++++ include/winnt.h | 5 +++ include/winternl.h | 1 + server/named_pipe.c | 2 +- server/process.c | 2 +- server/protocol.def | 10 +++++ server/security.h | 4 +- server/token.c | 82 +++++++++++++++++++++++++++++++++++++- 9 files changed, 165 insertions(+), 6 deletions(-) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 8f6869d4995..1a2143ca132 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -209,7 +209,7 @@ # @ stub NtEnumerateSystemEnvironmentValuesEx @ stdcall -syscall NtEnumerateValueKey(long long long ptr long ptr) @ stub NtExtendSection -# @ stub NtFilterToken +@ stdcall -syscall NtFilterToken(long long ptr ptr ptr ptr) @ stdcall -syscall NtFindAtom(ptr long ptr) @ stdcall -syscall NtFlushBuffersFile(long ptr) @ stdcall -syscall NtFlushInstructionCache(long ptr long) diff --git a/dlls/ntdll/unix/security.c b/dlls/ntdll/unix/security.c index 09a1d787c09..ad85020d6c0 100644 --- a/dlls/ntdll/unix/security.c +++ b/dlls/ntdll/unix/security.c @@ -604,6 +604,69 @@ NTSTATUS WINAPI NtAdjustPrivilegesToken( HANDLE token, BOOLEAN disable, TOKEN_PR } +/*********************************************************************** + * NtFilterToken (NTDLL.@) + */ +NTSTATUS WINAPI NtFilterToken( HANDLE token, ULONG flags, TOKEN_GROUPS *disable_sids, + TOKEN_PRIVILEGES *privileges, TOKEN_GROUPS *restrict_sids, HANDLE *new_token ) +{ + data_size_t privileges_len = 0; + data_size_t sids_len = 0; + SID *sids = NULL; + NTSTATUS status; + + TRACE( "%p %#x %p %p %p %p\n", token, flags, disable_sids, privileges, + restrict_sids, new_token ); + + if (flags) + FIXME( "flags %#x unsupported\n", flags ); + + if (restrict_sids) + FIXME( "support for restricting sids not yet implemented\n" ); + + if (privileges) + privileges_len = privileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); + + if (disable_sids) + { + DWORD len, i; + BYTE *tmp; + + for (i = 0; i < disable_sids->GroupCount; i++) + { + SID *sid = disable_sids->Groups[i].Sid; + sids_len += offsetof( SID, SubAuthority[sid->SubAuthorityCount] ); + } + + sids = malloc( sids_len ); + if (!sids) return STATUS_NO_MEMORY; + + for (i = 0, tmp = (BYTE *)sids; i < disable_sids->GroupCount; i++, tmp += len) + { + SID *sid = disable_sids->Groups[i].Sid; + len = offsetof( SID, SubAuthority[sid->SubAuthorityCount] ); + memcpy( tmp, disable_sids->Groups[i].Sid, len ); + } + } + + SERVER_START_REQ( filter_token ) + { + req->handle = wine_server_obj_handle( token ); + req->flags = flags; + req->privileges_size = privileges_len; + wine_server_add_data( req, privileges->Privileges, privileges_len ); + wine_server_add_data( req, sids, sids_len ); + status = wine_server_call( req ); + if (!status) *new_token = wine_server_ptr_handle( reply->new_handle ); + } + SERVER_END_REQ; + + free( sids ); + return status; +} + + + /*********************************************************************** * NtPrivilegeCheck (NTDLL.@) */ diff --git a/include/winnt.h b/include/winnt.h index 63567ba62ee..1eb82876f1b 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -4288,6 +4288,11 @@ typedef enum _TOKEN_INFORMATION_CLASS { TOKEN_ADJUST_SESSIONID | \ TOKEN_ADJUST_DEFAULT ) +#define DISABLE_MAX_PRIVILEGE 0x1 +#define SANDBOX_INERT 0x2 +#define LUA_TOKEN 0x4 +#define WRITE_RESTRICTED 0x8 + #ifndef _SECURITY_DEFINED #define _SECURITY_DEFINED diff --git a/include/winternl.h b/include/winternl.h index fc2f0285183..e47c7824fcc 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3045,6 +3045,7 @@ NTSYSAPI NTSTATUS WINAPI NtDuplicateToken(HANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES NTSYSAPI NTSTATUS WINAPI NtEnumerateKey(HANDLE,ULONG,KEY_INFORMATION_CLASS,void *,DWORD,DWORD *); NTSYSAPI NTSTATUS WINAPI NtEnumerateValueKey(HANDLE,ULONG,KEY_VALUE_INFORMATION_CLASS,PVOID,ULONG,PULONG); NTSYSAPI NTSTATUS WINAPI NtExtendSection(HANDLE,PLARGE_INTEGER); +NTSYSAPI NTSTATUS WINAPI NtFilterToken(HANDLE,ULONG,TOKEN_GROUPS*,TOKEN_PRIVILEGES*,TOKEN_GROUPS*,HANDLE*); NTSYSAPI NTSTATUS WINAPI NtFindAtom(const WCHAR*,ULONG,RTL_ATOM*); NTSYSAPI NTSTATUS WINAPI NtFlushBuffersFile(HANDLE,IO_STATUS_BLOCK*); NTSYSAPI NTSTATUS WINAPI NtFlushInstructionCache(HANDLE,LPCVOID,SIZE_T); diff --git a/server/named_pipe.c b/server/named_pipe.c index 1ac35cf1185..5c7f035619c 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -1161,7 +1161,7 @@ static int pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct async *as if (current->process->token) /* FIXME: use the client token */ { struct token *token; - if (!(token = token_duplicate( current->process->token, 0, SecurityImpersonation, NULL ))) + if (!(token = token_duplicate( current->process->token, 0, SecurityImpersonation, NULL, NULL, 0, NULL, 0 ))) return 0; if (current->token) release_object( current->token ); current->token = token; diff --git a/server/process.c b/server/process.c index 1c9148f1e0c..1fd110978af 100644 --- a/server/process.c +++ b/server/process.c @@ -581,7 +581,7 @@ struct process *create_process( int fd, struct process *parent, int inherit_all, : alloc_handle_table( process, 0 ); /* Note: for security reasons, starting a new process does not attempt * to use the current impersonation token for the new process */ - process->token = token_duplicate( parent->token, TRUE, 0, NULL ); + process->token = token_duplicate( parent->token, TRUE, 0, NULL, NULL, 0, NULL, 0 ); process->affinity = parent->affinity; } if (!process->handles || !process->token) goto error; diff --git a/server/protocol.def b/server/protocol.def index f64e7d0ca81..9437a303501 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3142,6 +3142,16 @@ enum caret_state obj_handle_t new_handle; /* duplicated handle */ @END +@REQ(filter_token) + obj_handle_t handle; /* handle to the token to duplicate */ + unsigned int flags; /* flags */ + data_size_t privileges_size; /* size of privileges */ + VARARG(privileges,LUID_AND_ATTRIBUTES,privileges_size); /* privileges to remove from new token */ + VARARG(disable_sids,SID); /* array of groups to remove from new token */ +@REPLY + obj_handle_t new_handle; /* filtered handle */ +@END + @REQ(access_check) obj_handle_t handle; /* handle to the token */ unsigned int desired_access; /* desired access to the object */ diff --git a/server/security.h b/server/security.h index 606dbb2ab2c..7c35300afe4 100644 --- a/server/security.h +++ b/server/security.h @@ -56,7 +56,9 @@ extern const PSID security_high_label_sid; extern struct token *token_create_admin(void); extern int token_assign_label( struct token *token, PSID label ); extern struct token *token_duplicate( struct token *src_token, unsigned primary, - int impersonation_level, const struct security_descriptor *sd ); + int impersonation_level, const struct security_descriptor *sd, + const LUID_AND_ATTRIBUTES *remove_privs, unsigned int remove_priv_count, + const SID *remove_groups, unsigned int remove_group_count ); extern int token_check_privileges( struct token *token, int all_required, const LUID_AND_ATTRIBUTES *reqprivs, unsigned int count, LUID_AND_ATTRIBUTES *usedprivs); diff --git a/server/token.c b/server/token.c index 23e85ebbff3..8404e651196 100644 --- a/server/token.c +++ b/server/token.c @@ -286,6 +286,19 @@ static int acl_is_valid( const ACL *acl, data_size_t size ) return TRUE; } +static unsigned int get_sid_count( const SID *sid, data_size_t size ) +{ + unsigned int count; + + for (count = 0; size >= sizeof(SID) && security_sid_len( sid ) <= size; count++) + { + size -= security_sid_len( sid ); + sid = (const SID *)((char *)sid + security_sid_len( sid )); + } + + return count; +} + /* checks whether all members of a security descriptor fit inside the size * of memory specified */ int sd_is_valid( const struct security_descriptor *sd, data_size_t size ) @@ -627,8 +640,36 @@ static struct token *create_token( unsigned primary, const SID *user, return token; } +static int filter_group( struct group *group, const SID *filter, unsigned int count ) +{ + unsigned int i; + + for (i = 0; i < count; i++) + { + if (security_equal_sid( &group->sid, filter )) return 1; + filter = (const SID *)((char *)filter + security_sid_len( filter )); + } + + return 0; +} + +static int filter_privilege( struct privilege *privilege, const LUID_AND_ATTRIBUTES *filter, unsigned int count ) +{ + unsigned int i; + + for (i = 0; i < count; i++) + { + if (!memcmp( &privilege->luid, &filter[i].Luid, sizeof(LUID) )) + return 1; + } + + return 0; +} + struct token *token_duplicate( struct token *src_token, unsigned primary, - int impersonation_level, const struct security_descriptor *sd ) + int impersonation_level, const struct security_descriptor *sd, + const LUID_AND_ATTRIBUTES *remove_privs, unsigned int remove_priv_count, + const SID *remove_groups, unsigned int remove_group_count) { const luid_t *modified_id = primary || (impersonation_level == src_token->impersonation_level) ? @@ -664,6 +705,12 @@ struct token *token_duplicate( struct token *src_token, unsigned primary, return NULL; } memcpy( newgroup, group, size ); + if (filter_group( group, remove_groups, remove_group_count )) + { + newgroup->enabled = 0; + newgroup->def = 0; + newgroup->deny_only = 1; + } list_add_tail( &token->groups, &newgroup->entry ); if (src_token->primary_group == &group->sid) { @@ -675,11 +722,14 @@ struct token *token_duplicate( struct token *src_token, unsigned primary, /* copy privileges */ LIST_FOR_EACH_ENTRY( privilege, &src_token->privileges, struct privilege, entry ) + { + if (filter_privilege( privilege, remove_privs, remove_priv_count )) continue; if (!privilege_add( token, &privilege->luid, privilege->enabled )) { release_object( token ); return NULL; } + } if (sd) default_set_sd( &token->obj, sd, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION ); @@ -1312,7 +1362,7 @@ DECL_HANDLER(duplicate_token) TOKEN_DUPLICATE, &token_ops ))) { - struct token *token = token_duplicate( src_token, req->primary, req->impersonation_level, sd ); + struct token *token = token_duplicate( src_token, req->primary, req->impersonation_level, sd, NULL, 0, NULL, 0 ); if (token) { reply->new_handle = alloc_handle_no_access_check( current->process, token, req->access, objattr->attributes ); @@ -1322,6 +1372,34 @@ DECL_HANDLER(duplicate_token) } } +/* creates a restricted version of a token */ +DECL_HANDLER(filter_token) +{ + struct token *src_token; + + if ((src_token = (struct token *)get_handle_obj( current->process, req->handle, TOKEN_DUPLICATE, &token_ops ))) + { + const LUID_AND_ATTRIBUTES *filter_privileges = get_req_data(); + unsigned int priv_count, group_count; + const SID *filter_groups; + struct token *token; + + priv_count = min( req->privileges_size, get_req_data_size() ) / sizeof(LUID_AND_ATTRIBUTES); + filter_groups = (const SID *)((char *)filter_privileges + priv_count * sizeof(LUID_AND_ATTRIBUTES)); + group_count = get_sid_count( filter_groups, get_req_data_size() - priv_count * sizeof(LUID_AND_ATTRIBUTES) ); + + token = token_duplicate( src_token, src_token->primary, src_token->impersonation_level, NULL, + filter_privileges, priv_count, filter_groups, group_count ); + if (token) + { + unsigned int access = get_handle_access( current->process, req->handle ); + reply->new_handle = alloc_handle_no_access_check( current->process, token, access, 0 ); + release_object( token ); + } + release_object( src_token ); + } +} + /* checks the specified privileges are held by the token */ DECL_HANDLER(check_token_privileges) { -- 2.28.0