- 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/dlls/advapi32/security.c b/dlls/advapi32/security.c
index fe4c962..6b12d94 100644
--- a/dlls/advapi32/security.c
+++ b/dlls/advapi32/security.c
@@ -238,9 +238,18 @@
                        LPVOID NewState, DWORD BufferLength,
                        LPVOID PreviousState, LPDWORD ReturnLength )
 {
-	return set_ntstatus( NtAdjustPrivilegesToken(TokenHandle, DisableAllPrivileges,
+    NTSTATUS status;
+
+    TRACE("\n");
+    
+    status = NtAdjustPrivilegesToken(TokenHandle, DisableAllPrivileges,
                                                      NewState, BufferLength, PreviousState,
-                                                     ReturnLength));
+                                                     ReturnLength);
+    SetLastError( RtlNtStatusToDosError( status ));
+    if ((status == STATUS_SUCCESS) || (status == STATUS_NOT_ALL_ASSIGNED))
+        return TRUE;
+    else
+        return FALSE;
 }
 
 /******************************************************************************
@@ -2996,10 +3005,24 @@
         TOKEN_TYPE TokenType,
         PHANDLE DuplicateTokenHandle )
 {
-    FIXME("%p 0x%08lx 0x%08x 0x%08x %p - stub\n", ExistingTokenHandle, dwDesiredAccess,
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
+    TRACE("%p 0x%08lx 0x%08x 0x%08x %p\n", ExistingTokenHandle, dwDesiredAccess,
           ImpersonationLevel, TokenType, DuplicateTokenHandle);
 
-    return FALSE;
+    InitializeObjectAttributes(
+        &ObjectAttributes,
+        NULL,
+        (lpTokenAttributes && lpTokenAttributes->bInheritHandle) ? OBJ_INHERIT : 0,
+        NULL,
+        lpTokenAttributes ? lpTokenAttributes->lpSecurityDescriptor : NULL );
+
+    return set_ntstatus( NtDuplicateToken( ExistingTokenHandle,
+                                           dwDesiredAccess,
+                                           &ObjectAttributes,
+                                           ImpersonationLevel,
+                                           TokenType,
+                                           DuplicateTokenHandle ) );
 }
 
 BOOL WINAPI DuplicateToken(
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c
index c359707..e70bf27 100644
--- a/dlls/ntdll/nt.c
+++ b/dlls/ntdll/nt.c
@@ -88,11 +88,26 @@
         IN TOKEN_TYPE TokenType,
         OUT PHANDLE NewToken)
 {
-	FIXME("(%p,0x%08lx,%p,0x%08x,0x%08x,%p),stub!\n",
-	ExistingToken, DesiredAccess, ObjectAttributes,
-	ImpersonationLevel, TokenType, NewToken);
-	dump_ObjectAttributes(ObjectAttributes);
-	return 0;
+    NTSTATUS status;
+
+    TRACE("(%p,0x%08lx,%p,0x%08x,0x%08x,%p)\n",
+        ExistingToken, DesiredAccess, ObjectAttributes,
+        ImpersonationLevel, TokenType, NewToken);
+        dump_ObjectAttributes(ObjectAttributes);
+
+    SERVER_START_REQ( duplicate_token )
+    {
+        req->handle = ExistingToken;
+        req->access = DesiredAccess;
+        req->inherit = ObjectAttributes && (ObjectAttributes->Attributes & OBJ_INHERIT);
+        req->primary = (TokenType == TokenPrimary);
+        req->impersonation_level = ImpersonationLevel;
+        status = wine_server_call( req );
+        if (!status) *NewToken = reply->new_handle;
+    }
+    SERVER_END_REQ;
+
+    return status;
 }
 
 /******************************************************************************
@@ -162,9 +177,34 @@
 	OUT PTOKEN_PRIVILEGES PreviousState,
 	OUT PDWORD ReturnLength)
 {
-	FIXME("(%p,0x%08x,%p,0x%08lx,%p,%p),stub!\n",
-	TokenHandle, DisableAllPrivileges, NewState, BufferLength, PreviousState, ReturnLength);
-	return 0;
+    NTSTATUS ret;
+
+    TRACE("(%p,0x%08x,%p,0x%08lx,%p,%p)\n",
+        TokenHandle, DisableAllPrivileges, NewState, BufferLength, PreviousState, ReturnLength);
+
+    SERVER_START_REQ( adjust_token_privileges )
+    {
+        req->handle = TokenHandle;
+        req->disable_all = DisableAllPrivileges;
+        req->get_modified_state = (PreviousState != NULL);
+        if (!DisableAllPrivileges)
+        {
+            wine_server_add_data( req, &NewState->Privileges,
+                                  NewState->PrivilegeCount * sizeof(NewState->Privileges[0]) );
+        }
+        if (PreviousState && BufferLength >= FIELD_OFFSET( TOKEN_PRIVILEGES, Privileges ))
+            wine_server_set_reply( req, &PreviousState->Privileges,
+                                   BufferLength - FIELD_OFFSET( TOKEN_PRIVILEGES, Privileges ) );
+        ret = wine_server_call( req );
+        if (PreviousState)
+        {
+            *ReturnLength = reply->len + FIELD_OFFSET( TOKEN_PRIVILEGES, Privileges );
+            PreviousState->PrivilegeCount = reply->len / sizeof(LUID_AND_ATTRIBUTES);
+        }
+    }
+    SERVER_END_REQ;
+
+    return ret;
 }
 
 /******************************************************************************
@@ -185,6 +225,7 @@
 	LPDWORD retlen )
 {
     unsigned int len = 0;
+    NTSTATUS status = STATUS_SUCCESS;
 
     TRACE("(%p,%ld,%p,%ld,%p)\n",
           token,tokeninfoclass,tokeninfo,tokeninfolength,retlen);
@@ -197,9 +238,6 @@
     case TokenGroups:
         len = sizeof(TOKEN_GROUPS);
         break;
-    case TokenPrivileges:
-        len = sizeof(TOKEN_PRIVILEGES);
-        break;
     case TokenOwner:
         len = sizeof(TOKEN_OWNER) + sizeof(SID);
         break;
@@ -271,11 +309,17 @@
         }
         break;
     case TokenPrivileges:
-        if (tokeninfo)
+        SERVER_START_REQ( get_token_privileges )
         {
             TOKEN_PRIVILEGES *tpriv = tokeninfo;
-            tpriv->PrivilegeCount = 1;
+            req->handle = token;
+            if (tpriv && tokeninfolength > FIELD_OFFSET( TOKEN_PRIVILEGES, Privileges ))
+                wine_server_set_reply( req, &tpriv->Privileges, tokeninfolength - FIELD_OFFSET( TOKEN_PRIVILEGES, Privileges ) );
+            status = wine_server_call( req );
+            *retlen = FIELD_OFFSET( TOKEN_PRIVILEGES, Privileges ) + reply->len;
+            if (tpriv) tpriv->PrivilegeCount = reply->len / sizeof(LUID_AND_ATTRIBUTES);
         }
+        SERVER_END_REQ;
         break;
     case TokenOwner:
         if (tokeninfo)
@@ -294,7 +338,7 @@
             return STATUS_NOT_IMPLEMENTED;
         }
     }
-    return 0;
+    return status;
 }
 
 /******************************************************************************
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index b375e0b..445ee70 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -3171,6 +3171,50 @@
 #define SET_GLOBAL_TASKMAN_WINDOW  0x04
 
 
+struct adjust_token_privileges_request
+{
+    struct request_header __header;
+    obj_handle_t  handle;
+    int           disable_all;
+    int           get_modified_state;
+    /* VARARG(privileges,LUID_AND_ATTRIBUTES); */
+};
+struct adjust_token_privileges_reply
+{
+    struct reply_header __header;
+    unsigned int  len;
+    /* VARARG(privileges,LUID_AND_ATTRIBUTES); */
+};
+
+
+struct get_token_privileges_request
+{
+    struct request_header __header;
+    obj_handle_t  handle;
+};
+struct get_token_privileges_reply
+{
+    struct reply_header __header;
+    unsigned int  len;
+    /* VARARG(privileges,LUID_AND_ATTRIBUTES); */
+};
+
+struct duplicate_token_request
+{
+    struct request_header __header;
+    obj_handle_t  handle;
+    unsigned int  access;
+    int           inherit;
+    int           primary;
+    int           impersonation_level;
+};
+struct duplicate_token_reply
+{
+    struct reply_header __header;
+    obj_handle_t  new_handle;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -3353,6 +3397,9 @@
     REQ_set_clipboard_info,
     REQ_open_token,
     REQ_set_global_windows,
+    REQ_adjust_token_privileges,
+    REQ_get_token_privileges,
+    REQ_duplicate_token,
     REQ_NB_REQUESTS
 };
 
