/* * 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 #include #include #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; 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, /* 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 ); } }