1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
9 * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ***************************************************************************/
24 #include "curl_setup.h"
26 #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY)
34 #include "curl_sspi.h"
35 #include "curl_multibyte.h"
38 #define _MPRINTF_REPLACE /* use the internal *printf() functions */
39 #include <curl/mprintf.h>
41 #include "curl_memory.h"
42 /* The last #include file should be: */
46 * Helper sspi error functions.
48 static int check_sspi_err(struct connectdata *conn,
49 SECURITY_STATUS status,
52 if(status != SEC_E_OK &&
53 status != SEC_I_COMPLETE_AND_CONTINUE &&
54 status != SEC_I_COMPLETE_NEEDED &&
55 status != SEC_I_CONTINUE_NEEDED) {
56 failf(conn->data, "SSPI error: %s failed: %s", function,
57 Curl_sspi_strerror(conn, status));
63 /* This is the SSPI-using version of this function */
64 CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
65 struct connectdata *conn)
67 struct SessionHandle *data = conn->data;
68 curl_socket_t sock = conn->sock[sockindex];
73 /* Needs GSS-API authentication */
74 SECURITY_STATUS status;
75 unsigned long sspi_ret_flags = 0;
77 SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
78 SecBufferDesc input_desc, output_desc, wrap_desc;
79 SecPkgContext_Sizes sspi_sizes;
80 CredHandle cred_handle;
81 CtxtHandle sspi_context;
82 PCtxtHandle context_handle = NULL;
83 SecPkgCredentials_Names names;
85 char *service_name = NULL;
86 unsigned short us_length;
88 unsigned char socksreq[4]; /* room for GSS-API exchange header only */
89 char *service = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];
91 /* GSS-API request looks like
92 * +----+------+-----+----------------+
93 * |VER | MTYP | LEN | TOKEN |
94 * +----+------+----------------------+
95 * | 1 | 1 | 2 | up to 2^16 - 1 |
96 * +----+------+-----+----------------+
99 /* prepare service name */
100 if(strchr(service, '/')) {
101 service_name = malloc(strlen(service));
103 return CURLE_OUT_OF_MEMORY;
104 memcpy(service_name, service, strlen(service));
107 service_name = malloc(strlen(service) + strlen(conn->proxy.name) + 2);
109 return CURLE_OUT_OF_MEMORY;
110 snprintf(service_name,strlen(service) +strlen(conn->proxy.name)+2,"%s/%s",
111 service,conn->proxy.name);
114 input_desc.cBuffers = 1;
115 input_desc.pBuffers = &sspi_recv_token;
116 input_desc.ulVersion = SECBUFFER_VERSION;
118 sspi_recv_token.BufferType = SECBUFFER_TOKEN;
119 sspi_recv_token.cbBuffer = 0;
120 sspi_recv_token.pvBuffer = NULL;
122 output_desc.cBuffers = 1;
123 output_desc.pBuffers = &sspi_send_token;
124 output_desc.ulVersion = SECBUFFER_VERSION;
126 sspi_send_token.BufferType = SECBUFFER_TOKEN;
127 sspi_send_token.cbBuffer = 0;
128 sspi_send_token.pvBuffer = NULL;
130 wrap_desc.cBuffers = 3;
131 wrap_desc.pBuffers = sspi_w_token;
132 wrap_desc.ulVersion = SECBUFFER_VERSION;
134 cred_handle.dwLower = 0;
135 cred_handle.dwUpper = 0;
137 status = s_pSecFn->AcquireCredentialsHandle(NULL,
138 (TCHAR *) TEXT("Kerberos"),
139 SECPKG_CRED_OUTBOUND,
147 if(check_sspi_err(conn, status, "AcquireCredentialsHandle")) {
148 failf(data, "Failed to acquire credentials.");
149 Curl_safefree(service_name);
150 s_pSecFn->FreeCredentialsHandle(&cred_handle);
151 return CURLE_COULDNT_CONNECT;
154 /* As long as we need to keep sending some context info, and there's no */
155 /* errors, keep sending it... */
159 sname = Curl_convert_UTF8_to_tchar(service_name);
161 return CURLE_OUT_OF_MEMORY;
163 status = s_pSecFn->InitializeSecurityContext(&cred_handle,
166 ISC_REQ_MUTUAL_AUTH |
167 ISC_REQ_ALLOCATE_MEMORY |
168 ISC_REQ_CONFIDENTIALITY |
169 ISC_REQ_REPLAY_DETECT,
171 SECURITY_NATIVE_DREP,
179 Curl_unicodefree(sname);
181 if(sspi_recv_token.pvBuffer) {
182 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
183 sspi_recv_token.pvBuffer = NULL;
184 sspi_recv_token.cbBuffer = 0;
187 if(check_sspi_err(conn, status, "InitializeSecurityContext")) {
188 Curl_safefree(service_name);
189 s_pSecFn->FreeCredentialsHandle(&cred_handle);
190 s_pSecFn->DeleteSecurityContext(&sspi_context);
191 if(sspi_recv_token.pvBuffer)
192 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
193 failf(data, "Failed to initialise security context.");
194 return CURLE_COULDNT_CONNECT;
197 if(sspi_send_token.cbBuffer != 0) {
198 socksreq[0] = 1; /* GSS-API subnegotiation version */
199 socksreq[1] = 1; /* authentication message type */
200 us_length = htons((short)sspi_send_token.cbBuffer);
201 memcpy(socksreq+2, &us_length, sizeof(short));
203 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
204 if(code || (4 != written)) {
205 failf(data, "Failed to send SSPI authentication request.");
206 Curl_safefree(service_name);
207 if(sspi_send_token.pvBuffer)
208 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
209 if(sspi_recv_token.pvBuffer)
210 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
211 s_pSecFn->FreeCredentialsHandle(&cred_handle);
212 s_pSecFn->DeleteSecurityContext(&sspi_context);
213 return CURLE_COULDNT_CONNECT;
216 code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
217 sspi_send_token.cbBuffer, &written);
218 if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
219 failf(data, "Failed to send SSPI authentication token.");
220 Curl_safefree(service_name);
221 if(sspi_send_token.pvBuffer)
222 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
223 if(sspi_recv_token.pvBuffer)
224 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
225 s_pSecFn->FreeCredentialsHandle(&cred_handle);
226 s_pSecFn->DeleteSecurityContext(&sspi_context);
227 return CURLE_COULDNT_CONNECT;
232 if(sspi_send_token.pvBuffer) {
233 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
234 sspi_send_token.pvBuffer = NULL;
236 sspi_send_token.cbBuffer = 0;
238 if(sspi_recv_token.pvBuffer) {
239 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
240 sspi_recv_token.pvBuffer = NULL;
242 sspi_recv_token.cbBuffer = 0;
244 if(status != SEC_I_CONTINUE_NEEDED)
247 /* analyse response */
249 /* GSS-API response looks like
250 * +----+------+-----+----------------+
251 * |VER | MTYP | LEN | TOKEN |
252 * +----+------+----------------------+
253 * | 1 | 1 | 2 | up to 2^16 - 1 |
254 * +----+------+-----+----------------+
257 result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
258 if(result || (actualread != 4)) {
259 failf(data, "Failed to receive SSPI authentication response.");
260 Curl_safefree(service_name);
261 s_pSecFn->FreeCredentialsHandle(&cred_handle);
262 s_pSecFn->DeleteSecurityContext(&sspi_context);
263 return CURLE_COULDNT_CONNECT;
266 /* ignore the first (VER) byte */
267 if(socksreq[1] == 255) { /* status / message type */
268 failf(data, "User was rejected by the SOCKS5 server (%u %u).",
269 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
270 Curl_safefree(service_name);
271 s_pSecFn->FreeCredentialsHandle(&cred_handle);
272 s_pSecFn->DeleteSecurityContext(&sspi_context);
273 return CURLE_COULDNT_CONNECT;
276 if(socksreq[1] != 1) { /* status / messgae type */
277 failf(data, "Invalid SSPI authentication response type (%u %u).",
278 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
279 Curl_safefree(service_name);
280 s_pSecFn->FreeCredentialsHandle(&cred_handle);
281 s_pSecFn->DeleteSecurityContext(&sspi_context);
282 return CURLE_COULDNT_CONNECT;
285 memcpy(&us_length, socksreq+2, sizeof(short));
286 us_length = ntohs(us_length);
288 sspi_recv_token.cbBuffer = us_length;
289 sspi_recv_token.pvBuffer = malloc(us_length);
291 if(!sspi_recv_token.pvBuffer) {
292 Curl_safefree(service_name);
293 s_pSecFn->FreeCredentialsHandle(&cred_handle);
294 s_pSecFn->DeleteSecurityContext(&sspi_context);
295 return CURLE_OUT_OF_MEMORY;
297 result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer,
298 sspi_recv_token.cbBuffer, &actualread);
300 if(result || (actualread != us_length)) {
301 failf(data, "Failed to receive SSPI authentication token.");
302 Curl_safefree(service_name);
303 if(sspi_recv_token.pvBuffer)
304 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
305 s_pSecFn->FreeCredentialsHandle(&cred_handle);
306 s_pSecFn->DeleteSecurityContext(&sspi_context);
307 return CURLE_COULDNT_CONNECT;
310 context_handle = &sspi_context;
313 Curl_safefree(service_name);
315 /* Everything is good so far, user was authenticated! */
316 status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
317 SECPKG_CRED_ATTR_NAMES,
319 s_pSecFn->FreeCredentialsHandle(&cred_handle);
320 if(check_sspi_err(conn, status, "QueryCredentialAttributes")) {
321 s_pSecFn->DeleteSecurityContext(&sspi_context);
322 s_pSecFn->FreeContextBuffer(names.sUserName);
323 failf(data, "Failed to determine user name.");
324 return CURLE_COULDNT_CONNECT;
326 infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",
328 s_pSecFn->FreeContextBuffer(names.sUserName);
331 socksreq[0] = 1; /* GSS-API subnegotiation version */
332 socksreq[1] = 2; /* encryption message type */
334 gss_enc = 0; /* no data protection */
335 /* do confidentiality protection if supported */
336 if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
338 /* else do integrity protection */
339 else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
342 infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
343 (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality") );
344 /* force to no data protection, avoid encryption/decryption for now */
347 * Sending the encryption type in clear seems wrong. It should be
348 * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
349 * The NEC reference implementations on which this is based is
352 * +------+------+------+.......................+
353 * + ver | mtyp | len | token |
354 * +------+------+------+.......................+
355 * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
356 * +------+------+------+.......................+
360 * - "ver" is the protocol version number, here 1 to represent the
361 * first version of the SOCKS/GSS-API protocol
363 * - "mtyp" is the message type, here 2 to represent a protection
364 * -level negotiation message
366 * - "len" is the length of the "token" field in octets
368 * - "token" is the GSS-API encapsulated protection level
370 * The token is produced by encapsulating an octet containing the
371 * required protection level using gss_seal()/gss_wrap() with conf_req
372 * set to FALSE. The token is verified using gss_unseal()/
377 if(data->set.socks5_gssapi_nec) {
378 us_length = htons((short)1);
379 memcpy(socksreq+2, &us_length, sizeof(short));
382 status = s_pSecFn->QueryContextAttributes(&sspi_context,
385 if(check_sspi_err(conn, status, "QueryContextAttributes")) {
386 s_pSecFn->DeleteSecurityContext(&sspi_context);
387 failf(data, "Failed to query security context attributes.");
388 return CURLE_COULDNT_CONNECT;
391 sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
392 sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
393 sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
395 if(!sspi_w_token[0].pvBuffer) {
396 s_pSecFn->DeleteSecurityContext(&sspi_context);
397 return CURLE_OUT_OF_MEMORY;
400 sspi_w_token[1].cbBuffer = 1;
401 sspi_w_token[1].pvBuffer = malloc(1);
402 if(!sspi_w_token[1].pvBuffer) {
403 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
404 s_pSecFn->DeleteSecurityContext(&sspi_context);
405 return CURLE_OUT_OF_MEMORY;
408 memcpy(sspi_w_token[1].pvBuffer,&gss_enc,1);
409 sspi_w_token[2].BufferType = SECBUFFER_PADDING;
410 sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
411 sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
412 if(!sspi_w_token[2].pvBuffer) {
413 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
414 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
415 s_pSecFn->DeleteSecurityContext(&sspi_context);
416 return CURLE_OUT_OF_MEMORY;
418 status = s_pSecFn->EncryptMessage(&sspi_context,
419 KERB_WRAP_NO_ENCRYPT,
422 if(check_sspi_err(conn, status, "EncryptMessage")) {
423 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
424 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
425 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
426 s_pSecFn->DeleteSecurityContext(&sspi_context);
427 failf(data, "Failed to query security context attributes.");
428 return CURLE_COULDNT_CONNECT;
430 sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
431 + sspi_w_token[1].cbBuffer
432 + sspi_w_token[2].cbBuffer;
433 sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
434 if(!sspi_send_token.pvBuffer) {
435 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
436 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
437 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
438 s_pSecFn->DeleteSecurityContext(&sspi_context);
439 return CURLE_OUT_OF_MEMORY;
442 memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
443 sspi_w_token[0].cbBuffer);
444 memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
445 sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
446 memcpy((PUCHAR) sspi_send_token.pvBuffer
447 +sspi_w_token[0].cbBuffer
448 +sspi_w_token[1].cbBuffer,
449 sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
451 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
452 sspi_w_token[0].pvBuffer = NULL;
453 sspi_w_token[0].cbBuffer = 0;
454 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
455 sspi_w_token[1].pvBuffer = NULL;
456 sspi_w_token[1].cbBuffer = 0;
457 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
458 sspi_w_token[2].pvBuffer = NULL;
459 sspi_w_token[2].cbBuffer = 0;
461 us_length = htons((short)sspi_send_token.cbBuffer);
462 memcpy(socksreq+2,&us_length,sizeof(short));
465 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
466 if(code || (4 != written)) {
467 failf(data, "Failed to send SSPI encryption request.");
468 if(sspi_send_token.pvBuffer)
469 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
470 s_pSecFn->DeleteSecurityContext(&sspi_context);
471 return CURLE_COULDNT_CONNECT;
474 if(data->set.socks5_gssapi_nec) {
475 memcpy(socksreq,&gss_enc,1);
476 code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written);
477 if(code || (1 != written)) {
478 failf(data, "Failed to send SSPI encryption type.");
479 s_pSecFn->DeleteSecurityContext(&sspi_context);
480 return CURLE_COULDNT_CONNECT;
484 code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
485 sspi_send_token.cbBuffer, &written);
486 if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
487 failf(data, "Failed to send SSPI encryption type.");
488 if(sspi_send_token.pvBuffer)
489 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
490 s_pSecFn->DeleteSecurityContext(&sspi_context);
491 return CURLE_COULDNT_CONNECT;
493 if(sspi_send_token.pvBuffer)
494 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
497 result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
498 if(result || (actualread != 4)) {
499 failf(data, "Failed to receive SSPI encryption response.");
500 s_pSecFn->DeleteSecurityContext(&sspi_context);
501 return CURLE_COULDNT_CONNECT;
504 /* ignore the first (VER) byte */
505 if(socksreq[1] == 255) { /* status / message type */
506 failf(data, "User was rejected by the SOCKS5 server (%u %u).",
507 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
508 s_pSecFn->DeleteSecurityContext(&sspi_context);
509 return CURLE_COULDNT_CONNECT;
512 if(socksreq[1] != 2) { /* status / message type */
513 failf(data, "Invalid SSPI encryption response type (%u %u).",
514 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
515 s_pSecFn->DeleteSecurityContext(&sspi_context);
516 return CURLE_COULDNT_CONNECT;
519 memcpy(&us_length, socksreq+2, sizeof(short));
520 us_length = ntohs(us_length);
522 sspi_w_token[0].cbBuffer = us_length;
523 sspi_w_token[0].pvBuffer = malloc(us_length);
524 if(!sspi_w_token[0].pvBuffer) {
525 s_pSecFn->DeleteSecurityContext(&sspi_context);
526 return CURLE_OUT_OF_MEMORY;
529 result = Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer,
530 sspi_w_token[0].cbBuffer, &actualread);
532 if(result || (actualread != us_length)) {
533 failf(data, "Failed to receive SSPI encryption type.");
534 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
535 s_pSecFn->DeleteSecurityContext(&sspi_context);
536 return CURLE_COULDNT_CONNECT;
540 if(!data->set.socks5_gssapi_nec) {
541 wrap_desc.cBuffers = 2;
542 sspi_w_token[0].BufferType = SECBUFFER_STREAM;
543 sspi_w_token[1].BufferType = SECBUFFER_DATA;
544 sspi_w_token[1].cbBuffer = 0;
545 sspi_w_token[1].pvBuffer = NULL;
547 status = s_pSecFn->DecryptMessage(&sspi_context,
552 if(check_sspi_err(conn, status, "DecryptMessage")) {
553 if(sspi_w_token[0].pvBuffer)
554 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
555 if(sspi_w_token[1].pvBuffer)
556 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
557 s_pSecFn->DeleteSecurityContext(&sspi_context);
558 failf(data, "Failed to query security context attributes.");
559 return CURLE_COULDNT_CONNECT;
562 if(sspi_w_token[1].cbBuffer != 1) {
563 failf(data, "Invalid SSPI encryption response length (%lu).",
564 (unsigned long)sspi_w_token[1].cbBuffer);
565 if(sspi_w_token[0].pvBuffer)
566 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
567 if(sspi_w_token[1].pvBuffer)
568 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
569 s_pSecFn->DeleteSecurityContext(&sspi_context);
570 return CURLE_COULDNT_CONNECT;
573 memcpy(socksreq,sspi_w_token[1].pvBuffer,sspi_w_token[1].cbBuffer);
574 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
575 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
578 if(sspi_w_token[0].cbBuffer != 1) {
579 failf(data, "Invalid SSPI encryption response length (%lu).",
580 (unsigned long)sspi_w_token[0].cbBuffer);
581 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
582 s_pSecFn->DeleteSecurityContext(&sspi_context);
583 return CURLE_COULDNT_CONNECT;
585 memcpy(socksreq,sspi_w_token[0].pvBuffer,sspi_w_token[0].cbBuffer);
586 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
589 infof(data, "SOCKS5 access with%s protection granted.\n",
590 (socksreq[0]==0)?"out GSS-API data":
591 ((socksreq[0]==1)?" GSS-API integrity":" GSS-API confidentiality"));
593 /* For later use if encryption is required
594 conn->socks5_gssapi_enctype = socksreq[0];
596 conn->socks5_sspi_context = sspi_context;
598 s_pSecFn->DeleteSecurityContext(&sspi_context);
599 conn->socks5_sspi_context = sspi_context;