[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kfm-devel
Subject:    PATCH - HTTP NTLM Authentication
From:       Karsten =?iso-8859-1?q?K=FCnne?= <kuenne () rentec ! com>
Date:       2004-09-22 0:10:54
Message-ID: 200409212010.59844.kuenne () rentec ! com
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]


I while ago I posted a message saying that I don't intend to support NTLM. 
Well, after I discovered libntlm I changed my mind :-).

This patch adds NTLM authentication to kio_http. It depends on libntlm which 
you can get from http://www.josefsson.org/libntlm . This is how NTLM 
authentication works:

- the server (usually IIS) offers "NTLM" (and at least in my tests also 
"Negotiate" but I couldn't figure out what to do with it as it apparently 
doesn't want Kerberos/GSSAPI here) in the "WWW-Authenticate" header on a 401 
response.

- the client sends a NTLM type 1 message (a request) in the "Authorization" 
header on the next message and it MUST NOT close the connection! (this is 
important, NTLM authentication doesn't work otherwise as it is 
connection-oriented and not request-oriented)

- the server then answers with 401 and sends a type 2 message (a challenge) in 
the "WWW-Authenticate" header

- the client then sends a type 3 message (a response) in the "Authorization" 
header on the next message on the SAME connection

- if everything was o.k., the server answers with a 200.

In order to test NTLM authentication I even set up a Windows 2000 server on my 
laptop (what a pain!). So, I tested it with Windows 2000 Server SP4 (IIS 5.0) 
and libntlm-0.3.3.

The patch is against CVS HEAD from today.


Karsten.
-- 
43rd Law of Computing:
	Anything that can go wr
fortune: Segmentation violation -- Core dumped

["ntlm.diff" (text/x-diff)]

diff -Naur kioslave/http.orig/Makefile.am kioslave/http/Makefile.am
--- kioslave/http.orig/Makefile.am	2004-07-13 15:17:45.000000000 -0400
+++ kioslave/http/Makefile.am	2004-09-21 18:26:48.000000000 -0400
@@ -3,8 +3,8 @@
 
 SUBDIRS = kcookiejar
 
-INCLUDES= -I$(top_srcdir)/interfaces -I$(top_srcdir)/kio/httpfilter $(all_includes) \
                $(GSSAPI_INCS)
-AM_LDFLAGS = $(all_libraries) $(GSSAPI_RPATH)
+INCLUDES= -I$(top_srcdir)/interfaces -I$(top_srcdir)/kio/httpfilter $(all_includes) \
$(GSSAPI_INCS) $(NTLM_INCS) +AM_LDFLAGS = $(all_libraries) $(GSSAPI_RPATH) \
$(NTLM_RPATH)  
 ####### Files
 
@@ -15,8 +15,8 @@
 
 kio_http_la_SOURCES = http.cc
 kio_http_la_METASOURCES = AUTO
-kio_http_la_LIBADD = $(LIB_KIO) $(top_builddir)/kio/httpfilter/libhttpfilter.la \
                $(GSSAPI_LIBS)
-kio_http_la_LDFLAGS = $(all_libraries) $(GSSAPI_RPATH) -module $(KDE_PLUGIN)
+kio_http_la_LIBADD = $(LIB_KIO) $(top_builddir)/kio/httpfilter/libhttpfilter.la \
$(GSSAPI_LIBS) $(NTLM_LIBS) +kio_http_la_LDFLAGS = $(all_libraries) $(GSSAPI_RPATH) \
$(NTLM_RPATH) -module $(KDE_PLUGIN)  
 kio_http_cache_cleaner_la_SOURCES = http_cache_cleaner.cpp
 kio_http_cache_cleaner_la_LIBADD  = $(LIB_KIO)
diff -Naur kioslave/http.orig/configure.in.in kioslave/http/configure.in.in
--- kioslave/http.orig/configure.in.in	2004-07-16 12:11:06.000000000 -0400
+++ kioslave/http/configure.in.in	2004-09-21 18:35:58.000000000 -0400
@@ -105,3 +105,76 @@
 AC_SUBST(GSSAPI_LIBS)
 AC_SUBST(GSSAPI_RPATH)
 
+dnl -------
+dnl Test for libntlm (NTLM support)
+dnl -------
+
+AC_MSG_CHECKING(whether to enable NTLM support)
+AC_ARG_WITH(ntlm,
+[  --with-ntlm=PATH      Set path for NTLM files [default=check]],
+[ case "$withval" in
+  yes)
+    with_ntlm=CHECK
+    ;;
+  esac ],
+[ with_ntlm=CHECK ]
+)dnl
+
+if test "x$with_ntlm" = "xCHECK" ; then
+  with_ntlm=NOTFOUND
+  search_incs="$kde_includes /usr/include /usr/local/include"
+  AC_FIND_FILE(ntlm.h, $search_incs, ntlm_incdir)
+  if test -r $ntlm_incdir/ntlm.h ; then
+    test "x$ntlm_incdir" != "x/usr/include" && NTLM_INCS="-I$ntlm_incdir"
+    with_ntlm=FOUND
+  fi
+  if test $with_ntlm = FOUND ; then
+    $with_ntlm=NOTFOUND
+    for ext in la so sl a dylib ; do
+      AC_FIND_FILE(libntlm.$ext, $kde_libraries /usr/lib /usr/local/lib,
+        ntlm_libdir)
+      if test -r $ntlm_libdir/libntlm.$ext ; then
+        if test "x$ntlm_libdir" != "x/usr/lib" ; then
+          NTLM_LIBS="-L$ntlm_libdir "
+          test "$USE_RPATH" = yes && NTLM_RPATH="-R $ntlm_libdir"
+        fi
+        NTLM_LIBS="${NTLM_LIBS}-lntlm"
+        with_ntlm=FOUND
+        break
+      fi
+    done
+  fi
+fi
+
+case "$with_ntlm" in
+no) AC_MSG_RESULT(no) ;;
+framework)
+  NTLM_LIBS="-Xlinker -framework -Xlinker NTLM"
+  AC_DEFINE_UNQUOTED(HAVE_LIBNTLM, 1, [Define if you have the NTLM library])
+  NTLM_SUBDIR="ntlm"
+  AC_MSG_RESULT(Apple framework)
+  ;;
+NOTFOUND) AC_MSG_RESULT(searched but not found) ;;
+*)
+  if test "x$with_ntlm" = "xFOUND" ; then
+    msg="incs=$ntlm_incdir libs=$ntlm_libdir"
+  else
+    msg="$with_ntlm"
+    NTLM_ROOT="$with_ntlm"
+    if test "x$NTLM_ROOT" != "x/usr" ; then
+      NTLM_INCS="-I${NTLM_ROOT}/include"
+      NTLM_LIBS="-L${NTLM_ROOT}/lib "
+      if test "$USE_RPATH" = "yes" ; then
+        NTLM_RPATH="-R ${NTLM_ROOT}/lib"
+      fi
+    fi
+    NTLM_LIBS="${NTLM_LIBS}-lntlm"
+  fi
+  AC_DEFINE_UNQUOTED(HAVE_LIBNTLM, 1, [Define if you have the NTLM library])
+  AC_MSG_RESULT($msg)
+  ;;
+esac
+
+AC_SUBST(NTLM_INCS)
+AC_SUBST(NTLM_LIBS)
+AC_SUBST(NTLM_RPATH)
diff -Naur kioslave/http.orig/http.cc kioslave/http/http.cc
--- kioslave/http.orig/http.cc	2004-09-15 06:06:33.000000000 -0400
+++ kioslave/http/http.cc	2004-09-21 18:26:48.000000000 -0400
@@ -76,6 +76,10 @@
 #include <gssapi.h>
 #endif /* GSSAPI_MIT */
 
