Changelog update
[public/netxms.git] / src / libnetxms / nxproc_win32.cpp
CommitLineData
b6883988
VK
1/*
2** NetXMS - Network Management System
3** NetXMS Foundation Library
4** Copyright (C) 2003-2017 Victor Kirhenshtein
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU Lesser General Public License as published
8** by the Free Software Foundation; either version 3 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU Lesser General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
7afa2e6d 20** File: nxproc_win32.cpp
b6883988
VK
21**
22**/
23
24#include "libnetxms.h"
25#include <nxproc.h>
7afa2e6d 26#include <aclapi.h>
b6883988
VK
27
28/**
29 * Create listener end for named pipe
30 */
c9ce784e 31NamedPipeListener *NamedPipeListener::create(const TCHAR *name, NamedPipeRequestHandler reqHandler, void *userArg, const TCHAR *user)
b6883988 32{
7afa2e6d 33 NamedPipeListener *listener = NULL;
b6883988 34
7afa2e6d
VK
35 PSID sidEveryone = NULL;
36 ACL *acl = NULL;
37 PSECURITY_DESCRIPTOR sd = NULL;
b6883988 38
7afa2e6d 39 TCHAR errorText[1024];
b6883988 40
7afa2e6d
VK
41 // Create a well-known SID for the Everyone group.
42 SID_IDENTIFIER_AUTHORITY sidAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
43 if (!AllocateAndInitializeSid(&sidAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &sidEveryone))
44 {
45 nxlog_debug(2, _T("NamedPipeListener(%s): AllocateAndInitializeSid failed (%s)"), name, GetSystemErrorText(GetLastError(), errorText, 1024));
46 goto cleanup;
47 }
48
49 // Initialize an EXPLICIT_ACCESS structure for an ACE.
50 // The ACE will allow either Everyone or given user to access pipe
51 EXPLICIT_ACCESS ea;
52 ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
53 ea.grfAccessPermissions = (FILE_GENERIC_READ | FILE_GENERIC_WRITE) & ~FILE_CREATE_PIPE_INSTANCE;
54 ea.grfAccessMode = SET_ACCESS;
55 ea.grfInheritance = NO_INHERITANCE;
56 if ((user == NULL) || (user[0] == 0) || !_tcscmp(user, _T("*")))
57 {
58 ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
59 ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
60 ea.Trustee.ptstrName = (LPTSTR)sidEveryone;
61 }
62 else
63 {
64 ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
65 ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
66 ea.Trustee.ptstrName = (LPTSTR)user;
67 nxlog_debug(2, _T("NamedPipeListener(%s): will allow connections only for user %s"), name, user);
68 }
69
70 // Create a new ACL that contains the new ACEs.
71 if (SetEntriesInAcl(1, &ea, NULL, &acl) != ERROR_SUCCESS)
72 {
73 nxlog_debug(2, _T("NamedPipeListener(%s): SetEntriesInAcl failed (%s)"), name, GetSystemErrorText(GetLastError(), errorText, 1024));
74 goto cleanup;
75 }
76
77 sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
78 if (sd == NULL)
79 {
80 nxlog_debug(2, _T("NamedPipeListener(%s): LocalAlloc failed (%s)"), name, GetSystemErrorText(GetLastError(), errorText, 1024));
81 goto cleanup;
82 }
83
84 if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
85 {
86 nxlog_debug(2, _T("NamedPipeListener(%s): InitializeSecurityDescriptor failed (%s)"), name, GetSystemErrorText(GetLastError(), errorText, 1024));
87 goto cleanup;
88 }
89
90 // Add the ACL to the security descriptor.
91 if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE))
92 {
93 nxlog_debug(2, _T("NamedPipeListener(%s): SetSecurityDescriptorDacl failed (%s)"), name, GetSystemErrorText(GetLastError(), errorText, 1024));
94 goto cleanup;
95 }
96
97 TCHAR path[MAX_PATH];
98 _sntprintf(path, MAX_PATH, _T("\\\\.\\pipe\\%s"), name);
99
100 SECURITY_ATTRIBUTES sa;
101 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
102 sa.bInheritHandle = FALSE;
103 sa.lpSecurityDescriptor = sd;
104 HANDLE hPipe = CreateNamedPipe(path, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, 8192, 8192, 0, &sa);
105 if (hPipe == INVALID_HANDLE_VALUE)
106 {
107 nxlog_debug(2, _T("NamedPipeListener(%s): CreateNamedPipe failed (%s)"), name, GetSystemErrorText(GetLastError(), errorText, 1024));
108 goto cleanup;
109 }
110
111 listener = new NamedPipeListener(name, hPipe, reqHandler, userArg, user);
b6883988 112
7afa2e6d
VK
113cleanup:
114 if (sd != NULL)
115 LocalFree(sd);
b6883988 116
7afa2e6d
VK
117 if (acl != NULL)
118 LocalFree(acl);
119
120 if (sidEveryone != NULL)
121 FreeSid(sidEveryone);
122
123 return listener;
b6883988
VK
124}
125
126/**
127 * Pipe destructor
128 */
129NamedPipeListener::~NamedPipeListener()
130{
131 CloseHandle(m_handle);
132 stop();
133}
134
135/**
136 * Named pipe server thread
137 */
138void NamedPipeListener::serverThread()
139{
140 nxlog_debug(2, _T("NamedPipeListener(%s): waiting for connection"), m_name);
141 while(!m_stop)
142 {
7afa2e6d
VK
143 BOOL connected = ConnectNamedPipe(m_handle, NULL);
144 if (connected || (GetLastError() == ERROR_PIPE_CONNECTED))
145 {
146 nxlog_debug(5, _T("NamedPipeListener(%s): accepted connection"), m_name);
147 NamedPipe *pipe = new NamedPipe(m_name, m_handle, NULL);
148 m_reqHandler(pipe, m_userArg);
149 delete pipe;
150 }
151 else
152 {
153 TCHAR errorText[1024];
154 nxlog_debug(2, _T("NamedPipeListener(%s): ConnectNamedPipe failed (%s)"), m_name, GetSystemErrorText(GetLastError(), errorText, 1024));
155 ThreadSleep(5);
156 }
b6883988
VK
157 }
158}
159
160/**
161 * Pipe destructor
162 */
163NamedPipe::~NamedPipe()
164{
7afa2e6d
VK
165 DWORD flags = 0;
166 GetNamedPipeInfo(m_handle, &flags, NULL, NULL, NULL);
167 if (flags & PIPE_SERVER_END)
168 DisconnectNamedPipe(m_handle);
169 else
170 CloseHandle(m_handle);
b6883988
VK
171 MutexDestroy(m_writeLock);
172}
173
174/**
175 * Create client end for named pipe
176 */
177NamedPipe *NamedPipe::connect(const TCHAR *name, UINT32 timeout)
178{
179 TCHAR path[MAX_PATH];
180 _sntprintf(path, MAX_PATH, _T("\\\\.\\pipe\\%s"), name);
181
182reconnect:
7afa2e6d 183 HANDLE h = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
b6883988
VK
184 if (h == INVALID_HANDLE_VALUE)
185 {
186 if (GetLastError() == ERROR_PIPE_BUSY)
187 {
188 if (WaitNamedPipe(path, timeout))
189 goto reconnect;
190 }
191 return NULL;
192 }
193
194 DWORD pipeMode = PIPE_READMODE_MESSAGE;
195 SetNamedPipeHandleState(h, &pipeMode, NULL, NULL);
196 return new NamedPipe(name, h, false);
197}
198
199/**
200 * Write to pipe
201 */
202bool NamedPipe::write(const void *data, size_t size)
203{
204 DWORD bytes;
7afa2e6d 205 if (!WriteFile(m_handle, data, (DWORD)size, &bytes, NULL))
b6883988
VK
206 return false;
207 return bytes == (DWORD)size;
208}
7afa2e6d
VK
209
210/**
211 * Get user name
212 */
213const TCHAR *NamedPipe::user()
214{
215 if (m_user[0] == 0)
216 {
217 if (!GetNamedPipeHandleState(m_handle, NULL, NULL, NULL, NULL, m_user, 64))
218 {
219 if (GetLastError() != ERROR_CANNOT_IMPERSONATE)
220 {
221 TCHAR errorText[1024];
222 nxlog_debug(5, _T("NamedPipeListener(%s): GetNamedPipeHandleState failed (%s)"), m_name, GetSystemErrorText(GetLastError(), errorText, 1024));
223 _tcscpy(m_user, _T("[unknown]"));
224 }
225 }
226 }
227 return m_user;
228}