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