+#ifdef HAVE_LIBNTLM
+#include <ntlm.h>
+#endif
+
 // Catch uncompatible crap (BR86019)
 #if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
 #include <gssapi/gssapi_generic.h>
@@ -2381,11 +2385,12 @@
         info.username = m_request.user;
       if ( checkCachedAuthentication( info ) && !info.digestInfo.isEmpty() )
       {
-        Authentication = info.digestInfo.startsWith("Basic") ? AUTH_Basic : \
AUTH_Digest ; +        Authentication = info.digestInfo.startsWith("Basic") ? \
AUTH_Basic : info.digestInfo.startsWith("NTLM") ? AUTH_NTLM : AUTH_Digest ;  \
m_state.user   = info.username;  m_state.passwd = info.password;
         m_strRealm = info.realmValue;
-        m_strAuthorization = info.digestInfo;
+        if ( Authentication != AUTH_NTLM ) // don't use the cached challenge
+          m_strAuthorization = info.digestInfo;
       }
     }
     else
@@ -2406,6 +2411,11 @@
           header += createNegotiateAuth();
           break;
 #endif
+#ifdef HAVE_LIBNTLM
+      case AUTH_NTLM:
+          header += createNTLMAuth();
+          break;
+#endif
       case AUTH_None:
       default:
           break;
