- Markus Moeller introduced two new options to libcurl:

CURLOPT_SOCKS5_GSSAPI_SERVICE and CURLOPT_SOCKS5_GSSAPI_NEC to allow libcurl
  to do GSS-style authentication with SOCKS5 proxies. The curl tool got the
  options called --socks5-gssapi-service and --socks5-gssapi-nec to enable
  these.
This commit is contained in:
Daniel Stenberg 2009-01-28 21:33:58 +00:00
parent 6e34c2d59a
commit de4610a55f
17 changed files with 1403 additions and 31 deletions

View File

@ -6,6 +6,13 @@
Changelog Changelog
Daniel Stenberg (28 Jan 2009)
- Markus Moeller introduced two new options to libcurl:
CURLOPT_SOCKS5_GSSAPI_SERVICE and CURLOPT_SOCKS5_GSSAPI_NEC to allow libcurl
to do GSS-style authentication with SOCKS5 proxies. The curl tool got the
options called --socks5-gssapi-service and --socks5-gssapi-nec to enable
these.
Daniel Stenberg (26 Jan 2009) Daniel Stenberg (26 Jan 2009)
- Chad Monroe provided the new CURLOPT_TFTP_BLKSIZE option that allows an app - Chad Monroe provided the new CURLOPT_TFTP_BLKSIZE option that allows an app
to set desired block size to use for TFTP transfers instead of the default to set desired block size to use for TFTP transfers instead of the default

View File

@ -1,8 +1,8 @@
Curl and libcurl 7.19.4 Curl and libcurl 7.19.4
Public curl releases: 110 Public curl releases: 110
Command line options: 129 Command line options: 131
curl_easy_setopt() options: 159 curl_easy_setopt() options: 161
Public functions in libcurl: 58 Public functions in libcurl: 58
Known libcurl bindings: 37 Known libcurl bindings: 37
Contributors: 700 Contributors: 700
@ -13,6 +13,9 @@ This release includes the following changes:
o the OpenSSL-specific code disables TICKET (rfc5077) which is enabled by o the OpenSSL-specific code disables TICKET (rfc5077) which is enabled by
default in openssl 0.9.8j default in openssl 0.9.8j
o Added CURLOPT_TFTP_BLKSIZE o Added CURLOPT_TFTP_BLKSIZE
o Added CURLOPT_SOCKS5_GSSAPI_SERVICE and CURLOPT_SOCKS5_GSSAPI_NEC - with
the corresponding curl options --socks5-gssapi-service and
--socks5-gssapi-nec
This release includes the following bugfixes: This release includes the following bugfixes:
@ -28,6 +31,6 @@ This release would not have looked like this without help, code, reports and
advice from friends like these: advice from friends like these:
Lisa Xu, Daniel Fandrich, Craig A West, Alexey Borzov, Sharad Gupta, Lisa Xu, Daniel Fandrich, Craig A West, Alexey Borzov, Sharad Gupta,
Peter Sylvester, Chad Monroe Peter Sylvester, Chad Monroe, Markus Moeller
Thanks! (and sorry if I forgot to mention someone) Thanks! (and sorry if I forgot to mention someone)

View File

@ -3,8 +3,6 @@ To be addressed in 7.19.4 (planned release: March 2009)
206 - A. Craig West's CURLOPT_HTTP_VERSION change for CONNECT 206 - A. Craig West's CURLOPT_HTTP_VERSION change for CONNECT
208 - Patch to allow GSSAPI authentication to a socks5 server
214 - progress bar prefix, second try (for the curl tool) 214 - progress bar prefix, second try (for the curl tool)
215 - Patch for Metalink Support (for the curl tool) 215 - Patch for Metalink Support (for the curl tool)

View File

