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