113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com>
1013498266Sopenharmony_ci *
1113498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1213498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1313498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1413498266Sopenharmony_ci *
1513498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1613498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1713498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1813498266Sopenharmony_ci *
1913498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
2013498266Sopenharmony_ci * KIND, either express or implied.
2113498266Sopenharmony_ci *
2213498266Sopenharmony_ci * SPDX-License-Identifier: curl
2313498266Sopenharmony_ci *
2413498266Sopenharmony_ci ***************************************************************************/
2513498266Sopenharmony_ci
2613498266Sopenharmony_ci#include "curl_setup.h"
2713498266Sopenharmony_ci
2813498266Sopenharmony_ci#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY)
2913498266Sopenharmony_ci
3013498266Sopenharmony_ci#include "urldata.h"
3113498266Sopenharmony_ci#include "sendf.h"
3213498266Sopenharmony_ci#include "cfilters.h"
3313498266Sopenharmony_ci#include "connect.h"
3413498266Sopenharmony_ci#include "strerror.h"
3513498266Sopenharmony_ci#include "timeval.h"
3613498266Sopenharmony_ci#include "socks.h"
3713498266Sopenharmony_ci#include "curl_sspi.h"
3813498266Sopenharmony_ci#include "curl_multibyte.h"
3913498266Sopenharmony_ci#include "warnless.h"
4013498266Sopenharmony_ci#include "strdup.h"
4113498266Sopenharmony_ci/* The last 3 #include files should be in this order */
4213498266Sopenharmony_ci#include "curl_printf.h"
4313498266Sopenharmony_ci#include "curl_memory.h"
4413498266Sopenharmony_ci#include "memdebug.h"
4513498266Sopenharmony_ci
4613498266Sopenharmony_ci/*
4713498266Sopenharmony_ci * Helper sspi error functions.
4813498266Sopenharmony_ci */
4913498266Sopenharmony_cistatic int check_sspi_err(struct Curl_easy *data,
5013498266Sopenharmony_ci                          SECURITY_STATUS status,
5113498266Sopenharmony_ci                          const char *function)
5213498266Sopenharmony_ci{
5313498266Sopenharmony_ci  if(status != SEC_E_OK &&
5413498266Sopenharmony_ci     status != SEC_I_COMPLETE_AND_CONTINUE &&
5513498266Sopenharmony_ci     status != SEC_I_COMPLETE_NEEDED &&
5613498266Sopenharmony_ci     status != SEC_I_CONTINUE_NEEDED) {
5713498266Sopenharmony_ci    char buffer[STRERROR_LEN];
5813498266Sopenharmony_ci    failf(data, "SSPI error: %s failed: %s", function,
5913498266Sopenharmony_ci          Curl_sspi_strerror(status, buffer, sizeof(buffer)));
6013498266Sopenharmony_ci    return 1;
6113498266Sopenharmony_ci  }
6213498266Sopenharmony_ci  return 0;
6313498266Sopenharmony_ci}
6413498266Sopenharmony_ci
6513498266Sopenharmony_ci/* This is the SSPI-using version of this function */
6613498266Sopenharmony_ciCURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
6713498266Sopenharmony_ci                                      struct Curl_easy *data)
6813498266Sopenharmony_ci{
6913498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
7013498266Sopenharmony_ci  curl_socket_t sock = conn->sock[cf->sockindex];
7113498266Sopenharmony_ci  CURLcode code;
7213498266Sopenharmony_ci  ssize_t actualread;
7313498266Sopenharmony_ci  ssize_t written;
7413498266Sopenharmony_ci  int result;
7513498266Sopenharmony_ci  /* Needs GSS-API authentication */
7613498266Sopenharmony_ci  SECURITY_STATUS status;
7713498266Sopenharmony_ci  unsigned long sspi_ret_flags = 0;
7813498266Sopenharmony_ci  unsigned char gss_enc;
7913498266Sopenharmony_ci  SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
8013498266Sopenharmony_ci  SecBufferDesc input_desc, output_desc, wrap_desc;
8113498266Sopenharmony_ci  SecPkgContext_Sizes sspi_sizes;
8213498266Sopenharmony_ci  CredHandle cred_handle;
8313498266Sopenharmony_ci  CtxtHandle sspi_context;
8413498266Sopenharmony_ci  PCtxtHandle context_handle = NULL;
8513498266Sopenharmony_ci  SecPkgCredentials_Names names;
8613498266Sopenharmony_ci  TimeStamp expiry;
8713498266Sopenharmony_ci  char *service_name = NULL;
8813498266Sopenharmony_ci  unsigned short us_length;
8913498266Sopenharmony_ci  unsigned long qop;
9013498266Sopenharmony_ci  unsigned char socksreq[4]; /* room for GSS-API exchange header only */
9113498266Sopenharmony_ci  const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
9213498266Sopenharmony_ci    data->set.str[STRING_PROXY_SERVICE_NAME]  : "rcmd";
9313498266Sopenharmony_ci  const size_t service_length = strlen(service);
9413498266Sopenharmony_ci
9513498266Sopenharmony_ci  /*   GSS-API request looks like
9613498266Sopenharmony_ci   * +----+------+-----+----------------+
9713498266Sopenharmony_ci   * |VER | MTYP | LEN |     TOKEN      |
9813498266Sopenharmony_ci   * +----+------+----------------------+
9913498266Sopenharmony_ci   * | 1  |  1   |  2  | up to 2^16 - 1 |
10013498266Sopenharmony_ci   * +----+------+-----+----------------+
10113498266Sopenharmony_ci   */
10213498266Sopenharmony_ci
10313498266Sopenharmony_ci  /* prepare service name */
10413498266Sopenharmony_ci  if(strchr(service, '/')) {
10513498266Sopenharmony_ci    service_name = strdup(service);
10613498266Sopenharmony_ci    if(!service_name)
10713498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
10813498266Sopenharmony_ci  }
10913498266Sopenharmony_ci  else {
11013498266Sopenharmony_ci    service_name = malloc(service_length +
11113498266Sopenharmony_ci                          strlen(conn->socks_proxy.host.name) + 2);
11213498266Sopenharmony_ci    if(!service_name)
11313498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
11413498266Sopenharmony_ci    msnprintf(service_name, service_length +
11513498266Sopenharmony_ci              strlen(conn->socks_proxy.host.name) + 2, "%s/%s",
11613498266Sopenharmony_ci              service, conn->socks_proxy.host.name);
11713498266Sopenharmony_ci  }
11813498266Sopenharmony_ci
11913498266Sopenharmony_ci  input_desc.cBuffers = 1;
12013498266Sopenharmony_ci  input_desc.pBuffers = &sspi_recv_token;
12113498266Sopenharmony_ci  input_desc.ulVersion = SECBUFFER_VERSION;
12213498266Sopenharmony_ci
12313498266Sopenharmony_ci  sspi_recv_token.BufferType = SECBUFFER_TOKEN;
12413498266Sopenharmony_ci  sspi_recv_token.cbBuffer = 0;
12513498266Sopenharmony_ci  sspi_recv_token.pvBuffer = NULL;
12613498266Sopenharmony_ci
12713498266Sopenharmony_ci  output_desc.cBuffers = 1;
12813498266Sopenharmony_ci  output_desc.pBuffers = &sspi_send_token;
12913498266Sopenharmony_ci  output_desc.ulVersion = SECBUFFER_VERSION;
13013498266Sopenharmony_ci
13113498266Sopenharmony_ci  sspi_send_token.BufferType = SECBUFFER_TOKEN;
13213498266Sopenharmony_ci  sspi_send_token.cbBuffer = 0;
13313498266Sopenharmony_ci  sspi_send_token.pvBuffer = NULL;
13413498266Sopenharmony_ci
13513498266Sopenharmony_ci  wrap_desc.cBuffers = 3;
13613498266Sopenharmony_ci  wrap_desc.pBuffers = sspi_w_token;
13713498266Sopenharmony_ci  wrap_desc.ulVersion = SECBUFFER_VERSION;
13813498266Sopenharmony_ci
13913498266Sopenharmony_ci  cred_handle.dwLower = 0;
14013498266Sopenharmony_ci  cred_handle.dwUpper = 0;
14113498266Sopenharmony_ci
14213498266Sopenharmony_ci  status = s_pSecFn->AcquireCredentialsHandle(NULL,
14313498266Sopenharmony_ci                                              (TCHAR *) TEXT("Kerberos"),
14413498266Sopenharmony_ci                                              SECPKG_CRED_OUTBOUND,
14513498266Sopenharmony_ci                                              NULL,
14613498266Sopenharmony_ci                                              NULL,
14713498266Sopenharmony_ci                                              NULL,
14813498266Sopenharmony_ci                                              NULL,
14913498266Sopenharmony_ci                                              &cred_handle,
15013498266Sopenharmony_ci                                              &expiry);
15113498266Sopenharmony_ci
15213498266Sopenharmony_ci  if(check_sspi_err(data, status, "AcquireCredentialsHandle")) {
15313498266Sopenharmony_ci    failf(data, "Failed to acquire credentials.");
15413498266Sopenharmony_ci    free(service_name);
15513498266Sopenharmony_ci    s_pSecFn->FreeCredentialsHandle(&cred_handle);
15613498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
15713498266Sopenharmony_ci  }
15813498266Sopenharmony_ci
15913498266Sopenharmony_ci  (void)curlx_nonblock(sock, FALSE);
16013498266Sopenharmony_ci
16113498266Sopenharmony_ci  /* As long as we need to keep sending some context info, and there's no  */
16213498266Sopenharmony_ci  /* errors, keep sending it...                                            */
16313498266Sopenharmony_ci  for(;;) {
16413498266Sopenharmony_ci    TCHAR *sname;
16513498266Sopenharmony_ci
16613498266Sopenharmony_ci    sname = curlx_convert_UTF8_to_tchar(service_name);
16713498266Sopenharmony_ci    if(!sname)
16813498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
16913498266Sopenharmony_ci
17013498266Sopenharmony_ci    status = s_pSecFn->InitializeSecurityContext(&cred_handle,
17113498266Sopenharmony_ci                                                 context_handle,
17213498266Sopenharmony_ci                                                 sname,
17313498266Sopenharmony_ci                                                 ISC_REQ_MUTUAL_AUTH |
17413498266Sopenharmony_ci                                                 ISC_REQ_ALLOCATE_MEMORY |
17513498266Sopenharmony_ci                                                 ISC_REQ_CONFIDENTIALITY |
17613498266Sopenharmony_ci                                                 ISC_REQ_REPLAY_DETECT,
17713498266Sopenharmony_ci                                                 0,
17813498266Sopenharmony_ci                                                 SECURITY_NATIVE_DREP,
17913498266Sopenharmony_ci                                                 &input_desc,
18013498266Sopenharmony_ci                                                 0,
18113498266Sopenharmony_ci                                                 &sspi_context,
18213498266Sopenharmony_ci                                                 &output_desc,
18313498266Sopenharmony_ci                                                 &sspi_ret_flags,
18413498266Sopenharmony_ci                                                 &expiry);
18513498266Sopenharmony_ci
18613498266Sopenharmony_ci    curlx_unicodefree(sname);
18713498266Sopenharmony_ci
18813498266Sopenharmony_ci    if(sspi_recv_token.pvBuffer) {
18913498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
19013498266Sopenharmony_ci      sspi_recv_token.pvBuffer = NULL;
19113498266Sopenharmony_ci      sspi_recv_token.cbBuffer = 0;
19213498266Sopenharmony_ci    }
19313498266Sopenharmony_ci
19413498266Sopenharmony_ci    if(check_sspi_err(data, status, "InitializeSecurityContext")) {
19513498266Sopenharmony_ci      free(service_name);
19613498266Sopenharmony_ci      s_pSecFn->FreeCredentialsHandle(&cred_handle);
19713498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
19813498266Sopenharmony_ci      if(sspi_recv_token.pvBuffer)
19913498266Sopenharmony_ci        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
20013498266Sopenharmony_ci      failf(data, "Failed to initialise security context.");
20113498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
20213498266Sopenharmony_ci    }
20313498266Sopenharmony_ci
20413498266Sopenharmony_ci    if(sspi_send_token.cbBuffer) {
20513498266Sopenharmony_ci      socksreq[0] = 1;    /* GSS-API subnegotiation version */
20613498266Sopenharmony_ci      socksreq[1] = 1;    /* authentication message type */
20713498266Sopenharmony_ci      us_length = htons((short)sspi_send_token.cbBuffer);
20813498266Sopenharmony_ci      memcpy(socksreq + 2, &us_length, sizeof(short));
20913498266Sopenharmony_ci
21013498266Sopenharmony_ci      written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
21113498266Sopenharmony_ci      if(code || (4 != written)) {
21213498266Sopenharmony_ci        failf(data, "Failed to send SSPI authentication request.");
21313498266Sopenharmony_ci        free(service_name);
21413498266Sopenharmony_ci        if(sspi_send_token.pvBuffer)
21513498266Sopenharmony_ci          s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
21613498266Sopenharmony_ci        if(sspi_recv_token.pvBuffer)
21713498266Sopenharmony_ci          s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
21813498266Sopenharmony_ci        s_pSecFn->FreeCredentialsHandle(&cred_handle);
21913498266Sopenharmony_ci        s_pSecFn->DeleteSecurityContext(&sspi_context);
22013498266Sopenharmony_ci        return CURLE_COULDNT_CONNECT;
22113498266Sopenharmony_ci      }
22213498266Sopenharmony_ci
22313498266Sopenharmony_ci      written = Curl_conn_cf_send(cf->next, data,
22413498266Sopenharmony_ci                                  (char *)sspi_send_token.pvBuffer,
22513498266Sopenharmony_ci                                  sspi_send_token.cbBuffer, &code);
22613498266Sopenharmony_ci      if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
22713498266Sopenharmony_ci        failf(data, "Failed to send SSPI authentication token.");
22813498266Sopenharmony_ci        free(service_name);
22913498266Sopenharmony_ci        if(sspi_send_token.pvBuffer)
23013498266Sopenharmony_ci          s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
23113498266Sopenharmony_ci        if(sspi_recv_token.pvBuffer)
23213498266Sopenharmony_ci          s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
23313498266Sopenharmony_ci        s_pSecFn->FreeCredentialsHandle(&cred_handle);
23413498266Sopenharmony_ci        s_pSecFn->DeleteSecurityContext(&sspi_context);
23513498266Sopenharmony_ci        return CURLE_COULDNT_CONNECT;
23613498266Sopenharmony_ci      }
23713498266Sopenharmony_ci
23813498266Sopenharmony_ci    }
23913498266Sopenharmony_ci
24013498266Sopenharmony_ci    if(sspi_send_token.pvBuffer) {
24113498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
24213498266Sopenharmony_ci      sspi_send_token.pvBuffer = NULL;
24313498266Sopenharmony_ci    }
24413498266Sopenharmony_ci    sspi_send_token.cbBuffer = 0;
24513498266Sopenharmony_ci
24613498266Sopenharmony_ci    if(sspi_recv_token.pvBuffer) {
24713498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
24813498266Sopenharmony_ci      sspi_recv_token.pvBuffer = NULL;
24913498266Sopenharmony_ci    }
25013498266Sopenharmony_ci    sspi_recv_token.cbBuffer = 0;
25113498266Sopenharmony_ci
25213498266Sopenharmony_ci    if(status != SEC_I_CONTINUE_NEEDED)
25313498266Sopenharmony_ci      break;
25413498266Sopenharmony_ci
25513498266Sopenharmony_ci    /* analyse response */
25613498266Sopenharmony_ci
25713498266Sopenharmony_ci    /*   GSS-API response looks like
25813498266Sopenharmony_ci     * +----+------+-----+----------------+
25913498266Sopenharmony_ci     * |VER | MTYP | LEN |     TOKEN      |
26013498266Sopenharmony_ci     * +----+------+----------------------+
26113498266Sopenharmony_ci     * | 1  |  1   |  2  | up to 2^16 - 1 |
26213498266Sopenharmony_ci     * +----+------+-----+----------------+
26313498266Sopenharmony_ci     */
26413498266Sopenharmony_ci
26513498266Sopenharmony_ci    result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
26613498266Sopenharmony_ci    if(result || (actualread != 4)) {
26713498266Sopenharmony_ci      failf(data, "Failed to receive SSPI authentication response.");
26813498266Sopenharmony_ci      free(service_name);
26913498266Sopenharmony_ci      s_pSecFn->FreeCredentialsHandle(&cred_handle);
27013498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
27113498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
27213498266Sopenharmony_ci    }
27313498266Sopenharmony_ci
27413498266Sopenharmony_ci    /* ignore the first (VER) byte */
27513498266Sopenharmony_ci    if(socksreq[1] == 255) { /* status / message type */
27613498266Sopenharmony_ci      failf(data, "User was rejected by the SOCKS5 server (%u %u).",
27713498266Sopenharmony_ci            (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
27813498266Sopenharmony_ci      free(service_name);
27913498266Sopenharmony_ci      s_pSecFn->FreeCredentialsHandle(&cred_handle);
28013498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
28113498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
28213498266Sopenharmony_ci    }
28313498266Sopenharmony_ci
28413498266Sopenharmony_ci    if(socksreq[1] != 1) { /* status / message type */
28513498266Sopenharmony_ci      failf(data, "Invalid SSPI authentication response type (%u %u).",
28613498266Sopenharmony_ci            (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
28713498266Sopenharmony_ci      free(service_name);
28813498266Sopenharmony_ci      s_pSecFn->FreeCredentialsHandle(&cred_handle);
28913498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
29013498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
29113498266Sopenharmony_ci    }
29213498266Sopenharmony_ci
29313498266Sopenharmony_ci    memcpy(&us_length, socksreq + 2, sizeof(short));
29413498266Sopenharmony_ci    us_length = ntohs(us_length);
29513498266Sopenharmony_ci
29613498266Sopenharmony_ci    sspi_recv_token.cbBuffer = us_length;
29713498266Sopenharmony_ci    sspi_recv_token.pvBuffer = malloc(us_length);
29813498266Sopenharmony_ci
29913498266Sopenharmony_ci    if(!sspi_recv_token.pvBuffer) {
30013498266Sopenharmony_ci      free(service_name);
30113498266Sopenharmony_ci      s_pSecFn->FreeCredentialsHandle(&cred_handle);
30213498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
30313498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
30413498266Sopenharmony_ci    }
30513498266Sopenharmony_ci    result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer,
30613498266Sopenharmony_ci                                sspi_recv_token.cbBuffer, &actualread);
30713498266Sopenharmony_ci
30813498266Sopenharmony_ci    if(result || (actualread != us_length)) {
30913498266Sopenharmony_ci      failf(data, "Failed to receive SSPI authentication token.");
31013498266Sopenharmony_ci      free(service_name);
31113498266Sopenharmony_ci      if(sspi_recv_token.pvBuffer)
31213498266Sopenharmony_ci        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
31313498266Sopenharmony_ci      s_pSecFn->FreeCredentialsHandle(&cred_handle);
31413498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
31513498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
31613498266Sopenharmony_ci    }
31713498266Sopenharmony_ci
31813498266Sopenharmony_ci    context_handle = &sspi_context;
31913498266Sopenharmony_ci  }
32013498266Sopenharmony_ci
32113498266Sopenharmony_ci  free(service_name);
32213498266Sopenharmony_ci
32313498266Sopenharmony_ci  /* Everything is good so far, user was authenticated! */
32413498266Sopenharmony_ci  status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
32513498266Sopenharmony_ci                                                SECPKG_CRED_ATTR_NAMES,
32613498266Sopenharmony_ci                                                &names);
32713498266Sopenharmony_ci  s_pSecFn->FreeCredentialsHandle(&cred_handle);
32813498266Sopenharmony_ci  if(check_sspi_err(data, status, "QueryCredentialAttributes")) {
32913498266Sopenharmony_ci    s_pSecFn->DeleteSecurityContext(&sspi_context);
33013498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(names.sUserName);
33113498266Sopenharmony_ci    failf(data, "Failed to determine user name.");
33213498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
33313498266Sopenharmony_ci  }
33413498266Sopenharmony_ci  else {
33513498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
33613498266Sopenharmony_ci    char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName);
33713498266Sopenharmony_ci    infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
33813498266Sopenharmony_ci          (user_utf8 ? user_utf8 : "(unknown)"));
33913498266Sopenharmony_ci    curlx_unicodefree(user_utf8);
34013498266Sopenharmony_ci#endif
34113498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(names.sUserName);
34213498266Sopenharmony_ci  }
34313498266Sopenharmony_ci
34413498266Sopenharmony_ci  /* Do encryption */
34513498266Sopenharmony_ci  socksreq[0] = 1;    /* GSS-API subnegotiation version */
34613498266Sopenharmony_ci  socksreq[1] = 2;    /* encryption message type */
34713498266Sopenharmony_ci
34813498266Sopenharmony_ci  gss_enc = 0; /* no data protection */
34913498266Sopenharmony_ci  /* do confidentiality protection if supported */
35013498266Sopenharmony_ci  if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
35113498266Sopenharmony_ci    gss_enc = 2;
35213498266Sopenharmony_ci  /* else do integrity protection */
35313498266Sopenharmony_ci  else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
35413498266Sopenharmony_ci    gss_enc = 1;
35513498266Sopenharmony_ci
35613498266Sopenharmony_ci  infof(data, "SOCKS5 server supports GSS-API %s data protection.",
35713498266Sopenharmony_ci        (gss_enc == 0)?"no":((gss_enc == 1)?"integrity":"confidentiality") );
35813498266Sopenharmony_ci  /* force to no data protection, avoid encryption/decryption for now */
35913498266Sopenharmony_ci  gss_enc = 0;
36013498266Sopenharmony_ci  /*
36113498266Sopenharmony_ci   * Sending the encryption type in clear seems wrong. It should be
36213498266Sopenharmony_ci   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
36313498266Sopenharmony_ci   * The NEC reference implementations on which this is based is
36413498266Sopenharmony_ci   * therefore at fault
36513498266Sopenharmony_ci   *
36613498266Sopenharmony_ci   *  +------+------+------+.......................+
36713498266Sopenharmony_ci   *  + ver  | mtyp | len  |   token               |
36813498266Sopenharmony_ci   *  +------+------+------+.......................+
36913498266Sopenharmony_ci   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
37013498266Sopenharmony_ci   *  +------+------+------+.......................+
37113498266Sopenharmony_ci   *
37213498266Sopenharmony_ci   *   Where:
37313498266Sopenharmony_ci   *
37413498266Sopenharmony_ci   *  - "ver" is the protocol version number, here 1 to represent the
37513498266Sopenharmony_ci   *    first version of the SOCKS/GSS-API protocol
37613498266Sopenharmony_ci   *
37713498266Sopenharmony_ci   *  - "mtyp" is the message type, here 2 to represent a protection
37813498266Sopenharmony_ci   *    -level negotiation message
37913498266Sopenharmony_ci   *
38013498266Sopenharmony_ci   *  - "len" is the length of the "token" field in octets
38113498266Sopenharmony_ci   *
38213498266Sopenharmony_ci   *  - "token" is the GSS-API encapsulated protection level
38313498266Sopenharmony_ci   *
38413498266Sopenharmony_ci   * The token is produced by encapsulating an octet containing the
38513498266Sopenharmony_ci   * required protection level using gss_seal()/gss_wrap() with conf_req
38613498266Sopenharmony_ci   * set to FALSE.  The token is verified using gss_unseal()/
38713498266Sopenharmony_ci   * gss_unwrap().
38813498266Sopenharmony_ci   *
38913498266Sopenharmony_ci   */
39013498266Sopenharmony_ci
39113498266Sopenharmony_ci  if(data->set.socks5_gssapi_nec) {
39213498266Sopenharmony_ci    us_length = htons((short)1);
39313498266Sopenharmony_ci    memcpy(socksreq + 2, &us_length, sizeof(short));
39413498266Sopenharmony_ci  }
39513498266Sopenharmony_ci  else {
39613498266Sopenharmony_ci    status = s_pSecFn->QueryContextAttributes(&sspi_context,
39713498266Sopenharmony_ci                                              SECPKG_ATTR_SIZES,
39813498266Sopenharmony_ci                                              &sspi_sizes);
39913498266Sopenharmony_ci    if(check_sspi_err(data, status, "QueryContextAttributes")) {
40013498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
40113498266Sopenharmony_ci      failf(data, "Failed to query security context attributes.");
40213498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
40313498266Sopenharmony_ci    }
40413498266Sopenharmony_ci
40513498266Sopenharmony_ci    sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
40613498266Sopenharmony_ci    sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
40713498266Sopenharmony_ci    sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
40813498266Sopenharmony_ci
40913498266Sopenharmony_ci    if(!sspi_w_token[0].pvBuffer) {
41013498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
41113498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
41213498266Sopenharmony_ci    }
41313498266Sopenharmony_ci
41413498266Sopenharmony_ci    sspi_w_token[1].cbBuffer = 1;
41513498266Sopenharmony_ci    sspi_w_token[1].pvBuffer = malloc(1);
41613498266Sopenharmony_ci    if(!sspi_w_token[1].pvBuffer) {
41713498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
41813498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
41913498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
42013498266Sopenharmony_ci    }
42113498266Sopenharmony_ci
42213498266Sopenharmony_ci    memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1);
42313498266Sopenharmony_ci    sspi_w_token[2].BufferType = SECBUFFER_PADDING;
42413498266Sopenharmony_ci    sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
42513498266Sopenharmony_ci    sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
42613498266Sopenharmony_ci    if(!sspi_w_token[2].pvBuffer) {
42713498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
42813498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
42913498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
43013498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
43113498266Sopenharmony_ci    }
43213498266Sopenharmony_ci    status = s_pSecFn->EncryptMessage(&sspi_context,
43313498266Sopenharmony_ci                                      KERB_WRAP_NO_ENCRYPT,
43413498266Sopenharmony_ci                                      &wrap_desc,
43513498266Sopenharmony_ci                                      0);
43613498266Sopenharmony_ci    if(check_sspi_err(data, status, "EncryptMessage")) {
43713498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
43813498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
43913498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
44013498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
44113498266Sopenharmony_ci      failf(data, "Failed to query security context attributes.");
44213498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
44313498266Sopenharmony_ci    }
44413498266Sopenharmony_ci    sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
44513498266Sopenharmony_ci      + sspi_w_token[1].cbBuffer
44613498266Sopenharmony_ci      + sspi_w_token[2].cbBuffer;
44713498266Sopenharmony_ci    sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
44813498266Sopenharmony_ci    if(!sspi_send_token.pvBuffer) {
44913498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
45013498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
45113498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
45213498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
45313498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
45413498266Sopenharmony_ci    }
45513498266Sopenharmony_ci
45613498266Sopenharmony_ci    memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
45713498266Sopenharmony_ci           sspi_w_token[0].cbBuffer);
45813498266Sopenharmony_ci    memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
45913498266Sopenharmony_ci           sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
46013498266Sopenharmony_ci    memcpy((PUCHAR) sspi_send_token.pvBuffer
46113498266Sopenharmony_ci           + sspi_w_token[0].cbBuffer
46213498266Sopenharmony_ci           + sspi_w_token[1].cbBuffer,
46313498266Sopenharmony_ci           sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
46413498266Sopenharmony_ci
46513498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
46613498266Sopenharmony_ci    sspi_w_token[0].pvBuffer = NULL;
46713498266Sopenharmony_ci    sspi_w_token[0].cbBuffer = 0;
46813498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
46913498266Sopenharmony_ci    sspi_w_token[1].pvBuffer = NULL;
47013498266Sopenharmony_ci    sspi_w_token[1].cbBuffer = 0;
47113498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
47213498266Sopenharmony_ci    sspi_w_token[2].pvBuffer = NULL;
47313498266Sopenharmony_ci    sspi_w_token[2].cbBuffer = 0;
47413498266Sopenharmony_ci
47513498266Sopenharmony_ci    us_length = htons((short)sspi_send_token.cbBuffer);
47613498266Sopenharmony_ci    memcpy(socksreq + 2, &us_length, sizeof(short));
47713498266Sopenharmony_ci  }
47813498266Sopenharmony_ci
47913498266Sopenharmony_ci  written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
48013498266Sopenharmony_ci  if(code || (4 != written)) {
48113498266Sopenharmony_ci    failf(data, "Failed to send SSPI encryption request.");
48213498266Sopenharmony_ci    if(sspi_send_token.pvBuffer)
48313498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
48413498266Sopenharmony_ci    s_pSecFn->DeleteSecurityContext(&sspi_context);
48513498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
48613498266Sopenharmony_ci  }
48713498266Sopenharmony_ci
48813498266Sopenharmony_ci  if(data->set.socks5_gssapi_nec) {
48913498266Sopenharmony_ci    memcpy(socksreq, &gss_enc, 1);
49013498266Sopenharmony_ci    written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
49113498266Sopenharmony_ci    if(code || (1 != written)) {
49213498266Sopenharmony_ci      failf(data, "Failed to send SSPI encryption type.");
49313498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
49413498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
49513498266Sopenharmony_ci    }
49613498266Sopenharmony_ci  }
49713498266Sopenharmony_ci  else {
49813498266Sopenharmony_ci    written = Curl_conn_cf_send(cf->next, data,
49913498266Sopenharmony_ci                                (char *)sspi_send_token.pvBuffer,
50013498266Sopenharmony_ci                                sspi_send_token.cbBuffer, &code);
50113498266Sopenharmony_ci    if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
50213498266Sopenharmony_ci      failf(data, "Failed to send SSPI encryption type.");
50313498266Sopenharmony_ci      if(sspi_send_token.pvBuffer)
50413498266Sopenharmony_ci        s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
50513498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
50613498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
50713498266Sopenharmony_ci    }
50813498266Sopenharmony_ci    if(sspi_send_token.pvBuffer)
50913498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
51013498266Sopenharmony_ci  }
51113498266Sopenharmony_ci
51213498266Sopenharmony_ci  result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
51313498266Sopenharmony_ci  if(result || (actualread != 4)) {
51413498266Sopenharmony_ci    failf(data, "Failed to receive SSPI encryption response.");
51513498266Sopenharmony_ci    s_pSecFn->DeleteSecurityContext(&sspi_context);
51613498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
51713498266Sopenharmony_ci  }
51813498266Sopenharmony_ci
51913498266Sopenharmony_ci  /* ignore the first (VER) byte */
52013498266Sopenharmony_ci  if(socksreq[1] == 255) { /* status / message type */
52113498266Sopenharmony_ci    failf(data, "User was rejected by the SOCKS5 server (%u %u).",
52213498266Sopenharmony_ci          (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
52313498266Sopenharmony_ci    s_pSecFn->DeleteSecurityContext(&sspi_context);
52413498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
52513498266Sopenharmony_ci  }
52613498266Sopenharmony_ci
52713498266Sopenharmony_ci  if(socksreq[1] != 2) { /* status / message type */
52813498266Sopenharmony_ci    failf(data, "Invalid SSPI encryption response type (%u %u).",
52913498266Sopenharmony_ci          (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
53013498266Sopenharmony_ci    s_pSecFn->DeleteSecurityContext(&sspi_context);
53113498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
53213498266Sopenharmony_ci  }
53313498266Sopenharmony_ci
53413498266Sopenharmony_ci  memcpy(&us_length, socksreq + 2, sizeof(short));
53513498266Sopenharmony_ci  us_length = ntohs(us_length);
53613498266Sopenharmony_ci
53713498266Sopenharmony_ci  sspi_w_token[0].cbBuffer = us_length;
53813498266Sopenharmony_ci  sspi_w_token[0].pvBuffer = malloc(us_length);
53913498266Sopenharmony_ci  if(!sspi_w_token[0].pvBuffer) {
54013498266Sopenharmony_ci    s_pSecFn->DeleteSecurityContext(&sspi_context);
54113498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
54213498266Sopenharmony_ci  }
54313498266Sopenharmony_ci
54413498266Sopenharmony_ci  result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer,
54513498266Sopenharmony_ci                              sspi_w_token[0].cbBuffer, &actualread);
54613498266Sopenharmony_ci
54713498266Sopenharmony_ci  if(result || (actualread != us_length)) {
54813498266Sopenharmony_ci    failf(data, "Failed to receive SSPI encryption type.");
54913498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
55013498266Sopenharmony_ci    s_pSecFn->DeleteSecurityContext(&sspi_context);
55113498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
55213498266Sopenharmony_ci  }
55313498266Sopenharmony_ci
55413498266Sopenharmony_ci
55513498266Sopenharmony_ci  if(!data->set.socks5_gssapi_nec) {
55613498266Sopenharmony_ci    wrap_desc.cBuffers = 2;
55713498266Sopenharmony_ci    sspi_w_token[0].BufferType = SECBUFFER_STREAM;
55813498266Sopenharmony_ci    sspi_w_token[1].BufferType = SECBUFFER_DATA;
55913498266Sopenharmony_ci    sspi_w_token[1].cbBuffer = 0;
56013498266Sopenharmony_ci    sspi_w_token[1].pvBuffer = NULL;
56113498266Sopenharmony_ci
56213498266Sopenharmony_ci    status = s_pSecFn->DecryptMessage(&sspi_context,
56313498266Sopenharmony_ci                                      &wrap_desc,
56413498266Sopenharmony_ci                                      0,
56513498266Sopenharmony_ci                                      &qop);
56613498266Sopenharmony_ci
56713498266Sopenharmony_ci    if(check_sspi_err(data, status, "DecryptMessage")) {
56813498266Sopenharmony_ci      if(sspi_w_token[0].pvBuffer)
56913498266Sopenharmony_ci        s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
57013498266Sopenharmony_ci      if(sspi_w_token[1].pvBuffer)
57113498266Sopenharmony_ci        s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
57213498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
57313498266Sopenharmony_ci      failf(data, "Failed to query security context attributes.");
57413498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
57513498266Sopenharmony_ci    }
57613498266Sopenharmony_ci
57713498266Sopenharmony_ci    if(sspi_w_token[1].cbBuffer != 1) {
57813498266Sopenharmony_ci      failf(data, "Invalid SSPI encryption response length (%lu).",
57913498266Sopenharmony_ci            (unsigned long)sspi_w_token[1].cbBuffer);
58013498266Sopenharmony_ci      if(sspi_w_token[0].pvBuffer)
58113498266Sopenharmony_ci        s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
58213498266Sopenharmony_ci      if(sspi_w_token[1].pvBuffer)
58313498266Sopenharmony_ci        s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
58413498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
58513498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
58613498266Sopenharmony_ci    }
58713498266Sopenharmony_ci
58813498266Sopenharmony_ci    memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
58913498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
59013498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
59113498266Sopenharmony_ci  }
59213498266Sopenharmony_ci  else {
59313498266Sopenharmony_ci    if(sspi_w_token[0].cbBuffer != 1) {
59413498266Sopenharmony_ci      failf(data, "Invalid SSPI encryption response length (%lu).",
59513498266Sopenharmony_ci            (unsigned long)sspi_w_token[0].cbBuffer);
59613498266Sopenharmony_ci      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
59713498266Sopenharmony_ci      s_pSecFn->DeleteSecurityContext(&sspi_context);
59813498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
59913498266Sopenharmony_ci    }
60013498266Sopenharmony_ci    memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer);
60113498266Sopenharmony_ci    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
60213498266Sopenharmony_ci  }
60313498266Sopenharmony_ci  (void)curlx_nonblock(sock, TRUE);
60413498266Sopenharmony_ci
60513498266Sopenharmony_ci  infof(data, "SOCKS5 access with%s protection granted.",
60613498266Sopenharmony_ci        (socksreq[0] == 0)?"out GSS-API data":
60713498266Sopenharmony_ci        ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
60813498266Sopenharmony_ci
60913498266Sopenharmony_ci  /* For later use if encryption is required
61013498266Sopenharmony_ci     conn->socks5_gssapi_enctype = socksreq[0];
61113498266Sopenharmony_ci     if(socksreq[0] != 0)
61213498266Sopenharmony_ci       conn->socks5_sspi_context = sspi_context;
61313498266Sopenharmony_ci     else {
61413498266Sopenharmony_ci       s_pSecFn->DeleteSecurityContext(&sspi_context);
61513498266Sopenharmony_ci       conn->socks5_sspi_context = sspi_context;
61613498266Sopenharmony_ci     }
61713498266Sopenharmony_ci  */
61813498266Sopenharmony_ci  return CURLE_OK;
61913498266Sopenharmony_ci}
62013498266Sopenharmony_ci#endif
621