| /* |
| * Tokens |
| * |
| * 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 |
| * 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 |
| */ |
| |
| #include "config.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "windef.h" |
| |
| #include "handle.h" |
| #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 |
| { |
| struct object obj; /* object header */ |
| struct list privileges; /* privileges available to the token */ |
| }; |
| |
| struct privilege |
| { |
| struct list entry; |
| LUID luid; |
| unsigned enabled : 1; /* is the privilege currently enabled? */ |
| unsigned def : 1; /* is the privilege enabled by default? */ |
| }; |
| |
| static void token_dump( struct object *obj, int verbose ); |
| static void token_destroy( struct object *obj ); |
| |
| static const struct object_ops token_ops = |
| { |
| sizeof(struct token), /* size */ |
| token_dump, /* dump */ |
| no_add_queue, /* add_queue */ |
| NULL, /* remove_queue */ |
| NULL, /* signaled */ |
| NULL, /* satisfied */ |
| no_signal, /* signal */ |
| no_get_fd, /* get_fd */ |
| token_destroy /* destroy */ |
| }; |
| |
| |
| static void token_dump( struct object *obj, int verbose ) |
| { |
| fprintf( stderr, "Security token\n" ); |
| } |
| |
| static inline int is_equal_luid( const LUID *luid1, const LUID *luid2 ) |
| { |
| return (luid1->LowPart == luid2->LowPart && luid1->HighPart == luid2->HighPart); |
| } |
| |
| static inline void luid_and_attr_from_privilege( LUID_AND_ATTRIBUTES *out, const struct privilege *in) |
| { |
| out->Luid = in->luid; |
| out->Attributes = |
| (in->enabled ? SE_PRIVILEGE_ENABLED : 0) | |
| (in->def ? SE_PRIVILEGE_ENABLED_BY_DEFAULT : 0); |
| } |
| |
| static struct privilege *privilege_add( struct token *token, const LUID *luid, int enabled ) |
| { |
| struct privilege *privilege = mem_alloc( sizeof(*privilege) ); |
| if (privilege) |
| { |
| privilege->luid = *luid; |
| privilege->def = privilege->enabled = (enabled != 0); |
| list_add_tail( &token->privileges, &privilege->entry ); |
| } |
| return privilege; |
| } |
| |
| static void privilege_remove( struct privilege *privilege ) |
| { |
| list_remove( &privilege->entry ); |
| free( privilege ); |
| } |
| |
| static void token_destroy( struct object *obj ) |
| { |
| struct token* token; |
| struct list *cursor, *cursor_next; |
| |
| assert( obj->ops == &token_ops ); |
| token = (struct token *)obj; |
| |
| LIST_FOR_EACH_SAFE( cursor, cursor_next, &token->privileges ) |
| { |
| struct privilege *privilege = LIST_ENTRY( cursor, struct privilege, entry ); |
| privilege_remove( privilege ); |
| } |
| } |
| |
| static struct token *create_token( const LUID_AND_ATTRIBUTES *privs, unsigned int priv_count ) |
| { |
| struct token *token = alloc_object( &token_ops ); |
| if (token) |
| { |
| int i; |
| list_init( &token->privileges ); |
| for (i = 0; i < priv_count; i++) |
| { |
| /* note: we don't check uniqueness: the caller must make sure |
| * privs doesn't contain any duplicate luids */ |
| if (!privilege_add( token, &privs[i].Luid, |
| privs[i].Attributes & SE_PRIVILEGE_ENABLED )) |
| { |
| release_object( token ); |
| return NULL; |
| } |
| } |
| } |
| return token; |
| } |
| |
| struct token *token_create_admin( void ) |
| { |
| const LUID_AND_ATTRIBUTES admin_privs[] = |
| { |
| { 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]) ); |
| } |
| |
| static struct privilege *token_find_privilege( struct token *token, const LUID *luid, int enabled_only) |
| { |
| struct privilege *privilege; |
| LIST_FOR_EACH_ENTRY( privilege, &token->privileges, struct privilege, entry ) |
| { |
| if (is_equal_luid( luid, &privilege->luid )) |
| { |
| if (enabled_only && !privilege->enabled) |
| return NULL; |
| return privilege; |
| } |
| } |
| return NULL; |
| } |
| |
| static unsigned int token_adjust_privileges( struct token *token, const LUID_AND_ATTRIBUTES *privs, |
| unsigned int count, LUID_AND_ATTRIBUTES *mod_privs, |
| unsigned int mod_privs_count) |
| { |
| int i; |
| unsigned int modified_count = 0; |
| |
| for (i = 0; i < count; i++) |
| { |
| struct privilege *privilege = |
| token_find_privilege( token, &privs[i].Luid, FALSE ); |
| if (!privilege) |
| { |
| set_error( STATUS_NOT_ALL_ASSIGNED ); |
| continue; |
| } |
| |
| if (privs[i].Attributes & SE_PRIVILEGE_REMOVE) |
| privilege_remove( privilege ); |
| else |
| { |
| /* save previous state for caller */ |
| if (mod_privs_count) |
| { |
| luid_and_attr_from_privilege(mod_privs, privilege); |
| mod_privs++; |
| mod_privs_count--; |
| modified_count++; |
| } |
| |
| if (privs[i].Attributes & SE_PRIVILEGE_ENABLED) |
| privilege->enabled = TRUE; |
| else |
| privilege->enabled = FALSE; |
| } |
| } |
| return modified_count; |
| } |
| |
| static void token_disable_privileges( struct token *token ) |
| { |
| struct privilege *privilege; |
| LIST_FOR_EACH_ENTRY( privilege, &token->privileges, struct privilege, entry ) |
| 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) |
| { |
| if( req->flags & OPEN_TOKEN_THREAD ) |
| { |
| struct thread *thread = get_thread_from_handle( req->handle, 0 ); |
| if (thread) |
| { |
| if (thread->token) |
| reply->token = alloc_handle( current->process, thread->token, TOKEN_ALL_ACCESS, 0); |
| else |
| set_error(STATUS_NO_TOKEN); |
| release_object( thread ); |
| } |
| } |
| else |
| { |
| struct process *process = get_process_from_handle( req->handle, 0 ); |
| if (process) |
| { |
| if (process->token) |
| reply->token = alloc_handle( current->process, process->token, TOKEN_ALL_ACCESS, 0); |
| else |
| set_error(STATUS_NO_TOKEN); |
| release_object( process ); |
| } |
| } |
| } |
| |
| /* adjust the privileges held by a token */ |
| DECL_HANDLER(adjust_token_privileges) |
| { |
| struct token *token; |
| unsigned int access = TOKEN_ADJUST_PRIVILEGES; |
| |
| if (req->get_modified_state) access |= TOKEN_QUERY; |
| |
| if ((token = (struct token *)get_handle_obj( current->process, req->handle, |
| access, &token_ops ))) |
| { |
| const LUID_AND_ATTRIBUTES *privs = get_req_data(); |
| LUID_AND_ATTRIBUTES *modified_privs = NULL; |
| unsigned int priv_count = get_req_data_size() / sizeof(LUID_AND_ATTRIBUTES); |
| unsigned int modified_priv_count = 0; |
| |
| if (req->get_modified_state && !req->disable_all) |
| { |
| int i; |
| /* count modified privs */ |
| for (i = 0; i < priv_count; i++) |
| { |
| struct privilege *privilege = |
| token_find_privilege( token, &privs[i].Luid, FALSE ); |
| if (privilege && req->get_modified_state) |
| modified_priv_count++; |
| } |
| reply->len = modified_priv_count; |
| modified_priv_count = min( modified_priv_count, get_reply_max_size() / sizeof(*modified_privs) ); |
| if (modified_priv_count) |
| modified_privs = set_reply_data_size( modified_priv_count * sizeof(*modified_privs) ); |
| } |
| reply->len = modified_priv_count * sizeof(*modified_privs); |
| |
| if (req->disable_all) |
| token_disable_privileges( token ); |
| else |
| modified_priv_count = token_adjust_privileges( token, privs, |
| priv_count, modified_privs, modified_priv_count ); |
| |
| release_object( token ); |
| } |
| } |
| |
| /* retrieves the list of privileges that may be held be the token */ |
| DECL_HANDLER(get_token_privileges) |
| { |
| struct token *token; |
| |
| if ((token = (struct token *)get_handle_obj( current->process, req->handle, |
| TOKEN_QUERY, |
| &token_ops ))) |
| { |
| int priv_count = 0; |
| LUID_AND_ATTRIBUTES *privs; |
| struct privilege *privilege; |
| |
| LIST_FOR_EACH_ENTRY( privilege, &token->privileges, struct privilege, entry ) |
| priv_count++; |
| |
| reply->len = priv_count * sizeof(*privs); |
| if (reply->len <= get_reply_max_size()) |
| { |
| privs = set_reply_data_size( priv_count * sizeof(*privs) ); |
| if (privs) |
| { |
| int i = 0; |
| LIST_FOR_EACH_ENTRY( privilege, &token->privileges, struct privilege, entry ) |
| { |
| luid_and_attr_from_privilege( &privs[i], privilege ); |
| i++; |
| } |
| } |
| } |
| else |
| set_error(STATUS_BUFFER_TOO_SMALL); |
| |
| release_object( token ); |
| } |
| } |
| |
| /* creates a duplicate of the token */ |
| DECL_HANDLER(duplicate_token) |
| { |
| struct token *src_token; |
| if ((src_token = (struct token *)get_handle_obj( current->process, req->handle, |
| TOKEN_DUPLICATE, |
| &token_ops ))) |
| { |
| /* FIXME: use req->primary and req->impersonation_level */ |
| struct token *token = create_token( NULL, 0 ); |
| if (token) |
| { |
| struct privilege *privilege; |
| unsigned int access; |
| |
| LIST_FOR_EACH_ENTRY( privilege, &src_token->privileges, struct privilege, entry ) |
| privilege_add( token, &privilege->luid, privilege->enabled ); |
| |
| access = req->access; |
| if (access & MAXIMUM_ALLOWED) access = TOKEN_ALL_ACCESS; /* FIXME: needs general solution */ |
| reply->new_handle = alloc_handle( current->process, token, access, req->inherit); |
| release_object( 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 ); |
| } |
| } |