cURL / Mailing Lists / curl-library / Single Mail

curl-library

[PATCH 1/2]Add NTLM single-sign-on support by using /usr/bin/ntlm_auth

From: Wu, Mandy <mandy.wu_at_intel.com>
Date: Mon, 18 Jul 2011 23:01:40 +0800

On Wed, 2011-07-13 at 05:52 +0800, Daniel Stenberg wrote:
> Two words about your changes to include/curl/curl.h:
>
> 1 - that's a public header for user applications so it cannot use #ifdef
> USE_NTLM_SSO. Error codes should be independent of build features anyway
> so you can just provide them unconditionally.
>
> 2 - speaking of said error codes, it seems CURLE_NTLM_AUTH_FAILED is very
> specific for exactly this authentication method without any real need.
> Couldn't just the existing CURLE_REMOTE_ACCESS_DENIED or similar be used?
>
I have removed the NTLM_AUTH specific error codes, and add DEBUGBUILD
support for running test cases. Here is the updated patch. Please help
review it. The patch for adding test cases for it will be sent in
another mail.

diff --git a/configure.ac b/configure.ac
index e9b49c7..35dc34d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2686,6 +2686,25 @@ then
   USE_MANUAL="no";
 fi

+dnl **********************************************************
+dnl path of NTLM single-sign-on helper ntlm_auth
+dnl
+AC_ARG_WITH(ntlm-auth,
+AC_HELP_STRING([--with-ntlm-auth=PATH],[Where to look for ntlm_auth,
path points to ntlm_auth installation (default: /usr/bin/ntlm_auth);])
+AC_HELP_STRING([--without-ntlm-auth], [disable ntlm single-sign-on by
using ntlm_auth]),
+ntlm_auth="$withval",
+[if test "$ac_cv_native_windows" = "yes"; then ntlm_auth="no"; else
ntlm_auth="/usr/bin/ntlm_auth"; fi])
+if test "$ntlm_auth" != "no"; then
+ AC_DEFINE(USE_NTLM_AUTH, 1, [Whether or not use Samba's 'winbind'
daemon helper 'ntlm_auth' for NTLM single-sign-on])
+ AC_SUBST(USE_NTLM_AUTH, [1])
+ if test "$ntlm_auth" = "yes"; then
+ dnl --with-ntlm-auth (without path) used, use default path
+ ntlm_auth="/usr/bin/ntlm_auth"
+ fi
+fi
+AC_SUBST(ntlm_auth)
+AC_DEFINE_UNQUOTED(NTLM_AUTH, "$ntlm_auth", [Samba's 'winbind' daemon
helper 'ntlm_auth' which can be used for NTLM single-sign-on])
+
 dnl
*************************************************************************
 dnl If the manual variable still is set, then we go with providing a
built-in
 dnl manual
@@ -2969,6 +2988,9 @@ if test "x$CURL_DISABLE_HTTP" != "x1"; then
   if test "x$USE_SSLEAY" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1" \
       -o "x$GNUTLS_ENABLED" = "x1" -o "x$NSS_ENABLED" = "x1"; then
     SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM"
+ if test "x$USE_NTLM_AUTH" = "x1"; then
+ SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM_SSO"
+ fi
   fi
 fi
 if test "x$USE_TLS_SRP" = "x1"; then
