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 = LocalAlloc(LMEM_FIXED, 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   LocalFree(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   DWORD r;
276   SC_CTX *sctx;
277   SecBuffer ExtraData;
278   DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
279                 ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
280                 ISC_REQ_USE_SUPPLIED_CREDS |
281                 ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
282 
283   SecBufferDesc	BufferOut;
284   SecBuffer  BuffersOut;
285 
286   if (!ctls || !ctls->pvio)
287     return 1;
288 
289   pvio= ctls->pvio;
290   sctx= (SC_CTX *)ctls->ssl;
291 
292   /* Initialie securifty context */
293   BuffersOut.BufferType= SECBUFFER_TOKEN;
294   BuffersOut.cbBuffer= 0;
295   BuffersOut.pvBuffer= NULL;
296 
297 
298   BufferOut.cBuffers= 1;
299   BufferOut.pBuffers= &BuffersOut;
300   BufferOut.ulVersion= SECBUFFER_VERSION;
301 
302   sRet = InitializeSecurityContext(&sctx->CredHdl,
303                                     NULL,
304                                     pvio->mysql->host,
305                                     SFlags,
306                                     0,
307                                     SECURITY_NATIVE_DREP,
308                                     NULL,
309                                     0,
310                                     &sctx->hCtxt,
311                                     &BufferOut,
312                                     &OutFlags,
313                                     NULL);
314 
315   if(sRet != SEC_I_CONTINUE_NEEDED)
316   {
317     ma_schannel_set_sec_error(pvio, sRet);
318     return sRet;
319   }
320 
321   /* send client hello packaet */
322   if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL)
323   {
324     ssize_t nbytes = (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer);
325 
326     if (nbytes <= 0)
327     {
328       sRet= SEC_E_INTERNAL_ERROR;
329       goto end;
330     }
331     r = (DWORD)nbytes;
332   }
333   sRet= ma_schannel_handshake_loop(pvio, TRUE, &ExtraData);
334 
335   /* allocate IO-Buffer for write operations: After handshake
336   was successful, we are able now to calculate payload */
337   if ((sRet = QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes )))
338     goto end;
339 
340   sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
341   if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, sctx->IoBufferSize)))
342   {
343     sRet= SEC_E_INSUFFICIENT_MEMORY;
344     goto end;
345   }
346 
347   return sRet;
348 end:
349   if (BuffersOut.pvBuffer)
350     FreeContextBuffer(BuffersOut.pvBuffer);
351   return sRet;
352 }
353 /* }}} */
354 
355 /* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext,
356                                                 DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
357 /*
358   Reads encrypted data from a SSL stream and decrypts it.
359 
360   SYNOPSIS
361     ma_schannel_read
362     pvio              pointer to Communication IO structure
363     phContext        a context handle
364     DecryptLength    size of decrypted buffer
365     ReadBuffer       Buffer for decrypted data
366     ReadBufferSize   size of ReadBuffer
367 
368 
369   DESCRIPTION
370     Reads decrypted data from a SSL stream and encrypts it.
371 
372   RETURN
373     SEC_E_OK         on success
374     SEC_E_*          if an error occurred
375 */
376 
ma_schannel_read_decrypt(MARIADB_PVIO * pvio,CtxtHandle * phContext,DWORD * DecryptLength,uchar * ReadBuffer,DWORD ReadBufferSize)377 SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
378                                          CtxtHandle * phContext,
379                                          DWORD *DecryptLength,
380                                          uchar *ReadBuffer,
381                                          DWORD ReadBufferSize)
382 {
383   ssize_t nbytes = 0;
384   DWORD dwOffset = 0;
385   SC_CTX *sctx;
386   SECURITY_STATUS sRet = 0;
387   SecBufferDesc Msg;
388   SecBuffer Buffers[4];
389   int i;
390 
391   if (!pvio || !pvio->methods || !pvio->methods->read || !pvio->ctls || !DecryptLength)
392     return SEC_E_INTERNAL_ERROR;
393 
394   sctx = (SC_CTX *)pvio->ctls->ssl;
395   *DecryptLength = 0;
396 
397   if (sctx->dataBuf.cbBuffer)
398   {
399     /* Have unread decrypted data from the last time, copy. */
400     nbytes = MIN(ReadBufferSize, sctx->dataBuf.cbBuffer);
401     memcpy(ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
402     sctx->dataBuf.pvBuffer = (char *)(sctx->dataBuf.pvBuffer) + nbytes;
403     sctx->dataBuf.cbBuffer -= (DWORD)nbytes;
404     *DecryptLength = (DWORD)nbytes;
405     return SEC_E_OK;
406   }
407 
408 
409   while (1)
410   {
411     /* Check for any encrypted data returned by last DecryptMessage() in SECBUFFER_EXTRA buffer. */
412     if (sctx->extraBuf.cbBuffer)
413     {
414       memmove(sctx->IoBuffer, sctx->extraBuf.pvBuffer, sctx->extraBuf.cbBuffer);
415       dwOffset = sctx->extraBuf.cbBuffer;
416       sctx->extraBuf.cbBuffer = 0;
417     }
418 
419     do {
420       assert(sctx->IoBufferSize > dwOffset);
421       if (dwOffset == 0 || sRet == SEC_E_INCOMPLETE_MESSAGE)
422       {
423         nbytes = pvio->methods->read(pvio, sctx->IoBuffer + dwOffset, (size_t)(sctx->IoBufferSize - dwOffset));
424         if (nbytes <= 0)
425         {
426           /* server closed connection, or an error */
427           // todo: error
428           return SEC_E_INVALID_HANDLE;
429         }
430         dwOffset += (DWORD)nbytes;
431       }
432       ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
433       Buffers[0].pvBuffer = sctx->IoBuffer;
434       Buffers[0].cbBuffer = dwOffset;
435 
436       Buffers[0].BufferType = SECBUFFER_DATA;
437       Buffers[1].BufferType = SECBUFFER_EMPTY;
438       Buffers[2].BufferType = SECBUFFER_EMPTY;
439       Buffers[3].BufferType = SECBUFFER_EMPTY;
440 
441       Msg.ulVersion = SECBUFFER_VERSION;    // Version number
442       Msg.cBuffers = 4;
443       Msg.pBuffers = Buffers;
444 
445       sRet = DecryptMessage(phContext, &Msg, 0, NULL);
446 
447     } while (sRet == SEC_E_INCOMPLETE_MESSAGE); /* Continue reading until full message arrives */
448 
449 
450     if (sRet != SEC_E_OK)
451     {
452       ma_schannel_set_sec_error(pvio, sRet);
453       return sRet;
454     }
455 
456     sctx->extraBuf.cbBuffer = 0;
457     sctx->dataBuf.cbBuffer = 0;
458     for (i = 0; i < 4; i++)
459     {
460       if (Buffers[i].BufferType == SECBUFFER_DATA)
461         sctx->dataBuf = Buffers[i];
462       if (Buffers[i].BufferType == SECBUFFER_EXTRA)
463         sctx->extraBuf = Buffers[i];
464     }
465 
466 
467     if (sctx->dataBuf.cbBuffer)
468     {
469       assert(sctx->dataBuf.pvBuffer);
470       /*
471         Copy at most ReadBufferSize bytes to output.
472         Store the rest (if any) to be processed next time.
473       */
474       nbytes = MIN(sctx->dataBuf.cbBuffer, ReadBufferSize);
475       memcpy((char *)ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
476       sctx->dataBuf.cbBuffer -= (unsigned long)nbytes;
477       sctx->dataBuf.pvBuffer = (char *)sctx->dataBuf.pvBuffer + nbytes;
478 
479       *DecryptLength = (DWORD)nbytes;
480       return SEC_E_OK;
481     }
482     // No data buffer, loop
483   }
484 }
485 /* }}} */
486 #include "win32_errmsg.h"
ma_schannel_verify_certs(MARIADB_TLS * ctls,BOOL verify_server_name)487 my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name)
488 {
489   SECURITY_STATUS status;
490 
491   MARIADB_PVIO *pvio= ctls->pvio;
492   MYSQL *mysql= pvio->mysql;
493   SC_CTX *sctx = (SC_CTX *)ctls->ssl;
494   const char *ca_file= mysql->options.ssl_ca;
495   const char* ca_path = mysql->options.ssl_capath;
496   const char *crl_file= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL;
497   const char* crl_path = mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL;
498   PCCERT_CONTEXT pServerCert= NULL;
499   char errmsg[256];
500   HCERTSTORE store= NULL;
501   int ret= 0;
502 
503   status = schannel_create_store(ca_file, ca_path, crl_file, crl_path, &store, errmsg, sizeof(errmsg));
504   if(status)
505     goto end;
506 
507   status = QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert);
508   if (status)
509   {
510     ma_format_win32_error(errmsg, sizeof(errmsg), GetLastError(),
511       "QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed.");
512     goto end;
513   }
514 
515   status = schannel_verify_server_certificate(
516       pServerCert,
517       store,
518       crl_file != 0 || crl_path != 0,
519       mysql->host,
520       verify_server_name,
521       errmsg, sizeof(errmsg));
522 
523   if (status)
524     goto end;
525 
526   ret= 1;
527 
528 end:
529   if (!ret)
530   {
531      pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
532       "SSL connection error: %s", errmsg);
533   }
534   if (pServerCert)
535     CertFreeCertificateContext(pServerCert);
536   if(store)
537     schannel_free_store(store);
538   return ret;
539 }
540 
541 
542 /* {{{ size_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext) */
543 /*
544   Decrypts data and write to SSL stream
545   SYNOPSIS
546     ma_schannel_write_decrypt
547     pvio              pointer to Communication IO structure
548     phContext        a context handle
549     DecryptLength    size of decrypted buffer
550     ReadBuffer       Buffer for decrypted data
551     ReadBufferSize   size of ReadBuffer
552 
553   DESCRIPTION
554     Write encrypted data to SSL stream.
555 
556   RETURN
557     SEC_E_OK         on success
558     SEC_E_*          if an error occurred
559 */
ma_schannel_write_encrypt(MARIADB_PVIO * pvio,uchar * WriteBuffer,size_t WriteBufferSize)560 ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
561                                  uchar *WriteBuffer,
562                                  size_t WriteBufferSize)
563 {
564   SECURITY_STATUS scRet;
565   SecBufferDesc Message;
566   SecBuffer Buffers[4];
567   DWORD cbMessage;
568   PBYTE pbMessage;
569   SC_CTX *sctx= (SC_CTX *)pvio->ctls->ssl;
570   size_t payload;
571   ssize_t nbytes;
572   DWORD write_size;
573 
574   payload= MIN(WriteBufferSize, sctx->Sizes.cbMaximumMessage);
575 
576   memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
577   pbMessage = sctx->IoBuffer + sctx->Sizes.cbHeader;
578   cbMessage = (DWORD)payload;
579 
580   Buffers[0].pvBuffer     = sctx->IoBuffer;
581   Buffers[0].cbBuffer     = sctx->Sizes.cbHeader;
582   Buffers[0].BufferType   = SECBUFFER_STREAM_HEADER;    // Type of the buffer
583 
584   Buffers[1].pvBuffer     = &sctx->IoBuffer[sctx->Sizes.cbHeader];
585   Buffers[1].cbBuffer     = (DWORD)payload;
586   Buffers[1].BufferType   = SECBUFFER_DATA;
587 
588   Buffers[2].pvBuffer     = &sctx->IoBuffer[sctx->Sizes.cbHeader] + payload;
589   Buffers[2].cbBuffer     = sctx->Sizes.cbTrailer;
590   Buffers[2].BufferType   = SECBUFFER_STREAM_TRAILER;
591 
592   Buffers[3].pvBuffer     = SECBUFFER_EMPTY;                    // Pointer to buffer 4
593   Buffers[3].cbBuffer     = SECBUFFER_EMPTY;                    // length of buffer 4
594   Buffers[3].BufferType   = SECBUFFER_EMPTY;                    // Type of the buffer 4
595 
596 
597   Message.ulVersion       = SECBUFFER_VERSION;
598   Message.cBuffers        = 4;
599   Message.pBuffers        = Buffers;
600   if ((scRet = EncryptMessage(&sctx->hCtxt, 0, &Message, 0))!= SEC_E_OK)
601     return -1;
602   write_size = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
603   nbytes = pvio->methods->write(pvio, sctx->IoBuffer, write_size);
604   return nbytes == write_size ? payload : -1;
605 }
606 /* }}} */
607 
608 extern char *ssl_protocol_version[5];
609 
610 /* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls) */
ma_tls_get_protocol_version(MARIADB_TLS * ctls)611 int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
612 {
613   SC_CTX *sctx;
614   SecPkgContext_ConnectionInfo ConnectionInfo;
615   if (!ctls->ssl)
616     return 1;
617 
618   sctx= (SC_CTX *)ctls->ssl;
619 
620   if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_CONNECTION_INFO, &ConnectionInfo) != SEC_E_OK)
621     return -1;
622 
623   switch(ConnectionInfo.dwProtocol)
624   {
625   case SP_PROT_SSL3_CLIENT:
626     return PROTOCOL_SSLV3;
627   case SP_PROT_TLS1_CLIENT:
628     return PROTOCOL_TLS_1_0;
629   case SP_PROT_TLS1_1_CLIENT:
630     return PROTOCOL_TLS_1_1;
631   case SP_PROT_TLS1_2_CLIENT:
632     return PROTOCOL_TLS_1_2;
633   default:
634     return -1;
635   }
636 }
637 /* }}} */
638