-- v3: ntdll: Implement NtCreateToken().
From: Dmitry Timoshkov dmitry@baikal.ru
This version of the patch passes most of the NtCreateToken() tests provided by Hans.
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/ntdll/ntdll.spec | 4 +- dlls/ntdll/unix/loader.c | 1 + dlls/ntdll/unix/security.c | 111 +++++++++++++++++++++++++++++++++++++ dlls/wow64/security.c | 36 ++++++++++++ dlls/wow64/struct32.h | 5 ++ dlls/wow64/syscall.h | 1 + dlls/wow64/wow64_private.h | 25 +++++++++ server/protocol.def | 20 +++++++ server/token.c | 110 ++++++++++++++++++++++++++++++++++-- 9 files changed, 306 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 011f0648522..37fc5be61d3 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -192,7 +192,7 @@ @ stdcall -syscall NtCreateThread(ptr long ptr long ptr ptr ptr long) @ stdcall -syscall NtCreateThreadEx(ptr long ptr long ptr ptr long long long long ptr) @ stdcall -syscall NtCreateTimer(ptr long ptr long) -# @ stub NtCreateToken +@ stdcall -syscall NtCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr) @ stdcall -syscall NtCreateTransaction(ptr long ptr ptr long long long long ptr ptr) @ stdcall -syscall NtCreateUserProcess(ptr ptr long long ptr ptr long long ptr ptr ptr) # @ stub NtCreateWaitablePort @@ -1235,7 +1235,7 @@ @ stdcall -private -syscall ZwCreateThread(ptr long ptr long ptr ptr ptr long) NtCreateThread @ stdcall -private -syscall ZwCreateThreadEx(ptr long ptr long ptr ptr long long long long ptr) NtCreateThreadEx @ stdcall -private -syscall ZwCreateTimer(ptr long ptr long) NtCreateTimer -# @ stub ZwCreateToken +@ stdcall -private -syscall ZwCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr) NtCreateToken @ stdcall -private -syscall ZwCreateUserProcess(ptr ptr long long ptr ptr long long ptr ptr ptr) NtCreateUserProcess # @ stub ZwCreateWaitablePort @ stdcall -private -syscall ZwDebugActiveProcess(long long) NtDebugActiveProcess diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 8d5fc79895c..b081054a651 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -166,6 +166,7 @@ static void * const syscalls[] = NtCreateThread, NtCreateThreadEx, NtCreateTimer, + NtCreateToken, NtCreateTransaction, NtCreateUserProcess, NtDebugActiveProcess, diff --git a/dlls/ntdll/unix/security.c b/dlls/ntdll/unix/security.c index 8351f8b1993..060638e6041 100644 --- a/dlls/ntdll/unix/security.c +++ b/dlls/ntdll/unix/security.c @@ -36,6 +36,117 @@
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
+static BOOL is_equal_sid( const SID *sid1, const SID *sid2 ) +{ + size_t size1 = offsetof( SID, SubAuthority[sid1->SubAuthorityCount] ); + size_t size2 = offsetof( SID, SubAuthority[sid2->SubAuthorityCount] ); + return size1 == size2 && !memcmp( sid1, sid2, size1 ); +} + +/*********************************************************************** + * NtCreateToken (NTDLL.@) + */ +NTSTATUS WINAPI NtCreateToken( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr, + TOKEN_TYPE type, LUID *token_id, LARGE_INTEGER *expire, TOKEN_USER *user, + TOKEN_GROUPS *groups, TOKEN_PRIVILEGES *privs, TOKEN_OWNER *owner, + TOKEN_PRIMARY_GROUP *group, TOKEN_DEFAULT_DACL *dacl, TOKEN_SOURCE *source) +{ + SECURITY_IMPERSONATION_LEVEL level = SecurityAnonymous; + unsigned int status, i, *attrs; + data_size_t objattr_size, groups_size, size; + struct object_attributes *objattr; + void *groups_info; + BYTE *p; + SID *sid; + int primary_group = -1; + + TRACE( "(%p,0x%08x,%p,%d,%p,%p,%p,%p,%p,%p,%p,%p,%p)\n", handle, (int)access, attr, + type, token_id, expire, user, groups, privs, owner, group, dacl, source ); + + /* all arguments are required except default dacl */ + if (!handle || !attr || !token_id || !expire || !user || !groups || !privs || !owner || !group || !source) + return STATUS_ACCESS_VIOLATION; + + *handle = 0; + if ((status = alloc_object_attributes( attr, &objattr, &objattr_size ))) return status; + + if (attr->SecurityQualityOfService) + { + SECURITY_QUALITY_OF_SERVICE *qos = attr->SecurityQualityOfService; + TRACE( "ObjectAttributes->SecurityQualityOfService = {%d, %d, %d, %s}\n", + (int)qos->Length, qos->ImpersonationLevel, qos->ContextTrackingMode, + qos->EffectiveOnly ? "TRUE" : "FALSE"); + level = qos->ImpersonationLevel; + } + + groups_size = groups->GroupCount * sizeof( attrs[0] ); + + for (i = 0; i < groups->GroupCount; i++) + { + SID *group_sid = group->PrimaryGroup; + sid = groups->Groups[i].Sid; + groups_size += offsetof( SID, SubAuthority[sid->SubAuthorityCount] ); + if (is_equal_sid( sid, group_sid )) + primary_group = i; + } + + if (primary_group == -1) + return STATUS_INVALID_PRIMARY_GROUP; + + groups_info = malloc( groups_size ); + if (!groups_info) + { + free( objattr ); + return STATUS_NO_MEMORY; + } + + attrs = (unsigned int *)groups_info; + p = (BYTE *)&attrs[groups->GroupCount]; + for (i = 0; i < groups->GroupCount; i++) + { + sid = groups->Groups[i].Sid; + attrs[i] = groups->Groups[i].Attributes; + size = offsetof( SID, SubAuthority[sid->SubAuthorityCount] ); + memcpy( p, sid, size ); + p += size; + } + + SERVER_START_REQ( create_token ) + { + req->token_id.low_part = token_id->LowPart; + req->token_id.high_part = token_id->HighPart; + req->access = access; + req->primary = (type == TokenPrimary); + req->impersonation_level = level; + req->expire = expire->QuadPart; + + wine_server_add_data( req, objattr, objattr_size ); + + sid = user->User.Sid; + wine_server_add_data( req, sid, offsetof( SID, SubAuthority[sid->SubAuthorityCount] ) ); + + req->group_count = groups->GroupCount; + wine_server_add_data( req, groups_info, groups_size ); + + req->primary_group = primary_group; + + req->priv_count = privs->PrivilegeCount; + wine_server_add_data( req, privs->Privileges, privs->PrivilegeCount * sizeof(privs->Privileges[0]) ); + + if (dacl && dacl->DefaultDacl) + wine_server_add_data( req, dacl->DefaultDacl, dacl->DefaultDacl->AclSize ); + + status = wine_server_call( req ); + if (!status) *handle = wine_server_ptr_handle( reply->token ); + } + SERVER_END_REQ; + + free( groups_info ); + free( objattr ); + + return status; +} +
/*********************************************************************** * NtOpenProcessToken (NTDLL.@) diff --git a/dlls/wow64/security.c b/dlls/wow64/security.c index 82bbb0d2b40..26d413baa13 100644 --- a/dlls/wow64/security.c +++ b/dlls/wow64/security.c @@ -154,6 +154,42 @@ NTSTATUS WINAPI wow64_NtCreateLowBoxToken( UINT *args ) }
+/********************************************************************** + * wow64_NtCreateToken + */ +NTSTATUS WINAPI wow64_NtCreateToken( UINT *args ) +{ + ULONG *handle_ptr = get_ptr( &args ); + ACCESS_MASK access = get_ulong( &args ); + OBJECT_ATTRIBUTES32 *attr32 = get_ptr( &args ); + TOKEN_TYPE type = get_ulong( &args ); + LUID *luid = get_ptr( &args ); + LARGE_INTEGER *expire = get_ptr( &args ); + TOKEN_USER32 *user32 = get_ptr( &args ); + TOKEN_GROUPS32 *groups32 = get_ptr( &args ); + TOKEN_PRIVILEGES *privs = get_ptr( &args ); + TOKEN_OWNER32 *owner32 = get_ptr( &args ); + TOKEN_PRIMARY_GROUP32 *group32 = get_ptr( &args ); + TOKEN_DEFAULT_DACL32 *dacl32 = get_ptr( &args ); + TOKEN_SOURCE *source = get_ptr( &args ); + + struct object_attr64 attr; + TOKEN_USER user; + TOKEN_OWNER owner; + TOKEN_PRIMARY_GROUP group; + TOKEN_DEFAULT_DACL dacl; + HANDLE handle = 0; + NTSTATUS status; + + status = NtCreateToken( &handle, access, objattr_32to64( &attr, attr32 ), type, luid, expire, + token_user_32to64( &user, user32 ), token_groups_32to64( groups32 ), privs, + token_owner_32to64( &owner, owner32 ), token_primary_group_32to64( &group, group32 ), + token_default_dacl_32to64( &dacl, dacl32 ), source ); + put_handle( handle_ptr, handle ); + return status; +} + + /********************************************************************** * wow64_NtDuplicateToken */ diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index 7dc12b75271..2e51e89fba0 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -372,6 +372,11 @@ typedef struct ULONG Owner; } TOKEN_OWNER32;
+typedef struct +{ + ULONG PrimaryGroup; +} TOKEN_PRIMARY_GROUP32; + typedef struct { SID_AND_ATTRIBUTES32 User; diff --git a/dlls/wow64/syscall.h b/dlls/wow64/syscall.h index 59586d3fbfa..fce71395439 100644 --- a/dlls/wow64/syscall.h +++ b/dlls/wow64/syscall.h @@ -70,6 +70,7 @@ SYSCALL_ENTRY( NtCreateThread ) \ SYSCALL_ENTRY( NtCreateThreadEx ) \ SYSCALL_ENTRY( NtCreateTimer ) \ + SYSCALL_ENTRY( NtCreateToken ) \ SYSCALL_ENTRY( NtCreateTransaction ) \ SYSCALL_ENTRY( NtCreateUserProcess ) \ SYSCALL_ENTRY( NtDebugActiveProcess ) \ diff --git a/dlls/wow64/wow64_private.h b/dlls/wow64/wow64_private.h index 5b09d16c9d9..b126699c83f 100644 --- a/dlls/wow64/wow64_private.h +++ b/dlls/wow64/wow64_private.h @@ -188,6 +188,31 @@ static inline OBJECT_ATTRIBUTES *objattr_32to64_redirect( struct object_attr64 * return attr; }
+static inline TOKEN_USER *token_user_32to64( TOKEN_USER *out, const TOKEN_USER32 *in ) +{ + out->User.Sid = ULongToPtr( in->User.Sid ); + out->User.Attributes = in->User.Attributes; + return out; +} + +static inline TOKEN_OWNER *token_owner_32to64( TOKEN_OWNER *out, const TOKEN_OWNER32 *in ) +{ + out->Owner = ULongToPtr( in->Owner ); + return out; +} + +static inline TOKEN_PRIMARY_GROUP *token_primary_group_32to64( TOKEN_PRIMARY_GROUP *out, const TOKEN_PRIMARY_GROUP32 *in ) +{ + out->PrimaryGroup = ULongToPtr( in->PrimaryGroup ); + return out; +} + +static inline TOKEN_DEFAULT_DACL *token_default_dacl_32to64( TOKEN_DEFAULT_DACL *out, const TOKEN_DEFAULT_DACL32 *in ) +{ + out->DefaultDacl = ULongToPtr( in->DefaultDacl ); + return out; +} + static inline void put_handle( ULONG *handle32, HANDLE handle ) { *handle32 = HandleToULong( handle ); diff --git a/server/protocol.def b/server/protocol.def index 919297c818c..5b00f2ac174 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3186,6 +3186,26 @@ enum caret_state @END
+/* Create a security token */ +@REQ(create_token) + struct luid token_id; + unsigned int access; /* access rights to the new token */ + int primary; /* is the new token to be a primary one? */ + int impersonation_level; /* impersonation level of the new token */ + abstime_t expire; /* expiration time */ + int group_count; + int primary_group; + int priv_count; + /* VARARG(objattr,object_attributes); */ + /* VARARG(user,sid); */ + /* VARARG(groups,sid); */ + /* VARARG(privs,luid_attr); */ + /* VARARG(dacl,acl); */ +@REPLY + obj_handle_t token; /* handle to the token */ +@END + + /* Open a security token */ @REQ(open_token) obj_handle_t handle; /* handle to the thread or process */ diff --git a/server/token.c b/server/token.c index 99f5e36e279..89e9b1b1eb1 100644 --- a/server/token.c +++ b/server/token.c @@ -472,7 +472,7 @@ static struct token *create_token( unsigned int primary, unsigned int session_id const struct sid_attrs *groups, unsigned int group_count, const struct luid_attr *privs, unsigned int priv_count, const struct acl *default_dacl, const struct luid *modified_id, - int impersonation_level, int elevation ) + unsigned int primary_group, int impersonation_level, int elevation ) { struct token *token = alloc_object( &token_ops ); if (token) @@ -519,8 +519,8 @@ static struct token *create_token( unsigned int primary, unsigned int session_id group->attrs = groups[i].attrs; copy_sid( &group->sid, groups[i].sid ); list_add_tail( &token->groups, &group->entry ); - /* Use first owner capable group as owner and primary group */ - if (!token->primary_group && (group->attrs & SE_GROUP_OWNER)) + + if (primary_group == i) { token->owner = &group->sid; token->primary_group = &group->sid; @@ -598,7 +598,7 @@ struct token *token_duplicate( struct token *src_token, unsigned primary,
token = create_token( primary, src_token->session_id, src_token->user, NULL, 0, NULL, 0, src_token->default_dacl, modified_id, - impersonation_level, src_token->elevation ); + 0, impersonation_level, src_token->elevation ); if (!token) return token;
/* copy groups */ @@ -773,7 +773,7 @@ struct token *token_create_admin( unsigned primary, int impersonation_level, int
token = create_token( primary, session_id, user_sid, admin_groups, ARRAY_SIZE( admin_groups ), admin_privs, ARRAY_SIZE( admin_privs ), default_dacl, - NULL, impersonation_level, elevation ); + NULL, 4 /* domain_users */, impersonation_level, elevation ); /* we really need a primary group */ assert( token->primary_group );
@@ -1089,6 +1089,106 @@ int check_object_access(struct token *token, struct object *obj, unsigned int *a }
+/* create a security token */ +DECL_HANDLER(create_token) +{ + struct token *token; + struct object_attributes *objattr; + struct sid *user; + struct sid_attrs *groups; + struct luid_attr *privs; + struct acl *dacl = NULL; + unsigned int i; + data_size_t data_size, groups_size; + struct acl *default_dacl = NULL; + unsigned int *attrs; + struct sid *sid; + + objattr = (struct object_attributes *)get_req_data(); + user = (struct sid *)get_req_data_after_objattr( objattr, &data_size ); + + if (!user || !sid_valid_size( user, data_size )) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + data_size -= sid_len( user ); + groups_size = req->group_count * sizeof( attrs[0] ); + + if (data_size < groups_size) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + if (req->primary_group < 0 || req->primary_group >= req->group_count) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + groups = malloc( req->group_count * sizeof( groups[0] ) ); + if (!groups) + { + set_error( STATUS_NO_MEMORY ); + return; + } + + attrs = (unsigned int *)((char *)user + sid_len( user )); + sid = (struct sid *)&attrs[req->group_count]; + + for (i = 0; i < req->group_count; i++) + { + groups[i].attrs = attrs[i]; + groups[i].sid = sid; + + if (!sid_valid_size( sid, data_size - groups_size )) + { + free( groups ); + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + groups_size += sid_len( sid ); + sid = (struct sid *)((char *)sid + sid_len( sid )); + } + + data_size -= groups_size; + + if (data_size < req->priv_count * sizeof( privs[0] )) + { + free( groups ); + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + privs = (struct luid_attr *)((char *)attrs + groups_size); + data_size -= req->priv_count * sizeof( privs[0] ); + + if (data_size) + { + dacl = (struct acl *)((char *)privs + req->priv_count * sizeof(privs[0])); + if (!acl_is_valid( dacl, data_size )) + { + free( groups ); + set_error( STATUS_INVALID_PARAMETER ); + return; + } + } + else + dacl = default_dacl = create_default_dacl( &domain_users_sid ); + + token = create_token( req->primary, default_session_id, user, groups, req->group_count, + privs, req->priv_count, dacl, NULL, req->primary_group, req->impersonation_level, 0 ); + if (token) + reply->token = alloc_handle( current->process, token, req->access, objattr->attributes ); + + free( default_dacl ); + free( groups ); +} + + /* open a security token */ DECL_HANDLER(open_token) {
Is there anything else besides mentioned in the comments (and fixed in the new version of the patch) that needs to be addressed?