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