@@ -3407,7 +3417,14 @@
     {
         if ( getAuthorization() )
         {
-           httpCloseConnection();
+           // for NTLM Authentication we have to keep the connection open!
+           if ( (Authentication == AUTH_NTLM) && (m_prevResponseCode != 0) )
+           {
+             m_bKeepAlive = true;
+             readBody( true );
+           }
+           else
+             httpCloseConnection();
            return false; // Try again.
         }
 
@@ -4827,6 +4844,14 @@
     };
   }
 #endif
+#ifdef HAVE_LIBNTLM
+  else if ( strncasecmp( p, "NTLM", 4 ) == 0)
+  {
+    f = AUTH_NTLM;
+    memcpy((void *)p, "NTLM", 4); // Correct for upper-case variations.
+    p += 4;
+  }
+#endif
   else
   {
     kdWarning(7113) << "(" << m_pid << ") Unsupported or invalid authorization "
@@ -5022,6 +5047,34 @@
       }
     }
 
+#ifdef HAVE_LIBNTLM
+    if ( Authentication == AUTH_NTLM )
+    {
+      QString auth = ( m_responseCode == 401 ) ? m_strAuthorization : \
m_strProxyAuthorization; +      if ( auth.length() > 4 )
+      {
+        prompt = false;
+        result = true;
+        kdDebug(7113) << "(" << m_pid << ") NTLM auth second phase, "
+                      << "sending response..." << endl;
+        if ( m_responseCode == 401 )
+        {
+          info.username = m_request.user;
+          info.password = m_request.passwd;
+          info.realmValue = m_strRealm;
+          info.digestInfo = m_strAuthorization;
+        }
+        else if ( m_responseCode == 407 )
+        {
+          info.username = m_proxyURL.user();
+          info.password = m_proxyURL.pass();
+          info.realmValue = m_strProxyRealm;
+          info.digestInfo = m_strProxyAuthorization;
+        }
+      }
+    }
+#endif
+
     if ( prompt )
     {
       switch ( m_responseCode )
@@ -5289,6 +5342,74 @@
 }
 #endif
 
