Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/ntdll/nt.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/ntdll.spec | 2 +- server/protocol.def | 15 ++++++ server/token.c | 72 ++++++++++++++++++++++++++-- 4 files changed, 214 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index c3f5df337f..29fff96036 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -70,6 +70,135 @@ WINE_DEFAULT_DEBUG_CHANNEL(ntdll); * Token */
+static BOOL get_primary_group_index( const TOKEN_PRIMARY_GROUP *primary, const TOKEN_GROUPS *groups, + unsigned int *index ) +{ + unsigned int i; + for (i = 0; i < groups->GroupCount; i++) + { + if (RtlEqualSid( primary->PrimaryGroup, groups->Groups[i].Sid )) + { + *index = i + 1; /* 0 is reserved for user */ + return TRUE; + } + } + return FALSE; +} + +static BOOL get_owner_index( const TOKEN_OWNER *owner, const TOKEN_USER *user, const TOKEN_GROUPS *groups, + unsigned int *index ) +{ + unsigned int i; + if (RtlEqualSid( owner->Owner, user->User.Sid )) + { + *index = 0; + return TRUE; + } + for (i = 0; i < groups->GroupCount; i++) + { + if (RtlEqualSid( owner->Owner, groups->Groups[i].Sid )) + { + *index = i + 1; + return TRUE; + } + } + return FALSE; +} + +/* create flat groups buffer and store user as first entry */ +static struct token_groups *flatten_groups( const TOKEN_USER *user, const TOKEN_GROUPS *groups, data_size_t *size ) +{ + struct token_groups *ret; + unsigned int i, sid_len, *attr; + char *sid; + + *size = sizeof(*ret) + sizeof(*attr) + RtlLengthSid( user->User.Sid ); + for (i = 0; i < groups->GroupCount; i++) + { + *size += sizeof(*attr); + *size += RtlLengthSid( groups->Groups[i].Sid ); + } + + if (!(ret = RtlAllocateHeap( GetProcessHeap(), 0, *size ))) return NULL; + attr = (unsigned int *)(ret + 1); + sid = (char *)attr + (groups->GroupCount + 1) * sizeof(*attr); + + *attr++ = user->User.Attributes; + sid_len = RtlLengthSid( user->User.Sid ); + memcpy( sid, user->User.Sid, sid_len ); + sid += sid_len; + + for (i = 0; i < groups->GroupCount; i++) + { + *attr++ = groups->Groups[i].Attributes; + sid_len = RtlLengthSid( groups->Groups[i].Sid ); + memcpy( sid, groups->Groups[i].Sid, sid_len ); + sid += sid_len; + } + + ret->count = groups->GroupCount + 1; + return ret; +} + +static unsigned int acl_size( const ACL *acl ) +{ + unsigned int i, ret = sizeof(*acl); + ACE_HEADER *ace = (ACE_HEADER *)(acl + 1); + if (!acl) return 0; + for (i = 0; i < acl->AceCount; i++) ret += ace[i].AceSize; + return ret; +} + +NTSTATUS WINAPI NtCreateToken( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attrs, TOKEN_TYPE type, + LUID *session, LARGE_INTEGER *expiration, TOKEN_USER *user, TOKEN_GROUPS *groups, + TOKEN_PRIVILEGES *privs, TOKEN_OWNER *owner, TOKEN_PRIMARY_GROUP *primary_group, + TOKEN_DEFAULT_DACL *dacl, TOKEN_SOURCE *source ) +{ + NTSTATUS status; + struct object_attributes *objattr; + struct token_groups *flat_groups; + data_size_t attr_size, flat_groups_size, default_dacl_size = dacl ? acl_size( dacl->DefaultDacl ) : 0; + ACL *default_dacl = dacl ? dacl->DefaultDacl : NULL; + unsigned int primary_group_index, owner_index; + + TRACE( "(%p,%08x,%s,%u,%p,%p,%p,%p,%p,%p,%p,%p,%p)\n", handle, access, debugstr_ObjectAttributes(attrs), + type, session, expiration, user, groups, privs, owner, primary_group, dacl, source ); + + if (!handle || !session || !expiration || !user || !groups || !privs || !owner || !primary_group || !source) + return STATUS_ACCESS_VIOLATION; + + if (!get_primary_group_index( primary_group, groups, &primary_group_index )) return STATUS_INVALID_PRIMARY_GROUP; + if (!get_owner_index( owner, user, groups, &owner_index )) return STATUS_INVALID_OWNER; + + if (!(flat_groups = flatten_groups( user, groups, &flat_groups_size ))) return STATUS_NO_MEMORY; + if ((status = alloc_object_attributes( attrs, &objattr, &attr_size ))) + { + RtlFreeHeap( GetProcessHeap(), 0, flat_groups ); + return status; + } + + SERVER_START_REQ( create_token ) + { + req->access = access; + req->primary = (type == TokenPrimary); + req->primary_group_index = primary_group_index; + req->owner_index = owner_index; + req->privilege_count = privs->PrivilegeCount; + req->default_dacl_size = default_dacl_size; + wine_server_add_data( req, objattr, attr_size ); + wine_server_add_data( req, privs->Privileges, privs->PrivilegeCount * sizeof(privs->Privileges[0]) ); + wine_server_add_data( req, default_dacl, default_dacl_size ); + wine_server_add_data( req, flat_groups, flat_groups_size ); + status = wine_server_call( req ); + if (!status) *handle = wine_server_ptr_handle( reply->token ); + } + SERVER_END_REQ; + + RtlFreeHeap( GetProcessHeap(), 0, flat_groups ); + RtlFreeHeap( GetProcessHeap(), 0, objattr ); + return status; +} + /****************************************************************************** * NtDuplicateToken [NTDLL.@] * ZwDuplicateToken [NTDLL.@] diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index c260b0dbec..27e51a165b 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -154,7 +154,7 @@ @ stub NtCreateThread @ stdcall NtCreateThreadEx(ptr long ptr long ptr ptr long long long long ptr) @ stdcall NtCreateTimer(ptr long ptr long) -@ stub NtCreateToken +@ stdcall NtCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr ) # @ stub NtCreateWaitablePort @ stdcall -arch=win32,arm64 NtCurrentTeb() # @ stub NtDebugActiveProcess diff --git a/server/protocol.def b/server/protocol.def index 35824ae952..3dae8ab521 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3286,6 +3286,21 @@ enum caret_state user_handle_t window; /* clipboard listener window */ @END
+/* Create a security token */ +@REQ(create_token) + unsigned int access; /* access rights to the new token */ + int primary; /* is the new token to be a primary one? */ + unsigned int primary_group_index; /* primary group index into groups array */ + unsigned int owner_index; /* owner index into groups array */ + unsigned int privilege_count; /* number of privileges */ + unsigned int default_dacl_size; /* size of default dacl */ + VARARG(objattr,object_attributes); /* object attributes */ + VARARG(default_dacl,ACL); /* token default dacl */ + VARARG(privileges,LUID_AND_ATTRIBUTES); /* token privileges */ + VARARG(groups,token_groups); /* token groups, first element is token user */ +@REPLY + obj_handle_t token; /* handle to the token */ +@END
/* Open a security token */ @REQ(open_token) diff --git a/server/token.c b/server/token.c index 0810a61353..4519ab5bba 100644 --- a/server/token.c +++ b/server/token.c @@ -527,7 +527,7 @@ static void token_destroy( struct object *obj ) * modified_id may be NULL, indicating that a new modified_id luid should be * allocated. */ -static struct token *create_token( unsigned primary, const SID *user, +static struct token *token_create( unsigned primary, const SID *user, const SID_AND_ATTRIBUTES *groups, unsigned int group_count, const LUID_AND_ATTRIBUTES *privs, unsigned int priv_count, const ACL *default_dacl, TOKEN_SOURCE source, @@ -638,7 +638,7 @@ struct token *token_duplicate( struct token *src_token, unsigned primary, return NULL; }
- token = create_token( primary, src_token->user, NULL, 0, + token = token_create( primary, src_token->user, NULL, 0, NULL, 0, src_token->default_dacl, src_token->source, modified_id, impersonation_level ); @@ -836,7 +836,7 @@ struct token *token_create_admin( void ) { logon_sid, SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY|SE_GROUP_LOGON_ID }, }; static const TOKEN_SOURCE admin_source = {"SeMgr", {0, 0}}; - token = create_token( TRUE, user_sid, admin_groups, sizeof(admin_groups)/sizeof(admin_groups[0]), + token = token_create( TRUE, user_sid, admin_groups, sizeof(admin_groups)/sizeof(admin_groups[0]), admin_privs, sizeof(admin_privs)/sizeof(admin_privs[0]), default_dacl, admin_source, NULL, -1 ); /* we really need a primary group */ @@ -1173,6 +1173,72 @@ int check_object_access(struct object *obj, unsigned int *access) return res; }
+/* create a security token */ +DECL_HANDLER(create_token) +{ + static const TOKEN_SOURCE source = {"SeMgr", {0, 0}}; + struct token *token; + struct unicode_str name; + const struct security_descriptor *sd; + const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, NULL ); + data_size_t data_size, privs_size; + const LUID_AND_ATTRIBUTES *privs; + const ACL *acl = NULL; + ACL *default_dacl = NULL; + const struct token_groups *groups; + unsigned int *attr, i; + const SID *sid; + + if (!objattr) return; + + privs = get_req_data_after_objattr( objattr, &data_size ); + privs_size = req->privilege_count * sizeof(*privs); + if (req->default_dacl_size) acl = (const ACL *)((char *)privs + privs_size); + + groups = (const struct token_groups *)((char *)privs + privs_size + req->default_dacl_size); + attr = (unsigned int *)(groups + 1); + sid = (const SID *)(attr + groups->count); /* first entry is user */ + if (!acl) acl = default_dacl = create_default_dacl( sid ); + + if ((token = token_create( req->primary, sid, NULL, 0, privs, req->privilege_count, acl, source, NULL, + SecurityIdentification ))) + { + sid = (const SID *)((char *)sid + security_sid_len( sid )); /* skip user */ + for (i = 1; i < groups->count; i++) + { + struct group *group; + size_t sid_len = security_sid_len( sid ); + + if (!(group = mem_alloc( FIELD_OFFSET(struct group, sid.SubAuthority[sid->SubAuthorityCount]) ))) + { + release_object( token ); + return; + } + + memcpy( &group->sid, sid, sid_len ); + group->enabled = (*attr & SE_GROUP_ENABLED) != 0; + group->def = (*attr & SE_GROUP_ENABLED_BY_DEFAULT) != 0; + group->logon = (*attr & SE_GROUP_LOGON_ID) != 0; + group->mandatory = (*attr & SE_GROUP_MANDATORY) != 0; + group->owner = (*attr & SE_GROUP_OWNER) != 0; + group->resource = (*attr & SE_GROUP_RESOURCE) != 0; + group->deny_only = (*attr & SE_GROUP_USE_FOR_DENY_ONLY) != 0; + list_add_tail( &token->groups, &group->entry ); + + if (req->primary_group_index == i) token->primary_group = &group->sid; + if (req->owner_index == i) token->owner = &group->sid; + + sid = (const SID *)((char *)sid + sid_len); + attr++; + } + if (!req->owner_index) token->owner = token->user; + + reply->token = alloc_handle( current->process, token, req->access, objattr->attributes ); + release_object( token ); + } + + free( default_dacl ); +}
/* open a security token */ DECL_HANDLER(open_token)