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