1 /************************************************************************************
2 Copyright (C) 2014 MariaDB Corporation Ab
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with this library; if not see <http://www.gnu.org/licenses>
16 or write to the Free Software Foundation, Inc.,
17 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
18
19 Author: Georg Richter
20
21 *************************************************************************************/
22 #include "ma_schannel.h"
23 #include "schannel_certs.h"
24 #include <assert.h>
25
26 #define SC_IO_BUFFER_SIZE 0x4000
27 #define MAX_SSL_ERR_LEN 100
28
29 #define SCHANNEL_PAYLOAD(A) ((A).cbMaximumMessage + (A).cbHeader + (A).cbTrailer)
30 void ma_schannel_set_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
31
32
33
34
35 /* {{{ void ma_schannel_set_sec_error */
ma_schannel_set_sec_error(MARIADB_PVIO * pvio,DWORD ErrorNo)36 void ma_schannel_set_sec_error(MARIADB_PVIO* pvio, DWORD ErrorNo)
37 {
38 MYSQL* mysql = pvio->mysql;
39 if (ErrorNo != SEC_E_OK)
40 mysql->net.extension->extended_errno = ErrorNo;
41 if (ErrorNo == SEC_E_INTERNAL_ERROR && GetLastError())
42 {
43 ma_schannel_set_win_error(pvio, GetLastError());
44 return;
45 }
46 ma_schannel_set_win_error(pvio, ErrorNo);
47 }
48 /* }}} */
49
50 #include "win32_errmsg.h"
51 /* {{{ void ma_schnnel_set_win_error */
ma_schannel_set_win_error(MARIADB_PVIO * pvio,DWORD ErrorNo)52 void ma_schannel_set_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo)
53 {
54 char buffer[256];
55 ma_format_win32_error(buffer, sizeof(buffer), ErrorNo, "SSL connection error: ");
56 pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, buffer);
57 return;
58 }
59 /* }}} */
60
61
62 /* }}} */
63
64 /* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData) */
65 /*
66 perform handshake loop
67
68 SYNOPSIS
69 ma_schannel_handshake_loop()
70 pvio Pointer to an Communication/IO structure
71 InitialRead TRUE if it's the very first read
72 ExtraData Pointer to an SecBuffer which contains extra data (sent by application)
73
74
75 */
76
ma_schannel_handshake_loop(MARIADB_PVIO * pvio,my_bool InitialRead,SecBuffer * pExtraData)77 SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData)
78 {
79 SecBufferDesc OutBuffer, InBuffer;
80 SecBuffer InBuffers[2], OutBuffers;
81 DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
82 TimeStamp tsExpiry;
83 SECURITY_STATUS rc;
84 PUCHAR IoBuffer;
85 BOOL fDoRead;
86 MARIADB_TLS *ctls= pvio->ctls;
87 SC_CTX *sctx= (SC_CTX *)ctls->ssl;
88
89
90 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
91 ISC_REQ_REPLAY_DETECT |
92 ISC_REQ_CONFIDENTIALITY |
93 ISC_RET_EXTENDED_ERROR |
94 ISC_REQ_ALLOCATE_MEMORY |
95 ISC_REQ_STREAM;
96
97
98 /* Allocate data buffer */
99 if (!(IoBuffer = malloc(SC_IO_BUFFER_SIZE)))
100 return SEC_E_INSUFFICIENT_MEMORY;
101
102 cbIoBuffer = 0;
103 fDoRead = InitialRead;
104
105 /* handshake loop: We will leave if handshake is finished
106 or an error occurs */
107
108 rc = SEC_I_CONTINUE_NEEDED;
109
110 while (rc == SEC_I_CONTINUE_NEEDED ||
111 rc == SEC_E_INCOMPLETE_MESSAGE ||
112 rc == SEC_I_INCOMPLETE_CREDENTIALS )
113 {
114 /* Read data */
115 if (rc == SEC_E_INCOMPLETE_MESSAGE ||
116 !cbIoBuffer)
117 {
118 if(fDoRead)
119 {
120 ssize_t nbytes = pvio->methods->read(pvio, IoBuffer + cbIoBuffer, (size_t)(SC_IO_BUFFER_SIZE - cbIoBuffer));
121 if (nbytes <= 0)
122 {
123 rc = SEC_E_INTERNAL_ERROR;
124 break;
125 }
126 cbData = (DWORD)nbytes;
127 cbIoBuffer += cbData;
128 }
129 else
130 fDoRead = TRUE;
131 }
132
133 /* input buffers
134 First buffer stores data received from server. leftover data
135 will be stored in second buffer with BufferType SECBUFFER_EXTRA */
136
137 InBuffers[0].pvBuffer = IoBuffer;
138 InBuffers[0].cbBuffer = cbIoBuffer;
139 InBuffers[0].BufferType = SECBUFFER_TOKEN;
140
141 InBuffers[1].pvBuffer = NULL;
142 InBuffers[1].cbBuffer = 0;
143 InBuffers[1].BufferType = SECBUFFER_EMPTY;
144
145 InBuffer.cBuffers = 2;
146 InBuffer.pBuffers = InBuffers;
147 InBuffer.ulVersion = SECBUFFER_VERSION;
148
149
150 /* output buffer */
151 OutBuffers.pvBuffer = NULL;
152 OutBuffers.BufferType= SECBUFFER_TOKEN;
153 OutBuffers.cbBuffer = 0;
154
155 OutBuffer.cBuffers = 1;
156 OutBuffer.pBuffers = &OutBuffers;
157 OutBuffer.ulVersion = SECBUFFER_VERSION;
158
159
160 rc = InitializeSecurityContextA(&sctx->CredHdl,
161 &sctx->hCtxt,
162 NULL,
163 dwSSPIFlags,
164 0,
165 SECURITY_NATIVE_DREP,
166 &InBuffer,
167 0,
168 NULL,
169 &OutBuffer,
170 &dwSSPIOutFlags,
171 &tsExpiry );
172
173
174 if (rc == SEC_E_OK ||
175 rc == SEC_I_CONTINUE_NEEDED ||
176 (FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)))
177 {
178 if(OutBuffers.cbBuffer && OutBuffers.pvBuffer)
179 {
180 ssize_t nbytes = pvio->methods->write(pvio, (uchar *)OutBuffers.pvBuffer, (size_t)OutBuffers.cbBuffer);
181 if(nbytes <= 0)
182 {
183 FreeContextBuffer(OutBuffers.pvBuffer);
184 DeleteSecurityContext(&sctx->hCtxt);
185 return SEC_E_INTERNAL_ERROR;
186 }
187 cbData= (DWORD)nbytes;
188 /* Free output context buffer */
189 FreeContextBuffer(OutBuffers.pvBuffer);
190 OutBuffers.pvBuffer = NULL;
191 }
192 }
193 /* check if we need to read more data */
194 switch (rc) {
195 case SEC_E_INCOMPLETE_MESSAGE:
196 /* we didn't receive all data, so just continue loop */
197 continue;
198 break;
199 case SEC_E_OK:
200 /* handshake completed, but we need to check if extra
201 data was sent (which contains encrypted application data) */
202 if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
203 {
204 if (!(pExtraData->pvBuffer= LocalAlloc(0, InBuffers[1].cbBuffer)))
205 return SEC_E_INSUFFICIENT_MEMORY;
206
207 MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
208 pExtraData->BufferType = SECBUFFER_TOKEN;
209 pExtraData->cbBuffer = InBuffers[1].cbBuffer;
210 }
211 else
212 {
213 pExtraData->BufferType= SECBUFFER_EMPTY;
214 pExtraData->pvBuffer= NULL;
215 pExtraData->cbBuffer= 0;
216 }
217 break;
218
219 case SEC_I_INCOMPLETE_CREDENTIALS:
220 /* Provided credentials didn't contain a valid client certificate.
221 We will try to connect anonymously, using current credentials */
222 fDoRead= FALSE;
223 rc= SEC_I_CONTINUE_NEEDED;
224 continue;
225 break;
226 default:
227 if (FAILED(rc))
228 {
229 goto loopend;
230 }
231 break;
232 }
233
234 if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
235 {
236 MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
237 cbIoBuffer = InBuffers[1].cbBuffer;
238 }
239 else
240 cbIoBuffer = 0;
241 }
242 loopend:
243 if (FAILED(rc))
244 {
245 ma_schannel_set_sec_error(pvio, rc);
246 DeleteSecurityContext(&sctx->hCtxt);
247 }
248 free(IoBuffer);
249
250 return rc;
251 }
252 /* }}} */
253
254 /* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls) */
255 /*
256 performs client side handshake
257
258 SYNOPSIS
259 ma_schannel_client_handshake()
260 ctls Pointer to a MARIADB_TLS structure
261
262 DESCRIPTION
263 initiates a client/server handshake. This function can be used
264 by clients only
265
266 RETURN
267 SEC_E_OK on success
268 */
269
ma_schannel_client_handshake(MARIADB_TLS * ctls)270 SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls)
271 {
272 MARIADB_PVIO *pvio;
273 SECURITY_STATUS sRet;
274 DWORD OutFlags;
275 SC_CTX *sctx;
276 SecBuffer ExtraData;
277 DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
278 ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
279 ISC_REQ_USE_SUPPLIED_CREDS |
280 ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
281
282 SecBufferDesc BufferOut;
283 SecBuffer BuffersOut;
284
285 if (!ctls || !ctls->pvio)
286 return 1;
287
288 pvio= ctls->pvio;
289 sctx= (SC_CTX *)ctls->ssl;
290
291 /* Initialie securifty context */
292 BuffersOut.BufferType= SECBUFFER_TOKEN;
293 BuffersOut.cbBuffer= 0;
294 BuffersOut.pvBuffer= NULL;
295
296
297 BufferOut.cBuffers= 1;
298 BufferOut.pBuffers= &BuffersOut;
299 BufferOut.ulVersion= SECBUFFER_VERSION;
300
301 sRet = InitializeSecurityContext(&sctx->CredHdl,
302 NULL,
303 pvio->mysql->host,
304 SFlags,
305 0,
306 SECURITY_NATIVE_DREP,
307 NULL,
308 0,
309 &sctx->hCtxt,
310 &BufferOut,
311 &OutFlags,
312 NULL);
313
314 if(sRet != SEC_I_CONTINUE_NEEDED)
315 {
316 ma_schannel_set_sec_error(pvio, sRet);
317 return sRet;
318 }
319
320 /* send client hello */
321 if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL)
322 {
323 ssize_t nbytes = (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer);
324
325 if (nbytes <= 0)
326 {
327 sRet= SEC_E_INTERNAL_ERROR;
328 goto end;
329 }
330 }
331 sRet= ma_schannel_handshake_loop(pvio, TRUE, &ExtraData);
332
333 /* allocate IO-Buffer for write operations: After handshake
334 was successful, we are able now to calculate payload */
335 if ((sRet = QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes )))
336 goto end;
337
338 sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
339 if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, sctx->IoBufferSize)))
340 {
341 sRet= SEC_E_INSUFFICIENT_MEMORY;
342 goto end;
343 }
344
345 return sRet;
346 end:
347 if (BuffersOut.pvBuffer)
348 FreeContextBuffer(BuffersOut.pvBuffer);
349 return sRet;
350 }
351 /* }}} */
352
353 /* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext,
354 DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
355 /*
356 Reads encrypted data from a SSL stream and decrypts it.
357
358 SYNOPSIS
359 ma_schannel_read
360 pvio pointer to Communication IO structure
361 phContext a context handle
362 DecryptLength size of decrypted buffer
363 ReadBuffer Buffer for decrypted data
364 ReadBufferSize size of ReadBuffer
365
366
367 DESCRIPTION
368 Reads decrypted data from a SSL stream and encrypts it.
369
370 RETURN
371 SEC_E_OK on success
372 SEC_E_* if an error occurred
373 */
374
ma_schannel_read_decrypt(MARIADB_PVIO * pvio,CtxtHandle * phContext,DWORD * DecryptLength,uchar * ReadBuffer,DWORD ReadBufferSize)375 SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
376 CtxtHandle * phContext,
377 DWORD *DecryptLength,
378 uchar *ReadBuffer,
379 DWORD ReadBufferSize)
380 {
381 ssize_t nbytes = 0;
382 DWORD dwOffset = 0;
383 SC_CTX *sctx;
384 SECURITY_STATUS sRet = 0;
385 SecBufferDesc Msg;
386 SecBuffer Buffers[4];
387 int i;
388
389 if (!pvio || !pvio->methods || !pvio->methods->read || !pvio->ctls || !DecryptLength)
390 return SEC_E_INTERNAL_ERROR;
391
392 sctx = (SC_CTX *)pvio->ctls->ssl;
393 *DecryptLength = 0;
394
395 if (sctx->dataBuf.cbBuffer)
396 {
397 /* Have unread decrypted data from the last time, copy. */
398 nbytes = MIN(ReadBufferSize, sctx->dataBuf.cbBuffer);
399 memcpy(ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
400 sctx->dataBuf.pvBuffer = (char *)(sctx->dataBuf.pvBuffer) + nbytes;
401 sctx->dataBuf.cbBuffer -= (DWORD)nbytes;
402 *DecryptLength = (DWORD)nbytes;
403 return SEC_E_OK;
404 }
405
406
407 while (1)
408 {
409 /* Check for any encrypted data returned by last DecryptMessage() in SECBUFFER_EXTRA buffer. */
410 if (sctx->extraBuf.cbBuffer)
411 {
412 memmove(sctx->IoBuffer, sctx->extraBuf.pvBuffer, sctx->extraBuf.cbBuffer);
413 dwOffset = sctx->extraBuf.cbBuffer;
414 sctx->extraBuf.cbBuffer = 0;
415 }
416
417 do {
418 assert(sctx->IoBufferSize > dwOffset);
419 if (dwOffset == 0 || sRet == SEC_E_INCOMPLETE_MESSAGE)
420 {
421 nbytes = pvio->methods->read(pvio, sctx->IoBuffer + dwOffset, (size_t)(sctx->IoBufferSize - dwOffset));
422 if (nbytes <= 0)
423 {
424 /* server closed connection, or an error */
425 // todo: error
426 return SEC_E_INVALID_HANDLE;
427 }
428 dwOffset += (DWORD)nbytes;
429 }
430 ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
431 Buffers[0].pvBuffer = sctx->IoBuffer;
432 Buffers[0].cbBuffer = dwOffset;
433
434 Buffers[0].BufferType = SECBUFFER_DATA;
435 Buffers[1].BufferType = SECBUFFER_EMPTY;
436 Buffers[2].BufferType = SECBUFFER_EMPTY;
437 Buffers[3].BufferType = SECBUFFER_EMPTY;
438
439 Msg.ulVersion = SECBUFFER_VERSION; // Version number
440 Msg.cBuffers = 4;
441 Msg.pBuffers = Buffers;
442
443 sRet = DecryptMessage(phContext, &Msg, 0, NULL);
444
445 } while (sRet == SEC_E_INCOMPLETE_MESSAGE); /* Continue reading until full message arrives */
446
447
448 if (sRet != SEC_E_OK)
449 {
450 ma_schannel_set_sec_error(pvio, sRet);
451 return sRet;
452 }
453
454 sctx->extraBuf.cbBuffer = 0;
455 sctx->dataBuf.cbBuffer = 0;
456 for (i = 0; i < 4; i++)
457 {
458 if (Buffers[i].BufferType == SECBUFFER_DATA)
459 sctx->dataBuf = Buffers[i];
460 if (Buffers[i].BufferType == SECBUFFER_EXTRA)
461 sctx->extraBuf = Buffers[i];
462 }
463
464
465 if (sctx->dataBuf.cbBuffer)
466 {
467 assert(sctx->dataBuf.pvBuffer);
468 /*
469 Copy at most ReadBufferSize bytes to output.
470 Store the rest (if any) to be processed next time.
471 */
472 nbytes = MIN(sctx->dataBuf.cbBuffer, ReadBufferSize);
473 memcpy((char *)ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
474 sctx->dataBuf.cbBuffer -= (unsigned long)nbytes;
475 sctx->dataBuf.pvBuffer = (char *)sctx->dataBuf.pvBuffer + nbytes;
476
477 *DecryptLength = (DWORD)nbytes;
478 return SEC_E_OK;
479 }
480 // No data buffer, loop
481 }
482 }
483 /* }}} */
484 #include "win32_errmsg.h"
ma_schannel_verify_certs(MARIADB_TLS * ctls,BOOL verify_server_name)485 my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name)
486 {
487 SECURITY_STATUS status;
488
489 MARIADB_PVIO *pvio= ctls->pvio;
490 MYSQL *mysql= pvio->mysql;
491 SC_CTX *sctx = (SC_CTX *)ctls->ssl;
492 const char *ca_file= mysql->options.ssl_ca;
493 const char* ca_path = mysql->options.ssl_capath;
494 const char *crl_file= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL;
495 const char* crl_path = mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL;
496 PCCERT_CONTEXT pServerCert= NULL;
497 char errmsg[256];
498 HCERTSTORE store= NULL;
499 int ret= 0;
500
501 status = schannel_create_store(ca_file, ca_path, crl_file, crl_path, &store, errmsg, sizeof(errmsg));
502 if(status)
503 goto end;
504
505 status = QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert);
506 if (status)
507 {
508 ma_format_win32_error(errmsg, sizeof(errmsg), GetLastError(),
509 "QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed.");
510 goto end;
511 }
512
513 status = schannel_verify_server_certificate(
514 pServerCert,
515 store,
516 crl_file != 0 || crl_path != 0,
517 mysql->host,
518 verify_server_name,
519 errmsg, sizeof(errmsg));
520
521 if (status)
522 goto end;
523
524 ret= 1;
525
526 end:
527 if (!ret)
528 {
529 pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
530 "SSL connection error: %s", errmsg);
531 }
532 if (pServerCert)
533 CertFreeCertificateContext(pServerCert);
534 if(store)
535 schannel_free_store(store);
536 return ret;
537 }
538
539
540 /* {{{ size_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext) */
541 /*
542 Decrypts data and write to SSL stream
543 SYNOPSIS
544 ma_schannel_write_decrypt
545 pvio pointer to Communication IO structure
546 phContext a context handle
547 DecryptLength size of decrypted buffer
548 ReadBuffer Buffer for decrypted data
549 ReadBufferSize size of ReadBuffer
550
551 DESCRIPTION
552 Write encrypted data to SSL stream.
553
554 RETURN
555 SEC_E_OK on success
556 SEC_E_* if an error occurred
557 */
ma_schannel_write_encrypt(MARIADB_PVIO * pvio,uchar * WriteBuffer,size_t WriteBufferSize)558 ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
559 uchar *WriteBuffer,
560 size_t WriteBufferSize)
561 {
562 SECURITY_STATUS scRet;
563 SecBufferDesc Message;
564 SecBuffer Buffers[4];
565 SC_CTX *sctx= (SC_CTX *)pvio->ctls->ssl;
566 size_t payload;
567 ssize_t nbytes;
568 DWORD write_size;
569
570 payload= MIN(WriteBufferSize, sctx->Sizes.cbMaximumMessage);
571
572 memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
573
574 Buffers[0].pvBuffer = sctx->IoBuffer;
575 Buffers[0].cbBuffer = sctx->Sizes.cbHeader;
576 Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
577
578 Buffers[1].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader];
579 Buffers[1].cbBuffer = (DWORD)payload;
580 Buffers[1].BufferType = SECBUFFER_DATA;
581
582 Buffers[2].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader] + payload;
583 Buffers[2].cbBuffer = sctx->Sizes.cbTrailer;
584 Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
585
586 Buffers[3].pvBuffer = SECBUFFER_EMPTY;
587 Buffers[3].cbBuffer = SECBUFFER_EMPTY;
588 Buffers[3].BufferType = SECBUFFER_EMPTY;
589
590 Message.ulVersion = SECBUFFER_VERSION;
591 Message.cBuffers = 4;
592 Message.pBuffers = Buffers;
593 if ((scRet = EncryptMessage(&sctx->hCtxt, 0, &Message, 0))!= SEC_E_OK)
594 return -1;
595 write_size = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
596 nbytes = pvio->methods->write(pvio, sctx->IoBuffer, write_size);
597 return nbytes == write_size ? payload : -1;
598 }
599 /* }}} */
600
601 extern char *ssl_protocol_version[5];
602
603 /* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls) */
ma_tls_get_protocol_version(MARIADB_TLS * ctls)604 int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
605 {
606 SC_CTX *sctx;
607 SecPkgContext_ConnectionInfo ConnectionInfo;
608 if (!ctls->ssl)
609 return 1;
610
611 sctx= (SC_CTX *)ctls->ssl;
612
613 if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_CONNECTION_INFO, &ConnectionInfo) != SEC_E_OK)
614 return -1;
615
616 switch(ConnectionInfo.dwProtocol)
617 {
618 case SP_PROT_SSL3_CLIENT:
619 return PROTOCOL_SSLV3;
620 case SP_PROT_TLS1_CLIENT:
621 return PROTOCOL_TLS_1_0;
622 case SP_PROT_TLS1_1_CLIENT:
623 return PROTOCOL_TLS_1_1;
624 case SP_PROT_TLS1_2_CLIENT:
625 return PROTOCOL_TLS_1_2;
626 default:
627 return -1;
628 }
629 }
630 /* }}} */
631