--- configure.in.orig Mon Apr 28 09:32:59 2003 +++ configure.in Fri Apr 25 16:52:01 2003 @@ -4660,6 +4660,24 @@ AC_DEFINE(SUNCTL) fi +# Check for Gssapi (krb5) libraries and headers +dnl ======================================================== +GSSAPI_INCLUDES= +GSSAPI_LIBS= +MOZ_ARG_WITH_STRING(gssapi, +[ --with-gssapi=PFX Location of GSSAPI libraries], + GSSAPI_DIR=$withval) +if test -n "$GSSAPI_DIR"; then + KRB5_CONFIG="${GSSAPI_DIR}/bin/krb5-config" + if test -f $KRB5_CONFIG; then + GSSAPI_INCLUDES=`$KRB5_CONFIG --cflags gssapi` + GSSAPI_LIBS=`$KRB5_CONFIG --libs gssapi` + AC_DEFINE(GSSAPI) + fi +fi +AC_SUBST(GSSAPI_INCLUDES) +AC_SUBST(GSSAPI_LIBS) + dnl ======================================================== dnl = dnl = Maintainer debug option (no --enable equivalent) --- config/autoconf.mk.in.orig Mon Apr 28 09:32:59 2003 +++ config/autoconf.mk.in Fri Apr 25 16:52:01 2003 @@ -412,3 +412,7 @@ MOZ_MAPINFO = @MOZ_MAPINFO@ MOZ_PHOENIX = @MOZ_PHOENIX@ + +# Gssapi (krb5) libraries and headers for the Negotiate auth method +GSSAPI_INCLUDES = @GSSAPI_INCLUDES@ +GSSAPI_LIBS = @GSSAPI_LIBS@ --- netwerk/build/Makefile.in.orig Mon Apr 28 09:32:59 2003 +++ netwerk/build/Makefile.in Fri Apr 25 16:52:01 2003 @@ -88,6 +88,7 @@ endif EXTRA_DSO_LDOPTS = \ + $(GSSAPI_LIBS) \ $(LIBS_DIR) \ $(EXTRA_DSO_LIBS) \ $(MOZ_UNICHARUTIL_LIBS) \ --- netwerk/build/nsNetCID.h.orig Mon Apr 28 09:32:59 2003 +++ netwerk/build/nsNetCID.h Fri Apr 25 16:52:01 2003 @@ -436,6 +436,14 @@ {0xaa, 0xe3, 0xde, 0x6b, 0x92, 0xda, 0xb6, 0x20} \ } +#define NS_HTTPNEGOTIATEAUTH_CID \ +{ /* 75c80fd0-accb-432c-af59-ec60668c3990 */ \ + 0x75c80fd0, \ + 0xaccb, \ + 0x432c, \ + {0xaf, 0x59, 0xec, 0x60, 0x66, 0x8c, 0x39, 0x90} \ +} + /****************************************************************************** * netwerk/protocol/res/ classes */ --- netwerk/build/nsNetModule.cpp.orig Mon Apr 28 09:32:59 2003 +++ netwerk/build/nsNetModule.cpp Fri Apr 25 16:52:01 2003 @@ -127,6 +127,10 @@ #include "nsHttpHandler.h" #include "nsHttpBasicAuth.h" #include "nsHttpDigestAuth.h" +#ifdef GSSAPI +#include "nsHttpGssapiAuth.h" +NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpGssapiAuth) +#endif #undef LOG NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpHandler, Init) @@ -810,6 +814,13 @@ NS_HTTPDIGESTAUTH_CID, NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "digest", nsHttpDigestAuthConstructor }, + +#ifdef GSSAPI + { "HTTP Negotiate Auth Encoder", + NS_HTTPNEGOTIATEAUTH_CID, + NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "gss-negotiate", + nsHttpGssapiAuthConstructor }, +#endif // from netwerk/protocol/ftp: { "The FTP Protocol Handler", --- netwerk/protocol/http/src/Makefile.in.orig Mon Apr 28 09:32:59 2003 +++ netwerk/protocol/http/src/Makefile.in Fri Apr 25 16:52:01 2003 @@ -53,6 +53,7 @@ nsHttpAuthCache.cpp \ nsHttpBasicAuth.cpp \ nsHttpDigestAuth.cpp \ + nsHttpGssapiAuth.cpp \ nsHttpTransaction.cpp \ nsHttpHandler.cpp \ nsHttpChannel.cpp \ @@ -60,7 +61,7 @@ $(NULL) -LOCAL_INCLUDES=-I$(srcdir)/../../../base/src +LOCAL_INCLUDES=-I$(srcdir)/../../../base/src $(GSSAPI_INCLUDES) # we don't want the shared lib, but we want to force the creation of a # static lib. --- netwerk/protocol/http/src/nsHttpChannel.cpp.orig Mon Apr 28 09:32:59 2003 +++ netwerk/protocol/http/src/nsHttpChannel.cpp Mon Apr 28 09:41:07 2003 @@ -49,6 +49,10 @@ #include "prprf.h" #include "nsEscape.h" +#ifdef GSSAPI +#define NEGOTIATE_AUTH "GSS-Negotiate" +#endif + static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); //----------------------------------------------------------------------------- @@ -1633,7 +1637,43 @@ LOG(("challenge=%s\n", challenge)); nsCAutoString creds; - nsresult rv = GetCredentials(challenge, proxyAuth, creds); + nsresult rv; +#ifndef GSSAPI + rv = GetCredentials(challenge, proxyAuth, creds); +#else + { + char *p, *q; + char *challs; + + /* Iterate over methods suggested by the server and find first one which + has not alredy been tryied (let GetCredentials() decide if or not the + method was tryied). Methods sent by the server should be re-ordered + and processed according to user's preferences/easy of use etc. At the + moment we rely on the server to send the methods in the "correct" + order, i.e. with the Negotiate method first. + */ + + challs = strdup(challenge); + p = challs; + do { + q = strchr(p, '\n'); + if (q) + *q = '\0'; + LOG(("Trying %s\n", p)); + rv = GetCredentials(p, proxyAuth, creds); + if (NS_SUCCEEDED(rv)) { + free(challs); + break; + } + + if (!q) { + free(challs); + return NS_ERROR_FAILURE; + } + p = q + 1; + } while (1); + } +#endif if (NS_FAILED(rv)) return rv; // set the authentication credentials @@ -1702,6 +1742,33 @@ nsCAutoString challenge; nsCOMPtr auth; +#ifdef GSSAPI + /* Check if the Negotiate method was tryied and failed. If so return + immediately so another method could have a try. Note that Negotiate is + not supported for proxy authentication. */ + + if (!proxyAuth) { + const char *host = mConnectionInfo->Host(); + PRInt32 port = mConnectionInfo->Port(); + nsCAutoString path; + nsHttpAuthEntry *entry = nsnull; + + rv = GetCurrentPath(path); + if (NS_FAILED(rv)) + return rv; + + authCache->GetAuthEntryForPath(host, port, path.get(), &entry); + if (entry && !entry->Pass()) { + /* some authentication method was already tryied and was not + sucessful (since we are again here). Suppose it was Negotiate + (since passwd is NULL) and do not try it again */ + if (strstr(challenges, NEGOTIATE_AUTH)) { + LOG(("Negotiate authentication was already tried and failed for this URL, trying another method")); + return NS_ERROR_FAILURE; + } + } + } +#endif rv = SelectChallenge(challenges, challenge, getter_AddRefs(auth)); if (!auth) { @@ -1741,6 +1808,11 @@ // try instead. // nsHttpAuthEntry *entry = nsnull; +#ifdef GSSAPI + if (PL_strncasecmp(challenge.get(), NEGOTIATE_AUTH, strlen(NEGOTIATE_AUTH)) == 0) + authCache->GetAuthEntryForPath(host, port, path.get(), &entry); + else +#endif authCache->GetAuthEntryForDomain(host, port, realm.get(), &entry); PRBool requireUserPass = PR_FALSE; --- netwerk/protocol/http/src/nsHttpGssapiAuth.h.orig Mon Apr 28 09:32:26 2003 +++ netwerk/protocol/http/src/nsHttpGssapiAuth.h Fri Apr 25 16:52:01 2003 @@ -0,0 +1,29 @@ +#ifndef nsGssapiAuth_h__ +#define nsGssapiAuth_h__ + +#ifdef GSSAPI + +#include "nsIHttpAuthenticator.h" + +#include "nsISignatureVerifier.h" +#include "nsString.h" +#include "nsCOMPtr.h" + +#define NEGOTIATE_AUTH "GSS-Negotiate" + +// The nsGssapiAuth class provides responses for the GSS-API Negotiate method +// as specified by Microsoft in draft-brezak-spnego-http-04.txt + +class nsHttpGssapiAuth : public nsIHttpAuthenticator +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIHTTPAUTHENTICATOR + + nsHttpGssapiAuth(); + virtual ~nsHttpGssapiAuth(); + private: + void LogGssError(int status, char *prefix); +}; +#endif /* GSSAPI */ +#endif /* nsGssapiAuth_h__ */ --- netwerk/protocol/http/src/nsHttpGssapiAuth.cpp.orig Mon Apr 28 09:32:34 2003 +++ netwerk/protocol/http/src/nsHttpGssapiAuth.cpp Fri Apr 25 16:52:01 2003 @@ -0,0 +1,177 @@ +#ifdef GSSAPI + +#include +#include "nsHttp.h" +#include "nsHttpGssapiAuth.h" +#include "nsIHttpChannel.h" +#include "nsIServiceManager.h" +#include "nsISupportsPrimitives.h" +#include "nsIURI.h" +#include "nsString.h" +#include "nsReadableUtils.h" +#include "plbase64.h" +#include "plstr.h" +#include "prprf.h" +#include "prmem.h" + +#include + +nsHttpGssapiAuth::nsHttpGssapiAuth() +{ + NS_INIT_ISUPPORTS(); +} + +nsHttpGssapiAuth::~nsHttpGssapiAuth() +{ +} + +NS_IMPL_ISUPPORTS1(nsHttpGssapiAuth, nsIHttpAuthenticator); + +void +nsHttpGssapiAuth::LogGssError(int min_stat, char *prefix) +{ + OM_uint32 new_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc status_string; + OM_uint32 ret; + nsCAutoString error(prefix); + + error += ": "; + do { + ret = gss_display_status (&new_stat, + min_stat, + GSS_C_MECH_CODE, + GSS_C_NO_OID, + &msg_ctx, + &status_string); + error += (char *)status_string.value; + error += "\n"; + gss_release_buffer (&new_stat, &status_string); + } while (!GSS_ERROR(ret) && msg_ctx != 0); + + LOG(("%s", ToNewCString(error))); + /* XXX Destroy error */ +} + +NS_IMETHODIMP +nsHttpGssapiAuth::GenerateCredentials(nsIHttpChannel *httpChannel, + const char *challenge, + const PRUnichar *username, + const PRUnichar *password, + nsISupports *extra, + char **creds) +{ + OM_uint32 major_status, minor_status; + gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + gss_name_t server; + + nsCOMPtr uri; + nsresult rv; + nsCAutoString service; + + LOG(("nsHttpGssapiAuth::GenerateCredentials() [challenge=%s]\n", challenge)); + + NS_ENSURE_ARG_POINTER(creds); + + PRBool isGssapiAuth = !PL_strncasecmp(challenge, NEGOTIATE_AUTH, strlen(NEGOTIATE_AUTH)); + NS_ENSURE_TRUE(isGssapiAuth, NS_ERROR_UNEXPECTED); + + rv = httpChannel->GetURI(getter_AddRefs(uri)); + if (NS_FAILED(rv)) return rv; + + rv = uri->GetAsciiHost(service); + if (NS_FAILED(rv)) return rv; + + /* XXX hostname should be convert to FQDN */ + LOG(("nsHttpGssapiAuth::GenerateCredentials() : hostname = %s\n", + service.get())); + + /* create GSS name of the service we are connecting to */ + service.Insert(NS_LITERAL_CSTRING("khttp@"), 0); + + input_token.value = (void *)service.get(); + input_token.length = service.Length() + 1; + major_status = gss_import_name(&minor_status, + &input_token, + GSS_C_NT_HOSTBASED_SERVICE, + &server); + /* XXX Is it necessary to free input_token.value or service ? */ + if (GSS_ERROR(major_status)) { + LogGssError(minor_status, "gss_import_name() failed"); + return NS_ERROR_FAILURE; + } + + input_token.length = 0; + input_token.value = nsnull; + + major_status = gss_init_sec_context(&minor_status, + GSS_C_NO_CREDENTIAL, + &gss_context, + server, + GSS_C_NO_OID, + GSS_C_DELEG_FLAG, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + nsnull, + &output_token, + nsnull, + nsnull); + if (GSS_ERROR(major_status)) { + LogGssError(minor_status, "gss_init_sec_context() failed"); + return NS_ERROR_FAILURE; + } + + if (output_token.length == 0) { + LOG(("No GSS output token to send, exiting")); + return NS_ERROR_FAILURE; + } + + char *encoded_token = PL_Base64Encode((char *)output_token.value, + output_token.length, + nsnull); + if (!encoded_token) + return NS_ERROR_OUT_OF_MEMORY; + + LOG(("Sending a token of length %d\n", output_token.length)); + + // allocate a buffer sizeof("GSS-Negotiate" + " " + b64output_token + "\0") + *creds = (char *) malloc (strlen(NEGOTIATE_AUTH) + 1 + strlen(encoded_token) + 1); + if (!(*creds)) { + PR_Free(encoded_token); + return NS_ERROR_OUT_OF_MEMORY; + } + + sprintf(*creds, "%s %s", NEGOTIATE_AUTH, encoded_token); + PR_Free(encoded_token); + + gss_release_buffer(&minor_status, &output_token); + + return NS_OK; +} + +NS_IMETHODIMP +nsHttpGssapiAuth::AreCredentialsReusable(PRBool *result) +{ + *result = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpGssapiAuth::ChallengeRequiresUserPass(const char *challenge, + PRBool *result) +{ + *result = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpGssapiAuth::AllocateMetaData(nsISupports **result) +{ + *result = nsnull; + return NS_OK; +} + +#endif /* GSSAPI */