mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	 d324c03d0c
			
		
	
	
	d324c03d0c
	
	
	
		
			
			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);
 | |
| }
 |