mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	Dead code cleanup: part_info->num_parts usage was wrong and working incorrectly in mysql_drop_partitions() because num_parts is already updated in prep_alter_part_table(). We don't have to update part_info->partitions because part_info is destroyed at alter_partition_lock_handling(). Cleanups: - DBUG_EVALUATE_IF() macro replaced by shorter form DBUG_IF(); - Typo in ER_KEY_COLUMN_DOES_NOT_EXITS. Refactorings: - Splitted write_log_replace_delete_frm() into write_log_delete_frm() and write_log_replace_frm(); - partition_info via DDL_LOG_STATE; - set_part_info_exec_log_entry() removed. DBUG_EVALUATE removed DBUG_EVALUTATE was only added for consistency together with DBUG_EVALUATE_IF. It is not used anywhere in the code. DBUG_SUICIDE() fix on release build On release DBUG_SUICIDE() was statement. It was wrong as DBUG_SUICIDE() is used in expression context.
		
			
				
	
	
		
			393 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			393 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* Copyright (c) 2011, 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 Street, Fifth Floor, Boston, MA  02110-1335  USA */
 | 
						|
 | 
						|
#include "handshake.h"
 | 
						|
 | 
						|
#include <mysql.h> // for MYSQL structure
 | 
						|
 | 
						|
 | 
						|
/// Client-side context for authentication handshake
 | 
						|
 | 
						|