+#ifdef HAVE_LIBNTLM
+QString HTTPProtocol::createNTLMAuth( bool isForProxy )
+{
+  uint len;
+  QString auth;
+  QCString user, passwd, strauth;
+  QByteArray buf;
+
+  if ( isForProxy )
+  {
+    auth = "Proxy-Authorization: NTLM ";
+    user = m_proxyURL.user().latin1();
+    passwd = m_proxyURL.pass().latin1();
+    strauth = m_strProxyAuthorization.latin1();
+    len = m_strProxyAuthorization.length();
+  }
+  else
+  {
+    auth = "Authorization: NTLM ";
+    user = m_state.user.latin1();
+    passwd = m_state.passwd.latin1();
+    strauth = m_strAuthorization.latin1();
+    len = m_strAuthorization.length();
+  }
+  kdDebug(7113) << "(" << m_pid << ") NTLM length: " << len << endl;
+  if ( user.isEmpty() || passwd.isEmpty() || len < 4 )
+    return QString::null;
+
+  if ( len > 4 )
+  {
+    // create a response
+    tSmbNtlmAuthChallenge challenge;
+    tSmbNtlmAuthResponse  response;
+
+    KCodecs::base64Decode( strauth.right( len - 5 ), buf );
+    memcpy((void *)&challenge, (const void *)buf.data(), sizeof(challenge));
+    buildSmbNtlmAuthResponse(&challenge, &response, user, passwd);
+    buf.duplicate((const char *)&response, SmbLength(&response));
+  }
+  else
+  {
+    // create a request
+    tSmbNtlmAuthRequest   request;
+
+    buildSmbNtlmAuthRequest(&request, user, NULL);
+    buf.duplicate((const char *)&request, SmbLength(&request));
+  }
+
+  // remove the challenge to prevent reuse
+  if ( isForProxy )
+    m_strProxyAuthorization = "NTLM";
+  else
+    m_strAuthorization = "NTLM";
+
+  auth += KCodecs::base64Encode( buf );
+  auth += "\r\n";
+
+  return auth;
+}
+#else
+
+// Dummy
+QString HTTPProtocol::createNTLMAuth( bool isForProxy )
+{
+  return QString::null;
+}
+#endif
+
 QString HTTPProtocol::createBasicAuth( bool isForProxy )
 {
   QString auth;
@@ -5684,6 +5805,8 @@
         ProxyAuthentication = AUTH_None;
       else if( m_strProxyAuthorization.startsWith("Basic") )
         ProxyAuthentication = AUTH_Basic;
+      else if( m_strProxyAuthorization.startsWith("NTLM") )
+        ProxyAuthentication = AUTH_NTLM;
       else
         ProxyAuthentication = AUTH_Digest;
     }
@@ -5697,6 +5820,8 @@
         m_strProxyAuthorization = info.digestInfo;
         if( m_strProxyAuthorization.startsWith("Basic") )
           ProxyAuthentication = AUTH_Basic;
+        else if( m_strProxyAuthorization.startsWith("NTLM") )
+          ProxyAuthentication = AUTH_NTLM;
         else
           ProxyAuthentication = AUTH_Digest;
       }
@@ -5727,6 +5852,11 @@
     case AUTH_Digest:
       header += createDigestAuth( true );
       break;
+#ifdef HAVE_LIBNTLM
+    case AUTH_NTLM:
+      header += createNTLMAuth( true );
+      break;
+#endif
     case AUTH_None:
     default:
       break;
diff -Naur kioslave/http.orig/http.h kioslave/http/http.h
--- kioslave/http.orig/http.h	2004-08-01 11:46:37.000000000 -0400
+++ kioslave/http/http.h	2004-09-21 18:26:48.000000000 -0400
@@ -58,7 +58,7 @@
   enum HTTP_REV    {HTTP_None, HTTP_Unknown, HTTP_10, HTTP_11, SHOUTCAST};
 
   /** Authorization method used **/
-  enum HTTP_AUTH   {AUTH_None, AUTH_Basic, AUTH_Digest, AUTH_Negotiate};
+  enum HTTP_AUTH   {AUTH_None, AUTH_Basic, AUTH_Digest, AUTH_Negotiate, AUTH_NTLM};
 
   /** HTTP / DAV method **/
   // Removed to interfaces/kio/http.h
@@ -434,6 +434,11 @@
   QString createDigestAuth( bool isForProxy = false );
 
   /**
+   * Creates the entity-header for NTLM authentication.
+   */
+  QString createNTLMAuth( bool isForProxy = false );
+
+  /**
    * Creates the entity-header for Negotiate authentication.
    */
   QString createNegotiateAuth();


[Attachment #6 (application/pgp-signature)]

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic