1 /*
2 Copyright (c) 2013, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "named_pipe_connection.h"
26
27 #include <AclAPI.h> // Windows access control API
28 #include "violite.h" // Vio
29 #include "channel_info.h" // Channel_info
30 #include "connection_handler_manager.h" // Connection_handler_manager
31 #include "log.h" // sql_print_error
32 #include "named_pipe.h" // create_server_named_pipe.
33 #include "sql_class.h" // THD
34
35
36 ///////////////////////////////////////////////////////////////////////////
37 // Channel_info_named_pipe implementation
38 ///////////////////////////////////////////////////////////////////////////
39
40 /**
41 This class abstracts the info. about windows named pipe of communication
42 with server from client.
43 */
44 class Channel_info_named_pipe : public Channel_info
45 {
46 // Handle to named pipe.
47 HANDLE m_handle;
48
49 protected:
create_and_init_vio() const50 virtual Vio* create_and_init_vio() const
51 {
52 return vio_new_win32pipe(m_handle);
53 }
54
55 public:
56 /**
57 Constructor that sets the pipe handle
58
59 @param handle connected pipe handle
60 */
Channel_info_named_pipe(HANDLE handle)61 Channel_info_named_pipe(HANDLE handle)
62 : m_handle(handle)
63 { }
64
create_thd()65 virtual THD* create_thd()
66 {
67 THD* thd= Channel_info::create_thd();
68
69 if (thd != NULL)
70 thd->security_context()->set_host_ptr(my_localhost, strlen(my_localhost));
71 return thd;
72 }
73
send_error_and_close_channel(uint errorcode,int error,bool senderror)74 virtual void send_error_and_close_channel(uint errorcode,
75 int error,
76 bool senderror)
77 {
78 Channel_info::send_error_and_close_channel(errorcode, error, senderror);
79
80 DisconnectNamedPipe(m_handle);
81 CloseHandle(m_handle);
82 }
83 };
84
85
86 ///////////////////////////////////////////////////////////////////////////
87 // Named_pipe_listener implementation
88 ///////////////////////////////////////////////////////////////////////////
89
setup_listener()90 bool Named_pipe_listener::setup_listener()
91 {
92 m_connect_overlapped.hEvent= CreateEvent(NULL, TRUE, FALSE, NULL);
93 if (!m_connect_overlapped.hEvent)
94 {
95 sql_print_error("Can't create event, last error=%u", GetLastError());
96 return true;
97 }
98
99 m_pipe_handle= create_server_named_pipe(
100 &mp_sa_pipe_security, global_system_variables.net_buffer_length,
101 m_pipe_name.c_str(), m_pipe_path_name, sizeof(m_pipe_path_name),
102 named_pipe_full_access_group);
103 if (m_pipe_handle == INVALID_HANDLE_VALUE)
104 return true;
105
106 return false;
107 }
108
109
listen_for_connection_event()110 Channel_info* Named_pipe_listener::listen_for_connection_event()
111 {
112 TCHAR last_error_msg[256];
113
114 /* wait for named pipe connection */
115 BOOL fConnected= ConnectNamedPipe(m_pipe_handle, &m_connect_overlapped);
116 if (!fConnected && (GetLastError() == ERROR_IO_PENDING))
117 {
118 /*
119 ERROR_IO_PENDING says async IO has started but not yet finished.
120 GetOverlappedResult will wait for completion.
121 */
122 DWORD bytes;
123 fConnected= GetOverlappedResult(m_pipe_handle, &m_connect_overlapped,
124 &bytes, TRUE);
125 }
126 if (abort_loop)
127 return NULL;
128 if (!fConnected)
129 fConnected= GetLastError() == ERROR_PIPE_CONNECTED;
130 if (!fConnected)
131 {
132 CloseHandle(m_pipe_handle);
133 mysql_rwlock_rdlock(&LOCK_named_pipe_full_access_group);
134 m_pipe_handle= CreateNamedPipe(
135 m_pipe_path_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC,
136 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
137 PIPE_UNLIMITED_INSTANCES,
138 (int)global_system_variables.net_buffer_length,
139 (int)global_system_variables.net_buffer_length,
140 NMPWAIT_USE_DEFAULT_WAIT, mp_sa_pipe_security);
141 mysql_rwlock_unlock(&LOCK_named_pipe_full_access_group);
142 if (m_pipe_handle == INVALID_HANDLE_VALUE)
143 {
144 DWORD last_error_num= GetLastError();
145 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
146 FORMAT_MESSAGE_MAX_WIDTH_MASK,
147 NULL, last_error_num,
148 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), last_error_msg,
149 sizeof(last_error_msg) / sizeof(TCHAR), NULL);
150 sql_print_error("Can't create new named pipe: %s", last_error_msg);
151 return NULL;
152 }
153 else
154 {
155 /* A new pipe has been successfully created, it's not connected yet so
156 return NULL to spin around and wait for connection on it.
157 */
158 return NULL;
159 }
160 }
161 HANDLE hConnectedPipe= m_pipe_handle;
162 /* create new pipe for new connection */
163 mysql_rwlock_rdlock(&LOCK_named_pipe_full_access_group);
164 m_pipe_handle= CreateNamedPipe(
165 m_pipe_path_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC,
166 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
167 (int)global_system_variables.net_buffer_length,
168 (int)global_system_variables.net_buffer_length, NMPWAIT_USE_DEFAULT_WAIT,
169 mp_sa_pipe_security);
170 mysql_rwlock_unlock(&LOCK_named_pipe_full_access_group);
171 if (m_pipe_handle == INVALID_HANDLE_VALUE)
172 {
173 DWORD last_error_num= GetLastError();
174 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
175 FORMAT_MESSAGE_MAX_WIDTH_MASK,
176 NULL, last_error_num,
177 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), last_error_msg,
178 sizeof(last_error_msg) / sizeof(TCHAR), NULL);
179 sql_print_error("Can't create new named pipe: %s", last_error_msg);
180 m_pipe_handle= hConnectedPipe;
181 return NULL; // We have to try again
182 }
183
184 Channel_info* channel_info= new (std::nothrow)
185 Channel_info_named_pipe(hConnectedPipe);
186 if (channel_info == NULL)
187 {
188 DisconnectNamedPipe(hConnectedPipe);
189 CloseHandle(hConnectedPipe);
190 return NULL;
191 }
192 return channel_info;
193 }
194
update_named_pipe_full_access_group(const char * new_group_name)195 bool Named_pipe_listener::update_named_pipe_full_access_group(
196 const char *new_group_name)
197 {
198 SECURITY_ATTRIBUTES *p_new_sa= nullptr;
199 const char *perror= nullptr;
200 TCHAR last_error_msg[256];
201
202 // Set up security attributes to provide full access to the owner
203 // and minimal read/write access to others.
204 if (my_security_attr_create(&p_new_sa, &perror, NAMED_PIPE_OWNER_PERMISSIONS,
205 NAMED_PIPE_EVERYONE_PERMISSIONS) != 0)
206 {
207 sql_print_error("my_security_attr_create: %s", perror);
208 return true;
209 }
210 if (new_group_name && new_group_name[0] != '\0')
211 {
212 if (my_security_attr_add_rights_to_group(
213 p_new_sa, new_group_name,
214 NAMED_PIPE_FULL_ACCESS_GROUP_PERMISSIONS))
215 {
216 sql_print_error("my_security_attr_add_rights_to_group failed for group: %s",
217 new_group_name);
218 return false;
219 }
220 }
221
222 mp_sa_pipe_security= p_new_sa;
223
224 // Set the DACL for the existing "listener" named pipe instance...
225 if (m_pipe_handle != INVALID_HANDLE_VALUE) {
226 PACL pdacl= NULL;
227 BOOL dacl_present_in_descriptor= FALSE;
228 BOOL dacl_defaulted= FALSE;
229 if (!GetSecurityDescriptorDacl(p_new_sa->lpSecurityDescriptor,
230 &dacl_present_in_descriptor, &pdacl,
231 &dacl_defaulted) ||
232 !dacl_present_in_descriptor)
233 {
234 DWORD last_error_num= GetLastError();
235 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
236 NULL, last_error_num,
237 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), last_error_msg,
238 sizeof(last_error_msg) / sizeof(TCHAR), NULL);
239 sql_print_error("GetSecurityDescriptorDacl failed: %s", last_error_msg);
240 return true;
241 }
242 DWORD res =
243 SetSecurityInfo(m_pipe_handle, SE_KERNEL_OBJECT,
244 DACL_SECURITY_INFORMATION, NULL, NULL, pdacl, NULL);
245 if (res != ERROR_SUCCESS)
246 {
247 char num_buff[20];
248 int10_to_str(res, num_buff, 10);
249 sql_print_error("SetSecurityInfo failed to update DACL on named pipe: %s", num_buff);
250 return true;
251 }
252 }
253 return false;
254 }
255
close_listener()256 void Named_pipe_listener::close_listener()
257 {
258 if (m_pipe_handle == INVALID_HANDLE_VALUE)
259 return;
260
261 DBUG_PRINT("quit", ("Deintializing Named_pipe_connection_acceptor"));
262
263 /* Create connection to the handle named pipe handler to break the loop */
264 HANDLE temp;
265 if ((temp= CreateFile(m_pipe_path_name, NAMED_PIPE_EVERYONE_PERMISSIONS, 0,
266 NULL, OPEN_EXISTING, 0, NULL)) !=
267 INVALID_HANDLE_VALUE)
268 {
269 WaitNamedPipe(m_pipe_path_name, 1000);
270 DWORD dwMode= PIPE_READMODE_BYTE | PIPE_WAIT;
271 SetNamedPipeHandleState(temp, &dwMode, NULL, NULL);
272 CancelIo(temp);
273 DisconnectNamedPipe(temp);
274 CloseHandle(temp);
275 }
276 CloseHandle(m_connect_overlapped.hEvent);
277 my_security_attr_free(mp_sa_pipe_security);
278 mp_sa_pipe_security= nullptr;
279 }
280