mysql5/mysql-5.7.27/sql/conn_handler/named_pipe_connection.cc

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;
}