Esempio n. 1
0
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
                                      struct connectdata *conn)
{
  struct SessionHandle *data = conn->data;
  curl_socket_t sock = conn->sock[sockindex];
  CURLcode code;
  ssize_t actualread;
  ssize_t written;
  int result;
  OM_uint32 gss_major_status, gss_minor_status, gss_status;
  OM_uint32 gss_ret_flags;
  int gss_conf_state, gss_enc;
  gss_buffer_desc  service = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc  gss_send_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc  gss_recv_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc  gss_w_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc* gss_token = GSS_C_NO_BUFFER;
  gss_name_t       server = GSS_C_NO_NAME;
  gss_name_t       gss_client_name = GSS_C_NO_NAME;
  unsigned short   us_length;
  char             *user=NULL;
  unsigned char socksreq[4]; /* room for GSS-API exchange header only */
  char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];

  /*   GSS-API request looks like
   * +----+------+-----+----------------+
   * |VER | MTYP | LEN |     TOKEN      |
   * +----+------+----------------------+
   * | 1  |  1   |  2  | up to 2^16 - 1 |
   * +----+------+-----+----------------+
   */

  /* prepare service name */
  if(strchr(serviceptr, '/')) {
    service.value = malloc(strlen(serviceptr));
    if(!service.value)
      return CURLE_OUT_OF_MEMORY;
    service.length = strlen(serviceptr);
    memcpy(service.value, serviceptr, service.length);

    gss_major_status = gss_import_name(&gss_minor_status, &service,
                                       (gss_OID) GSS_C_NULL_OID, &server);
  }
  else {
    service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2);
    if(!service.value)
      return CURLE_OUT_OF_MEMORY;
    service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1;
    snprintf(service.value, service.length+1, "%s@%s",
             serviceptr, conn->proxy.name);

    gss_major_status = gss_import_name(&gss_minor_status, &service,
                                       GSS_C_NT_HOSTBASED_SERVICE, &server);
  }

  gss_release_buffer(&gss_status, &service); /* clear allocated memory */

  if(check_gss_err(data, gss_major_status,
                   gss_minor_status, "gss_import_name()")) {
    failf(data, "Failed to create service name.");
    gss_release_name(&gss_status, &server);
    return CURLE_COULDNT_CONNECT;
  }

  /* As long as we need to keep sending some context info, and there's no  */
  /* errors, keep sending it...                                            */
  for(;;) {
    gss_major_status = Curl_gss_init_sec_context(data,
                                                 &gss_minor_status,
                                                 &gss_context,
                                                 server,
                                                 &Curl_krb5_mech_oid,
                                                 NULL,
                                                 gss_token,
                                                 &gss_send_token,
                                                 true,
                                                 &gss_ret_flags);

    if(gss_token != GSS_C_NO_BUFFER)
      gss_release_buffer(&gss_status, &gss_recv_token);
    if(check_gss_err(data, gss_major_status,
                     gss_minor_status, "gss_init_sec_context")) {
      gss_release_name(&gss_status, &server);
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_release_buffer(&gss_status, &gss_send_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      failf(data, "Failed to initial GSS-API token.");
      return CURLE_COULDNT_CONNECT;
    }

    if(gss_send_token.length != 0) {
      socksreq[0] = 1;    /* GSS-API subnegotiation version */
      socksreq[1] = 1;    /* authentication message type */
      us_length = htons((short)gss_send_token.length);
      memcpy(socksreq+2, &us_length, sizeof(short));

      code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
      if(code || (4 != written)) {
        failf(data, "Failed to send GSS-API authentication request.");
        gss_release_name(&gss_status, &server);
        gss_release_buffer(&gss_status, &gss_recv_token);
        gss_release_buffer(&gss_status, &gss_send_token);
        gss_delete_sec_context(&gss_status, &gss_context, NULL);
        return CURLE_COULDNT_CONNECT;
      }

      code = Curl_write_plain(conn, sock, (char *)gss_send_token.value,
                              gss_send_token.length, &written);

      if(code || ((ssize_t)gss_send_token.length != written)) {
        failf(data, "Failed to send GSS-API authentication token.");
        gss_release_name(&gss_status, &server);
        gss_release_buffer(&gss_status, &gss_recv_token);
        gss_release_buffer(&gss_status, &gss_send_token);
        gss_delete_sec_context(&gss_status, &gss_context, NULL);
        return CURLE_COULDNT_CONNECT;
      }

    }

    gss_release_buffer(&gss_status, &gss_send_token);
    gss_release_buffer(&gss_status, &gss_recv_token);
    if(gss_major_status != GSS_S_CONTINUE_NEEDED) break;

    /* analyse response */

    /*   GSS-API response looks like
     * +----+------+-----+----------------+
     * |VER | MTYP | LEN |     TOKEN      |
     * +----+------+----------------------+
     * | 1  |  1   |  2  | up to 2^16 - 1 |
     * +----+------+-----+----------------+
     */

    result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
    if(result || (actualread != 4)) {
      failf(data, "Failed to receive GSS-API authentication response.");
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    /* ignore the first (VER) byte */
    if(socksreq[1] == 255) { /* status / message type */
      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    if(socksreq[1] != 1) { /* status / messgae type */
      failf(data, "Invalid GSS-API authentication response type (%d %d).",
            socksreq[0], socksreq[1]);
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(&us_length, socksreq+2, sizeof(short));
    us_length = ntohs(us_length);

    gss_recv_token.length=us_length;
    gss_recv_token.value=malloc(us_length);
    if(!gss_recv_token.value) {
      failf(data,
            "Could not allocate memory for GSS-API authentication "
            "response token.");
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_OUT_OF_MEMORY;
    }

    result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
                              gss_recv_token.length, &actualread);

    if(result || (actualread != us_length)) {
      failf(data, "Failed to receive GSS-API authentication token.");
      gss_release_name(&gss_status, &server);
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    gss_token = &gss_recv_token;
  }

  gss_release_name(&gss_status, &server);

  /* Everything is good so far, user was authenticated! */
  gss_major_status = gss_inquire_context (&gss_minor_status, gss_context,
                                          &gss_client_name, NULL, NULL, NULL,
                                          NULL, NULL, NULL);
  if(check_gss_err(data, gss_major_status,
                   gss_minor_status, "gss_inquire_context")) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    gss_release_name(&gss_status, &gss_client_name);
    failf(data, "Failed to determine user name.");
    return CURLE_COULDNT_CONNECT;
  }
  gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
                                      &gss_send_token, NULL);
  if(check_gss_err(data, gss_major_status,
                   gss_minor_status, "gss_display_name")) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    gss_release_name(&gss_status, &gss_client_name);
    gss_release_buffer(&gss_status, &gss_send_token);
    failf(data, "Failed to determine user name.");
    return CURLE_COULDNT_CONNECT;
  }
  user=malloc(gss_send_token.length+1);
  if(!user) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    gss_release_name(&gss_status, &gss_client_name);
    gss_release_buffer(&gss_status, &gss_send_token);
    return CURLE_OUT_OF_MEMORY;
  }

  memcpy(user, gss_send_token.value, gss_send_token.length);
  user[gss_send_token.length] = '\0';
  gss_release_name(&gss_status, &gss_client_name);
  gss_release_buffer(&gss_status, &gss_send_token);
  infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",user);
  free(user);
  user=NULL;

  /* Do encryption */
  socksreq[0] = 1;    /* GSS-API subnegotiation version */
  socksreq[1] = 2;    /* encryption message type */

  gss_enc = 0; /* no data protection */
  /* do confidentiality protection if supported */
  if(gss_ret_flags & GSS_C_CONF_FLAG)
    gss_enc = 2;
  /* else do integrity protection */
  else if(gss_ret_flags & GSS_C_INTEG_FLAG)
    gss_enc = 1;

  infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
        (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
  /* force for the moment to no data protection */
  gss_enc = 0;
  /*
   * Sending the encryption type in clear seems wrong. It should be
   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
   * The NEC reference implementations on which this is based is
   * therefore at fault
   *
   *  +------+------+------+.......................+
   *  + ver  | mtyp | len  |   token               |
   *  +------+------+------+.......................+
   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
   *  +------+------+------+.......................+
   *
   *   Where:
   *
   *  - "ver" is the protocol version number, here 1 to represent the
   *    first version of the SOCKS/GSS-API protocol
   *
   *  - "mtyp" is the message type, here 2 to represent a protection
   *    -level negotiation message
   *
   *  - "len" is the length of the "token" field in octets
   *
   *  - "token" is the GSS-API encapsulated protection level
   *
   * The token is produced by encapsulating an octet containing the
   * required protection level using gss_seal()/gss_wrap() with conf_req
   * set to false.  The token is verified using gss_unseal()/
   * gss_unwrap().
   *
   */
  if(data->set.socks5_gssapi_nec) {
    us_length = htons((short)1);
    memcpy(socksreq+2, &us_length, sizeof(short));
  }
  else {
    gss_send_token.length = 1;
    gss_send_token.value = malloc(1);
    if(!gss_send_token.value) {
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_OUT_OF_MEMORY;
    }
    memcpy(gss_send_token.value, &gss_enc, 1);

    gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
                                GSS_C_QOP_DEFAULT, &gss_send_token,
                                &gss_conf_state, &gss_w_token);

    if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) {
      gss_release_buffer(&gss_status, &gss_send_token);
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      failf(data, "Failed to wrap GSS-API encryption value into token.");
      return CURLE_COULDNT_CONNECT;
    }
    gss_release_buffer(&gss_status, &gss_send_token);

    us_length = htons((short)gss_w_token.length);
    memcpy(socksreq+2, &us_length, sizeof(short));
  }

  code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
  if(code  || (4 != written)) {
    failf(data, "Failed to send GSS-API encryption request.");
    gss_release_buffer(&gss_status, &gss_w_token);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  if(data->set.socks5_gssapi_nec) {
    memcpy(socksreq, &gss_enc, 1);
    code = Curl_write_plain(conn, sock, socksreq, 1, &written);
    if(code || ( 1 != written)) {
      failf(data, "Failed to send GSS-API encryption type.");
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }
  }
  else {
    code = Curl_write_plain(conn, sock, (char *)gss_w_token.value,
                            gss_w_token.length, &written);
    if(code || ((ssize_t)gss_w_token.length != written)) {
      failf(data, "Failed to send GSS-API encryption type.");
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }
    gss_release_buffer(&gss_status, &gss_w_token);
  }

  result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
  if(result || (actualread != 4)) {
    failf(data, "Failed to receive GSS-API encryption response.");
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  /* ignore the first (VER) byte */
  if(socksreq[1] == 255) { /* status / message type */
    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
          socksreq[0], socksreq[1]);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  if(socksreq[1] != 2) { /* status / messgae type */
    failf(data, "Invalid GSS-API encryption response type (%d %d).",
          socksreq[0], socksreq[1]);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  memcpy(&us_length, socksreq+2, sizeof(short));
  us_length = ntohs(us_length);

  gss_recv_token.length= us_length;
  gss_recv_token.value=malloc(gss_recv_token.length);
  if(!gss_recv_token.value) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_OUT_OF_MEMORY;
  }
  result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
                            gss_recv_token.length, &actualread);

  if(result || (actualread != us_length)) {
    failf(data, "Failed to receive GSS-API encryptrion type.");
    gss_release_buffer(&gss_status, &gss_recv_token);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  if(!data->set.socks5_gssapi_nec) {
    gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
                                  &gss_recv_token, &gss_w_token,
                                  0, GSS_C_QOP_DEFAULT);

    if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) {
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      failf(data, "Failed to unwrap GSS-API encryption value into token.");
      return CURLE_COULDNT_CONNECT;
    }
    gss_release_buffer(&gss_status, &gss_recv_token);

    if(gss_w_token.length != 1) {
      failf(data, "Invalid GSS-API encryption response length (%d).",
            gss_w_token.length);
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(socksreq, gss_w_token.value, gss_w_token.length);
    gss_release_buffer(&gss_status, &gss_w_token);
  }
  else {
    if(gss_recv_token.length != 1) {
      failf(data, "Invalid GSS-API encryption response length (%d).",
            gss_recv_token.length);
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(socksreq, gss_recv_token.value, gss_recv_token.length);
    gss_release_buffer(&gss_status, &gss_recv_token);
  }

  infof(data, "SOCKS5 access with%s protection granted.\n",
        (socksreq[0]==0)?"out GSS-API data":
        ((socksreq[0]==1)?" GSS-API integrity":" GSS-API confidentiality"));

  conn->socks5_gssapi_enctype = socksreq[0];
  if(socksreq[0] == 0)
    gss_delete_sec_context(&gss_status, &gss_context, NULL);

  return CURLE_OK;
}
Esempio n. 2
0
/*
 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
 * destination server.
 */
CURLcode Curl_SOCKS5(const char *proxy_name,
                     const char *proxy_password,
                     const char *hostname,
                     int remote_port,
                     int sockindex,
                     struct connectdata *conn)
{
  /*
    According to the RFC1928, section "6.  Replies". This is what a SOCK5
    replies:

        +----+-----+-------+------+----------+----------+
        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1   | Variable |    2     |
        +----+-----+-------+------+----------+----------+

    Where:

    o  VER    protocol version: X'05'
    o  REP    Reply field:
    o  X'00' succeeded
  */

  unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
  ssize_t actualread;
  ssize_t written;
  int result;
  CURLcode code;
  curl_socket_t sock = conn->sock[sockindex];
  struct SessionHandle *data = conn->data;
  long timeout;
  bool socks5_resolve_local = (bool)(data->set.proxytype == CURLPROXY_SOCKS5);
  const size_t hostname_len = strlen(hostname);
  ssize_t packetsize = 0;

  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
  if(!socks5_resolve_local && hostname_len > 255)
  {
    infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
          "length > 255 [actual len=%zu]\n", hostname_len);
    socks5_resolve_local = TRUE;
  }

  /* get timeout */
  timeout = Curl_timeleft(conn, NULL, TRUE);

  if(timeout < 0) {
    /* time-out, bail out, go home */
    failf(data, "Connection time-out");
    return CURLE_OPERATION_TIMEDOUT;
  }

  curlx_nonblock(sock, TRUE);

  /* wait until socket gets connected */
  result = Curl_socket_ready(CURL_SOCKET_BAD, sock, (int)timeout);

  if(-1 == result) {
    failf(conn->data, "SOCKS5: no connection here");
    return CURLE_COULDNT_CONNECT;
  }
  else if(0 == result) {
    failf(conn->data, "SOCKS5: connection timeout");
    return CURLE_OPERATION_TIMEDOUT;
  }

  if(result & CURL_CSELECT_ERR) {
    failf(conn->data, "SOCKS5: error occured during connection");
    return CURLE_COULDNT_CONNECT;
  }

  socksreq[0] = 5; /* version */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
  socksreq[1] = (char)(proxy_name ? 3 : 2); /* number of methods (below) */
  socksreq[2] = 0; /* no authentication */
  socksreq[3] = 1; /* gssapi */
  socksreq[4] = 2; /* username/password */
#else
  socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
  socksreq[2] = 0; /* no authentication */
  socksreq[3] = 2; /* username/password */
#endif

  curlx_nonblock(sock, FALSE);

  code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
                          &written);
  if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
    failf(data, "Unable to send initial SOCKS5 request.");
    return CURLE_COULDNT_CONNECT;
  }

  curlx_nonblock(sock, TRUE);

  result = Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)timeout);

  if(-1 == result) {
    failf(conn->data, "SOCKS5 nothing to read");
    return CURLE_COULDNT_CONNECT;
  }
  else if(0 == result) {
    failf(conn->data, "SOCKS5 read timeout");
    return CURLE_OPERATION_TIMEDOUT;
  }

  if(result & CURL_CSELECT_ERR) {
    failf(conn->data, "SOCKS5 read error occured");
    return CURLE_RECV_ERROR;
  }

  curlx_nonblock(sock, FALSE);

  result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
                            timeout);
  if((result != CURLE_OK) || (actualread != 2)) {
    failf(data, "Unable to receive initial SOCKS5 response.");
    return CURLE_COULDNT_CONNECT;
  }

  if(socksreq[0] != 5) {
    failf(data, "Received invalid version in initial SOCKS5 response.");
    return CURLE_COULDNT_CONNECT;
  }
  if(socksreq[1] == 0) {
    /* Nothing to do, no authentication needed */
    ;
  }
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
  else if(socksreq[1] == 1) {
    code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
    if(code != CURLE_OK) {
      failf(data, "Unable to negotiate SOCKS5 gssapi context.");
      return CURLE_COULDNT_CONNECT;
    }
  }
