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-1335 USA */
15
16 #include "handshake.h"
17
18 #include <mysql.h> // for MYSQL structure
19
20
21 /// Client-side context for authentication handshake
22
23 class Handshake_client: public Handshake
24 {
25 /**
26 Name of the server's service for which we authenticate.
27
28 The service name is sent by server in the initial packet. If no
29 service name is used, this member is @c NULL.
30 */
31 SEC_WCHAR *m_service_name;
32
33 /// Buffer for storing service name obtained from server.
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);
41 ~Handshake_client();
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
51 /**
52 Create authentication handshake context for client.
53
54 @param con connection for communication with the peer
55 @param target name of the target service with which we will authenticate
56 (can be NULL if not used)
57
58 Some security packages (like Kerberos) require providing explicit name
59 of the service with which a client wants to authenticate. The server-side
60 authentication plugin sends this name in the greeting packet
61 (see @c win_auth_handshake_{server,client}() functions).
62 */
63
Handshake_client(Connection & con,const char * target,size_t len)64 Handshake_client::Handshake_client(Connection &con,
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
~Handshake_client()91 Handshake_client::~Handshake_client()
92 {
93 if (m_service_name)
94 free(m_service_name);
95 }
96
97
read_packet()98 Blob Handshake_client::read_packet()
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
write_packet(Blob & data)127 int Handshake_client::write_packet(Blob &data)
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= (uint)(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
207 /**
208 Process data sent by server.
209
210 @param[in] data blob with data from server
211
212 This method analyses data sent by server during authentication handshake.
213 If client should continue packet exchange, this method returns data to
214 be sent to the server next. If no more data needs to be exchanged, an
215 empty blob is returned and @c is_complete() is @c true. In case of error
216 an empty blob is returned and @c error() gives non-zero error code.
217
218 When invoked for the first time (in the first round of the handshake)
219 there is no data from the server (data blob is null) and the initial
220 packet is generated without an input.
221
222 @return Data to be sent to the server next or null blob if no more data
223 needs to be exchanged or in case of error.
224 */
225
process_data(const Blob & data)226 Blob Handshake_client::process_data(const Blob &data)
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-dbug="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
311 /**
312 Perform authentication handshake from client side.
313
314 @param[in] vio pointer to @c MYSQL_PLUGIN_VIO instance to be used
315 for communication with the server
316 @param[in] mysql pointer to a MySQL connection for which we authenticate
317
318 After reading the initial packet from server, containing its UPN to be
319 used as service name, client starts packet exchange by sending the first
320 packet in this exchange. While handshake is not yet completed, client
321 reads packets sent by the server and process them, possibly generating new
322 data to be sent to the server.
323
324 This function reports errors.
325
326 @return 0 on success.
327 */
328
win_auth_handshake_client(MYSQL_PLUGIN_VIO * vio,MYSQL * mysql)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 }
394