- Implement AdjustTokenPrivileges, DuplicateTokenEx and
GetTokenInformation (for the TokenPrivileges case).
- Return STATUS_NO_TOKEN for OpenThreadToken when there is no token
set for the thread.
diff --git a/server/object.h b/server/object.h
index e3c2a5d..c42b49c 100644
--- a/server/object.h
+++ b/server/object.h
@@ -153,7 +153,7 @@
/* token functions */
-extern struct token *create_token(void);
+extern struct token *create_admin_token(void);
/* atom functions */
diff --git a/server/process.c b/server/process.c
index fc69e50..36cf4c1 100644
--- a/server/process.c
+++ b/server/process.c
@@ -278,7 +278,7 @@
process->exe.namelen = 0;
process->exe.filename = NULL;
process->group_id = 0;
- process->token = create_token();
+ process->token = create_admin_token();
list_init( &process->locks );
list_init( &process->classes );
diff --git a/server/protocol.def b/server/protocol.def
index 94af8ad..1de2607 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2229,3 +2229,32 @@
#define SET_GLOBAL_SHELL_WINDOWS 0x01 /* set both main shell and listview windows */
#define SET_GLOBAL_PROGMAN_WINDOW 0x02
#define SET_GLOBAL_TASKMAN_WINDOW 0x04
+
+/* Adjust the privileges held by a token */
+@REQ(adjust_token_privileges)
+ obj_handle_t handle; /* handle to the token */
+ int disable_all; /* disable all privileges? */
+ int get_modified_state; /* get modified privileges? */
+ VARARG(privileges,LUID_AND_ATTRIBUTES); /* privileges to enable/disable/remove */
+@REPLY
+ unsigned int len; /* total length in bytes required to store token privileges */
+ VARARG(privileges,LUID_AND_ATTRIBUTES); /* modified privileges */
+@END
+
+/* Retrieves the set of privileges held by or available to a token */
+@REQ(get_token_privileges)
+ obj_handle_t handle; /* handle to the token */
+@REPLY
+ unsigned int len; /* total length in bytes required to store token 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 */
+ int inherit; /* inherit flag */
+ int primary; /* is the new token to be a primary one? */
+ int impersonation_level; /* impersonation level of the new token */
+@REPLY
+ obj_handle_t new_handle; /* duplicated handle */
+@END
diff --git a/server/request.h b/server/request.h
index 2b77919..3902c7b 100644
--- a/server/request.h
+++ b/server/request.h
@@ -283,6 +283,9 @@
DECL_HANDLER(set_clipboard_info);
DECL_HANDLER(open_token);
DECL_HANDLER(set_global_windows);
+DECL_HANDLER(adjust_token_privileges);
+DECL_HANDLER(get_token_privileges);
+DECL_HANDLER(duplicate_token);
#ifdef WANT_REQUEST_HANDLERS
@@ -469,6 +472,9 @@
(req_handler)req_set_clipboard_info,
(req_handler)req_open_token,
(req_handler)req_set_global_windows,
+ (req_handler)req_adjust_token_privileges,
+ (req_handler)req_get_token_privileges,
+ (req_handler)req_duplicate_token,
};
#endif /* WANT_REQUEST_HANDLERS */
diff --git a/server/token.c b/server/token.c
index 67d42b4..94a2167 100644
--- a/server/token.c
+++ b/server/token.c
@@ -35,9 +35,19 @@
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 =
{
@@ -48,7 +58,7 @@
NULL, /* signaled */
NULL, /* satified */
no_get_fd, /* get_fd */
- no_destroy /* destroy */
+ token_destroy /* destroy */
};
@@ -57,12 +67,163 @@
fprintf( stderr, "Security token\n" );
}
-struct token *create_token( void )
+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)
{
@@ -73,6 +234,8 @@
{
if (thread->token)
reply->token = alloc_handle( current->process, thread->token, TOKEN_ALL_ACCESS, 0);
+ else
+ set_error(STATUS_NO_TOKEN);
release_object( thread );
}
}
@@ -83,7 +246,117 @@
{
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 );
+ }
+}
diff --git a/server/trace.c b/server/trace.c
index b499013..bfd6919 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -407,6 +407,23 @@
remove_data( size );
}
+static void dump_varargs_LUID_AND_ATTRIBUTES( size_t size )
+{
+ const LUID_AND_ATTRIBUTES *lat = cur_data;
+ size_t len = size / sizeof(*lat);
+
+ fputc( '{', stderr );
+ while (len > 0)
+ {
+ fprintf( stderr, "{luid=%08lx%08lx,attr=%lx}",
+ lat->Luid.HighPart, lat->Luid.LowPart, lat->Attributes );
+ lat++;
+ if (--len) fputc( ',', stderr );
+ }
+ fputc( '}', stderr );
+ remove_data( size );
+}
+
typedef void (*dump_func)( const void *req );
/* Everything below this line is generated automatically by tools/make_requests */
@@ -2623,6 +2640,48 @@
fprintf( stderr, " old_taskman_window=%p", req->old_taskman_window );
}
+static void dump_adjust_token_privileges_request( const struct adjust_token_privileges_request *req )
+{
+ fprintf( stderr, " handle=%p,", req->handle );
+ fprintf( stderr, " disable_all=%d,", req->disable_all );
+ fprintf( stderr, " get_modified_state=%d,", req->get_modified_state );
+ fprintf( stderr, " privileges=" );
+ dump_varargs_LUID_AND_ATTRIBUTES( cur_size );
+}
+
+static void dump_adjust_token_privileges_reply( const struct adjust_token_privileges_reply *req )
+{
+ fprintf( stderr, " len=%08x,", req->len );
+ fprintf( stderr, " privileges=" );
+ dump_varargs_LUID_AND_ATTRIBUTES( cur_size );
+}
+
+static void dump_get_token_privileges_request( const struct get_token_privileges_request *req )
+{
+ fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_get_token_privileges_reply( const struct get_token_privileges_reply *req )
+{
+ fprintf( stderr, " len=%08x,", req->len );
+ 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 );
+ fprintf( stderr, " access=%08x,", req->access );
+ fprintf( stderr, " inherit=%d,", req->inherit );
+ fprintf( stderr, " primary=%d,", req->primary );
+ fprintf( stderr, " impersonation_level=%d", req->impersonation_level );
+}
+
+static void dump_duplicate_token_reply( const struct duplicate_token_reply *req )
+{
+ fprintf( stderr, " new_handle=%p", req->new_handle );
+}
+
static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_new_process_request,
(dump_func)dump_get_new_process_info_request,
@@ -2804,6 +2863,9 @@
(dump_func)dump_set_clipboard_info_request,
(dump_func)dump_open_token_request,
(dump_func)dump_set_global_windows_request,
+ (dump_func)dump_adjust_token_privileges_request,
+ (dump_func)dump_get_token_privileges_request,
+ (dump_func)dump_duplicate_token_request,
};
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -2987,6 +3049,9 @@
(dump_func)dump_set_clipboard_info_reply,
(dump_func)dump_open_token_reply,
(dump_func)dump_set_global_windows_reply,
+ (dump_func)dump_adjust_token_privileges_reply,
+ (dump_func)dump_get_token_privileges_reply,
+ (dump_func)dump_duplicate_token_reply,
};
static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -3170,6 +3235,9 @@
"set_clipboard_info",
"open_token",
"set_global_windows",
+ "adjust_token_privileges",
+ "get_token_privileges",
+ "duplicate_token",
};
/* ### make_requests end ### */