@ -821,7 +821,7 @@ The only wildcard is a single * character, which matches all hosts, and
effectively disables the proxy. Each name in this list is matched as either effectively disables the proxy. Each name in this list is matched as either
a domain which contains the hostname, or the hostname itself. For example, a domain which contains the hostname, or the hostname itself. For example,
local.com would match local.com, local.com:80, and www.local.com, but not local.com would match local.com, local.com:80, and www.local.com, but not
www.notlocal.com. www.notlocal.com. (Added in 7.19.4).
.IP "--ntlm" .IP "--ntlm"
(HTTP) Enables NTLM authentication. The NTLM authentication method was (HTTP) Enables NTLM authentication. The NTLM authentication method was
designed by Microsoft and is used by IIS web servers. It is a proprietary designed by Microsoft and is used by IIS web servers. It is a proprietary
@ -1114,6 +1114,21 @@ mutually exclusive.
If this option is used several times, the last one will be used. (This option If this option is used several times, the last one will be used. (This option
was previously wrongly documented and used as --socks without the number was previously wrongly documented and used as --socks without the number
appended.) appended.)
.IP "--socks5-gssapi-service <servicename>"
The default service name for a socks server is rcmd/server-fqdn. This option
allows you to change it.
Examples:
--socks5 proxy-name \fI--socks5-gssapi-service\fP sockd would use
sockd/proxy-name
--socks5 proxy-name \fI--socks5-gssapi-service\fP sockd/real-name would use
sockd/real-name for cases where the proxy-name does not match the princpal name.
(Added in 7.19.4).
.IP "--socks5-gssapi-nec"
As part of the gssapi negotiation a protection mode is negotiated. The rfc1961
says in section 4.3/4.4 it should be protected, but the NEC reference
implementation does not. The option \fI--socks5-gssapi-nec\fP allows the
unprotected exchange of the protection mode negotiation. (Added in 7.19.4).
.IP "--stderr <file>" .IP "--stderr <file>"
Redirect all writes to stderr to the specified file instead. If the file name Redirect all writes to stderr to the specified file instead. If the file name
is a plain '-', it is instead written to stdout. This option has no point when is a plain '-', it is instead written to stdout. This option has no point when

View File

@ -487,6 +487,16 @@ Set the parameter to 1 to make the library tunnel all operations through a
given HTTP proxy. There is a big difference between using a proxy and to given HTTP proxy. There is a big difference between using a proxy and to
tunnel through it. If you don't know what this means, you probably don't want tunnel through it. If you don't know what this means, you probably don't want
this tunneling option. this tunneling option.
.IP CURLOPT_SOCKS5_GSSAPI_SERVICE
Pass a char * as parameter to a string holding the name of the service. The
default service name for a SOCKS5 server is rcmd/server-fqdn. This option
allows you to change it. (Added in 7.19.4)
.IP CURLOPT_SOCKS5_GSSAPI_NEC
Pass a long set to 1 to enable or 0 to disable. As part of the gssapi
negotiation a protection mode is negotiated. The rfc1961 says in section
4.3/4.4 it should be protected, but the NEC reference implementation does not.
If enabled, this option allows the unprotected exchange of the protection mode
negotiation. (Added in 7.19.4).
.IP CURLOPT_INTERFACE .IP CURLOPT_INTERFACE
Pass a char * as parameter. This sets the interface name to use as outgoing Pass a char * as parameter. This sets the interface name to use as outgoing
network interface. The name can be an interface name, an IP address, or a host network interface. The name can be an interface name, an IP address, or a host

View File

