v2: Validate request data.
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/ntdll/nt.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/ntdll.spec | 2 +-
server/protocol.def | 15 ++++++
server/token.c | 119 +++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 258 insertions(+), 7 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..172a3414ab 100644
--- a/server/token.c
+++ b/server/token.c
@@ -221,6 +221,11 @@ const SID *security_unix_uid_to_sid( uid_t uid )
return &anonymous_logon_sid;
}
+static inline int sid_is_valid( const SID *sid, data_size_t size )
+{
+ return (size >= FIELD_OFFSET( SID, SubAuthority[0] ) && size >= security_sid_len( sid ));
+}
+
static int acl_is_valid( const ACL *acl, data_size_t size )
{
ULONG i;
@@ -269,8 +274,7 @@ static int acl_is_valid( const ACL *acl, data_size_t size )
default:
return FALSE;
}
- if (sid_size < FIELD_OFFSET(SID, SubAuthority[0]) || sid_size < security_sid_len( sid ))
- return FALSE;
+ if (!sid_is_valid( sid, sid_size )) return FALSE;
ace = ace_next( ace );
}
return TRUE;
@@ -326,7 +330,6 @@ int sd_is_valid( const struct security_descriptor *sd, data_size_t size )
dacl = sd_get_dacl( sd, &dummy );
if (dacl && !acl_is_valid( dacl, sd->dacl_len ))
return FALSE;
- offset += sd->dacl_len;
return TRUE;
}
@@ -527,7 +530,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 +641,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 +839,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 +1176,110 @@ 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;
+ const unsigned int *attr;
+ unsigned int i;
+ const SID *sid;
+
+ if (!objattr) return;
+
+ privs = get_req_data_after_objattr( objattr, &data_size );
+ privs_size = req->privilege_count * sizeof(*privs);
+ if (privs_size > data_size)
+ {
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+ data_size -= privs_size;
+
+ if (req->default_dacl_size)
+ {
+ acl = (const ACL *)((const char *)privs + privs_size);
+ if (!acl_is_valid( acl, req->default_dacl_size ))
+ {
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+ }
+ data_size -= req->default_dacl_size;
+
+ groups = (const struct token_groups *)((const char *)privs + privs_size + req->default_dacl_size);
+ if (data_size < sizeof(*groups) || !groups->count ||
+ sizeof(*groups) + groups->count * sizeof(*attr) > data_size ||
+ req->primary_group_index < 1 || req->primary_group_index >= groups->count ||
+ req->owner_index >= groups->count)
+ {
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+ attr = (const unsigned int *)(groups + 1);
+ data_size -= sizeof(*groups) + groups->count * sizeof(*attr);
+
+ sid = (const SID *)(attr + groups->count);
+ for (i = 0; i < groups->count; i++)
+ {
+ if (!sid_is_valid( sid, data_size ))
+ {
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+ data_size -= security_sid_len( sid );
+ sid = (const SID *)((const char *)sid + security_sid_len( sid ));
+ }
+
+ if (!acl) acl = default_dacl = create_default_dacl( sid );
+ sid = (const SID *)(attr + groups->count); /* first entry is user */
+
+ if ((token = token_create( req->primary, sid, NULL, 0, privs, req->privilege_count, acl, source, NULL,
+ SecurityIdentification )))
+ {
+ sid = (const SID *)((const char *)sid + security_sid_len( sid )); /* skip user */
+ attr++;
+ for (i = 1; i < groups->count; i++)
+ {
+ struct group *group;
+ if (!(group = mem_alloc( FIELD_OFFSET(struct group, sid.SubAuthority[sid->SubAuthorityCount]) )))
+ {
+ release_object( token );
+ return;
+ }
+
+ memcpy( &group->sid, sid, security_sid_len( sid ) );
+ 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 *)((const char *)sid + security_sid_len( sid ));
+ 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