cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Windows SSPI patch?

From: Christopher R. Palmer <crpalmer_at_vivisimo.com>
Date: Thu, 10 Mar 2005 08:29:00 -0500

I've cleaned up the patch to make it conform (or at least be closer to
conforming) to your coding style and I've tried to polish it up a little
bit. I think I've addressed all the comments that you had, below.

The documentation is still a little sparse, as I included only some notes
in the Makefile.vc6's.

This only compiles with the VC makefiles. I have no experience with the
other windows builds.

To compile the SSPI version, you would run

nmake vc-sspi

I made the patch against a fresh cvs checkout (as of a couple of minutes
ago). Let me know if there are more changes that you'd like to see me make
to it.

Cheers,
Chris.

Daniel Stenberg wrote:
> On Thu, 3 Mar 2005, Christopher R. Palmer wrote:
>
>> Great! I wasn't sure if you'd be interested because it's a little
>> painful to have very different pieces of code on different systems...
>
>
> Well, if the additions are useful and appreciated by users, they will be
> used and the code will remain functional that way even if I cannot build
> it myself.
>
>> I've attached the patch to this email. As I said, if you want to
>> incorporate it, I'm willing to do some work fixing up things you don't
>> like about it.
>
>
> Thanks a lot. I'm sure we can smoothen out the little quirks and have
> this added soon.
>
> Here's a few questions/remarks:
>
> o The indent levels are not done curl-style. You also seem inconsistent
> even
> with your own levels.
>
> o I think it is enough to depend on the USE_WINDOWS_SSPI define, and not
> check
> for WIN32 *as well*. Only Win32 users will/should define it.
>
> o Can anyone come up with a use case where both kinds of NTLM auth could be
> good? I mean, both the curl-provided as well as the SSPI-approach? Your
> patch assumes that if you want SSPI, you don't want the code curl offers.
>
> o You should check that strdup() returns a valid pointer or bail out nicely
> otherwise. We make an effort to have libcurl behave nicely even under
> low-memory situations.
>
> o Since assume default user when no user is given, how do we know
> internally
> that authentication is wanted at all? In the current code, we use
> "conn->bits.user_passwd != NULL" to trigger that. This is already a bad
> assumption for Negotiate (see KNOWN_BUGS issue #10) so it might be about
> time we took care of it.
>
> o Your modification of the #include <ca-bundle.h> to "ca-bundle.h" in
> lib/url.c is not a good idea since it breaks the build for all *nix
> systems
> (where the ca-bundle.h is generated in the build directory). You should
> instead run buildconf.bat to get your build env setup.
>
> o Considered any docs/notes on how to enable this and even possibly for
> what
> systems this is assumed to work on?
>
> o There were some absolute path references in there that I suspect you
> didn't
> intend to be included.
>

-- 
Christopher R. Palmer                     palmer_at_vivisimo.com
Chief Technology Officer                  www.vivisimo.com
Vivisimo, Inc.

diff -r --unified orig/curl/lib/Makefile.vc6 curl/lib/Makefile.vc6
--- orig/curl/lib/Makefile.vc6 2005-01-18 05:17:35.000000000 -0500
+++ curl/lib/Makefile.vc6 2005-03-07 11:24:47.687500000 -0500
@@ -42,6 +42,24 @@
 ZLIB_PATH = ../../zlib-1.2.1
 !ENDIF
 
+# USE_WINDOWS_SSPI uses windows libraries to allow NTLM authentication
+# without an openssl installation and offers the ability to authenticate
+# using the "current logged in user". It does however require that the
+# Windows SDK be installed.
+#
+# If, for some reason the Windows SDK is installed but not installed
+# in the default location, you can specify WINDOWS_SDK_PATH.
+# It can be downloaded from:
+# http://www.microsoft.com/msdownload/platformsdk/sdkupdate/
+
+# USE_WINDOWS_SSPI = 1
+
+!IFDEF WINDOWS_SSPI
+!IFNDEF WINDOWS_SDK_PATH
+WINDOWS_SDK_PATH = "C:\Program Files\Microsoft SDK"
+!ENDIF
+!ENDIF
+
 # Use the high resolution time by default. Comment this out to use low
 # resolution time and not require winmm.lib
 USEMM_LIBS = YES
@@ -69,6 +87,13 @@
 # RSAglue.lib was formerly needed in the SSLLIBS
 CFGSET = FALSE
 
+!IFDEF WINDOWS_SSPI
+CFLAGS = $(CFLAGS) /DUSE_WINDOWS_SSPI /I$(WINDOWS_SDK_PATH)\include
+LFLAGS = $(LFLAGS) $(WINDOWS_SDK_PATH)\lib\secur32.lib
+!ENDIF
+
+CFLAGS = $(CFLAGS) /DCURLDEBUG
+
 ######################
 # release
 
Only in curl/lib: Makefile.vc6.sav
Only in curl/lib: ca-bundle.h
diff -r --unified orig/curl/lib/http.c curl/lib/http.c
--- orig/curl/lib/http.c 2005-02-18 18:53:07.000000000 -0500
+++ curl/lib/http.c 2005-03-04 12:30:13.609375000 -0500
@@ -406,7 +406,7 @@
   /* Send proxy authentication header if needed */
   if (conn->bits.httpproxy &&
       (conn->bits.tunnel_proxy == proxytunnel)) {
-#ifdef USE_SSLEAY
+#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
     if(authproxy->want == CURLAUTH_NTLM) {
       auth=(char *)"NTLM";
       result = Curl_output_ntlm(conn, TRUE);
@@ -474,7 +474,7 @@
       }
       else
 #endif
-#ifdef USE_SSLEAY
+#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
       if(authhost->picked == CURLAUTH_NTLM) {
         auth=(char *)"NTLM";
         result = Curl_output_ntlm(conn, FALSE);
@@ -587,7 +587,7 @@
   }
   else
 #endif
-#ifdef USE_SSLEAY
+#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
     /* NTLM support requires the SSL crypto libs */
     if(checkprefix("NTLM", start)) {
       *availp |= CURLAUTH_NTLM;
Only in curl/lib: http.c.orig
Only in curl/lib: http.c.rej
diff -r --unified orig/curl/lib/http_ntlm.c curl/lib/http_ntlm.c
--- orig/curl/lib/http_ntlm.c 2005-02-22 07:10:30.000000000 -0500
+++ curl/lib/http_ntlm.c 2005-03-10 08:17:05.859375000 -0500
@@ -30,7 +30,7 @@
 */
 
 #ifndef CURL_DISABLE_HTTP
-#ifdef USE_SSLEAY
+#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
 /* We need OpenSSL for the crypto lib to provide us with MD4 and DES */
 
 /* -- WIN32 approved -- */
@@ -51,6 +51,8 @@
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
 
+#ifndef USE_WINDOWS_SSPI
+
 #include <openssl/des.h>
 #include <openssl/md4.h>
 #include <openssl/ssl.h>
@@ -71,6 +73,12 @@
 #define DESKEY(x) &x
 #endif
 
+#else
+
+#include <rpc.h>
+
+#endif
+
 /* The last #include file should be: */
 #include "memdebug.h"
 
@@ -130,14 +138,23 @@
 
       ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */
 
+#ifdef USE_WINDOWS_SSPI
+ if ((ntlm->type_2 = malloc(size+1)) == NULL) {
+ free(buffer);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ ntlm->n_type_2 = size;
+ memcpy(ntlm->type_2, buffer, size);
+#else
       if(size >= 48)
         /* the nonce of interest is index [24 .. 31], 8 bytes */
         memcpy(ntlm->nonce, &buffer[24], 8);
       /* FIX: add an else here! */
-
+
       /* at index decimal 20, there's a 32bit NTLM flag field */
 
       free(buffer);
+#endif
     }
     else {
       if(ntlm->state >= NTLMSTATE_TYPE1)
@@ -149,6 +166,8 @@
   return CURLNTLM_FINE;
 }
 
+#ifndef USE_WINDOWS_SSPI
+
 /*
  * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The
  * key schedule ks is also set.
@@ -275,6 +294,32 @@
   free(pw);
 }
 
+#endif
+
+#ifdef USE_WINDOWS_SSPI
+
+static void
+ntlm_sspi_cleanup(struct ntlmdata *ntlm)
+{
+ if (ntlm->type_2) {
+ free(ntlm->type_2);
+ ntlm->type_2 = NULL;
+ }
+ if (ntlm->has_handles) {
+ DeleteSecurityContext(&ntlm->c_handle);
+ FreeCredentialsHandle(&ntlm->handle);
+ ntlm->has_handles = 0;
+ }
+ if (ntlm->p_identity) {
+ if (ntlm->identity.User) free(ntlm->identity.User);
+ if (ntlm->identity.Password) free(ntlm->identity.Password);
+ if (ntlm->identity.Domain) free(ntlm->identity.Domain);
+ ntlm->p_identity = NULL;
+ }
+}
+
+#endif
+
 #define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
 #define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
   (((x) >>16)&0xff), ((x)>>24)
@@ -333,6 +378,90 @@
   switch(ntlm->state) {
   case NTLMSTATE_TYPE1:
   default: /* for the weird cases we (re)start here */
+#ifdef USE_WINDOWS_SSPI
+ {
+ SecBuffer buf;
+ SecBufferDesc desc;
+ SECURITY_STATUS status;
+ ULONG attrs;
+ const char *user;
+ int domlen;
+
+ ntlm_sspi_cleanup(ntlm);
+
+ user = strchr(userp, '\\');
+ if (!user)
+ user = strchr(userp, '/');
+
+ if (user) {
+ domain = userp;
+ domlen = user - userp;
+ user++;
+ }
+ else {
+ user = userp;
+ domain = "";
+ domlen = 0;
+ }
+
+ if (user && *user) {
+ /* note: initialize all of this before doing the mallocs so that
+ * it can be cleaned up later without leaking memory.
+ */
+ ntlm->p_identity = &ntlm->identity;
+ memset(ntlm->p_identity, 0, sizeof(*ntlm->p_identity));
+ if ((ntlm->identity.User = strdup(user)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ ntlm->identity.UserLength = strlen(user);
+ if ((ntlm->identity.Password = strdup(passwdp)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ ntlm->identity.PasswordLength = strlen(passwdp);
+ if ((ntlm->identity.Domain = malloc(domlen+1)) == NULL)
+ return CURLE_OUT_OF_MEMORY;
+ strncpy(ntlm->identity.Domain, domain, domlen);
+ ntlm->identity.Domain[domlen] = '\0';
+ ntlm->identity.DomainLength = domlen;
+ ntlm->identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+ }
+ else {
+ ntlm->p_identity = NULL;
+ }
+
+ if (AcquireCredentialsHandle(
+ NULL, "NTLM", SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity,
+ NULL, NULL, &ntlm->handle, NULL
+ ) != SEC_E_OK) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ desc.ulVersion = SECBUFFER_VERSION;
+ desc.cBuffers = 1;
+ desc.pBuffers = &buf;
+ buf.cbBuffer = sizeof(ntlmbuf);
+ buf.BufferType = SECBUFFER_TOKEN;
+ buf.pvBuffer = ntlmbuf;
+
+ status = InitializeSecurityContext(&ntlm->handle, NULL, (char *) host,
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONNECTION,
+ 0, SECURITY_NETWORK_DREP, NULL, 0,
+ &ntlm->c_handle, &desc, &attrs, NULL
+ );
+
+ if (status == SEC_I_COMPLETE_AND_CONTINUE ||
+ status == SEC_I_CONTINUE_NEEDED) {
+ CompleteAuthToken(&ntlm->c_handle, &desc);
+ }
+ else if (status != SEC_E_OK) {
+ FreeCredentialsHandle(&ntlm->handle);
+ return CURLE_RECV_ERROR;
+ }
+
+ ntlm->has_handles = 1;
+ size = buf.cbBuffer;
+ }
+#else
     hostoff = 32;
     domoff = hostoff + hostlen;
 
@@ -382,6 +511,7 @@
 
     /* initial packet length */
     size = 32 + hostlen + domlen;
+#endif
 
     /* now keeper of the base64 encoded package size */
     size = Curl_base64_encode((char *)ntlmbuf, size, &base64);
@@ -417,6 +547,41 @@
     */
 
   {
+#ifdef USE_WINDOWS_SSPI
+ SecBuffer type_2, type_3;
+ SecBufferDesc type_2_desc, type_3_desc;
+ SECURITY_STATUS status;
+ ULONG attrs;
+
+ type_2_desc.ulVersion = type_3_desc.ulVersion = SECBUFFER_VERSION;
+ type_2_desc.cBuffers = type_3_desc.cBuffers = 1;
+ type_2_desc.pBuffers = &type_2;
+ type_3_desc.pBuffers = &type_3;
+
+ type_2.BufferType = SECBUFFER_TOKEN;
+ type_2.pvBuffer = ntlm->type_2;
+ type_2.cbBuffer = ntlm->n_type_2;
+ type_3.BufferType = SECBUFFER_TOKEN;
+ type_3.pvBuffer = ntlmbuf;
+ type_3.cbBuffer = sizeof(ntlmbuf);
+
+ status = InitializeSecurityContext(&ntlm->handle, &ntlm->c_handle,
+ (char *) host,
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONNECTION,
+ 0, SECURITY_NETWORK_DREP, &type_2_desc,
+ 0, &ntlm->c_handle, &type_3_desc,
+ &attrs, NULL);
+
+ if (status != SEC_E_OK)
+ return CURLE_RECV_ERROR;
+
+ size = type_3.cbBuffer;
+
+ ntlm_sspi_cleanup(ntlm);
+
+#else
     int lmrespoff;
     int ntrespoff;
     int useroff;
@@ -556,6 +721,8 @@
     ntlmbuf[56] = (unsigned char)(size & 0xff);
     ntlmbuf[57] = (unsigned char)(size >> 8);
 
+#endif
+
     /* convert the binary blob into base64 */
     size = Curl_base64_encode((char *)ntlmbuf, size, &base64);
 
@@ -587,5 +754,16 @@
 
   return CURLE_OK;
 }
+
+
+void
+Curl_ntlm_cleanup(struct connectdata *conn)
+{
+#ifdef USE_WINDOWS_SSPI
+ ntlm_sspi_cleanup(&conn->ntlm);
+ ntlm_sspi_cleanup(&conn->proxyntlm);
+#endif
+}
+
 #endif /* USE_SSLEAY */
 #endif /* !CURL_DISABLE_HTTP */
Only in curl/lib: http_ntlm.c.orig
Only in curl/lib: http_ntlm.c.rej
diff -r --unified orig/curl/lib/http_ntlm.h curl/lib/http_ntlm.h
--- orig/curl/lib/http_ntlm.h 2004-03-30 01:39:24.000000000 -0500
+++ curl/lib/http_ntlm.h 2005-03-04 11:54:34.359375000 -0500
@@ -38,7 +38,7 @@
 /* this is for creating ntlm header output */
 CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);
 
-void Curl_ntlm_cleanup(struct SessionHandle *data);
+void Curl_ntlm_cleanup(struct connectdata *conn);
 
 
 /* Flag bits definitions based on http://davenport.sourceforge.net/ntlm.html */
Only in curl/lib: libcurl.lib
Only in curl/lib: release
Only in curl/lib: release-zlib
diff -r --unified orig/curl/lib/url.c curl/lib/url.c
--- orig/curl/lib/url.c 2005-03-09 17:13:52.000000000 -0500
+++ curl/lib/url.c 2005-03-10 08:18:04.500000000 -0500
@@ -832,7 +832,7 @@
   {
     long auth = va_arg(param, long);
     /* switch off bits we can't support */
-#ifndef USE_SSLEAY
+#if ! defined(USE_SSLEAY) && !defined(USE_WINDOWS_SSPI)
     auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
 #endif
 #ifndef HAVE_GSSAPI
@@ -1449,6 +1449,10 @@
       data->state.authhost.want;
 
     data->state.authproblem = FALSE;
+
+#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
+ Curl_ntlm_cleanup(conn);
+#endif
   }
 
   if(conn->curl_disconnect)
diff -r --unified orig/curl/lib/urldata.h curl/lib/urldata.h
--- orig/curl/lib/urldata.h 2005-02-09 08:06:40.000000000 -0500
+++ curl/lib/urldata.h 2005-03-04 17:08:16.203125000 -0500
@@ -188,10 +188,30 @@
   NTLMSTATE_LAST
 } curlntlm;
 
+#ifdef USE_WINDOWS_SSPI
+/* When including these headers, you must define either SECURITY_WIN32
+ * or SECURITY_KERNEL, indicating who is compiling the code.
+ */
+#define SECURITY_WIN32 1
+#include <sspi.h>
+#include <Security.h>
+#include <rpc.h>
+#endif
+
 /* Struct used for NTLM challenge-response authentication */
 struct ntlmdata {
   curlntlm state;
+#ifdef USE_WINDOWS_SSPI
+ CredHandle handle;
+ CtxtHandle c_handle;
+ SEC_WINNT_AUTH_IDENTITY identity;
+ SEC_WINNT_AUTH_IDENTITY *p_identity;
+ int has_handles;
+ void *type_2;
+ int n_type_2;
+#else
   unsigned char nonce[8];
+#endif
 };
 
 #ifdef HAVE_GSSAPI
Only in curl/lib: vc60.idb
Only in curl/lib: vc60.pch
diff -r --unified orig/curl/lib/version.c curl/lib/version.c
--- orig/curl/lib/version.c 2004-12-20 13:23:43.000000000 -0500
+++ curl/lib/version.c 2005-03-04 12:37:10.593750000 -0500
@@ -194,6 +194,8 @@
 #endif
 #ifdef USE_SSLEAY
   | CURL_VERSION_SSL
+#endif
+#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
   | CURL_VERSION_NTLM /* since this requires OpenSSL */
 #endif
 #ifdef HAVE_LIBZ
diff -r --unified orig/curl/src/Makefile.vc6 curl/src/Makefile.vc6
--- orig/curl/src/Makefile.vc6 2005-01-18 05:17:35.000000000 -0500
+++ curl/src/Makefile.vc6 2005-03-07 11:24:51.625000000 -0500
@@ -23,6 +23,24 @@
 OPENSSL_PATH = ../../openssl-0.9.7e
 !ENDIF
 
+# USE_WINDOWS_SSPI uses windows libraries to allow NTLM authentication
+# without an openssl installation and offers the ability to authenticate
+# using the "current logged in user". It does however require that the
+# Windows SDK be installed.
+#
+# If, for some reason the Windows SDK is installed but not installed
+# in the default location, you can specify WINDOWS_SDK_PATH.
+# It can be downloaded from:
+# http://www.microsoft.com/msdownload/platformsdk/sdkupdate/
+
+# USE_WINDOWS_SSPI = 1
+
+!IFDEF WINDOWS_SSPI
+!IFNDEF WINDOWS_SDK_PATH
+WINDOWS_SDK_PATH = "C:\Program Files\Microsoft SDK"
+!ENDIF
+!ENDIF
+
 ########################################################
 ## Nothing more to do below this line!
 ZLIB_CFLAGS = /DHAVE_ZLIB_H /DHAVE_ZLIB /DHAVE_LIBZ /I "$(ZLIB_PATH)"
@@ -49,6 +67,12 @@
 LFLAGS = /nologo /out:$(PROGRAM_NAME) /subsystem:console /machine:I386
 RESFLAGS = /i../include
 
+!IFDEF WINDOWS_SSPI
+CFLAGS = $(CFLAGS) /DUSE_WINDOWS_SSPI /I$(WINDOWS_SDK_PATH)\include
+LFLAGS = $(LFLAGS) $(WINDOWS_SDK_PATH)\lib\secur32.lib
+!ENDIF
+
+CFLAGS = $(CFLAGS) /DCURLDEBUG
 
 RELEASE_OBJS= \
         hugehelpr.obj \
Only in curl/src: curl.exe
Only in curl/src: curl.exp
Only in curl/src: curl.lib
Only in curl/src: curlr.res
Only in curl/src: getpassr.obj
Only in curl/src: homedirr.obj
Only in curl/src: hugehelp.c
Only in curl/src: hugehelpr.obj
Received on 2005-03-10