[PATCH 3/4] ntdll: Implement NtCreateToken.

Hans Leidekker hans at codeweavers.com
Wed May 2 03:45:43 CDT 2018


Signed-off-by: Hans Leidekker <hans at 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 */
+ at 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 */
+ at REPLY
+    obj_handle_t   token;                   /* handle to the token */
+ at 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)
-- 
2.11.0




More information about the wine-devel mailing list