| /* |
| * Tokens |
| * |
| * Copyright (C) 1998 Alexandre Julliard |
| * Copyright (C) 2003 Mike McCormack |
| * |
| * 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" |
| |
| struct token |
| { |
| struct object obj; /* object header */ |
| struct list privileges; /* privileges available to the token */ |
| }; |
| |
| struct privilege |
| { |
| struct list entry; |
| LUID luid; |
| int enabled : 1; /* is the privilege currently enabled? */ |
| int 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, /* satified */ |
| 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 *create_admin_token( void ) |
| { |
| static 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 */ |
| }; |
| 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; |
| } |
| |
| /* 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 ); |
| } |
| } |