diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index 7fd2ffc9331..bcc3f451b36 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -376,6 +376,34 @@ NTSTATUS WINAPI NtAdjustGroupsToken( return STATUS_NOT_IMPLEMENTED; } +/****************************************************************************** +* NtPrivilegeCheck [NTDLL.@] +* ZwPrivilegeCheck [NTDLL.@] +*/ +NTSTATUS WINAPI NtPrivilegeCheck( + HANDLE ClientToken, + PPRIVILEGE_SET RequiredPrivileges, + PBOOLEAN Result) +{ + NTSTATUS status; + SERVER_START_REQ( check_token_privileges ) + { + req->handle = ClientToken; + req->all_required = ((RequiredPrivileges->Control & PRIVILEGE_SET_ALL_NECESSARY) ? TRUE : FALSE); + wine_server_add_data( req, &RequiredPrivileges->Privilege, + RequiredPrivileges->PrivilegeCount * sizeof(RequiredPrivileges->Privilege[0]) ); + wine_server_set_reply( req, &RequiredPrivileges->Privilege, + RequiredPrivileges->PrivilegeCount * sizeof(RequiredPrivileges->Privilege[0]) ); + + status = wine_server_call( req ); + + if (status == STATUS_SUCCESS) + *Result = (reply->has_privileges ? TRUE : FALSE); + } + SERVER_END_REQ; + return status; +} + /* * Section */ diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 0ef50d76805..317a528f73a 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -156,7 +156,7 @@ @ stdcall NtOpenTimer(ptr long ptr) @ stub NtPlugPlayControl @ stdcall NtPowerInformation(long ptr long ptr long) -@ stub NtPrivilegeCheck +@ stdcall NtPrivilegeCheck(ptr ptr ptr) @ stub NtPrivilegeObjectAuditAlarm @ stub NtPrivilegedServiceAuditAlarm @ stdcall NtProtectVirtualMemory(long ptr ptr long ptr) @@ -746,7 +746,7 @@ @ stdcall ZwOpenThreadToken(long long long long) NtOpenThreadToken @ stdcall ZwOpenTimer(ptr long ptr) NtOpenTimer @ stub ZwPlugPlayControl -@ stub ZwPrivilegeCheck +@ stdcall ZwPrivilegeCheck(ptr ptr ptr) NtPrivilegeCheck @ stub ZwPrivilegeObjectAuditAlarm @ stub ZwPrivilegedServiceAuditAlarm @ stdcall ZwProtectVirtualMemory(long ptr ptr long ptr) NtProtectVirtualMemory diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 7caeae4ce62..eb415561c9a 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3237,6 +3237,21 @@ struct get_token_privileges_reply /* VARARG(privileges,LUID_AND_ATTRIBUTES); */ }; + +struct check_token_privileges_request +{ + struct request_header __header; + obj_handle_t handle; + int all_required; + /* VARARG(privileges,LUID_AND_ATTRIBUTES); */ +}; +struct check_token_privileges_reply +{ + struct reply_header __header; + int has_privileges; + /* VARARG(privileges,LUID_AND_ATTRIBUTES); */ +}; + struct duplicate_token_request { struct request_header __header; @@ -3490,6 +3505,7 @@ enum request REQ_set_global_windows, REQ_adjust_token_privileges, REQ_get_token_privileges, + REQ_check_token_privileges, REQ_duplicate_token, REQ_create_mailslot, REQ_open_mailslot, @@ -3685,6 +3701,7 @@ union generic_request struct set_global_windows_request set_global_windows_request; struct adjust_token_privileges_request adjust_token_privileges_request; struct get_token_privileges_request get_token_privileges_request; + struct check_token_privileges_request check_token_privileges_request; struct duplicate_token_request duplicate_token_request; struct create_mailslot_request create_mailslot_request; struct open_mailslot_request open_mailslot_request; @@ -3878,12 +3895,13 @@ union generic_reply struct set_global_windows_reply set_global_windows_reply; struct adjust_token_privileges_reply adjust_token_privileges_reply; struct get_token_privileges_reply get_token_privileges_reply; + struct check_token_privileges_reply check_token_privileges_reply; struct duplicate_token_reply duplicate_token_reply; struct create_mailslot_reply create_mailslot_reply; struct open_mailslot_reply open_mailslot_reply; struct set_mailslot_info_reply set_mailslot_info_reply; }; -#define SERVER_PROTOCOL_VERSION 169 +#define SERVER_PROTOCOL_VERSION 170 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/include/winnt.h b/include/winnt.h index 93ae35a0bcc..d8a99d9e40d 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -2823,6 +2823,8 @@ typedef struct _ACL_SIZE_INFORMATION #define SE_PRIVILEGE_REMOVE 0x00000004 #define SE_PRIVILEGE_USED_FOR_ACCESS 0x80000000 +#define PRIVILEGE_SET_ALL_NECESSARY 1 + #define SE_OWNER_DEFAULTED 0x00000001 #define SE_GROUP_DEFAULTED 0x00000002 #define SE_DACL_PRESENT 0x00000004 diff --git a/include/winternl.h b/include/winternl.h index b16efa498da..23cd84927e1 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1469,6 +1469,7 @@ NTSTATUS WINAPI NtOpenThread(HANDLE*,ACCESS_MASK,const OBJECT_ATTRIBUTES*,const NTSTATUS WINAPI NtOpenThreadToken(HANDLE,DWORD,BOOLEAN,HANDLE *); NTSTATUS WINAPI NtOpenTimer(HANDLE*, ACCESS_MASK, const OBJECT_ATTRIBUTES*); NTSTATUS WINAPI NtPowerInformation(POWER_INFORMATION_LEVEL,PVOID,ULONG,PVOID,ULONG); +NTSTATUS WINAPI NtPrivilegeCheck(HANDLE,PPRIVILEGE_SET,PBOOLEAN); NTSTATUS WINAPI NtProtectVirtualMemory(HANDLE,PVOID*,ULONG*,ULONG,ULONG*); NTSTATUS WINAPI NtPulseEvent(HANDLE,PULONG); NTSTATUS WINAPI NtQueueApcThread(HANDLE,PNTAPCFUNC,ULONG_PTR,ULONG_PTR,ULONG_PTR); diff --git a/server/object.h b/server/object.h index 23dd4b58849..44dfe18b93b 100644 --- a/server/object.h +++ b/server/object.h @@ -149,10 +149,6 @@ extern void registry_close_handle( struct object *obj, obj_handle_t hkey ); extern void init_signals(void); extern void close_signals(void); -/* token functions */ - -extern struct token *create_admin_token(void); - /* atom functions */ extern void close_atom_table(void); diff --git a/server/process.c b/server/process.c index c89fa1ce87c..12f9a1e1191 100644 --- a/server/process.c +++ b/server/process.c @@ -48,6 +48,7 @@ #include "request.h" #include "console.h" #include "user.h" +#include "security.h" /* process structure */ @@ -284,7 +285,7 @@ struct thread *create_process( int fd ) process->exe.namelen = 0; process->exe.filename = NULL; process->group_id = 0; - process->token = create_admin_token(); + process->token = token_create_admin(); list_init( &process->thread_list ); list_init( &process->locks ); list_init( &process->classes ); diff --git a/server/protocol.def b/server/protocol.def index 7825a584e48..3427fd070c0 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2275,6 +2275,16 @@ enum message_type VARARG(privileges,LUID_AND_ATTRIBUTES); /* privileges held by or available to a token */ @END +/* Check the token has the required privileges */ +@REQ(check_token_privileges) + obj_handle_t handle; /* handle to the token */ + int all_required; /* are all the privileges required for the check to succeed? */ + VARARG(privileges,LUID_AND_ATTRIBUTES); /* privileges to check */ +@REPLY + int has_privileges; /* does the token have the required privileges? */ + VARARG(privileges,LUID_AND_ATTRIBUTES); /* privileges held by or available to a token */ +@END + @REQ(duplicate_token) obj_handle_t handle; /* handle to the token to duplicate */ unsigned int access; /* access rights to the new token */ diff --git a/server/request.h b/server/request.h index 5d314db61db..49228dc9758 100644 --- a/server/request.h +++ b/server/request.h @@ -287,6 +287,7 @@ DECL_HANDLER(open_token); DECL_HANDLER(set_global_windows); DECL_HANDLER(adjust_token_privileges); DECL_HANDLER(get_token_privileges); +DECL_HANDLER(check_token_privileges); DECL_HANDLER(duplicate_token); DECL_HANDLER(create_mailslot); DECL_HANDLER(open_mailslot); @@ -481,6 +482,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_global_windows, (req_handler)req_adjust_token_privileges, (req_handler)req_get_token_privileges, + (req_handler)req_check_token_privileges, (req_handler)req_duplicate_token, (req_handler)req_create_mailslot, (req_handler)req_open_mailslot, diff --git a/server/security.h b/server/security.h new file mode 100644 index 00000000000..0be5f20dd0c --- /dev/null +++ b/server/security.h @@ -0,0 +1,55 @@ +/* + * Security Management + * + * Copyright (C) 2005 Robert Shearman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern const LUID SeIncreaseQuotaPrivilege; +extern const LUID SeSecurityPrivilege; +extern const LUID SeTakeOwnershipPrivilege; +extern const LUID SeLoadDriverPrivilege; +extern const LUID SeSystemProfilePrivilege; +extern const LUID SeSystemtimePrivilege; +extern const LUID SeProfileSingleProcessPrivilege; +extern const LUID SeIncreaseBasePriorityPrivilege; +extern const LUID SeCreatePagefilePrivilege; +extern const LUID SeBackupPrivilege; +extern const LUID SeRestorePrivilege; +extern const LUID SeShutdownPrivilege; +extern const LUID SeDebugPrivilege; +extern const LUID SeSystemEnvironmentPrivilege; +extern const LUID SeChangeNotifyPrivilege; +extern const LUID SeRemoteShutdownPrivilege; +extern const LUID SeUndockPrivilege; +extern const LUID SeManageVolumePrivilege; +extern const LUID SeImpersonatePrivilege; +extern const LUID SeCreateGlobalPrivilege; + +extern struct token *token_create_admin(void); +extern int token_check_privileges( struct token *token, int all_required, + const LUID_AND_ATTRIBUTES *reqprivs, + unsigned int count, LUID_AND_ATTRIBUTES *usedprivs); + +static inline int thread_single_check_privilege( struct thread *thread, const LUID *priv) +{ + struct token *token = thread_get_impersonation_token( thread ); + const LUID_AND_ATTRIBUTES privs = { *priv, 0 }; + + if (!token) return FALSE; + + return token_check_privileges( token, TRUE, &privs, 1, NULL ); +} diff --git a/server/thread.c b/server/thread.c index 059aec4587d..af6657efca2 100644 --- a/server/thread.c +++ b/server/thread.c @@ -767,6 +767,15 @@ struct thread_snapshot *thread_snap( int *count ) return snapshot; } +/* gets the current impersonation token */ +struct token *thread_get_impersonation_token( struct thread *thread ) +{ + if (thread->token) + return thread->token; + else + return thread->process->token; +} + /* signal that we are finished booting on the client side */ DECL_HANDLER(boot_done) { diff --git a/server/thread.h b/server/thread.h index d13199b6ffc..d471686bb8c 100644 --- a/server/thread.h +++ b/server/thread.h @@ -114,6 +114,7 @@ extern void thread_cancel_apc( struct thread *thread, struct object *owner, int extern int thread_add_inflight_fd( struct thread *thread, int client, int server ); extern int thread_get_inflight_fd( struct thread *thread, int client ); extern struct thread_snapshot *thread_snap( int *count ); +extern struct token *thread_get_impersonation_token( struct thread *thread ); /* ptrace functions */ diff --git a/server/token.c b/server/token.c index 94a2167622b..23fe27cd852 100644 --- a/server/token.c +++ b/server/token.c @@ -3,6 +3,7 @@ * * Copyright (C) 1998 Alexandre Julliard * Copyright (C) 2003 Mike McCormack + * Copyright (C) 2005 Robert Shearman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -31,6 +32,28 @@ #include "thread.h" #include "process.h" #include "request.h" +#include "security.h" + +const LUID SeIncreaseQuotaPrivilege = { 5, 0 }; +const LUID SeSecurityPrivilege = { 8, 0 }; +const LUID SeTakeOwnershipPrivilege = { 9, 0 }; +const LUID SeLoadDriverPrivilege = { 10, 0 }; +const LUID SeSystemProfilePrivilege = { 11, 0 }; +const LUID SeSystemtimePrivilege = { 12, 0 }; +const LUID SeProfileSingleProcessPrivilege = { 13, 0 }; +const LUID SeIncreaseBasePriorityPrivilege = { 14, 0 }; +const LUID SeCreatePagefilePrivilege = { 15, 0 }; +const LUID SeBackupPrivilege = { 17, 0 }; +const LUID SeRestorePrivilege = { 18, 0 }; +const LUID SeShutdownPrivilege = { 19, 0 }; +const LUID SeDebugPrivilege = { 20, 0 }; +const LUID SeSystemEnvironmentPrivilege = { 22, 0 }; +const LUID SeChangeNotifyPrivilege = { 23, 0 }; +const LUID SeRemoteShutdownPrivilege = { 24, 0 }; +const LUID SeUndockPrivilege = { 25, 0 }; +const LUID SeManageVolumePrivilege = { 28, 0 }; +const LUID SeImpersonatePrivilege = { 29, 0 }; +const LUID SeCreateGlobalPrivilege = { 30, 0 }; struct token { @@ -135,30 +158,30 @@ static struct token *create_token( const LUID_AND_ATTRIBUTES *privs, unsigned in return token; } -struct token *create_admin_token( void ) +struct token *token_create_admin( void ) { - static const LUID_AND_ATTRIBUTES admin_privs[] = + const LUID_AND_ATTRIBUTES admin_privs[] = { - { { 23, 0 }, SE_PRIVILEGE_ENABLED }, /* SeChangeNotifyPrivilege */ - { { 8, 0 }, 0 }, /* SeSecurityPrivilege */ - { { 17, 0 }, 0 }, /* SeBackupPrivilege */ - { { 18, 0 }, 0 }, /* SeRestorePrivilege */ - { { 12, 0 }, 0 }, /* SeSystemtimePrivilege */ - { { 19, 0 }, 0 }, /* SeShutdownPrivilege */ - { { 24, 0 }, 0 }, /* SeRemoteShutdownPrivilege */ - { { 9, 0 }, 0 }, /* SeTakeOwnershipPrivilege */ - { { 20, 0 }, 0 }, /* SeDebugPrivilege */ - { { 22, 0 }, 0 }, /* SeSystemEnvironmentPrivilege */ - { { 11, 0 }, 0 }, /* SeSystemProfilePrivilege */ - { { 13, 0 }, 0 }, /* SeProfileSingleProcessPrivilege */ - { { 14, 0 }, 0 }, /* SeIncreaseBasePriorityPrivilege */ - { { 10, 0 }, 0 }, /* SeLoadDriverPrivilege */ - { { 15, 0 }, 0 }, /* SeCreatePagefilePrivilege */ - { { 5, 0 }, 0 }, /* SeIncreaseQuotaPrivilege */ - { { 25, 0 }, 0 }, /* SeUndockPrivilege */ - { { 28, 0 }, 0 }, /* SeManageVolumePrivilege */ - { { 29, 0 }, SE_PRIVILEGE_ENABLED }, /* SeImpersonatePrivilege */ - { { 30, 0 }, SE_PRIVILEGE_ENABLED }, /* SeCreateGlobalPrivilege */ + { SeChangeNotifyPrivilege , SE_PRIVILEGE_ENABLED }, + { SeSecurityPrivilege , 0 }, + { SeBackupPrivilege , 0 }, + { SeRestorePrivilege , 0 }, + { SeSystemtimePrivilege , 0 }, + { SeShutdownPrivilege , 0 }, + { SeRemoteShutdownPrivilege , 0 }, + { SeTakeOwnershipPrivilege , 0 }, + { SeDebugPrivilege , 0 }, + { SeSystemEnvironmentPrivilege , 0 }, + { SeSystemProfilePrivilege , 0 }, + { SeProfileSingleProcessPrivilege, 0 }, + { SeIncreaseBasePriorityPrivilege, 0 }, + { SeLoadDriverPrivilege , 0 }, + { SeCreatePagefilePrivilege , 0 }, + { SeIncreaseQuotaPrivilege , 0 }, + { SeUndockPrivilege , 0 }, + { SeManageVolumePrivilege , 0 }, + { SeImpersonatePrivilege , SE_PRIVILEGE_ENABLED }, + { SeCreateGlobalPrivilege , SE_PRIVILEGE_ENABLED }, }; return create_token( admin_privs, sizeof(admin_privs)/sizeof(admin_privs[0]) ); } @@ -224,6 +247,35 @@ static void token_disable_privileges( struct token *token ) privilege->enabled = FALSE; } +int token_check_privileges( struct token *token, int all_required, + const LUID_AND_ATTRIBUTES *reqprivs, + unsigned int count, LUID_AND_ATTRIBUTES *usedprivs) +{ + int i; + unsigned int enabled_count = 0; + + for (i = 0; i < count; i++) + { + struct privilege *privilege = + token_find_privilege( token, &reqprivs[i].Luid, TRUE ); + + if (usedprivs) + usedprivs[i] = reqprivs[i]; + + if (privilege && privilege->enabled) + { + enabled_count++; + if (usedprivs) + usedprivs[i].Attributes |= SE_PRIVILEGE_USED_FOR_ACCESS; + } + } + + if (all_required) + return (enabled_count == count); + else + return (enabled_count > 0); +} + /* open a security token */ DECL_HANDLER(open_token) { @@ -360,3 +412,24 @@ DECL_HANDLER(duplicate_token) release_object( src_token ); } } + +/* checks the specified privileges are held by the token */ +DECL_HANDLER(check_token_privileges) +{ + struct token *token; + + if ((token = (struct token *)get_handle_obj( current->process, req->handle, + TOKEN_QUERY, + &token_ops ))) + { + unsigned int count = get_req_data_size() / sizeof(LUID_AND_ATTRIBUTES); + if (get_reply_max_size() >= count * sizeof(LUID_AND_ATTRIBUTES)) + { + LUID_AND_ATTRIBUTES *usedprivs = set_reply_data_size( count * sizeof(*usedprivs) ); + reply->has_privileges = token_check_privileges( token, req->all_required, get_req_data(), count, usedprivs ); + } + else + set_error( STATUS_BUFFER_OVERFLOW ); + release_object( token ); + } +} diff --git a/server/trace.c b/server/trace.c index 898b000f59d..d12c962c040 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2689,6 +2689,21 @@ static void dump_get_token_privileges_reply( const struct get_token_privileges_r dump_varargs_LUID_AND_ATTRIBUTES( cur_size ); } +static void dump_check_token_privileges_request( const struct check_token_privileges_request *req ) +{ + fprintf( stderr, " handle=%p,", req->handle ); + fprintf( stderr, " all_required=%d,", req->all_required ); + fprintf( stderr, " privileges=" ); + dump_varargs_LUID_AND_ATTRIBUTES( cur_size ); +} + +static void dump_check_token_privileges_reply( const struct check_token_privileges_reply *req ) +{ + fprintf( stderr, " has_privileges=%d,", req->has_privileges ); + fprintf( stderr, " privileges=" ); + dump_varargs_LUID_AND_ATTRIBUTES( cur_size ); +} + static void dump_duplicate_token_request( const struct duplicate_token_request *req ) { fprintf( stderr, " handle=%p,", req->handle ); @@ -2931,6 +2946,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_global_windows_request, (dump_func)dump_adjust_token_privileges_request, (dump_func)dump_get_token_privileges_request, + (dump_func)dump_check_token_privileges_request, (dump_func)dump_duplicate_token_request, (dump_func)dump_create_mailslot_request, (dump_func)dump_open_mailslot_request, @@ -3122,6 +3138,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_global_windows_reply, (dump_func)dump_adjust_token_privileges_reply, (dump_func)dump_get_token_privileges_reply, + (dump_func)dump_check_token_privileges_reply, (dump_func)dump_duplicate_token_reply, (dump_func)dump_create_mailslot_reply, (dump_func)dump_open_mailslot_reply, @@ -3313,6 +3330,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "set_global_windows", "adjust_token_privileges", "get_token_privileges", + "check_token_privileges", "duplicate_token", "create_mailslot", "open_mailslot",