确定 Windows 进程是否有权创建符号链接
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2094663/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Determine if Windows process has privilege to create symbolic link
提问by Jason R. Coombs
I want to programmatically determine if the current user (or process) has access to create symbolic links. In Windows (Vista and greater), one cannot create a symbolic link without the SeCreateSymbolicLinkPrivilege and by default, this is only assigned to administrators. If one attempts to create a symbolic link without this privilege, a Windows error 1314 (A required privilege is not held by the client) occurs.
我想以编程方式确定当前用户(或进程)是否有权创建符号链接。在 Windows(Vista 及更高版本)中,如果没有 SeCreateSymbolicLinkPrivilege,就无法创建符号链接,默认情况下,这仅分配给管理员。如果尝试在没有此权限的情况下创建符号链接,则会出现 Windows 错误 1314(客户端未拥有所需的权限)。
To demonstrate this restriction, I created a clean install of Windows, logged in as the initial Administrator account (restricted through UAC), and was unable to create a symlink in the home directory.
为了演示此限制,我创建了 Windows 的全新安装,以初始管理员帐户登录(通过 UAC 限制),但无法在主目录中创建符号链接。
After running the Command Prompt as Administrator or disabling UAC, that command executes without error.
以管理员身份运行命令提示符或禁用 UAC 后,该命令将正确执行。
According to this article, "every process executed on behalf of the user has a copy of the [access] token".
根据这篇文章,“代表用户执行的每个进程都有一个 [访问] 令牌的副本”。
So I've created a Python script to query for the permissions. For convenience and posterity, I include an excerpt below.
所以我创建了一个 Python 脚本来查询权限。为了方便和后代,我在下面摘录了一段。
The idea behind the script is to enumerate all privileges and determine if the process has the required privilege. Unfortunately, I find that the current process does not in fact have the desired privilege, even when it can create symlinks.
脚本背后的想法是枚举所有权限并确定进程是否具有所需的权限。不幸的是,我发现当前进程实际上没有所需的特权,即使它可以创建符号链接。
I suspect the problem is that even though the current user's privileges does not explicitly include the privilege, his group membership does afford that privilege.
我怀疑问题在于,即使当前用户的权限没有明确包含该权限,但他的组成员身份确实提供了该权限。
In short, how can I determine if a given process will have privilege to create symbolic links (without attempting to create one)? An example in C or C++ or Python is preferred, though anything utilizing the Windows API will be suitable.
简而言之,我如何确定给定进程是否有权创建符号链接(不尝试创建符号链接)?最好使用 C 或 C++ 或 Python 中的示例,但使用 Windows API 的任何内容都适用。
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_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 privileges
privileges = get_privilege_information()
print("found {0} privileges".format(privileges.count))
map(print, privileges)
采纳答案by Jason R. Coombs
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.
我找到了解决办法。以下 Python 代码是 Python 2.6 或 3.1 下的全功能脚本,演示了如何确定创建符号链接的权限。在管理员帐户下运行它会响应成功,而在来宾帐户下运行它会响应失败。
Note, the first 3/4 of the script is mostly API definitions. The novel work begins with get_process_token().
请注意,脚本的前 3/4 大部分是 API 定义。新颖的工作从 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),
('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 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_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 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_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_ASSIGNED
def main():
assigned = enable_symlink_privilege()
msg = ['failure', 'success'][assigned]
print("Symlink privilege assignment completed with {0}".format(msg))
if __name__ == '__main__': main()