#endif
  else if(socksreq[1] == 2) {
    /* Needs user name and password */
    size_t userlen, pwlen;
    int len;
    if(proxy_name && proxy_password) {
      userlen = strlen(proxy_name);
      pwlen = strlen(proxy_password);
    }
    else {
      userlen = 0;
      pwlen = 0;
    }

    /*   username/password request looks like
     * +----+------+----------+------+----------+
     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
     * +----+------+----------+------+----------+
     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
     * +----+------+----------+------+----------+
     */
    len = 0;
    socksreq[len++] = 1;    /* username/pw subnegotiation version */
    socksreq[len++] = (unsigned char) userlen;
    if(proxy_name && userlen)
      memcpy(socksreq + len, proxy_name, userlen);
    len += (int)userlen;
    socksreq[len++] = (unsigned char) pwlen;
    if(proxy_password && pwlen)
      memcpy(socksreq + len, proxy_password, pwlen);
    len += (int)pwlen;

    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
    if((code != CURLE_OK) || (len != written)) {
      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
      return CURLE_COULDNT_CONNECT;
    }

    result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
                         timeout);
    if((result != CURLE_OK) || (actualread != 2)) {
      failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
      return CURLE_COULDNT_CONNECT;
    }

    /* ignore the first (VER) byte */
    if(socksreq[1] != 0) { /* status */
      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    }

    /* Everything is good so far, user was authenticated! */
  }
  else {
    /* error */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    if(socksreq[1] == 255) {
#else
    if(socksreq[1] == 1) {
      failf(data,
            "SOCKS5 GSSAPI per-message authentication is not supported.");
      return CURLE_COULDNT_CONNECT;
    }
    else if(socksreq[1] == 255) {
#endif
      if(!proxy_name || !*proxy_name) {
        failf(data,
              "No authentication method was acceptable. (It is quite likely"
              " that the SOCKS5 server wanted a username/password, since none"
              " was supplied to the server on this connection.)");
      }
      else {
        failf(data, "No authentication method was acceptable.");
      }
      return CURLE_COULDNT_CONNECT;
    }
    else {
      failf(data,
            "Undocumented SOCKS5 mode attempted to be used by server.");
      return CURLE_COULDNT_CONNECT;
    }
  }

  /* Authentication is complete, now specify destination to the proxy */
  socksreq[0] = 5; /* version (SOCKS5) */
  socksreq[1] = 1; /* connect */
  socksreq[2] = 0; /* must be zero */

  if(!socks5_resolve_local) {
    packetsize = (ssize_t)(5 + hostname_len + 2);

    socksreq[3] = 3; /* ATYP: domain name = 3 */
    socksreq[4] = (char) hostname_len; /* address length */
    memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */

    *((unsigned short*)&socksreq[hostname_len+5]) =
      htons((unsigned short)remote_port);
  }
  else {
    struct Curl_dns_entry *dns;
    Curl_addrinfo *hp=NULL;
    int rc = Curl_resolv(conn, hostname, remote_port, &dns);

    packetsize = 10;

    socksreq[3] = 1; /* IPv4 = 1 */

    if(rc == CURLRESOLV_ERROR)
      return CURLE_COULDNT_RESOLVE_HOST;

    if(rc == CURLRESOLV_PENDING) {
      /* this requires that we're in "wait for resolve" state */
      code = Curl_wait_for_resolv(conn, &dns);
      if(code != CURLE_OK)
        return code;
    }

    /*
     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
     * returns a Curl_addrinfo pointer that may not always look the same.
     */
    if(dns)
      hp=dns->addr;
    if(hp) {
      char buf[64];
      unsigned short ip[4];
      Curl_printable_address(hp, buf, sizeof(buf));

      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
                      &ip[0], &ip[1], &ip[2], &ip[3])) {
        socksreq[4] = (unsigned char)ip[0];
        socksreq[5] = (unsigned char)ip[1];
        socksreq[6] = (unsigned char)ip[2];
        socksreq[7] = (unsigned char)ip[3];
      }
      else
        hp = NULL; /* fail! */

      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
    }
    if(!hp) {
      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
            hostname);
      return CURLE_COULDNT_RESOLVE_HOST;
    }

    *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
  }

