1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
9  * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
10  *
11  * This software is licensed as described in the file COPYING, which
12  * you should have received as part of this distribution. The terms
13  * are also available at https://curl.haxx.se/docs/copyright.html.
14  *
15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16  * copies of the Software, and permit persons to whom the Software is
17  * furnished to do so, under the terms of the COPYING file.
18  *
19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20  * KIND, either express or implied.
21  *
22  ***************************************************************************/
23 
24 #include "curl_setup.h"
25 
26 #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY)
27 
28 #include "urldata.h"
29 #include "sendf.h"
30 #include "connect.h"
31 #include "strerror.h"
32 #include "timeval.h"
33 #include "socks.h"
34 #include "curl_sspi.h"
35 #include "curl_multibyte.h"
36 #include "warnless.h"
37 #include "strdup.h"
38 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
41 #include "memdebug.h"
42 
43 /*
44  * Helper sspi error functions.
45  */
check_sspi_err(struct connectdata * conn,SECURITY_STATUS status,const char * function)46 static int check_sspi_err(struct connectdata *conn,
47                           SECURITY_STATUS status,
48                           const char *function)
49 {
50   if(status != SEC_E_OK &&
51      status != SEC_I_COMPLETE_AND_CONTINUE &&
52      status != SEC_I_COMPLETE_NEEDED &&
53      status != SEC_I_CONTINUE_NEEDED) {
54     failf(conn->data, "SSPI error: %s failed: %s", function,
55           Curl_sspi_strerror(conn, status));
56     return 1;
57   }
58   return 0;
59 }
60 
61 /* This is the SSPI-using version of this function */
Curl_SOCKS5_gssapi_negotiate(int sockindex,struct connectdata * conn)62 CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
63                                       struct connectdata *conn)
64 {
65   struct Curl_easy *data = conn->data;
66   curl_socket_t sock = conn->sock[sockindex];
67   CURLcode code;
68   ssize_t actualread;
69   ssize_t written;
70   int result;
71   /* Needs GSS-API authentication */
72   SECURITY_STATUS status;
73   unsigned long sspi_ret_flags = 0;
74   unsigned char gss_enc;
75   SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
76   SecBufferDesc input_desc, output_desc, wrap_desc;
77   SecPkgContext_Sizes sspi_sizes;
78   CredHandle cred_handle;
79   CtxtHandle sspi_context;
80   PCtxtHandle context_handle = NULL;
81   SecPkgCredentials_Names names;
82   TimeStamp expiry;
83   char *service_name = NULL;
84   unsigned short us_length;
85   unsigned long qop;
86   unsigned char socksreq[4]; /* room for GSS-API exchange header only */
87   const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
88                         data->set.str[STRING_PROXY_SERVICE_NAME]  : "rcmd";
89   const size_t service_length = strlen(service);
90 
91   /*   GSS-API request looks like
92    * +----+------+-----+----------------+
93    * |VER | MTYP | LEN |     TOKEN      |
94    * +----+------+----------------------+
95    * | 1  |  1   |  2  | up to 2^16 - 1 |
96    * +----+------+-----+----------------+
97    */
98 
99   /* prepare service name */
100   if(strchr(service, '/')) {
101     service_name = strdup(service);
102     if(!service_name)
103       return CURLE_OUT_OF_MEMORY;
104   }
105   else {
106     service_name = malloc(service_length +
107                           strlen(conn->socks_proxy.host.name) + 2);
108     if(!service_name)
109       return CURLE_OUT_OF_MEMORY;
110     snprintf(service_name, service_length +
111              strlen(conn->socks_proxy.host.name)+2, "%s/%s",
112              service, conn->socks_proxy.host.name);
113   }
114 
115   input_desc.cBuffers = 1;
116   input_desc.pBuffers = &sspi_recv_token;
117   input_desc.ulVersion = SECBUFFER_VERSION;
118 
119   sspi_recv_token.BufferType = SECBUFFER_TOKEN;
120   sspi_recv_token.cbBuffer = 0;
121   sspi_recv_token.pvBuffer = NULL;
122 
123   output_desc.cBuffers = 1;
124   output_desc.pBuffers = &sspi_send_token;
125   output_desc.ulVersion = SECBUFFER_VERSION;
126 
127   sspi_send_token.BufferType = SECBUFFER_TOKEN;
128   sspi_send_token.cbBuffer = 0;
129   sspi_send_token.pvBuffer = NULL;
130 
131   wrap_desc.cBuffers = 3;
132   wrap_desc.pBuffers = sspi_w_token;
133   wrap_desc.ulVersion = SECBUFFER_VERSION;
134 
135   cred_handle.dwLower = 0;
136   cred_handle.dwUpper = 0;
137 
138   status = s_pSecFn->AcquireCredentialsHandle(NULL,
139                                               (TCHAR *) TEXT("Kerberos"),
140                                               SECPKG_CRED_OUTBOUND,
141                                               NULL,
142                                               NULL,
143                                               NULL,
144                                               NULL,
145                                               &cred_handle,
146                                               &expiry);
147 
148   if(check_sspi_err(conn, status, "AcquireCredentialsHandle")) {
149     failf(data, "Failed to acquire credentials.");
150     free(service_name);
151     s_pSecFn->FreeCredentialsHandle(&cred_handle);
152     return CURLE_COULDNT_CONNECT;
153   }
154 
155   /* As long as we need to keep sending some context info, and there's no  */
156   /* errors, keep sending it...                                            */
157   for(;;) {
158     TCHAR *sname;
159 
160     sname = Curl_convert_UTF8_to_tchar(service_name);
161     if(!sname)
162       return CURLE_OUT_OF_MEMORY;
163 
164     status = s_pSecFn->InitializeSecurityContext(&cred_handle,
165                                                  context_handle,
166                                                  sname,
167                                                  ISC_REQ_MUTUAL_AUTH |
168                                                  ISC_REQ_ALLOCATE_MEMORY |
169                                                  ISC_REQ_CONFIDENTIALITY |
170                                                  ISC_REQ_REPLAY_DETECT,
171                                                  0,
172                                                  SECURITY_NATIVE_DREP,
173                                                  &input_desc,
174                                                  0,
175                                                  &sspi_context,
176                                                  &output_desc,
177                                                  &sspi_ret_flags,
178                                                  &expiry);
179 
180     Curl_unicodefree(sname);
181 
182     if(sspi_recv_token.pvBuffer) {
183       s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
184       sspi_recv_token.pvBuffer = NULL;
185       sspi_recv_token.cbBuffer = 0;
186     }
187 
188     if(check_sspi_err(conn, status, "InitializeSecurityContext")) {
189       free(service_name);
190       s_pSecFn->FreeCredentialsHandle(&cred_handle);
191       s_pSecFn->DeleteSecurityContext(&sspi_context);
192       if(sspi_recv_token.pvBuffer)
193         s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
194       failf(data, "Failed to initialise security context.");
195       return CURLE_COULDNT_CONNECT;
196     }
197 
198     if(sspi_send_token.cbBuffer != 0) {
199       socksreq[0] = 1;    /* GSS-API subnegotiation version */
200       socksreq[1] = 1;    /* authentication message type */
201       us_length = htons((short)sspi_send_token.cbBuffer);
202       memcpy(socksreq+2, &us_length, sizeof(short));
203 
204       code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
205       if(code || (4 != written)) {
206         failf(data, "Failed to send SSPI authentication request.");
207         free(service_name);
208         if(sspi_send_token.pvBuffer)
209           s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
210         if(sspi_recv_token.pvBuffer)
211           s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
212         s_pSecFn->FreeCredentialsHandle(&cred_handle);
213         s_pSecFn->DeleteSecurityContext(&sspi_context);
214         return CURLE_COULDNT_CONNECT;
215       }
216 
217       code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
218                               sspi_send_token.cbBuffer, &written);
219       if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
220         failf(data, "Failed to send SSPI authentication token.");
221         free(service_name);
222         if(sspi_send_token.pvBuffer)
223           s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
224         if(sspi_recv_token.pvBuffer)
225           s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
226         s_pSecFn->FreeCredentialsHandle(&cred_handle);
227         s_pSecFn->DeleteSecurityContext(&sspi_context);
228         return CURLE_COULDNT_CONNECT;
229       }
230 
231     }
232 
233     if(sspi_send_token.pvBuffer) {
234       s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
235       sspi_send_token.pvBuffer = NULL;
236     }
237     sspi_send_token.cbBuffer = 0;
238 
239     if(sspi_recv_token.pvBuffer) {
240       s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
241       sspi_recv_token.pvBuffer = NULL;
242     }
243     sspi_recv_token.cbBuffer = 0;
244 
245     if(status != SEC_I_CONTINUE_NEEDED)
246       break;
247 
248     /* analyse response */
249 
250     /*   GSS-API response looks like
251      * +----+------+-----+----------------+
252      * |VER | MTYP | LEN |     TOKEN      |
253      * +----+------+----------------------+
254      * | 1  |  1   |  2  | up to 2^16 - 1 |
255      * +----+------+-----+----------------+
256      */
257 
258     result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
259     if(result || (actualread != 4)) {
260       failf(data, "Failed to receive SSPI authentication response.");
261       free(service_name);
262       s_pSecFn->FreeCredentialsHandle(&cred_handle);
263       s_pSecFn->DeleteSecurityContext(&sspi_context);
264       return CURLE_COULDNT_CONNECT;
265     }
266 
267     /* ignore the first (VER) byte */
268     if(socksreq[1] == 255) { /* status / message type */
269       failf(data, "User was rejected by the SOCKS5 server (%u %u).",
270             (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
271       free(service_name);
272       s_pSecFn->FreeCredentialsHandle(&cred_handle);
273       s_pSecFn->DeleteSecurityContext(&sspi_context);
274       return CURLE_COULDNT_CONNECT;
275     }
276 
277     if(socksreq[1] != 1) { /* status / messgae type */
278       failf(data, "Invalid SSPI authentication response type (%u %u).",
279             (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
280       free(service_name);
281       s_pSecFn->FreeCredentialsHandle(&cred_handle);
282       s_pSecFn->DeleteSecurityContext(&sspi_context);
283       return CURLE_COULDNT_CONNECT;
284     }
285 
286     memcpy(&us_length, socksreq+2, sizeof(short));
287     us_length = ntohs(us_length);
288 
289     sspi_recv_token.cbBuffer = us_length;
290     sspi_recv_token.pvBuffer = malloc(us_length);
291 
292     if(!sspi_recv_token.pvBuffer) {
293       free(service_name);
294       s_pSecFn->FreeCredentialsHandle(&cred_handle);
295       s_pSecFn->DeleteSecurityContext(&sspi_context);
296       return CURLE_OUT_OF_MEMORY;
297     }
298     result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer,
299                                 sspi_recv_token.cbBuffer, &actualread);
300 
301     if(result || (actualread != us_length)) {
302       failf(data, "Failed to receive SSPI authentication token.");
303       free(service_name);
304       if(sspi_recv_token.pvBuffer)
305         s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
306       s_pSecFn->FreeCredentialsHandle(&cred_handle);
307       s_pSecFn->DeleteSecurityContext(&sspi_context);
308       return CURLE_COULDNT_CONNECT;
309     }
310 
311     context_handle = &sspi_context;
312   }
313 
314   free(service_name);
315 
316   /* Everything is good so far, user was authenticated! */
317   status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
318                                                 SECPKG_CRED_ATTR_NAMES,
319                                                 &names);
320   s_pSecFn->FreeCredentialsHandle(&cred_handle);
321   if(check_sspi_err(conn, status, "QueryCredentialAttributes")) {
322     s_pSecFn->DeleteSecurityContext(&sspi_context);
323     s_pSecFn->FreeContextBuffer(names.sUserName);
324     failf(data, "Failed to determine user name.");
325     return CURLE_COULDNT_CONNECT;
326   }
327   infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",
328         names.sUserName);
329   s_pSecFn->FreeContextBuffer(names.sUserName);
330 
331   /* Do encryption */
332   socksreq[0] = 1;    /* GSS-API subnegotiation version */
333   socksreq[1] = 2;    /* encryption message type */
334 
335   gss_enc = 0; /* no data protection */
336   /* do confidentiality protection if supported */
337   if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
338     gss_enc = 2;
339   /* else do integrity protection */
340   else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
341     gss_enc = 1;
342 
343   infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
344         (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality") );
345   /* force to no data protection, avoid encryption/decryption for now */
346   gss_enc = 0;
347   /*
348    * Sending the encryption type in clear seems wrong. It should be
349    * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
350    * The NEC reference implementations on which this is based is
351    * therefore at fault
352    *
353    *  +------+------+------+.......................+
354    *  + ver  | mtyp | len  |   token               |
355    *  +------+------+------+.......................+
356    *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
357    *  +------+------+------+.......................+
358    *
359    *   Where:
360    *
361    *  - "ver" is the protocol version number, here 1 to represent the
362    *    first version of the SOCKS/GSS-API protocol
363    *
364    *  - "mtyp" is the message type, here 2 to represent a protection
365    *    -level negotiation message
366    *
367    *  - "len" is the length of the "token" field in octets
368    *
369    *  - "token" is the GSS-API encapsulated protection level
370    *
371    * The token is produced by encapsulating an octet containing the
372    * required protection level using gss_seal()/gss_wrap() with conf_req
373    * set to FALSE.  The token is verified using gss_unseal()/
374    * gss_unwrap().
375    *
376    */
377 
378   if(data->set.socks5_gssapi_nec) {
379     us_length = htons((short)1);
380     memcpy(socksreq+2, &us_length, sizeof(short));
381   }
382   else {
383     status = s_pSecFn->QueryContextAttributes(&sspi_context,
384                                               SECPKG_ATTR_SIZES,
385                                               &sspi_sizes);
386     if(check_sspi_err(conn, status, "QueryContextAttributes")) {
387       s_pSecFn->DeleteSecurityContext(&sspi_context);
388       failf(data, "Failed to query security context attributes.");
389       return CURLE_COULDNT_CONNECT;
390     }
391 
392     sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
393     sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
394     sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
395 
396     if(!sspi_w_token[0].pvBuffer) {
397       s_pSecFn->DeleteSecurityContext(&sspi_context);
398       return CURLE_OUT_OF_MEMORY;
399     }
400 
401     sspi_w_token[1].cbBuffer = 1;
402     sspi_w_token[1].pvBuffer = malloc(1);
403     if(!sspi_w_token[1].pvBuffer) {
404       s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
405       s_pSecFn->DeleteSecurityContext(&sspi_context);
406       return CURLE_OUT_OF_MEMORY;
407     }
408 
409     memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1);
410     sspi_w_token[2].BufferType = SECBUFFER_PADDING;
411     sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
412     sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
413     if(!sspi_w_token[2].pvBuffer) {
414       s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
415       s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
416       s_pSecFn->DeleteSecurityContext(&sspi_context);
417       return CURLE_OUT_OF_MEMORY;
418     }
419     status = s_pSecFn->EncryptMessage(&sspi_context,
420                                       KERB_WRAP_NO_ENCRYPT,
421                                       &wrap_desc,
422                                       0);
423     if(check_sspi_err(conn, status, "EncryptMessage")) {
424       s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
425       s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
426       s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
427       s_pSecFn->DeleteSecurityContext(&sspi_context);
428       failf(data, "Failed to query security context attributes.");
429       return CURLE_COULDNT_CONNECT;
430     }
431     sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
432       + sspi_w_token[1].cbBuffer
433       + sspi_w_token[2].cbBuffer;
434     sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
435     if(!sspi_send_token.pvBuffer) {
436       s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
437       s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
438       s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
439       s_pSecFn->DeleteSecurityContext(&sspi_context);
440       return CURLE_OUT_OF_MEMORY;
441     }
442 
443     memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
444            sspi_w_token[0].cbBuffer);
445     memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
446            sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
447     memcpy((PUCHAR) sspi_send_token.pvBuffer
448            +sspi_w_token[0].cbBuffer
449            +sspi_w_token[1].cbBuffer,
450            sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
451 
452     s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
453     sspi_w_token[0].pvBuffer = NULL;
454     sspi_w_token[0].cbBuffer = 0;
455     s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
456     sspi_w_token[1].pvBuffer = NULL;
457     sspi_w_token[1].cbBuffer = 0;
458     s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
459     sspi_w_token[2].pvBuffer = NULL;
460     sspi_w_token[2].cbBuffer = 0;
461 
462     us_length = htons((short)sspi_send_token.cbBuffer);
463     memcpy(socksreq+2, &us_length, sizeof(short));
464   }
465 
466   code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
467   if(code || (4 != written)) {
468     failf(data, "Failed to send SSPI encryption request.");
469     if(sspi_send_token.pvBuffer)
470       s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
471     s_pSecFn->DeleteSecurityContext(&sspi_context);
472     return CURLE_COULDNT_CONNECT;
473   }
474 
475   if(data->set.socks5_gssapi_nec) {
476     memcpy(socksreq, &gss_enc, 1);
477     code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written);
478     if(code || (1 != written)) {
479       failf(data, "Failed to send SSPI encryption type.");
480       s_pSecFn->DeleteSecurityContext(&sspi_context);
481       return CURLE_COULDNT_CONNECT;
482     }
483   }
484   else {
485     code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
486                             sspi_send_token.cbBuffer, &written);
487     if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
488       failf(data, "Failed to send SSPI encryption type.");
489       if(sspi_send_token.pvBuffer)
490         s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
491       s_pSecFn->DeleteSecurityContext(&sspi_context);
492       return CURLE_COULDNT_CONNECT;
493     }
494     if(sspi_send_token.pvBuffer)
495       s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
496   }
497 
498   result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
499   if(result || (actualread != 4)) {
500     failf(data, "Failed to receive SSPI encryption response.");
501     s_pSecFn->DeleteSecurityContext(&sspi_context);
502     return CURLE_COULDNT_CONNECT;
503   }
504 
505   /* ignore the first (VER) byte */
506   if(socksreq[1] == 255) { /* status / message type */
507     failf(data, "User was rejected by the SOCKS5 server (%u %u).",
508           (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
509     s_pSecFn->DeleteSecurityContext(&sspi_context);
510     return CURLE_COULDNT_CONNECT;
511   }
512 
513   if(socksreq[1] != 2) { /* status / message type */
514     failf(data, "Invalid SSPI encryption response type (%u %u).",
515           (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
516     s_pSecFn->DeleteSecurityContext(&sspi_context);
517     return CURLE_COULDNT_CONNECT;
518   }
519 
520   memcpy(&us_length, socksreq+2, sizeof(short));
521   us_length = ntohs(us_length);
522 
523   sspi_w_token[0].cbBuffer = us_length;
524   sspi_w_token[0].pvBuffer = malloc(us_length);
525   if(!sspi_w_token[0].pvBuffer) {
526     s_pSecFn->DeleteSecurityContext(&sspi_context);
527     return CURLE_OUT_OF_MEMORY;
528   }
529 
530   result = Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer,
531                               sspi_w_token[0].cbBuffer, &actualread);
532 
533   if(result || (actualread != us_length)) {
534     failf(data, "Failed to receive SSPI encryption type.");
535     s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
536     s_pSecFn->DeleteSecurityContext(&sspi_context);
537     return CURLE_COULDNT_CONNECT;
538   }
539 
540 
541   if(!data->set.socks5_gssapi_nec) {
542     wrap_desc.cBuffers = 2;
543     sspi_w_token[0].BufferType = SECBUFFER_STREAM;
544     sspi_w_token[1].BufferType = SECBUFFER_DATA;
545     sspi_w_token[1].cbBuffer = 0;
546     sspi_w_token[1].pvBuffer = NULL;
547 
548     status = s_pSecFn->DecryptMessage(&sspi_context,
549                                       &wrap_desc,
550                                       0,
551                                       &qop);
552 
553     if(check_sspi_err(conn, status, "DecryptMessage")) {
554       if(sspi_w_token[0].pvBuffer)
555         s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
556       if(sspi_w_token[1].pvBuffer)
557         s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
558       s_pSecFn->DeleteSecurityContext(&sspi_context);
559       failf(data, "Failed to query security context attributes.");
560       return CURLE_COULDNT_CONNECT;
561     }
562 
563     if(sspi_w_token[1].cbBuffer != 1) {
564       failf(data, "Invalid SSPI encryption response length (%lu).",
565             (unsigned long)sspi_w_token[1].cbBuffer);
566       if(sspi_w_token[0].pvBuffer)
567         s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
568       if(sspi_w_token[1].pvBuffer)
569         s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
570       s_pSecFn->DeleteSecurityContext(&sspi_context);
571       return CURLE_COULDNT_CONNECT;
572     }
573 
574     memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
575     s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
576     s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
577   }
578   else {
579     if(sspi_w_token[0].cbBuffer != 1) {
580       failf(data, "Invalid SSPI encryption response length (%lu).",
581             (unsigned long)sspi_w_token[0].cbBuffer);
582       s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
583       s_pSecFn->DeleteSecurityContext(&sspi_context);
584       return CURLE_COULDNT_CONNECT;
585     }
586     memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer);
587     s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
588   }
589 
590   infof(data, "SOCKS5 access with%s protection granted.\n",
591         (socksreq[0]==0)?"out GSS-API data":
592         ((socksreq[0]==1)?" GSS-API integrity":" GSS-API confidentiality"));
593 
594   /* For later use if encryption is required
595      conn->socks5_gssapi_enctype = socksreq[0];
596      if(socksreq[0] != 0)
597        conn->socks5_sspi_context = sspi_context;
598      else {
599        s_pSecFn->DeleteSecurityContext(&sspi_context);
600        conn->socks5_sspi_context = sspi_context;
601      }
602   */
603   return CURLE_OK;
604 }
605 #endif
606