Windows多个session下不同用户使用Global命名空间的Mutex

来源:互联网 发布:贴贴相传网络曝光 编辑:程序博客网 时间:2024/04/27 13:37

Windows系统中可以同时登陆多个用户,各个用户在不同的session中。对于windows 7来说,可以通过切换用户的方式,实现多个用户的同时登陆。对于windows 2012等服务器操作系统来说,多个用户还可以使用远程桌面同时对系统进行操作。在多个session上运行的程序可能会使用到同一份资源(文件,注册表等),此时对于资源的访问也会需要同步控制。

笔者在尝试使用Mutex来进行同步控制时,遇到一些问题,在此做一记录。

在MSDN中,对于CreateMutex()函数的Mutex对象名参数的说明中有以下描述:

The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session name space. The remainder of the name can contain any character except the backslash character (\). For more information, see Kernel Object Namespaces. Fast user switching is implemented using Terminal Services sessions. Kernel object names must follow the guidelines outlined for Terminal Services so that applications can support multiple users.

以上内容说明Mutex的对象名存在命名空间的区分,如果想要在多个session中使用同一个命名互斥体,应使用“Global\”前缀,于是写代码如下:

#define MUTEX_NAME _T("Global\\TestGlobalMutex")HANDLE mutex = NULL;bool firstCreate = false;mutex = ::CreateMutex(NULL, FALSE, MUTEX_NAME);if(mutex != NULL){if(::GetLastError() != ERROR_ALREADY_EXISTS)firstCreate = true;}
代码在测试时发现,在一个session中,互斥体可以正确的创建和打开,但是在互斥体创建后,在另一个session中运行程序尝试创建该互斥体时,CreateMutex函数的返回值为NULL,添加GetLastError()函数后发现此时的错误码为5(ERROR_ACCESS_DENIED)。

在MSDN中,关于CreateMutex函数的返回值的解释中,有如下描述:

If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object,GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership.However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use theOpenMutex function.

看似此时应使用OpenMutex函数来进行Mutex对象的打开操作,但是尝试使用OpenMutex进行操作仍然返回错误码ERROR_ACCESS_DENIED。

继续翻看MSDN,在CreateMutex函数中对Mutex对象名称参数进行说明时,引用到了Kernel Object Namespaces文章。其中介绍内核对象的命名空间相关问题,下面有这样一段描述:

Starting with Windows Server 2003, Windows XP Service Pack 2 (SP2), and Windows 2000 Server Service Pack 4 (SP4), the creation of a file-mapping object in the global namespace (by usingCreateFileMapping) from a session other than session zero is a privileged operation. Because of this, an application running in an arbitrary terminal server session must have SeCreateGlobalPrivilege enabled in order to create a file-mapping object in the global namespace successfully. Note that the privilege check is limited to the creation of file-mapping objects, and does not apply to opening existing ones. For example, if a service or the system creates a file-mapping object, any process running in any session can access that file-mapping object provided that the user has the necessary access.

意思是使用全局命名空间的文件映射对象时,需要权限提升。但此处只是特指文件映射对象,不应该包含Mutex,抱着死马当活马医的心态,尝试在一个session中使用管理员权限打开程序,发现居然能正确的使用全局命名的Mutex了,此时另一个session本身就是以管理员帐户登陆的。难道就是需要权限提升才能使用全局Mutex?这未免太麻烦了。

继续在网上找资料,发现类似的问题也有人提出过,有这样一条回答:

Looks like you don't have all access to the mutex. Change the security descriptor at the time of creation to allow the user token of your current process to have full access on the object.
看起来好像和那个一直被置为空的安全描述符有关,仔细想想也许不是需要权限提升,而是Mutex是由管理员帐户登陆的session中创建,管理员帐户对该对象有控制权,之后在另一个session中也使用管理员权限打开mutex时,使用了用一个管理员帐户,相当于同一个帐户运行的程序。也就是说对象的控制权与运行程序时使用的用户有关。

在上述问题的回答中,答者给出了示例程序来创建一个赋予其他用户组访问权限的安全描述符,如下:

// CreateMyDACL.//    Create a security descriptor that contains the DACL //    you want.//    This function uses SDDL to make Deny and Allow ACEs.//// Parameter://    SECURITY_ATTRIBUTES * pSA//    Pointer to a SECURITY_ATTRIBUTES structure. It is your//    responsibility to properly initialize the //    structure and to free the structure's //    lpSecurityDescriptor member when you have//    finished using it. To free the structure's //    lpSecurityDescriptor member, call the //    LocalFree function.// // Return value://    FALSE if the address to the structure is NULL. //    Otherwise, this function returns the value from the//    ConvertStringSecurityDescriptorToSecurityDescriptor //    function.BOOL CreateMyDACL(SECURITY_ATTRIBUTES * pSA){     // Define the SDDL for the DACL. This example sets      // the following access:     //     Built-in guests are denied all access.     //     Anonymous logon is denied all access.     //     Authenticated users are allowed      //     read/write/execute access.     //     Administrators are allowed full control.     // Modify these values as needed to generate the proper     // DACL for your application.      TCHAR * szSD = TEXT("D:")       // Discretionary ACL        TEXT("(D;OICI;GA;;;BG)")     // Deny access to                                      // built-in guests        TEXT("(D;OICI;GA;;;AN)")     // Deny access to                                      // anonymous logon        TEXT("(A;OICI;GRGWGX;;;AU)") // Allow                                      // read/write/execute                                      // to authenticated                                      // users        TEXT("(A;OICI;GA;;;BA)");    // Allow full control                                      // to administrators    if (NULL == pSA)        return FALSE;     return ConvertStringSecurityDescriptorToSecurityDescriptor(                szSD,                SDDL_REVISION_1,                &(pSA->lpSecurityDescriptor),                NULL);}

看起来应该是这个原因了,将上述代码段添加到程序中,再一次尝试时,发现仍然会提示ERROR_ACCESS_DENIED,这就奇怪了,仔细看看上述创建安全描述符的代码,其中给予了认证用户读写执行的权限,管理员用户组是完全控制权限,猜测会不会是认证用户的权限不够高,互斥体对象需要完全控制权限才行。

修改代码,给认证用户组也赋予完全控制权限,编译程序再次进行测试,程序执行成功!!

BOOL CreateMyDACL(SECURITY_ATTRIBUTES* pSA){// Define the SDDL for the DACL. This example sets // the following access://     Built-in guests are denied all access.//     Anonymous logon is denied all access.//     Authenticated users are allowed full control access.//     Administrators are allowed full control.// Modify these values as needed to generate the proper// DACL for your application. TCHAR * szSD = TEXT("D:")       // Discretionary ACLTEXT("(D;OICI;GA;;;BG)")    // Deny access to // built-in guestsTEXT("(D;OICI;GA;;;AN)")    // Deny access to // anonymous logonTEXT("(A;OICI;GA;;;AU)")// Allow full control// to authenticated // usersTEXT("(A;OICI;GA;;;BA)");   // Allow full control // to administratorsif (NULL == pSA)return FALSE;return ConvertStringSecurityDescriptorToSecurityDescriptor(szSD,SDDL_REVISION_1,&(pSA->lpSecurityDescriptor),NULL);}
虽然问题得到了解决,但是部分细节仍然不是特别清楚,对于安全描述符不太了解,之前一直都是置空,之后还是需要多了解一下。

没办法添加附件,需要完整源代码的话请到这里下载:windows多用户多session下使用Mutex进行同步控制源代码


参考内容:

1,CreateMutex() -- access denied

2,Creating a DACL

0 0
原创粉丝点击