Determine if Windows process has privilege to create symbolic link Determine if Windows process has privilege to create symbolic link windows windows

Determine if Windows process has privilege to create symbolic link


I found a solution. The following Python code is a fully-functional script under Python 2.6 or 3.1 that demonstrates how one might determine privilege to create symlinks. Running this under the Administrator account responds with success, and running it under the Guest account responds with failure.

Note, the first 3/4 of the script is mostly API definitions. The novel work begins with get_process_token().

from __future__ import print_functionimport ctypesfrom ctypes import wintypesGetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcessGetCurrentProcess.restype = wintypes.HANDLEOpenProcessToken = ctypes.windll.advapi32.OpenProcessTokenOpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE))OpenProcessToken.restype = wintypes.BOOLclass LUID(ctypes.Structure):    _fields_ = [        ('low_part', wintypes.DWORD),        ('high_part', wintypes.LONG),        ]    def __eq__(self, other):        return (            self.high_part == other.high_part and            self.low_part == other.low_part            )    def __ne__(self, other):        return not (self==other)LookupPrivilegeValue = ctypes.windll.advapi32.LookupPrivilegeValueWLookupPrivilegeValue.argtypes = (    wintypes.LPWSTR, # system name    wintypes.LPWSTR, # name    ctypes.POINTER(LUID),    )LookupPrivilegeValue.restype = wintypes.BOOLclass TOKEN_INFORMATION_CLASS:    TokenUser = 1    TokenGroups = 2    TokenPrivileges = 3    # ... see http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspxSE_PRIVILEGE_ENABLED_BY_DEFAULT = (0x00000001)SE_PRIVILEGE_ENABLED            = (0x00000002)SE_PRIVILEGE_REMOVED            = (0x00000004)SE_PRIVILEGE_USED_FOR_ACCESS    = (0x80000000)class LUID_AND_ATTRIBUTES(ctypes.Structure):    _fields_ = [        ('LUID', LUID),        ('attributes', wintypes.DWORD),        ]    def is_enabled(self):        return bool(self.attributes & SE_PRIVILEGE_ENABLED)    def enable(self):        self.attributes |= SE_PRIVILEGE_ENABLED    def get_name(self):        size = wintypes.DWORD(10240)        buf = ctypes.create_unicode_buffer(size.value)        res = LookupPrivilegeName(None, self.LUID, buf, size)        if res == 0: raise RuntimeError        return buf[:size.value]    def __str__(self):        res = self.get_name()        if self.is_enabled(): res += ' (enabled)'        return resLookupPrivilegeName = ctypes.windll.advapi32.LookupPrivilegeNameWLookupPrivilegeName.argtypes = (    wintypes.LPWSTR, # lpSystemName    ctypes.POINTER(LUID), # lpLuid    wintypes.LPWSTR, # lpName    ctypes.POINTER(wintypes.DWORD), #cchName    )LookupPrivilegeName.restype = wintypes.BOOLclass TOKEN_PRIVILEGES(ctypes.Structure):    _fields_ = [        ('count', wintypes.DWORD),        ('privileges', LUID_AND_ATTRIBUTES*0),        ]    def get_array(self):        array_type = LUID_AND_ATTRIBUTES*self.count        privileges = ctypes.cast(self.privileges, ctypes.POINTER(array_type)).contents        return privileges    def __iter__(self):        return iter(self.get_array())PTOKEN_PRIVILEGES = ctypes.POINTER(TOKEN_PRIVILEGES)GetTokenInformation = ctypes.windll.advapi32.GetTokenInformationGetTokenInformation.argtypes = [    wintypes.HANDLE, # TokenHandle    ctypes.c_uint, # TOKEN_INFORMATION_CLASS value    ctypes.c_void_p, # TokenInformation    wintypes.DWORD, # TokenInformationLength    ctypes.POINTER(wintypes.DWORD), # ReturnLength    ]GetTokenInformation.restype = wintypes.BOOL# http://msdn.microsoft.com/en-us/library/aa375202%28VS.85%29.aspxAdjustTokenPrivileges = ctypes.windll.advapi32.AdjustTokenPrivilegesAdjustTokenPrivileges.restype = wintypes.BOOLAdjustTokenPrivileges.argtypes = [    wintypes.HANDLE,                # TokenHandle    wintypes.BOOL,                  # DisableAllPrivileges    PTOKEN_PRIVILEGES,              # NewState (optional)    wintypes.DWORD,                 # BufferLength of PreviousState    PTOKEN_PRIVILEGES,              # PreviousState (out, optional)    ctypes.POINTER(wintypes.DWORD), # ReturnLength    ]def get_process_token():    """    Get the current process token    """    token = wintypes.HANDLE()    TOKEN_ALL_ACCESS = 0xf01ff    res = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, token)    if not res > 0: raise RuntimeError("Couldn't get process token")    return tokendef get_symlink_luid():    """    Get the LUID for the SeCreateSymbolicLinkPrivilege    """    symlink_luid = LUID()    res = LookupPrivilegeValue(None, "SeCreateSymbolicLinkPrivilege", symlink_luid)    if not res > 0: raise RuntimeError("Couldn't lookup privilege value")    return symlink_luiddef get_privilege_information():    """    Get all privileges associated with the current process.    """    # first call with zero length to determine what size buffer we need    return_length = wintypes.DWORD()    params = [        get_process_token(),        TOKEN_INFORMATION_CLASS.TokenPrivileges,        None,        0,        return_length,        ]    res = GetTokenInformation(*params)    # assume we now have the necessary length in return_length    buffer = ctypes.create_string_buffer(return_length.value)    params[2] = buffer    params[3] = return_length.value    res = GetTokenInformation(*params)    assert res > 0, "Error in second GetTokenInformation (%d)" % res    privileges = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVILEGES)).contents    return privilegesdef report_privilege_information():    """    Report all privilege information assigned to the current process.    """    privileges = get_privilege_information()    print("found {0} privileges".format(privileges.count))    tuple(map(print, privileges))def enable_symlink_privilege():    """    Try to assign the symlink privilege to the current process token.    Return True if the assignment is successful.    """    # create a space in memory for a TOKEN_PRIVILEGES structure    #  with one element    size = ctypes.sizeof(TOKEN_PRIVILEGES)    size += ctypes.sizeof(LUID_AND_ATTRIBUTES)    buffer = ctypes.create_string_buffer(size)    tp = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVILEGES)).contents    tp.count = 1    tp.get_array()[0].enable()    tp.get_array()[0].LUID = get_symlink_luid()    token = get_process_token()    res = AdjustTokenPrivileges(token, False, tp, 0, None, None)    if res == 0:        raise RuntimeError("Error in AdjustTokenPrivileges")    ERROR_NOT_ALL_ASSIGNED = 1300    return ctypes.windll.kernel32.GetLastError() != ERROR_NOT_ALL_ASSIGNEDdef main():    assigned = enable_symlink_privilege()    msg = ['failure', 'success'][assigned]    print("Symlink privilege assignment completed with {0}".format(msg))if __name__ == '__main__': main()