@ -1162,6 +1162,12 @@ typedef enum {
/* block size for TFTP transfers */ /* block size for TFTP transfers */
CINIT(TFTP_BLKSIZE, LONG, 178), CINIT(TFTP_BLKSIZE, LONG, 178),
/* Socks Service */
CINIT(SOCKS5_GSSAPI_SERVICE, LONG, 179),
/* Socks Service */
CINIT(SOCKS5_GSSAPI_NEC, LONG, 180),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;

View File

@ -9,7 +9,8 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
http_negotiate.c http_ntlm.c inet_pton.c strtoofft.c strerror.c \ http_negotiate.c http_ntlm.c inet_pton.c strtoofft.c strerror.c \
hostares.c hostasyn.c hostip4.c hostip6.c hostsyn.c hostthre.c \ hostares.c hostasyn.c hostip4.c hostip6.c hostsyn.c hostthre.c \
inet_ntop.c parsedate.c select.c gtls.c sslgen.c tftp.c splay.c \ inet_ntop.c parsedate.c select.c gtls.c sslgen.c tftp.c splay.c \
strdup.c socks.c ssh.c nss.c qssl.c rawstr.c curl_addrinfo.c strdup.c socks.c ssh.c nss.c qssl.c rawstr.c curl_addrinfo.c \
socks_gssapi.c socks_sspi.c
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \

View File

@ -101,7 +101,7 @@ LFLAGS = /nologo /machine:$(MACHINE)
SSLLIBS = libeay32.lib ssleay32.lib SSLLIBS = libeay32.lib ssleay32.lib
ZLIBLIBSDLL= zdll.lib ZLIBLIBSDLL= zdll.lib
ZLIBLIBS = zlib.lib ZLIBLIBS = zlib.lib
WINLIBS = wsock32.lib wldap32.lib WINLIBS = wsock32.lib wldap32.lib secur32.lib
CFLAGS = $(CFLAGS) CFLAGS = $(CFLAGS)
CFGSET = FALSE CFGSET = FALSE
@ -489,6 +489,7 @@ X_OBJS= \
$(DIROBJ)\sendf.obj \ $(DIROBJ)\sendf.obj \
$(DIROBJ)\share.obj \ $(DIROBJ)\share.obj \
$(DIROBJ)\socks.obj \ $(DIROBJ)\socks.obj \
$(DIROBJ)\socks_sspi.obj \
$(DIROBJ)\speedcheck.obj \ $(DIROBJ)\speedcheck.obj \
$(DIROBJ)\splay.obj \ $(DIROBJ)\splay.obj \
$(DIROBJ)\ssh.obj \ $(DIROBJ)\ssh.obj \

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -60,14 +60,14 @@
* This is STUPID BLOCKING behaviour which we frown upon, but right now this * This is STUPID BLOCKING behaviour which we frown upon, but right now this
* is what we have... * is what we have...
*/ */
static int blockread_all(struct connectdata *conn, /* connection data */ int Curl_blockread_all(struct connectdata *conn, /* connection data */
curl_socket_t sockfd, /* read from this socket */ curl_socket_t sockfd, /* read from this socket */
char *buf, /* store read data here */ char *buf, /* store read data here */
ssize_t buffersize, /* max amount to read */ ssize_t buffersize, /* max amount to read */
ssize_t *n, /* amount bytes read */ ssize_t *n, /* amount bytes read */
long conn_timeout) /* timeout for data wait long conn_timeout) /* timeout for data wait
relative to relative to
conn->created */ conn->created */
{ {
ssize_t nread; ssize_t nread;
ssize_t allread = 0; ssize_t allread = 0;
@ -264,7 +264,7 @@ CURLcode Curl_SOCKS4(const char *proxy_name,
packetsize = 8; /* receive data size */ packetsize = 8; /* receive data size */
/* Receive response */ /* Receive response */
result = blockread_all(conn, sock, (char *)socksreq, packetsize, result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
&actualread, timeout); &actualread, timeout);
if((result != CURLE_OK) || (actualread != packetsize)) { if((result != CURLE_OK) || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS4 connect request ack."); failf(data, "Failed to receive SOCKS4 connect request ack.");
@ -429,9 +429,16 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
} }
socksreq[0] = 5; /* version */ 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[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
socksreq[2] = 0; /* no authentication */ socksreq[2] = 0; /* no authentication */
socksreq[3] = 2; /* username/password */ socksreq[3] = 2; /* username/password */
#endif
Curl_nonblock(sock, FALSE); Curl_nonblock(sock, FALSE);
@ -462,7 +469,8 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
Curl_nonblock(sock, FALSE); Curl_nonblock(sock, FALSE);
result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout); result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
timeout);
if((result != CURLE_OK) || (actualread != 2)) { if((result != CURLE_OK) || (actualread != 2)) {
failf(data, "Unable to receive initial SOCKS5 response."); failf(data, "Unable to receive initial SOCKS5 response.");
return CURLE_COULDNT_CONNECT; return CURLE_COULDNT_CONNECT;
@ -476,6 +484,15 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
/* Nothing to do, no authentication needed */ /* 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) { else if(socksreq[1] == 2) {
/* Needs user name and password */ /* Needs user name and password */
size_t userlen, pwlen; size_t userlen, pwlen;
@ -511,7 +528,7 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
return CURLE_COULDNT_CONNECT; return CURLE_COULDNT_CONNECT;
} }
result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
timeout); timeout);
if((result != CURLE_OK) || (actualread != 2)) { if((result != CURLE_OK) || (actualread != 2)) {
failf(data, "Unable to receive SOCKS5 sub-negotiation response."); failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
@ -529,12 +546,16 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
} }
else { else {
/* error */ /* error */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
if(socksreq[1] == 255) {
#else
if(socksreq[1] == 1) { if(socksreq[1] == 1) {
failf(data, failf(data,
"SOCKS5 GSSAPI per-message authentication is not supported."); "SOCKS5 GSSAPI per-message authentication is not supported.");
return CURLE_COULDNT_CONNECT; return CURLE_COULDNT_CONNECT;
} }
else if(socksreq[1] == 255) { else if(socksreq[1] == 255) {
#endif
if(!proxy_name || !*proxy_name) { if(!proxy_name || !*proxy_name) {
failf(data, failf(data,
"No authentication method was acceptable. (It is quite likely" "No authentication method was acceptable. (It is quite likely"
@ -616,6 +637,11 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
*((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port); *((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); code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize, &written);
if((code != CURLE_OK) || (written != packetsize)) { if((code != CURLE_OK) || (written != packetsize)) {
failf(data, "Failed to send SOCKS5 connect request."); failf(data, "Failed to send SOCKS5 connect request.");
@ -624,7 +650,12 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
packetsize = 10; /* minimum packet size is 10 */ packetsize = 10; /* minimum packet size is 10 */
result = blockread_all(conn, sock, (char *)socksreq, packetsize, #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); &actualread, timeout);
if((result != CURLE_OK) || (actualread != packetsize)) { if((result != CURLE_OK) || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS5 connect request ack."); failf(data, "Failed to receive SOCKS5 connect request ack.");
@ -674,15 +705,22 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
} }
/* At this point we already read first 10 bytes */ /* At this point we already read first 10 bytes */
if(packetsize > 10) { #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
packetsize -= 10; if(!conn->socks5_gssapi_enctype) {
result = blockread_all(conn, sock, (char *)&socksreq[10], packetsize, /* decrypt_gssapi_blockread already read the whole packet */
&actualread, timeout); #endif
if((result != CURLE_OK) || (actualread != packetsize)) { if(packetsize > 10) {
failf(data, "Failed to receive SOCKS5 connect request ack."); packetsize -= 10;
return CURLE_COULDNT_CONNECT; 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
Curl_nonblock(sock, TRUE); Curl_nonblock(sock, TRUE);
return CURLE_OK; /* Proxy was successful! */ return CURLE_OK; /* Proxy was successful! */

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -23,6 +23,20 @@
* $Id$ * $Id$
***************************************************************************/ ***************************************************************************/
/*
* Helper read-from-socket functions. Does the same as Curl_read() but it
* blocks until all bytes amount of buffersize will be read. No more, no less.
*
* This is STUPID BLOCKING behaviour which we frown upon, but right now this
* is what we have...
*/
int Curl_blockread_all(struct connectdata *conn,
curl_socket_t sockfd,
char *buf,
ssize_t buffersize,
ssize_t *n,
long conn_timeout);
/* /*
* This function logs in to a SOCKS4(a) proxy and sends the specifics to the * This function logs in to a SOCKS4(a) proxy and sends the specifics to the
* final destination server. * final destination server.
@ -45,4 +59,12 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
int sockindex, int sockindex,
struct connectdata *conn); struct connectdata *conn);
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
/*
* This function handles the sockss5 gssapie negotiation and initialisation
*/
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
struct connectdata *conn);
#endif #endif
#endif /* __SOCKS_H */

547
lib/socks_gssapi.c Normal file
View File

@ -0,0 +1,547 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2009, Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#ifdef HAVE_GSSAPI
#ifdef HAVE_OLD_GSSMIT
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
#endif
#ifndef gss_nt_service_name
#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
#endif
#include <string.h>
#ifdef NEED_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include "urldata.h"
#include "sendf.h"
#include "connect.h"
#include "timeval.h"
#include "socks.h"
static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
/* The last #include file should be: */
#include "memdebug.h"
/*
* Helper gssapi error functions.
*/
static int check_gss_err(struct SessionHandle *data,
OM_uint32 major_status,
OM_uint32 minor_status,
const char* function)
{
if(GSS_ERROR(major_status)) {
OM_uint32 maj_stat,min_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status_string;
char buf[1024];
size_t len;
len = 0;
msg_ctx = 0;
while(!msg_ctx) {
/* convert major status code (GSS-API error) to text */
maj_stat = gss_display_status(&min_stat, major_status,
GSS_C_GSS_CODE,
GSS_C_NULL_OID,
&msg_ctx, &status_string);
if(maj_stat == GSS_S_COMPLETE) {
if(sizeof(buf) > len + status_string.length + 1) {
strcpy(buf+len, (char*) status_string.value);
len += status_string.length;
}
gss_release_buffer(&min_stat, &status_string);
break;
}
gss_release_buffer(&min_stat, &status_string);
}
if(sizeof(buf) > len + 3) {
strcpy(buf+len, ".\n");
len += 2;
}
msg_ctx = 0;
while(!msg_ctx) {
/* convert minor status code (underlying routine error) to text */
maj_stat = gss_display_status(&min_stat, minor_status,
GSS_C_MECH_CODE,
GSS_C_NULL_OID,
&msg_ctx, &status_string);
if(maj_stat == GSS_S_COMPLETE) {
if(sizeof(buf) > len + status_string.length) {
strcpy(buf+len, (char*) status_string.value);
len += status_string.length;
}
gss_release_buffer(&min_stat, &status_string);
break;
}
gss_release_buffer(&min_stat, &status_string);
}
failf(data, "GSSAPI error: %s failed:\n%s\n", function, buf);
return(1);
}
return(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;
long timeout;
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;
u_short us_length;
char *user=NULL;
unsigned char socksreq[4]; /* room for gssapi exchange header only */
char *serviceptr = 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(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_nt_service_name, &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 = gss_init_sec_context(&gss_minor_status,
GSS_C_NO_CREDENTIAL,
&gss_context, server,
GSS_C_NULL_OID,
GSS_C_MUTUAL_FLAG |
GSS_C_REPLAY_FLAG,
0,
NULL,
gss_token,
NULL,
&gss_send_token,
&gss_ret_flags,
NULL);
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 GSSAPI token.");
return CURLE_COULDNT_CONNECT;
}
if(gss_send_token.length != 0) {
socksreq[0] = 1; /* gssapi 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 != CURLE_OK) || (4 != written)) {
failf(data, "Failed to send GSSAPI 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 != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) {
failf(data, "Failed to send GSSAPI 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 */
/* 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 GSSAPI 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 GSSAPI 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 GSSAPI 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, timeout);
if(result != CURLE_OK || actualread != us_length) {
failf(data, "Failed to receive GSSAPI 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 gssapi.\n",user);
free(user);
user=NULL;
/* 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(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 gssapi %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 GSSAPI 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 != CURLE_OK) || (4 != written)) {
failf(data, "Failed to send GSSAPI 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 != CURLE_OK) || ( 1 != written)) {
failf(data, "Failed to send GSSAPI 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 != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) {
failf(data, "Failed to send GSSAPI 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, timeout);
if(result != CURLE_OK || actualread != 4) {
failf(data, "Failed to receive GSSAPI 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 GSSAPI 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, timeout);
if(result != CURLE_OK || actualread != us_length) {
failf(data, "Failed to receive GSSAPI 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 GSSAPI 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 GSSAPI 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 GSSAPI 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 gssapi data":
((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));
conn->socks5_gssapi_enctype = socksreq[0];
if(socksreq[0] == 0)
gss_delete_sec_context(&gss_status, &gss_context, NULL);
return CURLE_OK;
}
#endif

649
lib/socks_sspi.c Normal file
View File

@ -0,0 +1,649 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2009, Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#ifdef USE_WINDOWS_SSPI
#include <ntsecapi.h>
#include <string.h>
#ifdef NEED_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include "urldata.h"
#include "sendf.h"
#include "connect.h"
#include "timeval.h"
#include "socks.h"
/* The last #include file should be: */
#include "memdebug.h"
/*
* Helper sspi error functions.
*/
typedef long OM_uint32;
static int check_sspi_err(struct SessionHandle *data,
OM_uint32 major_status,
OM_uint32 minor_status,
const char* function)
{
char *txt;
if(major_status != SEC_E_OK &&
major_status != SEC_I_COMPLETE_AND_CONTINUE &&
major_status != SEC_I_COMPLETE_NEEDED &&
major_status != SEC_I_CONTINUE_NEEDED) {
failf(data, "SSPI error: %s failed: %d\n", function, major_status);
switch (major_status) {
case SEC_I_COMPLETE_AND_CONTINUE:
txt="SEC_I_COMPLETE_AND_CONTINUE";
break;
case SEC_I_COMPLETE_NEEDED:
txt="SEC_I_COMPLETE_NEEDED";
break;
case SEC_I_CONTINUE_NEEDED:
txt="SEC_I_CONTINUE_NEEDED";
break;
case SEC_I_INCOMPLETE_CREDENTIALS:
txt="SEC_I_INCOMPLETE_CREDENTIALS";
break;
case SEC_E_INSUFFICIENT_MEMORY:
txt="SEC_E_INSUFFICIENT_MEMORY";
break;
case SEC_E_INTERNAL_ERROR:
txt="SEC_E_INTERNAL_ERROR";
break;
case SEC_E_INVALID_HANDLE:
txt="SEC_E_INVALID_HANDLE";
break;
case SEC_E_INVALID_TOKEN:
txt="SEC_E_INVALID_TOKEN";
break;
case SEC_E_LOGON_DENIED:
txt="SEC_E_LOGON_DENIED";
break;
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
txt="SEC_E_NO_AUTHENTICATING_AUTHORITY";
break;
case SEC_E_NO_CREDENTIALS:
txt="SEC_E_NO_CREDENTIALS";
break;
case SEC_E_TARGET_UNKNOWN:
txt="SEC_E_TARGET_UNKNOWN";
break;
case SEC_E_UNSUPPORTED_FUNCTION:
txt="SEC_E_UNSUPPORTED_FUNCTION";
break;
case SEC_E_WRONG_PRINCIPAL:
txt="SEC_E_WRONG_PRINCIPAL";
break;
default:
txt="Unknown error";
}
failf(data, "SSPI error: %s failed: %s\n", function, txt);
return(1);
}
return(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 */
OM_uint32 sspi_major_status, sspi_minor_status=0;
OM_uint32 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 = AcquireCredentialsHandle( NULL,
"Kerberos",
SECPKG_CRED_OUTBOUND,
NULL,
NULL,
NULL,
NULL,
&cred_handle,
&expiry);
if(check_sspi_err(data, sspi_major_status,sspi_minor_status,
"AcquireCredentialsHandle") ) {
failf(data, "Failed to acquire credentials.");
free(service_name);
service_name=NULL;
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 = InitializeSecurityContext( &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) {
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,
"InitializeSecurityContext") ){
free(service_name);
service_name=NULL;
FreeCredentialsHandle(&cred_handle);
DeleteSecurityContext(&sspi_context);
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;
FreeContextBuffer(sspi_send_token.pvBuffer);
FreeContextBuffer(sspi_recv_token.pvBuffer);
FreeCredentialsHandle(&cred_handle);
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 != written)) {
failf(data, "Failed to send SSPI authentication token.");
free(service_name);
service_name=NULL;
FreeContextBuffer(sspi_send_token.pvBuffer);
FreeContextBuffer(sspi_recv_token.pvBuffer);
FreeCredentialsHandle(&cred_handle);
DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
}
FreeContextBuffer(sspi_send_token.pvBuffer);
sspi_send_token.pvBuffer = NULL;
sspi_send_token.cbBuffer = 0;
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;
FreeCredentialsHandle(&cred_handle);
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;
FreeCredentialsHandle(&cred_handle);
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;
FreeCredentialsHandle(&cred_handle);
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;
FreeCredentialsHandle(&cred_handle);
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;
FreeContextBuffer(sspi_recv_token.pvBuffer);
FreeCredentialsHandle(&cred_handle);
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 =
QueryCredentialsAttributes(&cred_handle, SECPKG_CRED_ATTR_NAMES, &names);
FreeCredentialsHandle(&cred_handle);
if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
"QueryCredentialAttributes") ){
DeleteSecurityContext(&sspi_context);
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);
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 = QueryContextAttributes( &sspi_context,
SECPKG_ATTR_SIZES,
&sspi_sizes);
if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
"QueryContextAttributes")) {
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) {
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){
FreeContextBuffer(sspi_w_token[0].pvBuffer);
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) {
FreeContextBuffer(sspi_w_token[0].pvBuffer);
FreeContextBuffer(sspi_w_token[1].pvBuffer);
DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
sspi_major_status = EncryptMessage( &sspi_context,
KERB_WRAP_NO_ENCRYPT,
&wrap_desc,
0);
if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
"EncryptMessage") ) {
FreeContextBuffer(sspi_w_token[0].pvBuffer);
FreeContextBuffer(sspi_w_token[1].pvBuffer);
FreeContextBuffer(sspi_w_token[2].pvBuffer);
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) {
FreeContextBuffer(sspi_w_token[0].pvBuffer);
FreeContextBuffer(sspi_w_token[1].pvBuffer);
FreeContextBuffer(sspi_w_token[2].pvBuffer);
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);
FreeContextBuffer(sspi_w_token[0].pvBuffer);
sspi_w_token[0].pvBuffer = NULL;
sspi_w_token[0].cbBuffer = 0;
FreeContextBuffer(sspi_w_token[1].pvBuffer);
sspi_w_token[1].pvBuffer = NULL;
sspi_w_token[1].cbBuffer = 0;
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.");
FreeContextBuffer(sspi_send_token.pvBuffer);
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.");
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 != written)) {
failf(data, "Failed to send SSPI encryption type.");
FreeContextBuffer(sspi_send_token.pvBuffer);
DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
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.");
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]);
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]);
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) {
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.");
FreeContextBuffer(sspi_w_token[0].pvBuffer);
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 = DecryptMessage(&sspi_context, &wrap_desc, 0, &qop);
if(check_sspi_err(data,sspi_major_status,sspi_minor_status,
"DecryptMessage")) {
FreeContextBuffer(sspi_w_token[0].pvBuffer);
FreeContextBuffer(sspi_w_token[1].pvBuffer);
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);
FreeContextBuffer(sspi_w_token[0].pvBuffer);
FreeContextBuffer(sspi_w_token[1].pvBuffer);
DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
memcpy(socksreq,sspi_w_token[1].pvBuffer,sspi_w_token[1].cbBuffer);
FreeContextBuffer(sspi_w_token[0].pvBuffer);
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);
FreeContextBuffer(sspi_w_token[0].pvBuffer);
DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
memcpy(socksreq,sspi_w_token[0].pvBuffer,sspi_w_token[0].cbBuffer);
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 {
DeleteSecurityContext(&sspi_context);
conn->socks5_sspi_context = sspi_context;
}
*/
return CURLE_OK;
}
#endif

View File

@ -698,6 +698,20 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
set->new_file_perms = 0644; /* Default permissions */ set->new_file_perms = 0644; /* Default permissions */
set->new_directory_perms = 0755; /* Default permissions */ set->new_directory_perms = 0755; /* Default permissions */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
/*
* disallow unprotected protection negotiation NEC reference implementation
* seem not to follow rfc1961 section 4.3/4.4
*/
set->socks5_gssapi_nec = FALSE;
/* set default gssapi service name */
res = setstropt(&set->str[STRING_SOCKS5_GSSAPI_SERVICE],
(char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE);
if (res != CURLE_OK)
return res;
#endif
/* This is our preferred CA cert bundle/path since install time */ /* This is our preferred CA cert bundle/path since install time */
#if defined(CURL_CA_BUNDLE) #if defined(CURL_CA_BUNDLE)
res = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE); res = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE);
@ -1463,6 +1477,23 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
break; break;
#endif #endif
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
case CURLOPT_SOCKS5_GSSAPI_SERVICE:
/*
* Set gssapi service name
*/
result = setstropt(&data->set.str[STRING_SOCKS5_GSSAPI_SERVICE],
va_arg(param, char *));
break;
case CURLOPT_SOCKS5_GSSAPI_NEC:
/*
* set flag for nec socks5 support
*/
data->set.socks5_gssapi_nec = (bool)(0 != va_arg(param, long));
break;
#endif
case CURLOPT_WRITEHEADER: case CURLOPT_WRITEHEADER:
/* /*
* Custom pointer to pass the header write callback function * Custom pointer to pass the header write callback function

View File

@ -84,5 +84,6 @@ void Curl_close_connections(struct SessionHandle *data);
void Curl_reset_reqproto(struct connectdata *conn); void Curl_reset_reqproto(struct connectdata *conn);
#define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */
#define CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE "rcmd" /* default socks5 gssapi service */
#endif #endif

View File

@ -1051,6 +1051,9 @@ struct connectdata {
} proto; } proto;
int cselect_bits; /* bitmask of socket events */ int cselect_bits; /* bitmask of socket events */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
int socks5_gssapi_enctype;
#endif
}; };
/* The end of connectdata. */ /* The end of connectdata. */
@ -1366,6 +1369,9 @@ enum dupstring {
STRING_PROXYPASSWORD, /* Proxy <password>, if used */ STRING_PROXYPASSWORD, /* Proxy <password>, if used */
STRING_NOPROXY, /* List of hosts which should not use the proxy, if STRING_NOPROXY, /* List of hosts which should not use the proxy, if
used */ used */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
STRING_SOCKS5_GSSAPI_SERVICE, /* GSSAPI service name */
#endif
/* -- end of strings -- */ /* -- end of strings -- */
STRING_LAST /* not used, just an end-of-list marker */ STRING_LAST /* not used, just an end-of-list marker */
@ -1524,6 +1530,9 @@ struct UserDefined {
via an HTTP proxy */ via an HTTP proxy */
char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
unsigned int scope; /* address scope for IPv6 */ unsigned int scope; /* address scope for IPv6 */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
long socks5_gssapi_nec; /* flag to support nec socks5 server */
#endif
}; };
struct Names { struct Names {

View File

@ -221,8 +221,8 @@ LFLAGS = $(LFLAGS) $(SSL_IMP_LFLAGS) $(ZLIB_LFLAGS)
!ENDIF !ENDIF
LINKLIBS = $(LINKLIBS) wsock32.lib wldap32.lib LINKLIBS = $(LINKLIBS) wsock32.lib wldap32.lib secur32.lib
LINKLIBS_DEBUG = $(LINKLIBS_DEBUG) wsock32.lib wldap32.lib LINKLIBS_DEBUG = $(LINKLIBS_DEBUG) wsock32.lib wldap32.lib secur32.lib
all : release all : release

View File

@ -536,6 +536,10 @@ struct Configurable {
char *socksproxy; /* set to server string */ char *socksproxy; /* set to server string */
int socksver; /* set to CURLPROXY_SOCKS* define */ int socksver; /* set to CURLPROXY_SOCKS* define */
char *socks5_gssapi_service; /* set service name for gssapi principal
* default rcmd */
int socks5_gssapi_nec ; /* The NEC reference server does not protect
* the encryption type exchange */
bool tcp_nodelay; bool tcp_nodelay;
long req_retry; /* number of retries */ long req_retry; /* number of retries */
@ -807,6 +811,10 @@ static void help(void)
" --socks4a <host[:port]> SOCKS4a proxy on given host + port", " --socks4a <host[:port]> SOCKS4a proxy on given host + port",
" --socks5 <host[:port]> SOCKS5 proxy on given host + port", " --socks5 <host[:port]> SOCKS5 proxy on given host + port",
" --socks5-hostname <host[:port]> SOCKS5 proxy, pass host name to proxy", " --socks5-hostname <host[:port]> SOCKS5 proxy, pass host name to proxy",
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
" --socks5-gssapi-service <name> SOCKS5 proxy service name for gssapi",
" --socks5-gssapi-nec Compatibility with NEC SOCKS5 server",
#endif
" -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs", " -Y/--speed-limit Stop transfer if below speed-limit for 'speed-time' secs",
" -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30", " -y/--speed-time Time needed to trig speed-limit abort. Defaults to 30",
" -2/--sslv2 Use SSLv2 (SSL)", " -2/--sslv2 Use SSLv2 (SSL)",
@ -1669,6 +1677,10 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
{"$3", "keepalive-time", TRUE}, {"$3", "keepalive-time", TRUE},
{"$4", "post302", FALSE}, {"$4", "post302", FALSE},
{"$5", "noproxy", TRUE}, {"$5", "noproxy", TRUE},
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
{"$6", "socks5-gssapi-service", TRUE},
{"$7", "socks5-gssapi-nec", FALSE},
#endif
{"0", "http1.0", FALSE}, {"0", "http1.0", FALSE},
{"1", "tlsv1", FALSE}, {"1", "tlsv1", FALSE},
@ -2182,6 +2194,14 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
/* This specifies the noproxy list */ /* This specifies the noproxy list */
GetStr(&config->noproxy, nextarg); GetStr(&config->noproxy, nextarg);
break; break;
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
case '6': /* --socks5-gssapi-service */
GetStr(&config->socks5_gssapi_service, nextarg);
break;
case '7': /* --socks5-gssapi-nec*/
config->socks5_gssapi_nec = TRUE;
break;
#endif
} }
break; break;
case '#': /* --progress-bar */ case '#': /* --progress-bar */
@ -3700,6 +3720,10 @@ static void free_config_fields(struct Configurable *config)
free(config->referer); free(config->referer);
if (config->hostpubmd5) if (config->hostpubmd5)
free(config->hostpubmd5); free(config->hostpubmd5);
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
if(config->socks5_gssapi_service)
free(config->socks5_gssapi_service);
#endif
curl_slist_free_all(config->quote); /* checks for config->quote == NULL */ curl_slist_free_all(config->quote); /* checks for config->quote == NULL */
curl_slist_free_all(config->prequote); curl_slist_free_all(config->prequote);
@ -4768,6 +4792,16 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver); my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver);
} }
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
/* new in curl 7.19.4 */
if(config->socks5_gssapi_service)
my_setopt(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE,
config->socks5_gssapi_service);
/* new in curl 7.19.4 */
if(config->socks5_gssapi_nec)
my_setopt(curl, CURLOPT_SOCKS5_GSSAPI_NEC, config->socks5_gssapi_nec);
#endif
/* curl 7.13.0 */ /* curl 7.13.0 */
my_setopt(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account); my_setopt(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account);