summaryrefslogtreecommitdiffstats
path: root/plugin/win_auth_client/handshake.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plugin/win_auth_client/handshake.cc289
1 files changed, 289 insertions, 0 deletions
diff --git a/plugin/win_auth_client/handshake.cc b/plugin/win_auth_client/handshake.cc
new file mode 100644
index 00000000..cd63ee26
--- /dev/null
+++ b/plugin/win_auth_client/handshake.cc
@@ -0,0 +1,289 @@
+/* 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;
+}