mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 02:16:32 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			289 lines
		
	
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			289 lines
		
	
	
	
		
			7.1 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"
 | |
| 
 | |
| 
 | |
| /** Handshake class implementation **********************************/
 | |
| 
 | |
| /**
 | |
|   Create common part of handshake context.
 | |
| 
 | |
|   @param[in]  ssp   name of the SSP (Security Service Provider) to
 | |
|                     be used for authentication
 | |
|   @param[in]  side  is this handshake object used for server- or
 | |
|                     client-side handshake
 | |
| 
 | |
|   Prepare for handshake using the @c ssp security module. We use
 | |
|   "Negotiate" which picks best available module. Parameter @c side
 | |
|   tells if this is preparing for server or client side authentication
 | |
|   and is used to prepare appropriate credentials.
 | |
| */
 | |
| 
 | |
| Handshake::Handshake(const char *ssp, side_t side)
 | |
| : m_atts(0L), m_error(0), m_complete(FALSE),
 | |
|   m_have_credentials(false), m_have_sec_context(false)
 | |
| #ifndef DBUG_OFF
 | |
|   , m_ssp_info(NULL)
 | |
| #endif
 | |
| {
 | |
|   SECURITY_STATUS ret;
 | |
| 
 | |
|   // Obtain credentials for the authentication handshake.
 | |
| 
 | |
|   ret= AcquireCredentialsHandle(NULL, (SEC_CHAR*)ssp,
 | |
|          side == SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
 | |
|          NULL, NULL, NULL, NULL, &m_cred, &m_expire);
 | |
| 
 | |
|   if (ret != SEC_E_OK)
 | |
|   {
 | |
|     DBUG_PRINT("error", ("AcqireCredentialsHandle() failed"
 | |
|                          " with error %X", ret));
 | |
|     ERROR_LOG(ERROR, ("Could not obtain local credentials"
 | |
|                       " required for authentication"));
 | |
|     m_error= ret;
 | |
|   }
 | |
| 
 | |
|   m_have_credentials= true;
 | |
| }
 | |
| 
 | |
| 
 | |
| Handshake::~Handshake()
 | |
| {
 | |
|   if (m_have_credentials)
 | |
|     FreeCredentialsHandle(&m_cred);
 | |
|   if (m_have_sec_context)
 | |
|     DeleteSecurityContext(&m_sctx);
 | |
|   m_output.free();
 | |
| 
 | |
| #ifndef DBUG_OFF
 | |
|   if (m_ssp_info)
 | |
|     FreeContextBuffer(m_ssp_info);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Read and process data packets from the other end of a connection.
 | |
| 
 | |
|   @param[IN] con  a connection to read packets from
 | |
| 
 | |
|   Packets are read and processed until authentication handshake is 
 | |
|   complete. It is assumed that the peer will send at least one packet. 
 | |
|   Packets are processed with @c process_data() method. If new data is
 | |
|   generated during packet processing, this data is sent to the peer and
 | |
|   another round of packet exchange starts.
 | |
| 
 | |
|   @return 0 on success.
 | |
| 
 | |
|   @note In case of error, appropriate error message is logged.
 | |
| */
 | |
| int Handshake::packet_processing_loop()
 | |
| {
 | |
|   m_round= 0;
 | |
| 
 | |
|   do {
 | |
|     ++m_round;
 | |
|     // Read packet send by the peer
 | |
| 
 | |
|     DBUG_PRINT("info", ("Waiting for packet"));
 | |
|     Blob packet= read_packet();
 | |
|     if (error())
 | |
|     {
 | |
|       ERROR_LOG(ERROR, ("Error reading packet in round %d", m_round));
 | |
|       return 1;
 | |
|     }
 | |
|     DBUG_PRINT("info", ("Got packet of length %d", packet.len()));
 | |
| 
 | |
|     /*
 | |
|       Process received data, possibly generating new data to be sent.
 | |
|     */
 | |
| 
 | |
|     Blob new_data= process_data(packet);
 | |
| 
 | |
|     if (error())
 | |
|     {
 | |
|       ERROR_LOG(ERROR, ("Error processing packet in round %d", m_round));
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       If new data has been generated, send it to the peer. Otherwise
 | |
|       handshake must be completed.
 | |
|     */
 | |
| 
 | |
|     if (!new_data.is_null())
 | |
|     {
 | |
|       DBUG_PRINT("info", ("Round %d started", m_round));
 | |
| 
 | |
|       DBUG_PRINT("info", ("Sending packet of length %d", new_data.len()));
 | |
|       int ret= write_packet(new_data);
 | |
|       if (ret)
 | |
|       {
 | |
|         ERROR_LOG(ERROR, ("Error writing packet in round %d", m_round));
 | |
|         return 1;
 | |
|       }
 | |
|       DBUG_PRINT("info", ("Data sent"));
 | |
|     }
 | |
|     else if (!is_complete())
 | |
|     {
 | |
|       ERROR_LOG(ERROR, ("No data to send in round %d"
 | |
|                         " but handshake is not complete", m_round));
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       To protect against malicious clients, break handshake exchange if
 | |
|       too many rounds.
 | |
|     */
 | |
| 
 | |
|     if (m_round > MAX_HANDSHAKE_ROUNDS)
 | |
|     {
 | |
|       ERROR_LOG(ERROR, ("Authentication handshake could not be completed"
 | |
|                         " after %d rounds", m_round));
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   } while(!is_complete());
 | |
| 
 | |
|   ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round));
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifndef DBUG_OFF
 | |
| 
 | |
| /**
 | |
|   Get name of the security package which was used in authentication.
 | |
| 
 | |
|   This method should be called only after handshake was completed. It is
 | |
|   available only in debug builds.
 | |
| 
 | |
|   @return Name of security package or NULL if it can not be obtained.
 | |
| */
 | |
| 
 | |
| const char* Handshake::ssp_name()
 | |
| {
 | |
|   if (!m_ssp_info && m_complete)
 | |
|   {
 | |
|     SecPkgContext_PackageInfo pinfo;
 | |
| 
 | |
|     int ret= QueryContextAttributes(&m_sctx, SECPKG_ATTR_PACKAGE_INFO, &pinfo);
 | |
| 
 | |
|     if (SEC_E_OK == ret)
 | |
|     {
 | |
|       m_ssp_info= pinfo.PackageInfo;
 | |
|     }
 | |
|     else
 | |
|       DBUG_PRINT("error",
 | |
|                  ("Could not obtain SSP info from authentication context"
 | |
|                   ", QueryContextAttributes() failed with error %X", ret));
 | |
|   }
 | |
| 
 | |
|   return m_ssp_info ? m_ssp_info->Name : NULL;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Process result of @c {Initialize,Accept}SecurityContext() function.
 | |
| 
 | |
|   @param[in]  ret   return code from @c {Initialize,Accept}SecurityContext()
 | |
|                     function
 | |
| 
 | |
|   This function analyses return value of Windows
 | |
|   @c {Initialize,Accept}SecurityContext() function. A call to
 | |
|   @c CompleteAuthToken() is done if requested. If authentication is complete,
 | |
|   this fact is marked in the internal state of the Handshake object.
 | |
|   If errors are detected the object is moved to error state.
 | |
| 
 | |
|   @return True if error has been detected.
 | |
| */
 | |
| 
 | |
| bool Handshake::process_result(int ret)
 | |
| {
 | |
|   /*
 | |
|     First check for errors and set the m_complete flag if the result
 | |
|     indicates that handshake is complete.
 | |
|   */
 | |
| 
 | |
|   switch (ret)
 | |
|   {
 | |
|   case SEC_E_OK:
 | |
|   case SEC_I_COMPLETE_NEEDED:
 | |
|     // Handshake completed
 | |
|     m_complete= true;
 | |
|     break;
 | |
| 
 | |
|   case SEC_I_CONTINUE_NEEDED:
 | |
|   case SEC_I_COMPLETE_AND_CONTINUE:
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     m_error= ret;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   m_have_sec_context= true;
 | |
| 
 | |
|   /*
 | |
|     If the result indicates a need for this, complete the authentication
 | |
|     token.
 | |
|   */
 | |
| 
 | |
|   switch (ret)
 | |
|   {
 | |
|   case SEC_I_COMPLETE_NEEDED:
 | |
|   case SEC_I_COMPLETE_AND_CONTINUE:
 | |
|     ret= CompleteAuthToken(&m_sctx, &m_output);
 | |
|     if (ret != 0)
 | |
|     {
 | |
|       DBUG_PRINT("error", ("CompleteAuthToken() failed with error %X", ret));
 | |
|       m_error= ret;
 | |
|       return true;
 | |
|     }
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** Security_buffer class implementation **********************************/
 | |
| 
 | |
| 
 | |
| Security_buffer::Security_buffer(const Blob &blob): m_allocated(false)
 | |
| {
 | |
|   init(blob.ptr(), blob.len());
 | |
| }
 | |
| 
 | |
| 
 | |
| Security_buffer::Security_buffer(): m_allocated(true)
 | |
| {
 | |
|   init(NULL, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Security_buffer::free(void)
 | |
| {
 | |
|   if (!m_allocated)
 | |
|     return;
 | |
|   if (!ptr())
 | |
|     return;
 | |
|   FreeContextBuffer(ptr());
 | |
|   m_allocated= false;
 | |
| }
 | 
