MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
handshake_client.cc
1 /* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
15 
16 #include "handshake.h"
17 
18 #include <mysql.h> // for MYSQL structure
19 
20 
22 
24 {
31  SEC_WCHAR *m_service_name;
32 
34  SEC_WCHAR m_service_name_buf[MAX_SERVICE_NAME_LENGTH];
35 
36  Connection &m_con;
37 
38 public:
39 
40  Handshake_client(Connection &con, const char *target, size_t len);
42 
43  Blob first_packet();
44  Blob process_data(const Blob&);
45 
46  Blob read_packet();
47  int write_packet(Blob &data);
48 };
49 
50 
65  const char *target, size_t len)
66 : Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con)
67 {
68  if (!target || 0 == len)
69  return;
70 
71  // Convert received UPN to internal WCHAR representation.
72 
73  m_service_name= utf8_to_wchar(target, &len);
74 
75  if (m_service_name)
76  DBUG_PRINT("info", ("Using target service: %S\n", m_service_name));
77  else
78  {
79  /*
80  Note: we ignore errors here - m_target will be NULL, the target name
81  will not be used and system will fall-back to NTLM authentication. But
82  we leave trace in error log.
83  */
84  ERROR_LOG(WARNING, ("Could not decode UPN sent by the server"
85  "; target service name will not be used"
86  " and Kerberos authentication will not work"));
87  }
88 }
89 
90 
91 Handshake_client::~Handshake_client()
92 {
93  if (m_service_name)
94  free(m_service_name);
95 }
96 
97 
99 {
100  /*
101  We do a fake read in the first round because first
102  packet from the server containing UPN must be read
103  before the handshake context is created and the packet
104  processing loop starts. We return an empty blob here
105  and process_data() function will ignore it.
106  */
107  if (m_round == 1)
108  return Blob();
109 
110  // Otherwise we read packet from the connection.
111 
112  Blob packet= m_con.read();
113  m_error= m_con.error();
114  if (!m_error && packet.is_null())
115  m_error= true; // (no specific error code assigned)
116 
117  if (m_error)
118  return Blob();
119 
120  DBUG_PRINT("dump", ("Got the following bytes"));
121  DBUG_DUMP("dump", packet.ptr(), packet.len());
122  return packet;
123 }
124 
125 
126 
128 {
129  /*
130  Length of the first data payload send by client authentication plugin is
131  limited to 255 bytes (because it is wrapped inside client authentication
132  packet and is length-encoded with 1 byte for the length).
133 
134  If the data payload is longer than 254 bytes, then it is sent in two parts:
135  first part of length 255 will be embedded in the authentication packet,
136  second part will be sent in the following packet. Byte 255 of the first
137  part contains information about the total length of the payload. It is a
138  number of blocks of size 512 bytes which is sufficient to store the
139  combined packets.
140 
141  Server's logic for reading first client's payload is as follows
142  (see Handshake_server::read_packet()):
143  1. Read data from the authentication packet, if it is shorter than 255 bytes
144  then that is all data sent by client.
145  2. If there is 255 bytes of data in the authentication packet, read another
146  packet and append it to the data, skipping byte 255 of the first packet
147  which can be used to allocate buffer of appropriate size.
148  */
149 
150  size_t len2= 0; // length of the second part of first data payload
151  byte saved_byte; // for saving byte 255 in which data length is stored
152 
153  if (m_round == 1 && data.len() > 254)
154  {
155  len2= data.len() - 254;
156  DBUG_PRINT("info", ("Splitting first packet of length %lu"
157  ", %lu bytes will be sent in a second part",
158  data.len(), len2));
159  /*
160  Store in byte 255 the number of 512b blocks that are needed to
161  keep all the data.
162  */
163  unsigned block_count= data.len()/512 + ((data.len() % 512) ? 1 : 0);
164 
165 #if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)
166 
167  /*
168  For testing purposes, use wrong block count to see how server
169  handles this.
170  */
171  DBUG_EXECUTE_IF("winauth_first_packet_test",{
172  block_count= data.len() == 601 ? 0 :
173  data.len() == 602 ? 1 :
174  block_count;
175  });
176 
177 #endif
178 
179  DBUG_ASSERT(block_count < (unsigned)0x100);
180  saved_byte= data[254];
181  data[254] = block_count;
182 
183  data.trim(255);
184  }
185 
186  DBUG_PRINT("dump", ("Sending the following data"));
187  DBUG_DUMP("dump", data.ptr(), data.len());
188  int ret= m_con.write(data);
189 
190  if (ret)
191  return ret;
192 
193  // Write second part if it is present.
194  if (len2)
195  {
196  data[254]= saved_byte;
197  Blob data2(data.ptr() + 254, len2);
198  DBUG_PRINT("info", ("Sending second part of data"));
199  DBUG_DUMP("info", data2.ptr(), data2.len());
200  ret= m_con.write(data2);
201  }
202 
203  return ret;
204 }
205 
206 
227 {
228 #if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)
229  /*
230  Code for testing the logic for sending the first client payload.
231 
232  A fake data of length given by environment variable TEST_PACKET_LENGTH
233  (or default 255 bytes) is sent to the server. First 2 bytes of the
234  payload contain its total length (LSB first). The length of test data
235  is limited to 2048 bytes.
236 
237  Upon receiving test data, server will check that data is correct and
238  refuse connection. If server detects data errors it will crash on
239  assertion.
240 
241  This code is executed if debug flag "winauth_first_packet_test" is
242  set, e.g. using client option:
243 
244  --debug="d,winauth_first_packet_test"
245 
246  The same debug flag must be enabled in the server, e.g. using
247  statement:
248 
249  SET GLOBAL debug= '+d,winauth_first_packet_test';
250  */
251 
252  static byte test_buf[2048];
253 
254  if (m_round == 1
255  && DBUG_EVALUATE_IF("winauth_first_packet_test", true, false))
256  {
257  const char *env= getenv("TEST_PACKET_LENGTH");
258  size_t len= env ? atoi(env) : 0;
259  if (!len)
260  len= 255;
261  if (len > sizeof(test_buf))
262  len= sizeof(test_buf);
263 
264  // Store data length in first 2 bytes.
265  byte *ptr= test_buf;
266  *ptr++= len & 0xFF;
267  *ptr++= len >> 8;
268 
269  // Fill remaining bytes with known values.
270  for (byte b= 0; ptr < test_buf + len; ++ptr, ++b)
271  *ptr= b;
272 
273  return Blob(test_buf, len);
274  };
275 
276 #endif
277 
278  Security_buffer input(data);
279  SECURITY_STATUS ret;
280 
281  m_output.free();
282 
283  ret= InitializeSecurityContextW(
284  &m_cred,
285  m_round == 1 ? NULL : &m_sctx, // partial context
286  m_service_name, // service name
287  ASC_REQ_ALLOCATE_MEMORY, // requested attributes
288  0, // reserved
289  SECURITY_NETWORK_DREP, // data representation
290  m_round == 1 ? NULL : &input, // input data
291  0, // reserved
292  &m_sctx, // context
293  &m_output, // output data
294  &m_atts, // attributes
295  &m_expire); // expire date
296 
297  if (process_result(ret))
298  {
299  DBUG_PRINT("error",
300  ("InitializeSecurityContext() failed with error %X", ret));
301  return Blob();
302  }
303 
304  return m_output.as_blob();
305 }
306 
307 
308 /**********************************************************************/
309 
310 
329 int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
330 {
331  DBUG_ENTER("win_auth_handshake_client");
332 
333  /*
334  Check if we should enable logging.
335  */
336  {
337  const char *opt= getenv("AUTHENTICATION_WIN_LOG");
338  int opt_val= opt ? atoi(opt) : 0;
339  if (opt && !opt_val)
340  {
341  if (!strncasecmp("on", opt, 2)) opt_val= 2;
342  if (!strncasecmp("yes", opt, 3)) opt_val= 2;
343  if (!strncasecmp("true", opt, 4)) opt_val= 2;
344  if (!strncasecmp("debug", opt, 5)) opt_val= 4;
345  if (!strncasecmp("dbug", opt, 4)) opt_val= 4;
346  }
347  set_log_level(opt_val);
348  }
349 
350  ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user));
351 
352  // Create connection object.
353 
354  Connection con(vio);
355  DBUG_ASSERT(!con.error());
356 
357  // Read initial packet from server containing service name.
358 
359  Blob service_name= con.read();
360 
361  if (con.error() || service_name.is_null())
362  {
363  ERROR_LOG(ERROR, ("Error reading initial packet"));
364  DBUG_RETURN(CR_ERROR);
365  }
366  DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len()));
367 
368  // Create authentication handshake context using the given service name.
369 
370  Handshake_client hndshk(con,
371  service_name[0] ? (char *)service_name.ptr() : NULL,
372  service_name.len());
373  if (hndshk.error())
374  {
375  ERROR_LOG(ERROR, ("Could not create authentication handshake context"));
376  DBUG_RETURN(CR_ERROR);
377  }
378 
379  DBUG_ASSERT(!hndshk.error());
380 
381  /*
382  Read and process packets from server until handshake is complete.
383  Note that the first read from server is dummy
384  (see Handshake_client::read_packet()) as we already have read the
385  first packet to establish service name.
386  */
387  if (hndshk.packet_processing_loop())
388  DBUG_RETURN(CR_ERROR);
389 
390  DBUG_ASSERT(!hndshk.error() && hndshk.is_complete());
391 
392  DBUG_RETURN(CR_OK);
393 }