--Boundary-03=_9oI7Agketp09a/J Content-Type: multipart/mixed; boundary="Boundary-01=_2oI7A9Lbm2H6VNk" Content-Transfer-Encoding: 7bit Content-Disposition: inline --Boundary-01=_2oI7A9Lbm2H6VNk Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Somebody on kde-devel suggested that I forward this to kfm-devel. =2D--------- Forwarded Message ---------- Subject: Negotiate authentication for HTTP Date: Wednesday 07 July 2004 00:33 =46rom: Karsten K=FCnne To: kde-devel@mail.kde.org Hi, After Mozilla came out with support for negotiate authentication and we try to deploy it for our internal webservice I couldn't let Konqueror stand in the dark and be abandoned by our users. So I hacked together a patch for kioslave/http in order to support negotiate authentication. This patch wor= ks fine with apache and mod_auth_kerb-5.0-rc5 and heimdal on the server side.= I tested it on a SuSE 9.0 system with KDE 3.2.3 but the diff is against KDE CVS from yesterday (there aren't many changes in this area between 3.2.3 a= nd CVS). I don't know whether I got the autoconf magic right, I'm not an expe= rt on that. Also, this implementation will most likely NOT work with IIS and SPNEGO and it doesn't support mutual authentication as it does not support multiple roundtrips between client and server. I have no way of testing against IIS so I can't work on this. But if you're using apache and mod_auth_kerb-5.0 it should work fine. It also supports multiple WWW-Authenticate headers, for instance "Negotiate" and "Basic" (this is what our server sends). If "Negotiate" repeatedly fails it falls back to the next lower authentication (in our case "Basic"). This is the behavior we want at our site. All the changes in http.cc and http.h are ifdef'd with HAVE_LIBGSSAPI so it should be transparent if this is not defined. Please have a look and let me know whether this is useful for inclusion into kdelibs. Karsten. =2D- The Psblurtex is an 18-inch long anaconda that hides in the gentlemen's outfitting departments of Amazonian stores and is often bought by mistake since its colors are those of the London Reform Club. Once tied around its victim's neck, it strangles him gently and then claims the insurance before running off to Germany where it lives in hiding. -- Mike Harding, "The Armchair Anarchist's Almanac" =2D------------------------------------------------------ Karsten. =2D-=20 We are all worms. But I do believe I am a glowworm. -- Winston Churchill --Boundary-01=_2oI7A9Lbm2H6VNk Content-Type: text/x-diff; charset="iso-8859-1"; name="gssapi.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="gssapi.diff" diff -Naur http.orig/Makefile.am http/Makefile.am =2D-- http.orig/Makefile.am 2003-10-09 17:38:24.000000000 -0400 +++ http/Makefile.am 2004-07-06 22:09:26.000000000 -0400 @@ -3,8 +3,8 @@ =20 SUBDIRS =3D kcookiejar =20 =2DINCLUDES=3D -I$(top_srcdir)/interfaces -I$(top_srcdir)/kio/httpfilter $(= all_includes) =2DAM_LDFLAGS =3D $(all_libraries) +INCLUDES=3D -I$(top_srcdir)/interfaces -I$(top_srcdir)/kio/httpfilter $(al= l_includes) $(GSSAPI_INCS) +AM_LDFLAGS =3D $(all_libraries) $(GSSAPI_RPATH) =20 ####### Files =20 @@ -15,8 +15,8 @@ =20 kio_http_la_SOURCES =3D http.cc kio_http_la_METASOURCES =3D AUTO =2Dkio_http_la_LIBADD =3D $(LIB_KIO) $(top_builddir)/kio/httpfilter/libhttp= filter.la =2Dkio_http_la_LDFLAGS =3D $(all_libraries) -module $(KDE_PLUGIN) +kio_http_la_LIBADD =3D $(LIB_KIO) $(top_builddir)/kio/httpfilter/libhttpfi= lter.la $(GSSAPI_LIBS) +kio_http_la_LDFLAGS =3D $(all_libraries) $(GSSAPI_RPATH) -module $(KDE_PLU= GIN) =20 kio_http_cache_cleaner_la_SOURCES =3D http_cache_cleaner.cpp kio_http_cache_cleaner_la_LIBADD =3D $(LIB_KIO) diff -Naur http.orig/configure.in.in http/configure.in.in =2D-- http.orig/configure.in.in 1969-12-31 19:00:00.000000000 -0500 +++ http/configure.in.in 2004-07-06 23:49:22.000000000 -0400 @@ -0,0 +1,92 @@ +AC_MSG_CHECKING(whether to enable GSSAPI support) +AC_ARG_WITH(gssapi, +[ --with-gssapi=3DPATH Set path for GSSAPI files [default=3Dcheck]], +[ case "$withval" in + yes) + with_gssapi=3DCHECK + ;; + esac ], +[ with_gssapi=3DCHECK ] +)dnl + +if test "x$with_gssapi" =3D "xCHECK" ; then + with_gssapi=3DNOTFOUND + KDE_FIND_PATH(krb5-config, KRB5_CONFIG, [${prefix}/bin ${exec_prefix}/bi= n /usr/bin /usr/local/bin /opt/local/bin], [ + AC_MSG_WARN([Could not find krb5-config]) + ]) + + if test -n "$KRB5_CONFIG"; then + GSSAPI_INCS=3D"`$KRB5_CONFIG --cflags gssapi`" + GSSAPI_LIBS=3D"`$KRB5_CONFIG --libs gssapi`" + if test "$USE_RPATH" =3D yes; then + for args in $GSSAPI_LIBS; do + case $args in + -L*) + GSSAPI_RPATH=3D"$GSSAPI_RPATH $args" + ;; + esac + done + GSSAPI_RPATH=3D`echo $GSSAPI_RPATH | sed -e "s/-L/-R/g"` + fi + gssapi_incdir=3D"$GSSAPI_INCS" + gssapi_libdir=3D"$GSSAPI_LIBS" + with_gssapi=3DFOUND + else + search_incs=3D"$kde_includes /usr/include /usr/local/include" + AC_FIND_FILE(gssapi.h, $search_incs, gssapi_incdir) + if test -r $gssapi_incdir/gssapi.h ; then + test "x$gssapi_incdir" !=3D "x/usr/include" && GSSAPI_INCS=3D"-I$gss= api_incdir" + with_gssapi=3DFOUND + fi + if test $with_gssapi =3D FOUND ; then + with_gssapi=3DNOTFOUND + for ext in la so sl a dylib ; do + AC_FIND_FILE(libgssapi.$ext, $kde_libraries /usr/lib /usr/local/li= b, + gssapi_libdir) + if test -r $gssapi_libdir/libgssapi.$ext ; then + if test "x$gssapi_libdir" !=3D "x/usr/lib" ; then + GSSAPI_LIBS=3D"-L$gssapi_libdir " + test "$USE_RPATH" =3D yes && GSSAPI_RPATH=3D"-R $gssapi_libdir" + fi + GSSAPI_LIBS=3D"${GSSAPI_LIBS}-lgssapi -lkrb5 -lasn1 -lcrypto -lr= oken -lcrypt ${LIBRESOLV}" + with_gssapi=3DFOUND + break + fi + done + fi + fi +fi + +case "$with_gssapi" in +no) AC_MSG_RESULT(no) ;; +framework) + GSSAPI_LIBS=3D"-Xlinker -framework -Xlinker GSSAPI" + AC_DEFINE_UNQUOTED(HAVE_LIBGSSAPI, 1, [Define if you have GSSAPI librari= es]) + GSSAPI_SUBDIR=3D"gssapi" + AC_MSG_RESULT(Apple framework) + ;; +NOTFOUND) AC_MSG_RESULT(searched but not found) ;; +*) + if test "x$with_gssapi" =3D "xFOUND" ; then + msg=3D"incs=3D$gssapi_incdir libs=3D$gssapi_libdir" + else + msg=3D"$with_gssapi" + GSSAPI_ROOT=3D"$with_gssapi" + if test "x$GSSAPI_ROOT" !=3D "x/usr" ; then + GSSAPI_INCS=3D"-I${GSSAPI_ROOT}/include" + GSSAPI_LIBS=3D"-L${GSSAPI_ROOT}/lib " + if test "$USE_RPATH" =3D "yes" ; then + GSSAPI_RPATH=3D"-R ${GSSAPI_ROOT}/lib" + fi + fi + GSSAPI_LIBS=3D"${GSSAPI_LIBS}-lgssapi -lkrb5 -lasn1 -lcrypto -lroken -= lcrypt ${LIBRESOLV}" + fi + AC_DEFINE_UNQUOTED(HAVE_LIBGSSAPI, 1, [Define if you have GSSAPI librari= es]) + AC_MSG_RESULT($msg) + ;; +esac + +AC_SUBST(GSSAPI_INCS) +AC_SUBST(GSSAPI_LIBS) +AC_SUBST(GSSAPI_RPATH) + diff -Naur http.orig/http.cc http/http.cc =2D-- http.orig/http.cc 2004-06-16 09:16:48.000000000 -0400 +++ http/http.cc 2004-07-06 22:09:26.000000000 -0400 @@ -69,6 +69,10 @@ #include "httpfilter.h" #include "http.h" =20 +#ifdef HAVE_LIBGSSAPI +#include +#endif + using namespace KIO; =20 extern "C" { @@ -347,6 +351,9 @@ m_strRealm =3D QString::null; m_strAuthorization =3D QString::null; Authentication =3D AUTH_None; +#ifdef HAVE_LIBGSSAPI + PrevAuthentication =3D AUTH_None; +#endif =20 // Obtain the proxy and remote server timeout values m_proxyConnTimeout =3D proxyConnectTimeout(); @@ -2350,7 +2357,12 @@ =20 // Only check for a cached copy if the previous // response was NOT a 401 or 407. +#ifdef HAVE_LIBGSSAPI + // no caching for Negotiate auth. + if ( !m_request.bNoAuth && m_responseCode !=3D 401 && m_responseCode != =3D 407 && Authentication !=3D AUTH_Negotiate ) +#else if ( !m_request.bNoAuth && m_responseCode !=3D 401 && m_responseCode != =3D 407 ) +#endif { kdDebug(7113) << "(" << m_pid << ") Calling checkCachedAuthenticatio= n " << endl; AuthInfo info; @@ -2380,6 +2392,11 @@ case AUTH_Digest: header +=3D createDigestAuth(); break; +#ifdef HAVE_LIBGSSAPI + case AUTH_Negotiate: + header +=3D createNegotiateAuth(); + break; +#endif case AUTH_None: default: break; @@ -2739,6 +2756,9 @@ m_bUnauthorized =3D true; m_request.bCachedWrite =3D false; // Don't put in cache mayCache =3D false; +#ifdef HAVE_LIBGSSAPI + PrevAuthentication =3D AUTH_None; +#endif } // else if (m_responseCode =3D=3D 416) // Range not supported @@ -4771,23 +4791,62 @@ =20 if ( strncasecmp( p, "Basic", 5 ) =3D=3D 0 ) { =2D f =3D AUTH_Basic; =2D p +=3D 5; =2D strAuth =3D "Basic"; // Correct for upper-case variations. +#ifdef HAVE_LIBGSSAPI + if ( !b && PrevAuthentication < AUTH_Basic ) + { +#endif + f =3D AUTH_Basic; + p +=3D 5; + strAuth =3D "Basic"; // Correct for upper-case variations. +#ifdef HAVE_LIBGSSAPI + PrevAuthentication =3D AUTH_Basic; + } else f =3D PrevAuthentication; +#endif } else if ( strncasecmp (p, "Digest", 6) =3D=3D 0 ) { =2D f =3D AUTH_Digest; =2D memcpy((void *)p, "Digest", 6); // Correct for upper-case variations. =2D p +=3D 6; +#ifdef HAVE_LIBGSSAPI + if ( !b && PrevAuthentication < AUTH_Digest ) + { +#endif + f =3D AUTH_Digest; + memcpy((void *)p, "Digest", 6); // Correct for upper-case variations. + p +=3D 6; +#ifdef HAVE_LIBGSSAPI + PrevAuthentication =3D AUTH_Digest; + } else f =3D PrevAuthentication; +#endif } else if (strncasecmp( p, "MBS_PWD_COOKIE", 14 ) =3D=3D 0) { =2D // Found on http://www.webscription.net/baen/default.asp =2D f =3D AUTH_Basic; =2D p +=3D 14; =2D strAuth =3D "Basic"; +#ifdef HAVE_LIBGSSAPI + if ( !b && PrevAuthentication < AUTH_Basic ) + { +#endif + // Found on http://www.webscription.net/baen/default.asp + f =3D AUTH_Basic; + p +=3D 14; + strAuth =3D "Basic"; +#ifdef HAVE_LIBGSSAPI + PrevAuthentication =3D AUTH_Basic; + } else f =3D PrevAuthentication; +#endif + } +#ifdef HAVE_LIBGSSAPI + else if ( strncasecmp( p, "Negotiate", 9 ) =3D=3D 0 ) + { + // if we get two 401 in a row let's assume for now that + // Negotiate isn't working and ignore it + if ( !b && PrevAuthentication < AUTH_Negotiate + && !(m_responseCode =3D=3D 401 && m_prevResponseCode =3D=3D 40= 1) ) + { + f =3D AUTH_Negotiate; + memcpy((void *)p, "Negotiate", 9); // Correct for upper-case variati= ons. + p +=3D 9; + PrevAuthentication =3D AUTH_Negotiate; + } else f =3D PrevAuthentication; } +#endif else { kdWarning(7113) << "(" << m_pid << ") Unsupported or invalid authoriza= tion " @@ -4816,9 +4875,11 @@ ProxyAuthentication =3D f; m_iProxyAuthCount++; } +#if !defined(HAVE_LIBGSSAPI) else m_iWWWAuthCount++; return; +#endif } =20 while (*p) @@ -5063,7 +5124,16 @@ result =3D true; else { +#ifdef HAVE_LIBGSSAPI + if (Authentication =3D=3D AUTH_Negotiate) + { + if (!repeatFailure) + result =3D true; + } + else if ( m_request.disablePassDlg =3D=3D false ) +#else if ( m_request.disablePassDlg =3D=3D false ) +#endif { kdDebug( 7113 ) << "(" << m_pid << ") Prompting the user for autho= rization..." << endl; promptInfo( info ); @@ -5127,6 +5197,100 @@ } } =20 +#ifdef HAVE_LIBGSSAPI +QCString HTTPProtocol::gssError( int major_status, int minor_status ) +{ + OM_uint32 new_status; + OM_uint32 msg_ctx =3D 0; + gss_buffer_desc major_string; + gss_buffer_desc minor_string; + OM_uint32 ret; + QCString errorstr; + + errorstr =3D ""; + + do { + ret =3D gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, = GSS_C_NULL_OID, &msg_ctx, &major_string); + errorstr +=3D (const char *)major_string.value; + errorstr +=3D " "; + ret =3D gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE,= GSS_C_NULL_OID, &msg_ctx, &minor_string); + errorstr +=3D (const char *)minor_string.value; + errorstr +=3D " "; + } while (!GSS_ERROR(ret) && msg_ctx !=3D 0); + + return errorstr; +} + +QString HTTPProtocol::createNegotiateAuth() +{ + QString auth; + QCString servicename; + QByteArray input; + OM_uint32 major_status, minor_status; + OM_uint32 req_flags =3D 0; + gss_buffer_desc input_token =3D GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token =3D GSS_C_EMPTY_BUFFER; + gss_name_t server; + gss_ctx_id_t ctx; + gss_OID mech_oid; + static gss_OID_desc krb5_oid_desc =3D {9, (void *) "\x2a\x86\x48\x86\xf7= \x12\x01\x02\x02"}; + + ctx =3D GSS_C_NO_CONTEXT; + mech_oid =3D &krb5_oid_desc; + + // the service name is "HTTP/f.q.d.n" + servicename =3D "HTTP@"; + servicename +=3D m_state.hostname.ascii(); + + input_token.value =3D (void *)servicename.data(); + input_token.length =3D servicename.length() + 1; + + major_status =3D gss_import_name(&minor_status, &input_token, + GSS_C_NT_HOSTBASED_SERVICE, &server); + + input_token.value =3D NULL; + input_token.length =3D 0; + + if (GSS_ERROR(major_status)) { + kdWarning(7113) << "(" << m_pid << ") gss_import_name failed: " << gss= Error(major_status, minor_status) << endl; + return QString::null; + } + + major_status =3D gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, + &ctx, server, mech_oid, + req_flags, GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + GSS_C_NO_BUFFER, NULL, &output_token, + NULL, NULL); + + + if (GSS_ERROR(major_status) || (output_token.length =3D=3D 0)) { + kdWarning(7113) << "(" << m_pid << ") gss_init_sec_context failed: " <= < gssError(major_status, minor_status) << endl; + gss_release_name(&minor_status, &server); + if (ctx !=3D GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER); + ctx =3D GSS_C_NO_CONTEXT; + } + return QString::null; + } + + input.duplicate((const char *)output_token.value, output_token.length); + auth =3D "Authorization: Negotiate "; + auth +=3D KCodecs::base64Encode( input ); + auth +=3D "\r\n"; + + // free everything + gss_release_name(&minor_status, &server); + if (ctx !=3D GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER); + ctx =3D GSS_C_NO_CONTEXT; + } + gss_release_buffer(&minor_status, &output_token); + + return auth; +} +#endif + QString HTTPProtocol::createBasicAuth( bool isForProxy ) { QString auth; diff -Naur http.orig/http.h http/http.h =2D-- http.orig/http.h 2004-06-16 09:16:48.000000000 -0400 +++ http/http.h 2004-07-06 22:09:26.000000000 -0400 @@ -58,7 +58,11 @@ enum HTTP_REV {HTTP_None, HTTP_Unknown, HTTP_10, HTTP_11, SHOUTCAST}; =20 /** Authorization method used **/ +#ifdef HAVE_LIBGSSAPI + enum HTTP_AUTH {AUTH_None, AUTH_Basic, AUTH_Digest, AUTH_Negotiate}; +#else enum HTTP_AUTH {AUTH_None, AUTH_Basic, AUTH_Digest}; +#endif =20 /** HTTP / DAV method **/ // Removed to interfaces/kio/http.h @@ -433,6 +437,18 @@ */ QString createDigestAuth( bool isForProxy =3D false ); =20 +#ifdef HAVE_LIBGSSAPI + /** + * Creates the entity-header for Negotiate authentication. + */ + QString createNegotiateAuth(); + + /** + * create GSS error string + */ + QCString gssError( int major_status, int minor_status ); +#endif + /** * Calcualtes the message digest response based on RFC 2617. */ @@ -526,6 +542,9 @@ QString m_strAuthorization; QString m_strProxyAuthorization; HTTP_AUTH Authentication; +#ifdef HAVE_LIBGSSAPI + HTTP_AUTH PrevAuthentication; +#endif HTTP_AUTH ProxyAuthentication; bool m_bUnauthorized; short unsigned int m_iProxyAuthCount; --Boundary-01=_2oI7A9Lbm2H6VNk-- --Boundary-03=_9oI7Agketp09a/J Content-Type: application/pgp-signature Content-Description: signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.2 (GNU/Linux) iD8DBQBA7Io86/OfRf0i9j8RAnC3AKCDsTnrCUvNPs+Z6Tz6yvoVF+GEdwCeMA8W CUdu9ViZKZmRUPn6PpaSoEE= =aqa3 -----END PGP SIGNATURE----- --Boundary-03=_9oI7Agketp09a/J--