diff -rNu --exclude Makefile --exclude _xpidlgen --exclude .deps mozilla/netwerk/build/Makefile.in /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/build/Makefile.in --- mozilla/netwerk/build/Makefile.in Tue Jul 30 04:24:28 2002 +++ /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/build/Makefile.in Mon Nov 25 11:29:44 2002 @@ -62,6 +62,13 @@ $(DIST)/lib/$(LIB_PREFIX)nkres_s.$(LIB_SUFFIX) \ $(DIST)/lib/$(LIB_PREFIX)nkabout_s.$(LIB_SUFFIX) \ $(DIST)/lib/$(LIB_PREFIX)nkkwd_s.$(LIB_SUFFIX) \ + /usr/heimdal/lib/libgssapi.a \ + /usr/heimdal/lib/libkrb5.a \ + /usr/heimdal/lib/libasn1.a \ + /usr/heimdal/lib/libroken.a \ + /usr/lib/libdb.a \ + /usr/lib/libresolv.a \ + /software/openssl-0.9.6/lib/libcrypto.a \ $(NULL) LOCAL_INCLUDES = \ diff -rNu --exclude Makefile --exclude _xpidlgen --exclude .deps mozilla/netwerk/protocol/http/src/Makefile.in /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/protocol/http/src/Makefile.in --- mozilla/netwerk/protocol/http/src/Makefile.in Tue Jul 30 04:24:55 2002 +++ /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/protocol/http/src/Makefile.in Tue Nov 26 12:46:05 2002 @@ -53,10 +53,11 @@ nsHttpAuthCache.cpp \ nsHttpBasicAuth.cpp \ nsHttpDigestAuth.cpp \ + nsHttpGssapiAuth.cpp \ nsHttpPipeline.cpp \ $(NULL) -LOCAL_INCLUDES=-I$(srcdir)/../../../streamconv/converters +LOCAL_INCLUDES=-I$(srcdir)/../../../streamconv/converters -I/usr/heimdal/include # we don't want the shared lib, but we want to force the creation of a # static lib. diff -rNu --exclude Makefile --exclude _xpidlgen --exclude .deps mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp --- mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp Thu Aug 8 06:52:06 2002 +++ /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp Tue Nov 26 11:03:53 2002 @@ -50,6 +50,11 @@ static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); +#ifdef GSSAPI +#include "nsHttpGssapiAuth.h" +#define NEGOTIATE_AUTH "GSS-Negotiate" +#endif + //----------------------------------------------------------------------------- // nsHttpChannel //----------------------------------------------------------------------------- @@ -1585,6 +1590,10 @@ const char *challenge; PRBool proxyAuth = (httpStatus == 407); + nsCAutoString creds; + char *p, *q; + char *challenges; + nsresult rv; if (proxyAuth) challenge = mResponseHead->PeekHeader(nsHttp::Proxy_Authenticate); @@ -1598,9 +1607,33 @@ LOG(("challenge=%s\n", challenge)); - nsCAutoString creds; - nsresult rv = GetCredentials(challenge, proxyAuth, creds); - if (NS_FAILED(rv)) return rv; + /* XXX The methods sent by the server should be re-ordered and processed + according to preferences/easy of use etc. Perhaps method + SelectChallenge() could be used for this but we used the simplest + solution (to keep the patch small). At the moment we rely on the server + to send the methods in the "correct" order, i.e. with the Negotiate + method first. + */ + challenges = strdup(challenge); + p = challenges; + do { + q = strchr(p, '\n'); + if (q != NULL) + *q = '\0'; + LOG(("Trying %s\n", p)); + rv = GetCredentials(p, proxyAuth, creds); + if (NS_SUCCEEDED(rv)) { + free(challenges); + break; + } + + if (q == NULL) { + free(challenges); + return NS_ERROR_FAILURE; + } + p = q + 1; + } while (1); + // set the authentication credentials if (proxyAuth) @@ -1666,6 +1699,51 @@ nsCAutoString challenge; nsCOMPtr auth; + nsHttpAuthEntry *entry = nsnull; + + const char *host; + nsCAutoString path; + nsXPIDLString *user; + nsXPIDLString *pass; + PRInt32 port; + + if (proxyAuth) { + host = mConnectionInfo->ProxyHost(); + port = mConnectionInfo->ProxyPort(); + user = &mProxyUser; + pass = &mProxyPass; + } + else { + host = mConnectionInfo->Host(); + port = mConnectionInfo->Port(); + user = &mUser; + pass = &mPass; + + rv = GetCurrentPath(path); + if (NS_FAILED(rv)) return rv; + } + +#ifdef GSSAPI + authCache->GetAuthEntryForPath(host, port, path.get(), &entry); + if (entry && entry->Pass() == NULL) { + /* 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. Exit immediately so another method + could have a try. This could be done within the SelectChallenge + method. + */ + /* XXX This could be done by the SelectChallenge() method. The entry + structure could contain a list of methods, which were used (or + tryied). + */ + + if (strstr(challenges, NEGOTIATE_AUTH) != NULL) { + LOG(("GSSAPI 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) { @@ -1677,6 +1755,7 @@ rv = ParseRealm(challenge.get(), realm); if (NS_FAILED(rv)) return rv; +#if 0 const char *host; nsCAutoString path; nsXPIDLString *user; @@ -1698,6 +1777,7 @@ rv = GetCurrentPath(path); if (NS_FAILED(rv)) return rv; } +#endif // // if we already tried some credentials for this transaction, then @@ -1705,9 +1785,18 @@ // in the cache have changed, in which case we'd want to give them a // try instead. // - nsHttpAuthEntry *entry = nsnull; - authCache->GetAuthEntryForDomain(host, port, realm.get(), &entry); +#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); if (entry) { +#ifdef GSSAPI + /* passwords are not used for the GSS Negotiate method, so we don't need + to process them */ + if (PL_strncasecmp(challenge.get(), NEGOTIATE_AUTH, strlen(NEGOTIATE_AUTH)) != 0) +#endif if (user->Equals(entry->User()) && pass->Equals(entry->Pass())) { LOG(("clearing bad credentials from the auth cache\n")); // ok, we've already tried this user:pass combo, so clear the @@ -1732,12 +1821,19 @@ } if (!entry && user->IsEmpty()) { - // at this point we are forced to interact with the user to get their - // username and password for this domain. - rv = PromptForUserPass(host, port, proxyAuth, realm.get(), - getter_Copies(*user), - getter_Copies(*pass)); - if (NS_FAILED(rv)) return rv; +#ifdef GSSAPI + /* Krb5 tickets from a disk are used for the Negotiate method. So we + will not ask the user for their password when using this method. */ + if (PL_strncasecmp(challenge.get(), NEGOTIATE_AUTH, strlen(NEGOTIATE_AUTH)) != 0) +#endif + { + // at this point we are forced to interact with the user to get their + // username and password for this domain. + rv = PromptForUserPass(host, port, proxyAuth, realm.get(), + getter_Copies(*user), + getter_Copies(*pass)); + if (NS_FAILED(rv)) return rv; + } } // ask the auth cache for a container for any meta data it might want to @@ -1818,6 +1914,18 @@ nsresult rv; nsCOMPtr serv = do_GetService(contractid.get(), &rv); +#ifdef GSSAPI + if (NS_FAILED(rv)) { + if (PL_strncasecmp(scheme, NEGOTIATE_AUTH, strlen(NEGOTIATE_AUTH)) == 0) { + /* XXX I don't know how to make do_GetService() work with my + nsHttpGssapiAuth object (and return appropriate object). Thus, + I create it here "by hand" */ + *auth = :: new nsHttpGssapiAuth(); + NS_ADDREF(*auth); + return NS_OK; + } + } +#endif if (NS_FAILED(rv)) return rv; *auth = serv; --- mozilla/netwerk/protocol/http/src/nsHttpGssapiAuth.cpp Thu Jan 1 01:00:00 1970 +++ /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/protocol/http/src/nsHttpGssapiAuth.cpp Tue Nov 26 13:40:39 2002 @@ -0,0 +1,165 @@ +#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 = NULL; + + 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, + NULL, + &output_token, + NULL, + NULL); + 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 == NULL) + 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 == NULL) { + 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::AllocateMetaData(nsISupports **result) +{ + *result = nsnull; + return NS_OK; +} diff -rNu --exclude Makefile --exclude _xpidlgen --exclude .deps mozilla/netwerk/protocol/http/src/nsHttpGssapiAuth.h /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/protocol/http/src/nsHttpGssapiAuth.h --- mozilla/netwerk/protocol/http/src/nsHttpGssapiAuth.h Thu Jan 1 01:00:00 1970 +++ /home/kouril/soft-local/mozilla-1.1/mozilla/netwerk/protocol/http/src/nsHttpGssapiAuth.h Tue Nov 26 11:34:26 2002 @@ -0,0 +1,26 @@ +#ifndef nsGssapiAuth_h__ +#define nsGssapiAuth_h__ + +#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 /* nsGssapiAuth_h__ */