diff --git a/include/curl/curl.h b/include/curl/curl.h
index a9d42fa..3df8361 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -600,6 +600,7 @@ typedef enum {
 #define CURLAUTH_GSSNEGOTIATE (1<<2) /* GSS-Negotiate */
 #define CURLAUTH_NTLM (1<<3) /* NTLM */
 #define CURLAUTH_DIGEST_IE (1<<4) /* Digest with IE flavour */
+#define CURLAUTH_NTLM_SSO (1<<5) /* NTLM single-sign-on */
 #define CURLAUTH_ONLY (1<<31) /* used together with a single
other
                                          type to force no auth or just
that
                                          single type */
@@ -2092,6 +2093,8 @@ typedef struct {
 #define CURL_VERSION_CONV (1<<12) /* character conversions
supported */
 #define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking
supported */
 #define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported
*/
+#define CURL_VERSION_NTLM_SSO (1<<15) /* NTLM single-sign-on is
supported
+ by using ntlm_auth */

 /*
  * NAME curl_version_info()
diff --git a/lib/http.c b/lib/http.c
index 56d263b..367edb0 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -307,6 +307,8 @@ static bool pickoneauth(struct auth *pick)
     pick->picked = CURLAUTH_DIGEST;
   else if(avail & CURLAUTH_NTLM)
     pick->picked = CURLAUTH_NTLM;
+ else if(avail & CURLAUTH_NTLM_SSO)
+ pick->picked = CURLAUTH_NTLM_SSO;
   else if(avail & CURLAUTH_BASIC)
     pick->picked = CURLAUTH_BASIC;
   else {
@@ -393,7 +395,9 @@ static CURLcode http_perhapsrewind(struct
connectdata *conn)
   if((expectsend == -1) || (expectsend > bytessent)) {
     /* There is still data left to send */
     if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
- (data->state.authhost.picked == CURLAUTH_NTLM)) {
+ (data->state.authhost.picked == CURLAUTH_NTLM) ||
+ (data->state.authproxy.picked == CURLAUTH_NTLM_SSO) ||
+ (data->state.authhost.picked == CURLAUTH_NTLM_SSO)) {
       if(((expectsend - bytessent) < 2000) ||
          (conn->ntlm.state != NTLMSTATE_NONE)) {
         /* The NTLM-negotiation has started *OR* there is just a little
(<2K)
@@ -554,6 +558,15 @@ output_auth_headers(struct connectdata *conn,
   }
   else
 #endif
+#ifdef USE_NTLM_SSO
+ if(authstatus->picked == CURLAUTH_NTLM_SSO) {
+ auth="NTLM_SSO";
+ result = Curl_output_ntlm_sso(conn, proxy);
+ if(result)
+ return result;
+ }
+ else
+#endif
 #ifndef CURL_DISABLE_CRYPTO_AUTH
   if(authstatus->picked == CURLAUTH_DIGEST) {
     auth="Digest";
@@ -766,13 +779,35 @@ CURLcode Curl_http_input_auth(struct connectdata
*conn,
     if(checkprefix("NTLM", start)) {
       *availp |= CURLAUTH_NTLM;
       authp->avail |= CURLAUTH_NTLM;
- if(authp->picked == CURLAUTH_NTLM) {
+ if(authp->picked == CURLAUTH_NTLM ||
+ authp->picked == CURLAUTH_NTLM_SSO) {
         /* NTLM authentication is picked and activated */
         CURLntlm ntlm =
           Curl_input_ntlm(conn, (bool)(httpcode == 407), start);
-
- if(CURLNTLM_BAD != ntlm)
+ if(CURLNTLM_BAD != ntlm) {
           data->state.authproblem = FALSE;
+#ifdef USE_NTLM_SSO
+ if(authp->picked == CURLAUTH_NTLM_SSO) {
+ *availp &= ~CURLAUTH_NTLM;
+ authp->avail &= ~CURLAUTH_NTLM;
+ *availp |= CURLAUTH_NTLM_SSO;
+ authp->avail |= CURLAUTH_NTLM_SSO;
+
+ /* Get the challenge-message which will be passed to
+ * ntlm_auth for generating the type 3 message later */
+ while(*start && ISSPACE(*start))
+ start++;
+ if(checkprefix("NTLM", start)) {
+ start += strlen("NTLM");
+ while(*start && ISSPACE(*start))
+ start++;
+ if(*start)
+ if((conn->challenge_header = strdup(start)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+#endif
+ }
         else {
           infof(data, "Authentication problem. Ignoring this.\n");
           data->state.authproblem = TRUE;
diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c
index c3ceadc..85b4e93 100644
--- a/lib/http_ntlm.c
+++ b/lib/http_ntlm.c
@@ -43,6 +43,15 @@
 #include <unistd.h>
 #endif

+#ifdef USE_NTLM_SSO
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#endif
+
 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
 #include <netdb.h>
 #endif
@@ -674,6 +683,277 @@ static void unicodecpy(unsigned char *dest,
 }
 #endif

+#ifdef USE_NTLM_SSO
+static void sso_ntlm_close(struct connectdata *conn)
+{
+ if(conn->fd_helper != -1) {
+ close(conn->fd_helper);
+ conn->fd_helper = -1;
+ }
+
+ if(conn->pid) {
+ int ret, i;
+ for(i = 0; i < 4; i++) {
+ ret = waitpid(conn->pid, NULL, WNOHANG);
+ if(ret == conn->pid || errno == ECHILD)
+ break;
+ switch(i) {
+ case 0:
+ kill(conn->pid, SIGTERM);
+ break;
+ case 2:
+ kill(conn->pid, SIGKILL);
+ break;
+ case 1:
+ case 3:
+ break;
+ }
+ }
+ conn->pid = 0;
+ }
+
+ Curl_safefree(conn->challenge_header);
+ conn->challenge_header = NULL;
+ Curl_safefree(conn->response_header);
+ conn->response_header = NULL;
+}
+
+static CURLcode sso_ntlm_initiate(struct connectdata *conn,
+ const char *userp)
+{
+ int sockfds[2];
+ pid_t pid;
+ const char *username;
+ char *slash, *domain = NULL;
+ const char *ntlm_auth;
+
+ /* Return if communication with ntlm_auth already set up */
+ if(conn->fd_helper != -1 || conn->pid) {
+ return CURLE_OK;
+ }
+
+ username = userp;
+ slash = strpbrk(username, "\\/");
+ if(slash) {
+ if((domain = strdup(username)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ slash = domain + (slash - username);
+ *slash = '\0';
+ username = username + (slash - domain) + 1;
+ }
+
+ /* When DEBUGBUILD is defined and environment variable NTLM_AUTH is
set
+ * (in test case 2005), use a fake_ntlm to do NTLM
challenge/response,
+ * which only accept commands and output strings pre-written/saved in
+ * test case 2005 */
+#ifdef DEBUGBUILD
+ ntlm_auth=getenv("NTLM_AUTH");
+#endif
+ if(!ntlm_auth)
+ ntlm_auth = NTLM_AUTH;
+
+ if(access(ntlm_auth, X_OK) != 0)
+ goto done;
+
+ if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
+ goto done;
+
+ pid = fork();
+ if(!pid) {
+ /* child process */
+ if(dup2(sockfds[1], 0) == -1 || dup2(sockfds[1], 1) == -1)
+ exit(1);
+
+ execl(ntlm_auth, "--helper-protocol", "ntlmssp-client-1",
+ "--use-cached-creds", "--username", username,
+ domain?"--domain":NULL, domain, NULL);
+ exit(1);
+ }
+ else if(pid == -1) {
+ close(sockfds[0]);
+ close(sockfds[1]);
+ goto done;
+ }
+
+ close(sockfds[1]);
+ conn->fd_helper = sockfds[0];
+ conn->pid = pid;
+ Curl_safefree(domain);
+ return CURLE_OK;
+
+done:
+ Curl_safefree(domain);
+ return CURLE_REMOTE_ACCESS_DENIED;
+}
+
+static CURLcode sso_ntlm_response(struct connectdata *conn,
+ const char *input, curlntlm state)
+{
+ ssize_t size;
+ char buf[200]; /* enough, type 1, 3 message length is less then 200
*/
+ char *tmpbuf = buf;
+ size_t len_in = strlen(input), len_out = sizeof(buf);
+
+ while(len_in > 0) {
+ int written = write(conn->fd_helper, input, len_in);
+ if(written == -1) {
+ /* Interrupted by a signal, retry it */
+ if(errno == EINTR)
+ continue;
+ /* write failed if other errors happen */
+ goto done;
+ }
+ input += written;
+ len_in -= written;
+ }
+ /* Read one line */
+ while(len_out > 0) {
+ size = read(conn->fd_helper, tmpbuf, len_out);
+ if(size == -1) {
+ if(errno == EINTR)
+ continue;
+ goto done;
+ }
+ else if(size == 0)
+ goto done;
+ else if(tmpbuf[size - 1] == '\n') {
+ tmpbuf[size - 1] = '\0';
+ goto wrfinish;
+ }
+ tmpbuf += size;
+ len_out -= size;
+ }
+ goto done;
+wrfinish:
+ /* Samba/winbind installed but not configured */
+ if(state == NTLMSTATE_TYPE1 &&
+ size == 3 &&
+ buf[0] == 'P' && buf[1] == 'W')
+ return CURLE_REMOTE_ACCESS_DENIED;
+ /* invalid response */
+ if(size < 4)
+ goto done;
+ if(state == NTLMSTATE_TYPE1 &&
+ (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
+ goto done;
+ if(state == NTLMSTATE_TYPE2 &&
+ (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
+ (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
+ goto done;
+
+ conn->response_header = aprintf("NTLM %.*s", size - 4, buf + 3);
+ return CURLE_OK;
+done:
+ return CURLE_REMOTE_ACCESS_DENIED;
+}
+
+/*this is for creating ntlm header output by delegating
challenge/response
+ *to a Samba's daemon helper ntlm_auth */
+CURLcode Curl_output_ntlm_sso(struct connectdata *conn,
+ bool proxy)
+{
+ /* point to the address of the pointer that holds the string to sent
to the
+ server, which is for a plain host or for a HTTP proxy */
+ char **allocuserpwd;
+ /* point to the name and password for this */
+ const char *userp;
+ /* point to the correct struct with this */
+ struct ntlmdata *ntlm;
+ struct auth *authp;
+
+ CURLcode res = CURLE_OK;
+ char *input;
+
+ DEBUGASSERT(conn);
+ DEBUGASSERT(conn->data);
+
+ if(proxy) {
+ allocuserpwd = &conn->allocptr.proxyuserpwd;
+ userp = conn->proxyuser;
+ ntlm = &conn->proxyntlm;
+ authp = &conn->data->state.authproxy;
+ }
+ else {
+ allocuserpwd = &conn->allocptr.userpwd;
+ userp = conn->user;
+ ntlm = &conn->ntlm;
+ authp = &conn->data->state.authhost;
+ }
+ authp->done = FALSE;
+
+ /* not set means empty */
+ if(!userp)
+ userp="";
+
+ switch(ntlm->state) {
+ case NTLMSTATE_TYPE1:
+ default:
+ /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
+ * by delegating the NTLM challenge/response protocal to a helper
+ * in ntlm_auth.
+ * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
+ * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
+ * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
+ * The preprocessor variable 'USE_NTLM_AUTH' indicates whether
+ * this feature is enabled. Another one 'NTLM_AUTH' contains
absolute
+ * path of it.
+ * If NTLM single-sign-on fails, go back to original request
+ * handling process.
+ */
+ /* Clean data before using them */
+ conn->fd_helper = -1;
+ conn->pid = 0;
+ conn->challenge_header = NULL;
+ conn->response_header = NULL;
+ /* Create communication with ntlm_auth */
+ res = sso_ntlm_initiate(conn, userp);
+ if(res)
+ return res;
+ res = sso_ntlm_response(conn, "YR\n", ntlm->state);
+ if(res)
+ return res;
+
+ Curl_safefree(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
+ proxy?"Proxy-":"",
+ conn->response_header);
+ DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
+ Curl_safefree(conn->response_header);
+ conn->response_header = NULL;
+ break;
+ case NTLMSTATE_TYPE2:
+ input = aprintf("TT %s\n", conn->challenge_header);
+ res = sso_ntlm_response(conn,
+ input,
+ ntlm->state);
+ if(res)
+ return res;
+
+ Curl_safefree(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
+ proxy?"Proxy-":"",
+ conn->response_header);
+ DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
+ ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
+ authp->done = TRUE;
+ sso_ntlm_close(conn);
+ free(input);
+ break;
+ case NTLMSTATE_TYPE3:
+ /* connection is already authenticated,
+ * don't send a header in future requests */
+ if(*allocuserpwd) {
+ free(*allocuserpwd);
+ *allocuserpwd=NULL;
+ }
+ authp->done = TRUE;
+ break;
+ }
+
+ return CURLE_OK;
+}
+#endif /* USE_NTLM_SSO */
+
 /* this is for creating ntlm header output */
 CURLcode Curl_output_ntlm(struct connectdata *conn,
                           bool proxy)
@@ -1320,6 +1600,9 @@ Curl_ntlm_cleanup(struct connectdata *conn)
   ntlm_sspi_cleanup(&conn->ntlm);
   ntlm_sspi_cleanup(&conn->proxyntlm);
 #else
+#ifdef USE_NTLM_SSO
+ sso_ntlm_close(conn);
+#endif
   (void)conn;
 #endif
 }
diff --git a/lib/http_ntlm.h b/lib/http_ntlm.h
index c7422d7..7f19a59 100644
--- a/lib/http_ntlm.h
+++ b/lib/http_ntlm.h
@@ -38,6 +38,12 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn,
bool proxy,
 /* this is for creating ntlm header output */
 CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);

+#ifdef USE_NTLM_SSO
+/* this is for creating ntlm header output by delegating
challenge/response
+ * to a Samba's daemon helper ntlm_auth */
+CURLcode Curl_output_ntlm_sso(struct connectdata *conn, bool proxy);
+#endif
+
 void Curl_ntlm_cleanup(struct connectdata *conn);
 #ifndef USE_NTLM
 #define Curl_ntlm_cleanup(x)
diff --git a/lib/setup.h b/lib/setup.h
index 1e96c76..c742188 100644
--- a/lib/setup.h
+++ b/lib/setup.h
@@ -567,6 +567,11 @@ int netware_init(void);
 #if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || \
    defined(USE_GNUTLS) || defined(USE_NSS)
 #define USE_NTLM
+#if defined(USE_NTLM_AUTH)
+/* Support NTLM single-sign-on by using Samba's winbind daemon helper
+ 'ntlm_auth' */
+#define USE_NTLM_SSO
+#endif
 #endif
 #endif

diff --git a/lib/url.c b/lib/url.c
index c5b642f..adb96c4 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1406,6 +1406,10 @@ CURLcode Curl_setopt(struct SessionHandle *data,
CURLoption option,
 #ifndef USE_NTLM
     auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
 #endif
+#ifndef USE_NTLM_SSO
+ auth &= ~CURLAUTH_NTLM_SSO; /* no NTLM single-sign-on without SSL
+ and ntlm_auth */
+#endif
 #ifndef USE_HTTP_NEGOTIATE
     auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI
or
                                        WINDOWS_SSPI */
@@ -1467,6 +1471,10 @@ CURLcode Curl_setopt(struct SessionHandle *data,
CURLoption option,
 #ifndef USE_NTLM
     auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
 #endif
+#ifndef USE_NTLM_SSO
+ auth &= ~CURLAUTH_NTLM_SSO; /* no NTLM single-sign-on without SSL
+ and ntlm_auth */
+#endif
 #ifndef USE_HTTP_NEGOTIATE
     auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI
or
                                        WINDOWS_SSPI */
@@ -3002,7 +3010,8 @@ ConnectionExists(struct SessionHandle *data,
         }
         if((needle->handler->protocol & CURLPROTO_FTP) ||
            ((needle->handler->protocol & CURLPROTO_HTTP) &&
- (data->state.authhost.want==CURLAUTH_NTLM))) {
+ ((data->state.authhost.want==CURLAUTH_NTLM) ||
+ (data->state.authhost.want==CURLAUTH_NTLM_SSO)))) {
           /* This is FTP or HTTP+NTLM, verify that we're using the same
name
              and password as well */
           if(!strequal(needle->user, check->user) ||
diff --git a/lib/urldata.h b/lib/urldata.h
index d256968..d2638aa 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -905,6 +905,15 @@ struct connectdata {
                                single requests! */
   struct ntlmdata proxyntlm; /* NTLM data for proxy */

+#ifdef USE_NTLM_SSO
+ /* data used for communication with Samba's winbind daemon helper
+ ntlm_auth */
+ int fd_helper;
+ pid_t pid;
+ char* challenge_header;
+ char* response_header;
+#endif
+
   char syserr_buf [256]; /* buffer for Curl_strerror() */

 #ifdef CURLRES_ASYNCH
diff --git a/lib/version.c b/lib/version.c
index c471dc1..6b9ff50 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -243,6 +243,9 @@ static curl_version_info_data version_info = {
 #ifdef USE_NTLM
   | CURL_VERSION_NTLM
 #endif
+#ifdef USE_NTLM_SSO
+ | CURL_VERSION_NTLM_SSO
+#endif
 #ifdef USE_WINDOWS_SSPI
   | CURL_VERSION_SSPI
 #endif
diff --git a/src/main.c b/src/main.c
index 4639964..b064412 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1870,6 +1870,7 @@ static ParameterError getparameter(char *flag, /*
f or -long-flag */
     {"*k", "digest", FALSE},
     {"*l", "negotiate", FALSE},
     {"*m", "ntlm", FALSE},
+ {"*M", "ntlm-sso", FALSE},
     {"*n", "basic", FALSE},
     {"*o", "anyauth", FALSE},
 #ifdef USE_WATT32
@@ -2224,6 +2225,17 @@ static ParameterError getparameter(char *flag, /*
f or -long-flag */
           config->authtype &= ~CURLAUTH_NTLM;
         break;

+ case 'M': /* --ntlm-sso */
+ if(toggle) {
+ if(curlinfo->features & CURL_VERSION_NTLM_SSO)
+ config->authtype |= CURLAUTH_NTLM_SSO;
+ else
+ return PARAM_LIBCURL_DOESNT_SUPPORT;
+ }
+ else
+ config->authtype &= ~CURLAUTH_NTLM_SSO;
+ break;
+
       case 'n': /* --basic for completeness */
         if(toggle)
           config->authtype |= CURLAUTH_BASIC;
@@ -3204,6 +3216,7 @@ static ParameterError getparameter(char *flag, /*
f or -long-flag */
           {"IPv6", CURL_VERSION_IPV6},
           {"Largefile", CURL_VERSION_LARGEFILE},
           {"NTLM", CURL_VERSION_NTLM},
+ {"NTLM_SSO", CURL_VERSION_NTLM_SSO},
           {"SPNEGO", CURL_VERSION_SPNEGO},
           {"SSL", CURL_VERSION_SSL},
           {"SSPI", CURL_VERSION_SSPI},

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2011-07-18