日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

nng 服务器底层给管道设置安全描述符,开启listen,实现与chrome跨进程通信

發布時間:2024/1/1 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 nng 服务器底层给管道设置安全描述符,开启listen,实现与chrome跨进程通信 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近在工作中使用nng庫時,當message_addr為ipc類型時,兩個進程之間的通信會失敗。

具體現象為:我自己寫的xdr win32控制臺程序經過提權之后,用管理員的權限,以服務的方式啟動,當打開瀏覽器,跟瀏覽器進程進行通信時,發現獲取不到SSLNet 網絡數據流。

初步懷疑是權限問題。

驗證方法:

管理員權限打開xdr,用管理員權限打開chrome,能夠獲取到SSLNet網絡數據流;

管理員權限打開xdr,用普通用戶權限打開chrome,不能獲取SSLNet;

普通用戶權限打開xdr,用管理員權限打開chrome,不能獲取SSLNet;

普通用戶權限打開xdr,用普通用戶權限打開chrome,能夠獲取到SSLNet網絡數據流;

經過查閱資料和相關源碼得知:

1. nng內部會創建一個NamedPipe。

2. 創建管道時,C++的CreateNamedPipe函數有一個參數是傳入 LPSECURITY_ATTRIBUTES,該參數是用來存儲安全描述符。

一般情況下我們會傳NULL,如果為空,則創建的pipe默認繼承當前進程的權限,也就是xdr的權限。

那么在xdr是管理員權限創建pipe時,客戶端用普通權限去讀寫就會去失敗;反之用普通用戶創建pipe,客戶端用管理員權限去讀寫也會失敗。

確定問題的存在位置后,接下來嘗試解決方案。

1. 第一步:設置安全描述符,要同時有管理員和普通用戶兩種權限。

2. 第二步:如何在nng創建pipe之前,將安全描述符傳遞進來。

第一步:生成安全描述符。

microsoft關于安全描述符的介紹都很全面,但是不夠詳細,特別是一些函數的用法之類。

這里主要提到DACL和SACL,概念這里不再累述,大家感興趣去搜索微軟的官方文檔。

這里參考微軟給出的例子,生成了一個安全描述符:鏈接如下

Creating a Security Descriptor for a New Object in C++ - Win32 apps | Microsoft DocsThe following example creates a security descriptor for a new registry key using the following process. Similar code can be used to create a security descriptor for other object types.https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--實際運行時,不報錯,但是不生效。

經過多方排查,最后發現在給 EveryoneSID 和 AdminSID 設置??grfAccessPermissions 時,用的是KEY_xxx,這種類型是對注冊表生效,但是對管道要用GENERIC_xxx,或者FILE_xxx.

這里AdminSID我選擇的是GENERIC_ALL,EveryoneSID我選擇的是(GENERIC_READ | GENERIC_WRITE)。

更改之后執行,成功。xdr和chrome不管是普通權限還是管理員權限,都能交叉訪問,功能正常。

這里我的2個SID_IDENTIFIER_AUTHORITY都是在堆上創建,以及 PSECURITY_DESCRIPTOR 和 PACL也是在棧上創建,所以用完一定要調API釋放。但釋放的位置不能一設置到nng就釋放,這樣安全描述符還是沒生效。我是在nng的pipe全部交互完畢,退出的時候才釋放。

也許存在在棧上可以創建的SID_IDENTIFIER_AUTHORITY、PSECURITY_DESCRIPTOR和PACL,大家可以嘗試下。

源碼一會附在最后。

第二步:在nng創建pipe之前,將安全描述符傳遞進來

首先,nng的ipc服務端創建listen有2種方式。

一種是?nng_listen, 傳入sock, addr等參數,即時創建 + 開啟監聽。

另一種是?nng_listener_create先創建但是不開啟監聽,nng_listener_set_ptr設置安全描述符,最后調用nng_listener_start開啟監聽。

很明顯,我們需要用第二種方式,才可以設置安全描述符。

這里的關鍵點是?nng_listener_set_ptr的其中一個參數要傳 NNG_OPT_IPC_SECURITY_DESCRIPTOR,這個參數類型在微軟文檔很難找到相關的說明了,我當時設置的時候也一度懷疑是不是用錯了。NNG_OPT_IPC_SECURITY_DESCRIPTOR此只寫選項可用于 Windows 平臺上的偵聽器,以配置NNG_OPT_IPC_SECURITY_DESCRIPTOR創建底層命名管道時使用的選項。詳情可以看看下面的鏈接:

https://nng.nanomsg.org/man/v1.1.0/nng_ipc.7.html#:~:text=NNG_OPT_IPC_SECURITY_DESCRIPTOR%20%28PSECURITY_DESCRIPTOR%29%20This%20write-only%20option%20may%20be%20used,to%20listeners%20that%20have%20not%20been%20started%20yet.https://nng.nanomsg.org/man/v1.1.0/nng_ipc.7.html#:~:text=NNG_OPT_IPC_SECURITY_DESCRIPTOR%20%28PSECURITY_DESCRIPTOR%29%20This%20write-only%20option%20may%20be%20used,to%20listeners%20that%20have%20not%20been%20started%20yet.

