blob: 59fa6e6e575c7c6066b1bf154c6c08325a842c0d [file] [log] [blame]
Brian Curtind40e6f72010-07-08 21:39:08 +00001"""
2A module built to test if the current process has the privilege to
3create symlinks on Windows.
4"""
5
6# allow script to run natively under python 2.6+
7from __future__ import print_function
8
9import ctypes
10from ctypes import wintypes
11
12GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess
13GetCurrentProcess.restype = wintypes.HANDLE
14OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken
Brian Curtin74e45612010-07-09 15:58:59 +000015OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD,
16 ctypes.POINTER(wintypes.HANDLE))
Brian Curtind40e6f72010-07-08 21:39:08 +000017OpenProcessToken.restype = wintypes.BOOL
18
19class LUID(ctypes.Structure):
20 _fields_ = [
21 ('low_part', wintypes.DWORD),
22 ('high_part', wintypes.LONG),
23 ]
24
25 def __eq__(self, other):
26 return (
27 self.high_part == other.high_part and
28 self.low_part == other.low_part
29 )
30
31 def __ne__(self, other):
32 return not (self==other)
33
34LookupPrivilegeValue = ctypes.windll.advapi32.LookupPrivilegeValueW
35LookupPrivilegeValue.argtypes = (
36 wintypes.LPWSTR, # system name
37 wintypes.LPWSTR, # name
38 ctypes.POINTER(LUID),
39 )
40LookupPrivilegeValue.restype = wintypes.BOOL
41
42class TOKEN_INFORMATION_CLASS:
43 TokenUser = 1
44 TokenGroups = 2
45 TokenPrivileges = 3
46 # ... see http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx
47
48SE_PRIVILEGE_ENABLED_BY_DEFAULT = (0x00000001)
49SE_PRIVILEGE_ENABLED = (0x00000002)
50SE_PRIVILEGE_REMOVED = (0x00000004)
51SE_PRIVILEGE_USED_FOR_ACCESS = (0x80000000)
52
53class LUID_AND_ATTRIBUTES(ctypes.Structure):
54 _fields_ = [
55 ('LUID', LUID),
56 ('attributes', wintypes.DWORD),
57 ]
58
59 def is_enabled(self):
60 return bool(self.attributes & SE_PRIVILEGE_ENABLED)
61
62 def enable(self):
63 self.attributes |= SE_PRIVILEGE_ENABLED
64
65 def get_name(self):
66 size = wintypes.DWORD(10240)
67 buf = ctypes.create_unicode_buffer(size.value)
68 res = LookupPrivilegeName(None, self.LUID, buf, size)
69 if res == 0:
70 raise RuntimeError
71 return buf[:size.value]
72
73 def __str__(self):
74 name = self.name
75 fmt = ['{name}', '{name} (enabled)'][self.is_enabled()]
76 return fmt.format(**vars())
77
78LookupPrivilegeName = ctypes.windll.advapi32.LookupPrivilegeNameW
79LookupPrivilegeName.argtypes = (
80 wintypes.LPWSTR, # lpSystemName
81 ctypes.POINTER(LUID), # lpLuid
82 wintypes.LPWSTR, # lpName
83 ctypes.POINTER(wintypes.DWORD), #cchName
84 )
85LookupPrivilegeName.restype = wintypes.BOOL
86
87class TOKEN_PRIVILEGES(ctypes.Structure):
88 _fields_ = [
89 ('count', wintypes.DWORD),
90 ('privileges', LUID_AND_ATTRIBUTES*0),
91 ]
92
93 def get_array(self):
94 array_type = LUID_AND_ATTRIBUTES*self.count
Brian Curtin74e45612010-07-09 15:58:59 +000095 privileges = ctypes.cast(self.privileges,
96 ctypes.POINTER(array_type)).contents
Brian Curtind40e6f72010-07-08 21:39:08 +000097 return privileges
98
99 def __iter__(self):
100 return iter(self.get_array())
101
102PTOKEN_PRIVILEGES = ctypes.POINTER(TOKEN_PRIVILEGES)
103
104GetTokenInformation = ctypes.windll.advapi32.GetTokenInformation
105GetTokenInformation.argtypes = [
106 wintypes.HANDLE, # TokenHandle
107 ctypes.c_uint, # TOKEN_INFORMATION_CLASS value
108 ctypes.c_void_p, # TokenInformation
109 wintypes.DWORD, # TokenInformationLength
110 ctypes.POINTER(wintypes.DWORD), # ReturnLength
111 ]
112GetTokenInformation.restype = wintypes.BOOL
113
114# http://msdn.microsoft.com/en-us/library/aa375202%28VS.85%29.aspx
115AdjustTokenPrivileges = ctypes.windll.advapi32.AdjustTokenPrivileges
116AdjustTokenPrivileges.restype = wintypes.BOOL
117AdjustTokenPrivileges.argtypes = [
118 wintypes.HANDLE, # TokenHandle
119 wintypes.BOOL, # DisableAllPrivileges
120 PTOKEN_PRIVILEGES, # NewState (optional)
121 wintypes.DWORD, # BufferLength of PreviousState
122 PTOKEN_PRIVILEGES, # PreviousState (out, optional)
123 ctypes.POINTER(wintypes.DWORD), # ReturnLength
124 ]
125
126def get_process_token():
127 "Get the current process token"
128 token = wintypes.HANDLE()
129 TOKEN_ALL_ACCESS = 0xf01ff
130 res = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, token)
131 if not res > 0:
132 raise RuntimeError("Couldn't get process token")
133 return token
134
135def get_symlink_luid():
136 "Get the LUID for the SeCreateSymbolicLinkPrivilege"
137 symlink_luid = LUID()
Brian Curtin74e45612010-07-09 15:58:59 +0000138 res = LookupPrivilegeValue(None, "SeCreateSymbolicLinkPrivilege",
139 symlink_luid)
Brian Curtind40e6f72010-07-08 21:39:08 +0000140 if not res > 0:
141 raise RuntimeError("Couldn't lookup privilege value")
142 return symlink_luid
143
144def get_privilege_information():
145 "Get all privileges associated with the current process."
146 # first call with zero length to determine what size buffer we need
147
148 return_length = wintypes.DWORD()
149 params = [
150 get_process_token(),
151 TOKEN_INFORMATION_CLASS.TokenPrivileges,
152 None,
153 0,
154 return_length,
155 ]
156
157 res = GetTokenInformation(*params)
158
159 # assume we now have the necessary length in return_length
160
161 buffer = ctypes.create_string_buffer(return_length.value)
162 params[2] = buffer
163 params[3] = return_length.value
164
165 res = GetTokenInformation(*params)
166 assert res > 0, "Error in second GetTokenInformation (%d)" % res
167
168 privileges = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVILEGES)).contents
169 return privileges
170
171def report_privilege_information():
172 "Report all privilege information assigned to the current process."
173 privileges = get_privilege_information()
174 print("found {0} privileges".format(privileges.count))
175 tuple(map(print, privileges))
176
177def enable_symlink_privilege():
178 """
179 Try to assign the symlink privilege to the current process token.
180 Return True if the assignment is successful.
181 """
182 # create a space in memory for a TOKEN_PRIVILEGES structure
183 # with one element
184 size = ctypes.sizeof(TOKEN_PRIVILEGES)
185 size += ctypes.sizeof(LUID_AND_ATTRIBUTES)
186 buffer = ctypes.create_string_buffer(size)
187 tp = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVILEGES)).contents
188 tp.count = 1
189 tp.get_array()[0].enable()
190 tp.get_array()[0].LUID = get_symlink_luid()
191 token = get_process_token()
192 res = AdjustTokenPrivileges(token, False, tp, 0, None, None)
193 if res == 0:
194 raise RuntimeError("Error in AdjustTokenPrivileges")
195
196 ERROR_NOT_ALL_ASSIGNED = 1300
197 return ctypes.windll.kernel32.GetLastError() != ERROR_NOT_ALL_ASSIGNED
198
199def main():
200 assigned = enable_symlink_privilege()
201 msg = ['failure', 'success'][assigned]
202
203 print("Symlink privilege assignment completed with {0}".format(msg))
204
205if __name__ == '__main__':
206 main()