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