@@ -3540,6 +3587,9 @@
     struct set_clipboard_info_request set_clipboard_info_request;
     struct open_token_request open_token_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 duplicate_token_request duplicate_token_request;
 };
 union generic_reply
 {
@@ -3725,8 +3775,11 @@
     struct set_clipboard_info_reply set_clipboard_info_reply;
     struct open_token_reply open_token_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 duplicate_token_reply duplicate_token_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 156
+#define SERVER_PROTOCOL_VERSION 157
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winnt.h b/include/winnt.h
index 1dbe3af..4abae5c 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -2808,6 +2808,7 @@
 
 #define SE_PRIVILEGE_ENABLED_BY_DEFAULT 0x00000001
 #define SE_PRIVILEGE_ENABLED 		0x00000002
+#define SE_PRIVILEGE_REMOVE		0x00000004
 #define SE_PRIVILEGE_USED_FOR_ACCESS 	0x80000000
 
 #define SE_OWNER_DEFAULTED		0x00000001
diff --git a/include/winternl.h b/include/winternl.h
index cd202ea..23c4b27 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1396,6 +1396,7 @@
 NTSTATUS  WINAPI NtDeleteValueKey(HKEY,const UNICODE_STRING *);
 NTSTATUS  WINAPI NtDeviceIoControlFile(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,ULONG,PVOID,ULONG,PVOID,ULONG);
 NTSTATUS  WINAPI NtDuplicateObject(HANDLE,HANDLE,HANDLE,PHANDLE,ACCESS_MASK,ULONG,ULONG);
+NTSTATUS  WINAPI NtDuplicateToken(HANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,SECURITY_IMPERSONATION_LEVEL,TOKEN_TYPE,PHANDLE);
 NTSTATUS  WINAPI NtEnumerateKey(HKEY,ULONG,KEY_INFORMATION_CLASS,void *,DWORD,DWORD *);
 NTSTATUS  WINAPI NtEnumerateValueKey(HKEY,ULONG,KEY_VALUE_INFORMATION_CLASS,PVOID,ULONG,PULONG);
 NTSTATUS  WINAPI NtFlushBuffersFile(HANDLE,IO_STATUS_BLOCK*);
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 ### */