273 lines
8.9 KiB
C++
273 lines
8.9 KiB
C++
/*
|
|
Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "named_pipe_connection.h"
|
|
|
|
#include <AclAPI.h> // Windows access control API
|
|
#include "violite.h" // Vio
|
|
#include "channel_info.h" // Channel_info
|
|
#include "connection_handler_manager.h" // Connection_handler_manager
|
|
#include "log.h" // sql_print_error
|
|
#include "named_pipe.h" // create_server_named_pipe.
|
|
#include "sql_class.h" // THD
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Channel_info_named_pipe implementation
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
This class abstracts the info. about windows named pipe of communication
|
|
with server from client.
|
|
*/
|
|
class Channel_info_named_pipe : public Channel_info
|
|
{
|
|
// Handle to named pipe.
|
|
HANDLE m_handle;
|
|
|
|
protected:
|
|
virtual Vio* create_and_init_vio() const
|
|
{
|
|
return vio_new_win32pipe(m_handle);
|
|
}
|
|
|
|
public:
|
|
/**
|
|
Constructor that sets the pipe handle
|
|
|
|
@param handle connected pipe handle
|
|
*/
|
|
Channel_info_named_pipe(HANDLE handle)
|
|
: m_handle(handle)
|
|
{ }
|
|
|
|
virtual THD* create_thd()
|
|
{
|
|
THD* thd= Channel_info::create_thd();
|
|
|
|
if (thd != NULL)
|
|
thd->security_context()->set_host_ptr(my_localhost, strlen(my_localhost));
|
|
return thd;
|
|
}
|
|
|
|
virtual void send_error_and_close_channel(uint errorcode,
|
|
int error,
|
|
bool senderror)
|
|
{
|
|
Channel_info::send_error_and_close_channel(errorcode, error, senderror);
|
|
|
|
DisconnectNamedPipe(m_handle);
|
|
CloseHandle(m_handle);
|
|
}
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Named_pipe_listener implementation
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Named_pipe_listener::setup_listener()
|
|
{
|
|
m_connect_overlapped.hEvent= CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (!m_connect_overlapped.hEvent)
|
|
{
|
|
sql_print_error("Can't create event, last error=%u", GetLastError());
|
|
return true;
|
|
}
|
|
|
|
m_pipe_handle= create_server_named_pipe(
|
|
&mp_sa_pipe_security, global_system_variables.net_buffer_length,
|
|
m_pipe_name.c_str(), m_pipe_path_name, sizeof(m_pipe_path_name),
|
|
named_pipe_full_access_group);
|
|
if (m_pipe_handle == INVALID_HANDLE_VALUE)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
Channel_info* Named_pipe_listener::listen_for_connection_event()
|
|
{
|
|
TCHAR last_error_msg[256];
|
|
|
|
/* wait for named pipe connection */
|
|
BOOL fConnected= ConnectNamedPipe(m_pipe_handle, &m_connect_overlapped);
|
|
if (!fConnected && (GetLastError() == ERROR_IO_PENDING))
|
|
{
|
|
/*
|
|
ERROR_IO_PENDING says async IO has started but not yet finished.
|
|
GetOverlappedResult will wait for completion.
|
|
*/
|
|
DWORD bytes;
|
|
fConnected= GetOverlappedResult(m_pipe_handle, &m_connect_overlapped,
|
|
&bytes, TRUE);
|
|
}
|
|
if (abort_loop)
|
|
return NULL;
|
|
if (!fConnected)
|
|
fConnected= GetLastError() == ERROR_PIPE_CONNECTED;
|
|
if (!fConnected)
|
|
{
|
|
CloseHandle(m_pipe_handle);
|
|
mysql_rwlock_rdlock(&LOCK_named_pipe_full_access_group);
|
|
m_pipe_handle= CreateNamedPipe(
|
|
m_pipe_path_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC,
|
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
(int)global_system_variables.net_buffer_length,
|
|
(int)global_system_variables.net_buffer_length,
|
|
NMPWAIT_USE_DEFAULT_WAIT, mp_sa_pipe_security);
|
|
mysql_rwlock_unlock(&LOCK_named_pipe_full_access_group);
|
|
if (m_pipe_handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD last_error_num= GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
|
NULL, last_error_num,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), last_error_msg,
|
|
sizeof(last_error_msg) / sizeof(TCHAR), NULL);
|
|
sql_print_error("Can't create new named pipe: %s", last_error_msg);
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
/* A new pipe has been successfully created, it's not connected yet so
|
|
return NULL to spin around and wait for connection on it.
|
|
*/
|
|
return NULL;
|
|
}
|
|
}
|
|
HANDLE hConnectedPipe= m_pipe_handle;
|
|
/* create new pipe for new connection */
|
|
mysql_rwlock_rdlock(&LOCK_named_pipe_full_access_group);
|
|
m_pipe_handle= CreateNamedPipe(
|
|
m_pipe_path_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC,
|
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
|
|
(int)global_system_variables.net_buffer_length,
|
|
(int)global_system_variables.net_buffer_length, NMPWAIT_USE_DEFAULT_WAIT,
|
|
mp_sa_pipe_security);
|
|
mysql_rwlock_unlock(&LOCK_named_pipe_full_access_group);
|
|
if (m_pipe_handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD last_error_num= GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
|
NULL, last_error_num,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), last_error_msg,
|
|
sizeof(last_error_msg) / sizeof(TCHAR), NULL);
|
|
sql_print_error("Can't create new named pipe: %s", last_error_msg);
|
|
m_pipe_handle= hConnectedPipe;
|
|
return NULL; // We have to try again
|
|
}
|
|
|
|
Channel_info* channel_info= new (std::nothrow)
|
|
Channel_info_named_pipe(hConnectedPipe);
|
|
if (channel_info == NULL)
|
|
{
|
|
DisconnectNamedPipe(hConnectedPipe);
|
|
CloseHandle(hConnectedPipe);
|
|
return NULL;
|
|
}
|
|
return channel_info;
|
|
}
|
|
|
|
bool Named_pipe_listener::update_named_pipe_full_access_group(
|
|
const char *new_group_name)
|
|
{
|
|
SECURITY_ATTRIBUTES *p_new_sa= nullptr;
|
|
const char *perror= nullptr;
|
|
TCHAR last_error_msg[256];
|
|
|
|
// Set up security attributes to provide full access to the owner
|
|
// and minimal read/write access to others.
|
|
if (my_security_attr_create(&p_new_sa, &perror, NAMED_PIPE_OWNER_PERMISSIONS,
|
|
NAMED_PIPE_EVERYONE_PERMISSIONS) != 0)
|
|
{
|
|
sql_print_error("my_security_attr_create: %s", perror);
|
|
return true;
|
|
}
|
|
if (new_group_name && new_group_name[0] != '\0')
|
|
{
|
|
if (my_security_attr_add_rights_to_group(
|
|
p_new_sa, new_group_name,
|
|
NAMED_PIPE_FULL_ACCESS_GROUP_PERMISSIONS))
|
|
{
|
|
sql_print_error("my_security_attr_add_rights_to_group failed for group: %s",
|
|
new_group_name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mp_sa_pipe_security= p_new_sa;
|
|
|
|
// Set the DACL for the existing "listener" named pipe instance...
|
|
if (m_pipe_handle != INVALID_HANDLE_VALUE) {
|
|
PACL pdacl= NULL;
|
|
BOOL dacl_present_in_descriptor= FALSE;
|
|
BOOL dacl_defaulted= FALSE;
|
|
if (!GetSecurityDescriptorDacl(p_new_sa->lpSecurityDescriptor,
|
|
&dacl_present_in_descriptor, &pdacl,
|
|
&dacl_defaulted) ||
|
|
!dacl_present_in_descriptor)
|
|
{
|
|
DWORD last_error_num= GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, last_error_num,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), last_error_msg,
|
|
sizeof(last_error_msg) / sizeof(TCHAR), NULL);
|
|
sql_print_error("GetSecurityDescriptorDacl failed: %s", last_error_msg);
|
|
return true;
|
|
}
|
|
DWORD res =
|
|
SetSecurityInfo(m_pipe_handle, SE_KERNEL_OBJECT,
|
|
DACL_SECURITY_INFORMATION, NULL, NULL, pdacl, NULL);
|
|
if (res != ERROR_SUCCESS)
|
|
{
|
|
char num_buff[20];
|
|
int10_to_str(res, num_buff, 10);
|
|
sql_print_error("SetSecurityInfo failed to update DACL on named pipe: %s", num_buff);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Named_pipe_listener::close_listener()
|
|
{
|
|
if (m_pipe_handle == INVALID_HANDLE_VALUE)
|
|
return;
|
|
|
|
DBUG_PRINT("quit", ("Deintializing Named_pipe_connection_acceptor"));
|
|
|
|
/* Create connection to the handle named pipe handler to break the loop */
|
|
HANDLE temp;
|
|
if ((temp= CreateFile(m_pipe_path_name, NAMED_PIPE_EVERYONE_PERMISSIONS, 0,
|
|
NULL, OPEN_EXISTING, 0, NULL)) !=
|
|
INVALID_HANDLE_VALUE)
|
|
{
|
|
WaitNamedPipe(m_pipe_path_name, 1000);
|
|
DWORD dwMode= PIPE_READMODE_BYTE | PIPE_WAIT;
|
|
SetNamedPipeHandleState(temp, &dwMode, NULL, NULL);
|
|
CancelIo(temp);
|
|
DisconnectNamedPipe(temp);
|
|
CloseHandle(temp);
|
|
}
|
|
CloseHandle(m_connect_overlapped.hEvent);
|
|
my_security_attr_free(mp_sa_pipe_security);
|
|
mp_sa_pipe_security= nullptr;
|
|
}
|