上面兩步完成后,整個xdr與chrome之間的交互就能夠正常進行了。

下面貼出關鍵部分源碼:

1. 設置權限:

bool XdrIpc::assign_permissions() {DWORD dwRes, dwDisposition;EXPLICIT_ACCESS ea[2];SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;SECURITY_ATTRIBUTES sa;if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,SECURITY_WORLD_RID,0, 0, 0, 0, 0, 0, 0,&m_pEveryoneSID)){spdlog::critical("[IPC] AllocateAndInitializeSid Error. {}", GetLastError());return false;}ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));ea[0].grfAccessPermissions = (GENERIC_READ | GENERIC_WRITE);ea[0].grfAccessMode = SET_ACCESS;ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;ea[0].Trustee.ptstrName = (LPTSTR)m_pEveryoneSID;if (!AllocateAndInitializeSid(&SIDAuthNT, 2,SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS,0, 0, 0, 0, 0, 0,&m_pAdminSID)){spdlog::critical("[IPC] AllocateAndInitializeSid Error. {}", GetLastError());return false;}ea[1].grfAccessPermissions = GENERIC_ALL;ea[1].grfAccessMode = SET_ACCESS;ea[1].grfInheritance = NO_INHERITANCE;ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;ea[1].Trustee.ptstrName = (LPTSTR)m_pAdminSID;// Create a new ACL that contains the new ACEs.dwRes = SetEntriesInAcl(2, ea, NULL, &m_pACL);if (ERROR_SUCCESS != dwRes){spdlog::critical("[IPC] SetEntriesInAcl Error. {}", GetLastError());return false;}// Initialize a security descriptor. m_pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);if (NULL == m_pSD){spdlog::critical("[IPC] LocalAlloc Error. {}", GetLastError());return false;}if (!InitializeSecurityDescriptor(m_pSD, SECURITY_DESCRIPTOR_REVISION)){spdlog::critical("[IPC] InitializeSecurityDescriptor Error. {}", GetLastError());return false;}if (!SetSecurityDescriptorDacl(m_pSD, TRUE, m_pACL, FALSE)) // not a default DACL {spdlog::critical("[IPC] SetSecurityDescriptorDacl Error. {}", GetLastError());return false;}return true; }

2. 創建 nng 服務端的 listen

bool XdrIpc::setRPCListener(const char* addr) {int rv = 0;if ((rv = nng_listener_create(&listener, sock, addr)) != 0){spdlog::critical(fmt::format("[RPC] listen failed at: {}", rv));return false;}if ((rv = nng_listener_set_ptr(listener, NNG_OPT_IPC_SECURITY_DESCRIPTOR, m_pSD)) != 0){spdlog::critical(fmt::format("[RPC] set listener failed at: {}", rv));return false;}if ((rv = nng_listener_start(listener, 0)) != 0){spdlog::critical(fmt::format("[RPC] start listener failed at: {}", rv));return false;}return true; }

3. nng init 時,關聯 降權 和 listen 的代碼

bool XdrIpc::init(const char* addr, bool listen) {const std::lock_guard<std::mutex> lock(mutex);if (strlen(addr) < 3){throw std::runtime_error(fmt::format("[RPC] init failed. error addr {}", addr));}if (0 == strnicmp(addr, "ipc", 3)){m_messageType = MESSAGE_TYPE::IPC;}else if (0 == strnicmp(addr, "tcp", 3)){m_messageType = MESSAGE_TYPE::TCP;}try{if (listen){int rv = 0; if ((rv = nng_sub0_open(&sock)) != 0){throw std::runtime_error(fmt::format("[RPC] open failed at: {}", rv));}if ((rv = nng_setopt(sock, NNG_OPT_SUB_SUBSCRIBE, "", 0)) < 0){throw std::runtime_error(fmt::format("[RPC] set failed at: {}", rv));}if(m_messageType == MESSAGE_TYPE::IPC){if (!assign_permissions()){cleanUpSD();throw std::runtime_error(fmt::format("[RPC] assign permissions failed."));}if (!setRPCListener(addr)){cleanUpSD();throw std::runtime_error(fmt::format("[RPC] listen failed."));}}else if(m_messageType == MESSAGE_TYPE::TCP){if ((rv = nng_listen(sock, addr, NULL, 0)) < 0){throw std::runtime_error(fmt::format("[RPC] tcp listen failed at: {}", rv));}}}else{int rv = 0;/// nni_sock_shutdownif ((rv = nng_pub0_open(&sock)) != 0) {throw std::runtime_error(fmt::format("[RPC] open failed at: {}", rv));}if ((rv = nng_dial(sock, addr, &dialer, NNG_FLAG_NONBLOCK)) != 0) {throw std::runtime_error(fmt::format("[RPC] dial failed at: {}", rv));}}}catch (std::exception& e){spdlog::critical("[RPC] init failed. {}", e.what());return false;}return true; }

總結

以上是生活随笔為你收集整理的nng 服务器底层给管道设置安全描述符,开启listen,实现与chrome跨进程通信的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。