class Handshake_client: public Handshake
 | 
						|
{
 | 
						|
  /**
 | 
						|
    Name of the server's service for which we authenticate.
 | 
						|
 | 
						|
    The service name is sent by server in the initial packet. If no
 | 
						|
    service name is used, this member is @c NULL.
 | 
						|
  */
 | 
						|
  SEC_WCHAR  *m_service_name;
 | 
						|
 | 
						|
  /// Buffer for storing service name obtained from server.
 | 
						|
  SEC_WCHAR   m_service_name_buf[MAX_SERVICE_NAME_LENGTH];
 | 
						|
 | 
						|
  Connection &m_con;
 | 
						|
 | 
						|
public:
 | 
						|
 | 
						|
  Handshake_client(Connection &con, const char *target, size_t len);
 | 
						|
  ~Handshake_client();
 | 
						|
 | 
						|
  Blob  first_packet();
 | 
						|
  Blob  process_data(const Blob&);
 | 
						|
 | 
						|
  Blob read_packet();
 | 
						|
  int write_packet(Blob &data);
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create authentication handshake context for client.
 | 
						|
 | 
						|
  @param con     connection for communication with the peer 
 | 
						|
  @param target  name of the target service with which we will authenticate
 | 
						|
                 (can be NULL if not used)
 | 
						|
 | 
						|
  Some security packages (like Kerberos) require providing explicit name
 | 
						|
  of the service with which a client wants to authenticate. The server-side
 | 
						|
  authentication plugin sends this name in the greeting packet
 | 
						|
  (see @c win_auth_handshake_{server,client}() functions).
 | 
						|
*/
 | 
						|
 | 
						|
Handshake_client::Handshake_client(Connection &con, 
 | 
						|
                                   const char *target, size_t len)
 | 
						|
: Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con)
 | 
						|
{
 | 
						|
  if (!target || 0 == len)
 | 
						|
    return;
 | 
						|
 | 
						|
  // Convert received UPN to internal WCHAR representation.
 | 
						|
 | 
						|
  m_service_name= utf8_to_wchar(target, &len);
 | 
						|
 | 
						|
  if (m_service_name)
 | 
						|
    DBUG_PRINT("info", ("Using target service: %S\n", m_service_name));
 | 
						|
  else
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Note: we ignore errors here - m_target will be NULL, the target name
 | 
						|
      will not be used and system will fall-back to NTLM authentication. But
 | 
						|
      we leave trace in error log.
 | 
						|
    */
 | 
						|
    ERROR_LOG(WARNING, ("Could not decode UPN sent by the server"
 | 
						|
                        "; target service name will not be used"
 | 
						|
                        " and Kerberos authentication will not work"));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Handshake_client::~Handshake_client()
 | 
						|
{
 | 
						|
  if (m_service_name)
 | 
						|
    free(m_service_name);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Blob Handshake_client::read_packet()
 | 
						|
{
 | 
						|
  /*
 | 
						|
    We do a fake read in the first round because first
 | 
						|
    packet from the server containing UPN must be read
 | 
						|
    before the handshake context is created and the packet
 | 
						|
    processing loop starts. We return an empty blob here
 | 
						|
    and process_data() function will ignore it.
 | 
						|
  */
 | 
						|
  if (m_round == 1)
 | 
						|
    return Blob();
 | 
						|
 | 
						|
  // Otherwise we read packet from the connection.
 | 
						|
 | 
						|
  Blob packet= m_con.read();
 | 
						|
  m_error= m_con.error();
 | 
						|
  if (!m_error && packet.is_null())
 | 
						|
    m_error= true;  // (no specific error code assigned)
 | 
						|
 | 
						|
  if (m_error)
 | 
						|
    return Blob();
 | 
						|
 | 
						|
  DBUG_PRINT("dump", ("Got the following bytes"));
 | 
						|
  DBUG_DUMP("dump", packet.ptr(), packet.len());
 | 
						|
  return packet;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
int Handshake_client::write_packet(Blob &data)
 | 
						|
{
 | 
						|
  /*
 | 
						|
   Length of the first data payload send by client authentication plugin is
 | 
						|
   limited to 255 bytes (because it is wrapped inside client authentication
 | 
						|
   packet and is length-encoded with 1 byte for the length).
 | 
						|
 | 
						|
   If the data payload is longer than 254 bytes, then it is sent in two parts:
 | 
						|
   first part of length 255 will be embedded in the authentication packet, 
 | 
						|
   second part will be sent in the following packet. Byte 255 of the first 
 | 
						|
   part contains information about the total length of the payload. It is a
 | 
						|
   number of blocks of size 512 bytes which is sufficient to store the
 | 
						|
   combined packets.
 | 
						|
 | 
						|
   Server's logic for reading first client's payload is as follows
 | 
						|
   (see Handshake_server::read_packet()):
 | 
						|
   1. Read data from the authentication packet, if it is shorter than 255 bytes 
 | 
						|
      then that is all data sent by client.
 | 
						|
   2. If there is 255 bytes of data in the authentication packet, read another
 | 
						|
      packet and append it to the data, skipping byte 255 of the first packet
 | 
						|
      which can be used to allocate buffer of appropriate size.
 | 
						|
  */
 | 
						|
 | 
						|
  size_t len2= 0;   // length of the second part of first data payload
 | 
						|
  byte saved_byte;  // for saving byte 255 in which data length is stored
 | 
						|
 | 
						|
  if (m_round == 1 && data.len() > 254)
 | 
						|
  {
 | 
						|
    len2= data.len() - 254;
 | 
						|
    DBUG_PRINT("info", ("Splitting first packet of length %lu"
 | 
						|
                        ", %lu bytes will be sent in a second part", 
 | 
						|
                        data.len(), len2));
 | 
						|
    /* 
 | 
						|
      Store in byte 255 the number of 512b blocks that are needed to
 | 
						|
      keep all the data.
 | 
						|
    */
 | 
						|
    unsigned block_count= (uint)(data.len()/512) + ((data.len() % 512) ? 1 : 0);
 | 
						|
 | 
						|
#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)
 | 
						|
 | 
						|
    /*
 | 
						|
      For testing purposes, use wrong block count to see how server
 | 
						|
      handles this.
 | 
						|
    */
 | 
						|
    DBUG_EXECUTE_IF("winauth_first_packet_test",{
 | 
						|
      block_count= data.len() == 601 ? 0 :
 | 
						|
                   data.len() == 602 ? 1 : 
 | 
						|
                   block_count;
 | 
						|
    });
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
    DBUG_ASSERT(block_count < (unsigned)0x100);
 | 
						|
    saved_byte= data[254];
 | 
						|
    data[254] = block_count;
 | 
						|
 | 
						|
    data.trim(255);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_PRINT("dump", ("Sending the following data"));
 | 
						|
  DBUG_DUMP("dump", data.ptr(), data.len());
 | 
						|
  int ret= m_con.write(data);
 | 
						|
 | 
						|
  if (ret)
 | 
						|
    return ret;
 | 
						|
 | 
						|
  // Write second part if it is present.
 | 
						|
  if (len2)
 | 
						|
  {
 | 
						|
    data[254]= saved_byte;
 | 
						|
    Blob data2(data.ptr() + 254, len2);
 | 
						|
    DBUG_PRINT("info", ("Sending second part of data"));
 | 
						|
    DBUG_DUMP("info", data2.ptr(), data2.len());
 | 
						|
    ret= m_con.write(data2);
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Process data sent by server.
 | 
						|
 | 
						|
  @param[in]  data  blob with data from server
 | 
						|
 | 
						|
  This method analyses data sent by server during authentication handshake.
 | 
						|
  If client should continue packet exchange, this method returns data to
 | 
						|
  be sent to the server next. If no more data needs to be exchanged, an
 | 
						|
  empty blob is returned and @c is_complete() is @c true. In case of error
 | 
						|
  an empty blob is returned and @c error() gives non-zero error code.
 | 
						|
 | 
						|
  When invoked for the first time (in the first round of the handshake)
 | 
						|
  there is no data from the server (data blob is null) and the initial
 | 
						|
  packet is generated without an input.
 | 
						|
 | 
						|
  @return Data to be sent to the server next or null blob if no more data
 | 
						|
  needs to be exchanged or in case of error.
 | 
						|
*/
 | 
						|
 | 
						|
Blob Handshake_client::process_data(const Blob &data)
 | 
						|
{
 | 
						|
#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)
 | 
						|
  /*
 | 
						|
    Code for testing the logic for sending the first client payload.
 | 
						|
 | 
						|
    A fake data of length given by environment variable TEST_PACKET_LENGTH
 | 
						|
    (or default 255 bytes) is sent to the server. First 2 bytes of the
 | 
						|
    payload contain its total length (LSB first). The length of test data
 | 
						|
    is limited to 2048 bytes.
 | 
						|
 | 
						|
    Upon receiving test data, server will check that data is correct and
 | 
						|
    refuse connection. If server detects data errors it will crash on 
 | 
						|
    assertion.
 | 
						|
 | 
						|
    This code is executed if debug flag "winauth_first_packet_test" is
 | 
						|
    set, e.g. using client option:
 | 
						|
 | 
						|
     --debug-dbug="d,winauth_first_packet_test"
 | 
						|
 | 
						|
     The same debug flag must be enabled in the server, e.g. using 
 | 
						|
     statement:
 | 
						|
 | 
						|
     SET GLOBAL debug= '+d,winauth_first_packet_test'; 
 | 
						|
  */
 | 
						|
 | 
						|
  static byte test_buf[2048];
 | 
						|
 | 
						|
  if (m_round == 1 
 | 
						|
      && DBUG_IF("winauth_first_packet_test"))
 | 
						|
  {
 | 
						|
    const char *env= getenv("TEST_PACKET_LENGTH");
 | 
						|
    size_t len= env ? atoi(env) : 0;
 | 
						|
    if (!len)
 | 
						|
      len= 255;
 | 
						|
    if (len > sizeof(test_buf))
 | 
						|
      len= sizeof(test_buf);
 | 
						|
 | 
						|
    // Store data length in first 2 bytes.
 | 
						|
    byte *ptr= test_buf;
 | 
						|
    *ptr++= len & 0xFF;
 | 
						|
    *ptr++= len >> 8;
 | 
						|
 | 
						|
    // Fill remaining bytes with known values.
 | 
						|
    for (byte b= 0; ptr < test_buf + len; ++ptr, ++b)
 | 
						|
      *ptr= b;
 | 
						|
 | 
						|
    return Blob(test_buf, len);
 | 
						|
  };
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
  Security_buffer  input(data);
 | 
						|
  SECURITY_STATUS  ret;
 | 
						|
 | 
						|
  m_output.free();
 | 
						|
 | 
						|
  ret= InitializeSecurityContextW(
 | 
						|
         &m_cred,
 | 
						|
         m_round == 1 ? NULL : &m_sctx,        // partial context
 | 
						|
         m_service_name,                       // service name
 | 
						|
         ASC_REQ_ALLOCATE_MEMORY,              // requested attributes
 | 
						|
         0,                                    // reserved
 | 
						|
         SECURITY_NETWORK_DREP,                // data representation
 | 
						|
         m_round == 1 ? NULL : &input,         // input data
 | 
						|
         0,                                    // reserved
 | 
						|
         &m_sctx,                              // context
 | 
						|
         &m_output,                            // output data
 | 
						|
         &m_atts,                              // attributes
 | 
						|
         &m_expire);                           // expire date
 | 
						|
 | 
						|
  if (process_result(ret))
 | 
						|
  {
 | 
						|
    DBUG_PRINT("error",
 | 
						|
               ("InitializeSecurityContext() failed with error %X", ret));
 | 
						|
    return Blob();
 | 
						|
  }
 | 
						|
 | 
						|
  return m_output.as_blob();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**********************************************************************/
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Perform authentication handshake from client side.
 | 
						|
 | 
						|
  @param[in]  vio    pointer to @c MYSQL_PLUGIN_VIO instance to be used
 | 
						|
                     for communication with the server
 | 
						|
  @param[in]  mysql  pointer to a MySQL connection for which we authenticate
 | 
						|
 | 
						|
  After reading the initial packet from server, containing its UPN to be
 | 
						|
  used as service name, client starts packet exchange by sending the first
 | 
						|
  packet in this exchange. While handshake is not yet completed, client
 | 
						|
  reads packets sent by the server and process them, possibly generating new
 | 
						|
  data to be sent to the server.
 | 
						|
 | 
						|
  This function reports errors.
 | 
						|
 | 
						|
  @return 0 on success.
 | 
						|
*/
 | 
						|
 | 
						|
int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
 | 
						|
{
 | 
						|
  DBUG_ENTER("win_auth_handshake_client");
 | 
						|
 | 
						|
  /*
 | 
						|
    Check if we should enable logging.
 | 
						|
  */
 | 
						|
  {
 | 
						|
    const char *opt= getenv("AUTHENTICATION_WIN_LOG");
 | 
						|
    int opt_val= opt ? atoi(opt) : 0;
 | 
						|
    if (opt && !opt_val)
 | 
						|
    {
 | 
						|
      if (!strncasecmp("on", opt, 2))    opt_val= 2;
 | 
						|
      if (!strncasecmp("yes", opt, 3))   opt_val= 2;
 | 
						|
      if (!strncasecmp("true", opt, 4))  opt_val= 2;
 | 
						|
      if (!strncasecmp("debug", opt, 5)) opt_val= 4;
 | 
						|
      if (!strncasecmp("dbug", opt, 4))  opt_val= 4;
 | 
						|
    }
 | 
						|
    set_log_level(opt_val);
 | 
						|
  }
 | 
						|
 | 
						|
  ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user));
 | 
						|
 | 
						|
  // Create connection object.
 | 
						|
 | 
						|
  Connection con(vio);
 | 
						|
  DBUG_ASSERT(!con.error());
 | 
						|
 | 
						|
  // Read initial packet from server containing service name.
 | 
						|
 | 
						|
  Blob service_name= con.read();
 | 
						|
 | 
						|
  if (con.error() || service_name.is_null())
 | 
						|
  {
 | 
						|
    ERROR_LOG(ERROR, ("Error reading initial packet"));
 | 
						|
    DBUG_RETURN(CR_ERROR);
 | 
						|
  }
 | 
						|
  DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len()));
 | 
						|
 | 
						|
  // Create authentication handshake context using the given service name.
 | 
						|
 | 
						|
  Handshake_client hndshk(con,
 | 
						|
                          service_name[0] ? (char *)service_name.ptr() : NULL,
 | 
						|
                          service_name.len());
 | 
						|
  if (hndshk.error())
 | 
						|
  {
 | 
						|
    ERROR_LOG(ERROR, ("Could not create authentication handshake context"));
 | 
						|
    DBUG_RETURN(CR_ERROR);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_ASSERT(!hndshk.error());
 | 
						|
 | 
						|
  /*
 | 
						|
    Read and process packets from server until handshake is complete.
 | 
						|
    Note that the first read from server is dummy 
 | 
						|
    (see Handshake_client::read_packet()) as we already have read the 
 | 
						|
    first packet to establish service name.
 | 
						|
  */
 | 
						|
  if (hndshk.packet_processing_loop())
 | 
						|
    DBUG_RETURN(CR_ERROR);
 | 
						|
 | 
						|
  DBUG_ASSERT(!hndshk.error() && hndshk.is_complete());
 | 
						|
 | 
						|
  DBUG_RETURN(CR_OK);
 | 
						|
}
 |