#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
  if(conn->socks5_gssapi_enctype) {
    failf(data, "SOCKS5 gssapi protection not yet implemented.");
  } else
#endif
  code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize, &written);
  if((code != CURLE_OK) || (written != packetsize)) {
    failf(data, "Failed to send SOCKS5 connect request.");
    return CURLE_COULDNT_CONNECT;
  }

  packetsize = 10; /* minimum packet size is 10 */

#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
  if(conn->socks5_gssapi_enctype) {
    failf(data, "SOCKS5 gssapi protection not yet implemented.");
  } else
#endif
    result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
                           &actualread, timeout);
  if((result != CURLE_OK) || (actualread != packetsize)) {
    failf(data, "Failed to receive SOCKS5 connect request ack.");
    return CURLE_COULDNT_CONNECT;
  }

  if(socksreq[0] != 5) { /* version */
    failf(data,
          "SOCKS5 reply has wrong version, version should be 5.");
    return CURLE_COULDNT_CONNECT;
  }
  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
      failf(data,
            "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
  }

  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
     1928, so the reply packet should be read until the end to avoid errors at
     subsequent protocol level.

    +----+-----+-------+------+----------+----------+
    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+

     ATYP:
     o  IP v4 address: X'01', BND.ADDR = 4 byte
     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
     o  IP v6 address: X'04', BND.ADDR = 16 byte
     */

  /* Calculate real packet size */
  if(socksreq[3] == 3) {
    /* domain name */
    int addrlen = (int) socksreq[4];
    packetsize = 5 + addrlen + 2;
  }
  else if(socksreq[3] == 4) {
    /* IPv6 */
    packetsize = 4 + 16 + 2;
  }

  /* At this point we already read first 10 bytes */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
  if(!conn->socks5_gssapi_enctype) {
    /* decrypt_gssapi_blockread already read the whole packet */
#endif
    if(packetsize > 10) {
      packetsize -= 10;
      result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
                                  packetsize, &actualread, timeout);
      if((result != CURLE_OK) || (actualread != packetsize)) {
        failf(data, "Failed to receive SOCKS5 connect request ack.");
        return CURLE_COULDNT_CONNECT;
      }
    }
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
  }
