Déterminer si le processus Windows a le privilège de créer un lien symbolique

Je veux déterminer par programme si l’utilisateur actuel (ou le processus) a access pour créer des liens symboliques. Sous Windows (Vista et versions ultérieures), il est impossible de créer un lien symbolique sans SeCreateSymbolicLinkPrivilege et, par défaut, ce lien est uniquement atsortingbué aux administrateurs. Si l’on tente de créer un lien symbolique sans ce privilège, une erreur Windows 1314 (Un privilège requirejs n’est pas détenu par le client) se produit.

Pour démontrer cette ressortingction, j’ai créé une installation propre de Windows, connecté en tant que compte administrateur initial (restreint via UAC), et je n’ai pas pu créer de lien symbolique dans le répertoire de base.

Invite de commandes montre que mklink échoue en raison de privilèges insuffisants

Après avoir exécuté l’invite de commandes en tant qu’administrateur ou désactivé UAC, cette commande s’exécute sans erreur.

Selon cet article , “chaque processus exécuté pour le compte de l’utilisateur dispose d’une copie du jeton [access]”.

J’ai donc créé un script Python pour interroger les permissions . Pour plus de commodité et de postérité, j’inclus un extrait ci-dessous.

L’idée derrière le script est d’énumérer tous les privilèges et de déterminer si le processus dispose du privilège requirejs. Malheureusement, je trouve que le processus en cours n’a pas le privilège souhaité, même s’il peut créer des liens symboliques.

Je soupçonne que le problème est que même si les privilèges de l’utilisateur actuel n’incluent pas explicitement le privilège, son appartenance à un groupe offre ce privilège.

En bref, comment puis-je déterminer si un processus donné aura le privilège de créer des liens symboliques (sans tenter d’en créer un)? Un exemple en C ou C ++ ou Python est préférable, bien que tout ce qui utilise l’API Windows convienne.

def get_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 token def get_privilege_information(): # 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_ssortingng_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 privileges privileges = get_privilege_information() print("found {0} privileges".format(privileges.count)) map(print, privileges) 

J’ai trouvé une solution. Le code Python suivant est un script entièrement fonctionnel sous Python 2.6 ou 3.1 qui montre comment déterminer les privilèges pour créer des liens symboliques. L’exécuter sous le compte Administrateur répond avec succès et l’exécuter sous le compte Invité répond par un échec.

Notez que le premier 3/4 du script est principalement constitué de définitions d’API. Le roman commence par get_process_token ().

 from __future__ import print_function import ctypes from ctypes import wintypes GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess GetCurrentProcess.restype = wintypes.HANDLE OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)) OpenProcessToken.restype = wintypes.BOOL class 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.LookupPrivilegeValueW LookupPrivilegeValue.argtypes = ( wintypes.LPWSTR, # system name wintypes.LPWSTR, # name ctypes.POINTER(LUID), ) LookupPrivilegeValue.restype = wintypes.BOOL class TOKEN_INFORMATION_CLASS: TokenUser = 1 TokenGroups = 2 TokenPrivileges = 3 # ... see http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx SE_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), ('atsortingbutes', wintypes.DWORD), ] def is_enabled(self): return bool(self.atsortingbutes & SE_PRIVILEGE_ENABLED) def enable(self): self.atsortingbutes |= 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 res LookupPrivilegeName = ctypes.windll.advapi32.LookupPrivilegeNameW LookupPrivilegeName.argtypes = ( wintypes.LPWSTR, # lpSystemName ctypes.POINTER(LUID), # lpLuid wintypes.LPWSTR, # lpName ctypes.POINTER(wintypes.DWORD), #cchName ) LookupPrivilegeName.restype = wintypes.BOOL class 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.GetTokenInformation GetTokenInformation.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.aspx AdjustTokenPrivileges = ctypes.windll.advapi32.AdjustTokenPrivileges AdjustTokenPrivileges.restype = wintypes.BOOL AdjustTokenPrivileges.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 token def 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_luid def 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_ssortingng_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 privileges def 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_ssortingng_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_ASSIGNED def main(): assigned = enable_symlink_privilege() msg = ['failure', 'success'][assigned] print("Symlink privilege assignment completed with {0}".format(msg)) if __name__ == '__main__': main()