#endif

  curlx_nonblock(sock, TRUE);
  return CURLE_OK; /* Proxy was successful! */
}
Esempio n. 3
0
/* This is the SSPI-using version of this function */
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
                                      struct connectdata *conn)
{
  struct SessionHandle *data = conn->data;
  curl_socket_t sock = conn->sock[sockindex];
  CURLcode code;
  ssize_t actualread;
  ssize_t written;
  int result;
  long timeout;
  /* Needs GSSAPI authentication */
  SECURITY_STATUS sspi_major_status, sspi_minor_status=0;
  unsigned long sspi_ret_flags=0;
  int gss_enc;
  SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
  SecBufferDesc input_desc, output_desc, wrap_desc;
  SecPkgContext_Sizes sspi_sizes;
  CredHandle cred_handle;
  CtxtHandle sspi_context;
  PCtxtHandle context_handle = NULL;
  SecPkgCredentials_Names names;
  TimeStamp expiry;
  char *service_name=NULL;
  u_short us_length;
  ULONG qop;
  unsigned char socksreq[4]; /* room for gssapi exchange header only */
  char *service = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];

  /* get timeout */
  timeout = Curl_timeleft(conn, NULL, TRUE);

  /*   GSSAPI request looks like
   * +----+------+-----+----------------+
   * |VER | MTYP | LEN |     TOKEN      |
   * +----+------+----------------------+
   * | 1  |  1   |  2  | up to 2^16 - 1 |
   * +----+------+-----+----------------+
   */

  /* prepare service name */
  if (strchr(service, '/')) {
    service_name = malloc(strlen(service));
    if(!service_name)
      return CURLE_OUT_OF_MEMORY;
    memcpy(service_name, service, strlen(service));
  }
  else {
    service_name = malloc(strlen(service) + strlen(conn->proxy.name) + 2);
    if(!service_name)
      return CURLE_OUT_OF_MEMORY;
    snprintf(service_name,strlen(service) +strlen(conn->proxy.name)+2,"%s/%s",
             service,conn->proxy.name);
  }

  input_desc.cBuffers = 1;
  input_desc.pBuffers = &sspi_recv_token;
  input_desc.ulVersion = SECBUFFER_VERSION;

  sspi_recv_token.BufferType = SECBUFFER_TOKEN;
  sspi_recv_token.cbBuffer = 0;
  sspi_recv_token.pvBuffer = NULL;

  output_desc.cBuffers = 1;
  output_desc.pBuffers = &sspi_send_token;
  output_desc.ulVersion = SECBUFFER_VERSION;

  sspi_send_token.BufferType = SECBUFFER_TOKEN;
  sspi_send_token.cbBuffer = 0;
  sspi_send_token.pvBuffer = NULL;

  wrap_desc.cBuffers = 3;
  wrap_desc.pBuffers = sspi_w_token;
  wrap_desc.ulVersion = SECBUFFER_VERSION;

  cred_handle.dwLower = 0;
  cred_handle.dwUpper = 0;

  sspi_major_status = s_pSecFn->AcquireCredentialsHandleA( NULL,
                                                           (char *)"Kerberos",
                                                           SECPKG_CRED_OUTBOUND,
                                                           NULL,
                                                           NULL,
                                                           NULL,
                                                           NULL,
                                                           &cred_handle,
                                                           &expiry);

  if(check_sspi_err(data, sspi_major_status,sspi_minor_status,
                    "AcquireCredentialsHandleA") ) {
    failf(data, "Failed to acquire credentials.");
    free(service_name);
    service_name=NULL;
    s_pSecFn->FreeCredentialsHandle(&cred_handle);
    return CURLE_COULDNT_CONNECT;
  }

  /* As long as we need to keep sending some context info, and there's no  */
  /* errors, keep sending it...                                            */
  for(;;) {

    sspi_major_status = s_pSecFn->InitializeSecurityContextA(
                                    &cred_handle,
                                    context_handle,
                                    service_name,
                                    ISC_REQ_MUTUAL_AUTH |
                                    ISC_REQ_ALLOCATE_MEMORY |
                                    ISC_REQ_CONFIDENTIALITY |
                                    ISC_REQ_REPLAY_DETECT,
                                    0,
                                    SECURITY_NATIVE_DREP,
                                    &input_desc,
                                    0,
                                    &sspi_context,
                                    &output_desc,
                                    &sspi_ret_flags,
                                    &expiry);

    if(sspi_recv_token.pvBuffer) {
      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
      sspi_recv_token.pvBuffer = NULL;
      sspi_recv_token.cbBuffer = 0;
    }

    if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
                      "InitializeSecurityContextA") ){
      free(service_name);
      service_name=NULL;
      s_pSecFn->FreeCredentialsHandle(&cred_handle);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
      failf(data, "Failed to initialise security context.");
      return CURLE_COULDNT_CONNECT;
    }

    if(sspi_send_token.cbBuffer != 0) {
      socksreq[0] = 1;    /* gssapi subnegotiation version */
      socksreq[1] = 1;    /* authentication message type */
      us_length = htons((short)sspi_send_token.cbBuffer);
      memcpy(socksreq+2, &us_length, sizeof(short));

      code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
      if((code != CURLE_OK) || (4 != written)) {
        failf(data, "Failed to send SSPI authentication request.");
        free(service_name);
        service_name=NULL;
        s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
        s_pSecFn->FreeCredentialsHandle(&cred_handle);
        s_pSecFn->DeleteSecurityContext(&sspi_context);
        return CURLE_COULDNT_CONNECT;
      }

      code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
                              sspi_send_token.cbBuffer, &written);
      if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
        failf(data, "Failed to send SSPI authentication token.");
        free(service_name);
        service_name=NULL;
        s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
        s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
        s_pSecFn->FreeCredentialsHandle(&cred_handle);
        s_pSecFn->DeleteSecurityContext(&sspi_context);
        return CURLE_COULDNT_CONNECT;
      }

    }

    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
    sspi_send_token.pvBuffer = NULL;
    sspi_send_token.cbBuffer = 0;
    s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
    sspi_recv_token.pvBuffer = NULL;
    sspi_recv_token.cbBuffer = 0;
    if(sspi_major_status != SEC_I_CONTINUE_NEEDED)  break;

    /* analyse response */

    /*   GSSAPI response looks like
     * +----+------+-----+----------------+
     * |VER | MTYP | LEN |     TOKEN      |
     * +----+------+----------------------+
     * | 1  |  1   |  2  | up to 2^16 - 1 |
     * +----+------+-----+----------------+
     */

    result=Curl_blockread_all(conn, sock, (char *)socksreq, 4,
                              &actualread, timeout);
    if(result != CURLE_OK || actualread != 4) {
      failf(data, "Failed to receive SSPI authentication response.");
      free(service_name);
      service_name=NULL;
      s_pSecFn->FreeCredentialsHandle(&cred_handle);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }

    /* ignore the first (VER) byte */
    if(socksreq[1] == 255) { /* status / message type */
      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      free(service_name);
      service_name=NULL;
      s_pSecFn->FreeCredentialsHandle(&cred_handle);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }

    if(socksreq[1] != 1) { /* status / messgae type */
      failf(data, "Invalid SSPI authentication response type (%d %d).",
            socksreq[0], socksreq[1]);
      free(service_name);
      service_name=NULL;
      s_pSecFn->FreeCredentialsHandle(&cred_handle);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(&us_length, socksreq+2, sizeof(short));
    us_length = ntohs(us_length);

    sspi_recv_token.cbBuffer = us_length;
    sspi_recv_token.pvBuffer = malloc(us_length);

    if(!sspi_recv_token.pvBuffer) {
      free(service_name);
      service_name=NULL;
      s_pSecFn->FreeCredentialsHandle(&cred_handle);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_OUT_OF_MEMORY;
    }
    result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer,
                                sspi_recv_token.cbBuffer,
                                &actualread, timeout);

    if(result != CURLE_OK || actualread != us_length) {
      failf(data, "Failed to receive SSPI authentication token.");
      free(service_name);
      service_name=NULL;
      s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
      s_pSecFn->FreeCredentialsHandle(&cred_handle);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }

    context_handle = &sspi_context;
  }

  free(service_name);
  service_name=NULL;

  /* Everything is good so far, user was authenticated! */
  sspi_major_status = s_pSecFn->QueryCredentialsAttributes( &cred_handle,
                                                            SECPKG_CRED_ATTR_NAMES,
                                                            &names);
  s_pSecFn->FreeCredentialsHandle(&cred_handle);
  if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
                    "QueryCredentialAttributes") ){
    s_pSecFn->DeleteSecurityContext(&sspi_context);
    s_pSecFn->FreeContextBuffer(names.sUserName);
    failf(data, "Failed to determine user name.");
    return CURLE_COULDNT_CONNECT;
  }
  infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",
        names.sUserName);
  s_pSecFn->FreeContextBuffer(names.sUserName);

  /* Do encryption */
  socksreq[0] = 1;    /* gssapi subnegotiation version */
  socksreq[1] = 2;    /* encryption message type */

  gss_enc = 0; /* no data protection */
  /* do confidentiality protection if supported */
  if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
    gss_enc = 2;
  /* else do integrity protection */
  else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
    gss_enc = 1;

  infof(data, "SOCKS5 server supports gssapi %s data protection.\n",
        (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality") );
  /* force to no data protection, avoid encryption/decryption for now */
  gss_enc = 0;
  /*
   * Sending the encryption type in clear seems wrong. It should be
   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
   * The NEC reference implementations on which this is based is
   * therefore at fault
   *
   *  +------+------+------+.......................+
   *  + ver  | mtyp | len  |   token               |
   *  +------+------+------+.......................+
   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
   *  +------+------+------+.......................+
   *
   *   Where:
   *
   *  - "ver" is the protocol version number, here 1 to represent the
   *    first version of the SOCKS/GSS-API protocol
   *
   *  - "mtyp" is the message type, here 2 to represent a protection
   *    -level negotiation message
   *
   *  - "len" is the length of the "token" field in octets
   *
   *  - "token" is the GSS-API encapsulated protection level
   *
   * The token is produced by encapsulating an octet containing the
   * required protection level using gss_seal()/gss_wrap() with conf_req
   * set to FALSE.  The token is verified using gss_unseal()/
   * gss_unwrap().
   *
   */

  if(data->set.socks5_gssapi_nec) {
    us_length = htons((short)1);
    memcpy(socksreq+2, &us_length, sizeof(short));
  }
  else {
    sspi_major_status = s_pSecFn->QueryContextAttributesA( &sspi_context,
                                                           SECPKG_ATTR_SIZES,
                                                           &sspi_sizes);
    if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
                      "QueryContextAttributesA")) {
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      failf(data, "Failed to query security context attributes.");
      return CURLE_COULDNT_CONNECT;
    }

    sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
    sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
    sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);

    if(!sspi_w_token[0].pvBuffer) {
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_OUT_OF_MEMORY;
    }

    sspi_w_token[1].cbBuffer = 1;
    sspi_w_token[1].pvBuffer = malloc(1);
    if(!sspi_w_token[1].pvBuffer){
      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_OUT_OF_MEMORY;
    }

    memcpy(sspi_w_token[1].pvBuffer,&gss_enc,1);
    sspi_w_token[2].BufferType = SECBUFFER_PADDING;
    sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
    sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
    if(!sspi_w_token[2].pvBuffer) {
      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_OUT_OF_MEMORY;
    }
    sspi_major_status = s_pSecFn->EncryptMessage( &sspi_context,
                                                  KERB_WRAP_NO_ENCRYPT,
                                                  &wrap_desc,
                                                  0);
    if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
                      "EncryptMessage") ) {
      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      failf(data, "Failed to query security context attributes.");
      return CURLE_COULDNT_CONNECT;
    }
    sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
      + sspi_w_token[1].cbBuffer
      + sspi_w_token[2].cbBuffer;
    sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
    if(!sspi_send_token.pvBuffer) {
      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_OUT_OF_MEMORY;
    }

    memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
           sspi_w_token[0].cbBuffer);
    memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
           sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
    memcpy((PUCHAR) sspi_send_token.pvBuffer
           +sspi_w_token[0].cbBuffer
           +sspi_w_token[1].cbBuffer,
           sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);

    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
    sspi_w_token[0].pvBuffer = NULL;
    sspi_w_token[0].cbBuffer = 0;
    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
    sspi_w_token[1].pvBuffer = NULL;
    sspi_w_token[1].cbBuffer = 0;
    s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
    sspi_w_token[2].pvBuffer = NULL;
    sspi_w_token[2].cbBuffer = 0;

    us_length = htons((short)sspi_send_token.cbBuffer);
    memcpy(socksreq+2,&us_length,sizeof(short));
  }

  code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
  if((code != CURLE_OK) || (4 != written)) {
    failf(data, "Failed to send SSPI encryption request.");
    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
    s_pSecFn->DeleteSecurityContext(&sspi_context);
    return CURLE_COULDNT_CONNECT;
  }

  if(data->set.socks5_gssapi_nec) {
    memcpy(socksreq,&gss_enc,1);
    code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written);
    if((code != CURLE_OK) || (1 != written)) {
      failf(data, "Failed to send SSPI encryption type.");
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }
  } else {
    code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
                            sspi_send_token.cbBuffer, &written);
    if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
      failf(data, "Failed to send SSPI encryption type.");
      s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }
    s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
  }

  result=Curl_blockread_all(conn, sock, (char *)socksreq, 4,
                            &actualread, timeout);
  if(result != CURLE_OK || actualread != 4) {
    failf(data, "Failed to receive SSPI encryption response.");
    s_pSecFn->DeleteSecurityContext(&sspi_context);
    return CURLE_COULDNT_CONNECT;
  }

  /* ignore the first (VER) byte */
  if(socksreq[1] == 255) { /* status / message type */
    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
          socksreq[0], socksreq[1]);
    s_pSecFn->DeleteSecurityContext(&sspi_context);
    return CURLE_COULDNT_CONNECT;
  }

  if(socksreq[1] != 2) { /* status / message type */
    failf(data, "Invalid SSPI encryption response type (%d %d).",
          socksreq[0], socksreq[1]);
    s_pSecFn->DeleteSecurityContext(&sspi_context);
    return CURLE_COULDNT_CONNECT;
  }

  memcpy(&us_length, socksreq+2, sizeof(short));
  us_length = ntohs(us_length);

  sspi_w_token[0].cbBuffer = us_length;
  sspi_w_token[0].pvBuffer = malloc(us_length);
  if(!sspi_w_token[0].pvBuffer) {
    s_pSecFn->DeleteSecurityContext(&sspi_context);
    return CURLE_OUT_OF_MEMORY;
  }

  result=Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer,
                            sspi_w_token[0].cbBuffer,
                            &actualread, timeout);

  if(result != CURLE_OK || actualread != us_length) {
    failf(data, "Failed to receive SSPI encryption type.");
    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
    s_pSecFn->DeleteSecurityContext(&sspi_context);
    return CURLE_COULDNT_CONNECT;
  }


  if(!data->set.socks5_gssapi_nec) {
    wrap_desc.cBuffers = 2;
    sspi_w_token[0].BufferType = SECBUFFER_STREAM;
    sspi_w_token[1].BufferType = SECBUFFER_DATA;
    sspi_w_token[1].cbBuffer = 0;
    sspi_w_token[1].pvBuffer = NULL;

    sspi_major_status = s_pSecFn->DecryptMessage( &sspi_context,
                                                  &wrap_desc,
                                                  0,
                                                  &qop);

    if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
                      "DecryptMessage")) {
      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      failf(data, "Failed to query security context attributes.");
      return CURLE_COULDNT_CONNECT;
    }

    if(sspi_w_token[1].cbBuffer != 1) {
      failf(data, "Invalid SSPI encryption response length (%d).",
            sspi_w_token[1].cbBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(socksreq,sspi_w_token[1].pvBuffer,sspi_w_token[1].cbBuffer);
    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
    s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
  } else {
    if(sspi_w_token[0].cbBuffer != 1) {
      failf(data, "Invalid SSPI encryption response length (%d).",
            sspi_w_token[0].cbBuffer);
      s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
      s_pSecFn->DeleteSecurityContext(&sspi_context);
      return CURLE_COULDNT_CONNECT;
    }
    memcpy(socksreq,sspi_w_token[0].pvBuffer,sspi_w_token[0].cbBuffer);
    s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
  }

  infof(data, "SOCKS5 access with%s protection granted.\n",
        (socksreq[0]==0)?"out gssapi data":
        ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));

  /* For later use if encryption is required
     conn->socks5_gssapi_enctype = socksreq[0];
     if (socksreq[0] != 0)
     conn->socks5_sspi_context = sspi_context;
     else {
     s_pSecFn->DeleteSecurityContext(&sspi_context);
     conn->socks5_sspi_context = sspi_context;
     }
  */
  return CURLE_OK;
}
Esempio n. 4
0
/*
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
* destination server.
*
* Reference :
*   http://socks.permeo.com/protocol/socks4.protocol
*
* Note :
*   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
*   Nonsupport "Identification Protocol (RFC1413)"
*/
CURLcode Curl_SOCKS4(const char *proxy_name,
                     const char *hostname,
                     int remote_port,
                     int sockindex,
                     struct connectdata *conn,
                     bool protocol4a)
{
#define SOCKS4REQLEN 262
  unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
                                           id */
  int result;
  CURLcode code;
  curl_socket_t sock = conn->sock[sockindex];
  long timeout;
  struct SessionHandle *data = conn->data;

  /* get timeout */
  timeout = Curl_timeleft(conn, NULL, TRUE);

  if(timeout < 0) {
    /* time-out, bail out, go home */
    failf(data, "Connection time-out");
    return CURLE_OPERATION_TIMEDOUT;
  }

  curlx_nonblock(sock, FALSE);

  /*
   * Compose socks4 request
   *
   * Request format
   *
   *     +----+----+----+----+----+----+----+----+----+----+....+----+
   *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
   *     +----+----+----+----+----+----+----+----+----+----+....+----+
   * # of bytes:  1    1      2              4           variable       1
   */

  socksreq[0] = 4; /* version (SOCKS4) */
  socksreq[1] = 1; /* connect */
  *((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port);

  /* DNS resolve only for SOCKS4, not SOCKS4a */
  if (!protocol4a) {
    struct Curl_dns_entry *dns;
    Curl_addrinfo *hp=NULL;
    int rc;

    rc = Curl_resolv(conn, hostname, remote_port, &dns);

    if(rc == CURLRESOLV_ERROR)
      return CURLE_COULDNT_RESOLVE_PROXY;

    if(rc == CURLRESOLV_PENDING)
      /* ignores the return code, but 'dns' remains NULL on failure */
      (void)Curl_wait_for_resolv(conn, &dns);

    /*
     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
     * returns a Curl_addrinfo pointer that may not always look the same.
     */
    if(dns)
      hp=dns->addr;
    if(hp) {
      char buf[64];
      unsigned short ip[4];
      Curl_printable_address(hp, buf, sizeof(buf));

      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
                      &ip[0], &ip[1], &ip[2], &ip[3])) {
        /* Set DSTIP */
        socksreq[4] = (unsigned char)ip[0];
        socksreq[5] = (unsigned char)ip[1];
        socksreq[6] = (unsigned char)ip[2];
        socksreq[7] = (unsigned char)ip[3];
      }
      else
        hp = NULL; /* fail! */

      Curl_resolv_unlock(data, dns); /* not used anymore from now on */

    }
    if(!hp) {
      failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
            hostname);
      return CURLE_COULDNT_RESOLVE_HOST;
    }
  }

  /*
   * This is currently not supporting "Identification Protocol (RFC1413)".
   */
  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
  if(proxy_name)
    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);

  /*
   * Make connection
   */
  {
    ssize_t actualread;
    ssize_t written;
    ssize_t hostnamelen = 0;
    int packetsize = 9 +
      (int)strlen((char*)socksreq + 8); /* size including NUL */

    /* If SOCKS4a, set special invalid IP address 0.0.0.x */
    if (protocol4a) {
      socksreq[4] = 0;
      socksreq[5] = 0;
      socksreq[6] = 0;
      socksreq[7] = 1;
      /* If still enough room in buffer, also append hostname */
      hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
      if (packetsize + hostnamelen <= SOCKS4REQLEN)
        strcpy((char*)socksreq + packetsize, hostname);
      else
        hostnamelen = 0; /* Flag: hostname did not fit in buffer */
    }

    /* Send request */
    code = Curl_write_plain(conn, sock, (char *)socksreq,
                            packetsize + hostnamelen,
                            &written);
    if((code != CURLE_OK) || (written != packetsize + hostnamelen)) {
      failf(data, "Failed to send SOCKS4 connect request.");
      return CURLE_COULDNT_CONNECT;
    }
    if (protocol4a && hostnamelen == 0) {
      /* SOCKS4a with very long hostname - send that name separately */
      hostnamelen = (ssize_t)strlen(hostname) + 1;
      code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
                              &written);
      if((code != CURLE_OK) || (written != hostnamelen)) {
        failf(data, "Failed to send SOCKS4 connect request.");
        return CURLE_COULDNT_CONNECT;
      }
    }

    packetsize = 8; /* receive data size */

    /* Receive response */
    result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
                           &actualread, timeout);
    if((result != CURLE_OK) || (actualread != packetsize)) {
      failf(data, "Failed to receive SOCKS4 connect request ack.");
      return CURLE_COULDNT_CONNECT;
    }

    /*
     * Response format
     *
     *     +----+----+----+----+----+----+----+----+
     *     | VN | CD | DSTPORT |      DSTIP        |
     *     +----+----+----+----+----+----+----+----+
     * # of bytes:  1    1      2              4
     *
     * VN is the version of the reply code and should be 0. CD is the result
     * code with one of the following values:
     *
     * 90: request granted
     * 91: request rejected or failed
     * 92: request rejected because SOCKS server cannot connect to
     *     identd on the client
     * 93: request rejected because the client program and identd
     *     report different user-ids
     */

    /* wrong version ? */
    if(socksreq[0] != 0) {
      failf(data,
            "SOCKS4 reply has wrong version, version should be 4.");
      return CURLE_COULDNT_CONNECT;
    }

    /* Result */
    switch(socksreq[1])
    {
    case 90:
      if (protocol4a)
        infof(data, "SOCKS4a request granted.\n");
      else
        infof(data, "SOCKS4 request granted.\n");
      break;
    case 91:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected or failed.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    case 92:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected because SOCKS server cannot connect to "
            "identd on the client.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    case 93:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected because the client program and identd "
            "report different user-ids.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    default:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", Unknown.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    }
  }

  curlx_nonblock(sock, TRUE);

  return CURLE_OK; /* Proxy was successful! */
}