fixes bug 15860 "support http/1.1 digest authentication"

patch=jab@atdot.org,pach@cs.cmu.edu,darin@netscape.com
r=dougt@netscape.com
sr=rpotts@netscape.com
This commit is contained in:
darin%netscape.com 2001-12-04 23:49:06 +00:00
Родитель 7d640e36ad
Коммит 43dc5ff391
11 изменённых файлов: 1073 добавлений и 297 удалений

Просмотреть файл

@ -31,11 +31,29 @@ interface nsIHttpAuthenticator : nsISupports
/**
* Called to generate the authentication credentials for a particular
* server/proxy challenge.
*
* @param channel - the http channel requesting credentials
* @param challenge - the server specified auth challenge
* @param username - the username from which to generate credentials
* @param password - the password from which to generate credentials
* @param extra - additional information stored in the auth cache
*/
string generateCredentials(in nsIHttpChannel channel,
in string challenge,
in wstring username,
in wstring password);
in wstring password,
in nsISupports metadata);
/**
* indicates if credentials returned from GenerateCredentials can be reused
*/
boolean areCredentialsReusable();
/**
* allocate authenticator specific metadata implementation. implementations
* that don't need to store extra data in the auth cache can simply return NULL.
*/
nsISupports allocateMetaData();
};
%{C++

Просмотреть файл

@ -35,6 +35,7 @@ REQUIRES = xpcom \
mimetype \
intl \
exthandler \
caps \
$(NULL)
CPPSRCS = \
@ -49,6 +50,7 @@ CPPSRCS = \
nsHttpChunkedDecoder.cpp \
nsHttpAuthCache.cpp \
nsHttpBasicAuth.cpp \
nsHttpDigestAuth.cpp \
$(NULL)
LOCAL_INCLUDES=-I$(srcdir)/../../../streamconv/converters

Просмотреть файл

@ -28,6 +28,7 @@ REQUIRES = xpcom \
mimetype \
intl \
exthandler \
caps \
$(NULL)
include <$(DEPTH)/config/config.mak>
@ -48,6 +49,7 @@ CPP_OBJS= \
.\$(OBJDIR)\nsHttpChunkedDecoder.obj \
.\$(OBJDIR)\nsHttpAuthCache.obj \
.\$(OBJDIR)\nsHttpBasicAuth.obj \
.\$(OBJDIR)\nsHttpDigestAuth.obj \
$(NULL)
include <$(DEPTH)\config\rules.mak>

Просмотреть файл

@ -141,4 +141,7 @@ PRTimeToSeconds(PRTime t_usec)
#undef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
// nsCRT::strdup likes to convert nsnull to ""
#define strdup_if(s) (s ? nsCRT::strdup(s) : nsnull)
#endif // nsHttp_h__

Просмотреть файл

@ -25,7 +25,7 @@
#include "nsHttp.h"
#include "nsHttpAuthCache.h"
#include "nsString.h"
#include "plstr.h"
#include "nsCRT.h"
#include "prprf.h"
//-----------------------------------------------------------------------------
@ -60,50 +60,55 @@ nsHttpAuthCache::Init()
}
nsresult
nsHttpAuthCache::GetCredentialsForPath(const char *host,
nsHttpAuthCache::GetAuthEntryForPath(const char *host,
PRInt32 port,
const char *path,
nsACString &realm,
nsACString &creds)
nsHttpAuthEntry **entry)
{
LOG(("nsHttpAuthCache::GetCredentialsForPath [host=%s:%d path=%s]\n",
LOG(("nsHttpAuthCache::GetAuthEntryForPath [host=%s:%d path=%s]\n",
host, port, path));
nsCAutoString key;
nsEntryList *list = LookupEntryList(host, port, key);
if (!list)
nsHttpAuthNode *node = LookupAuthNode(host, port, key);
if (!node)
return NS_ERROR_NOT_AVAILABLE;
return list->GetCredentialsForPath(path, realm, creds);
return node->GetAuthEntryForPath(path, entry);
}
nsresult
nsHttpAuthCache::GetCredentialsForDomain(const char *host,
nsHttpAuthCache::GetAuthEntryForDomain(const char *host,
PRInt32 port,
const char *realm,
nsACString &creds)
nsHttpAuthEntry **entry)
{
LOG(("nsHttpAuthCache::GetCredentialsForDomain [host=%s:%d realm=%s]\n",
LOG(("nsHttpAuthCache::GetAuthEntryForDomain [host=%s:%d realm=%s]\n",
host, port, realm));
nsCAutoString key;
nsEntryList *list = LookupEntryList(host, port, key);
if (!list)
nsHttpAuthNode *node = LookupAuthNode(host, port, key);
if (!node)
return NS_ERROR_NOT_AVAILABLE;
return list->GetCredentialsForRealm(realm, creds);
return node->GetAuthEntryForRealm(realm, entry);
}
nsresult
nsHttpAuthCache::SetCredentials(const char *host,
nsHttpAuthCache::SetAuthEntry(const char *host,
PRInt32 port,
const char *path,
const char *realm,
const char *creds)
const char *creds,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
nsISupports *metadata)
{
nsresult rv;
LOG(("nsHttpAuthCache::SetCredentials\n"));
LOG(("nsHttpAuthCache::SetAuthEntry [host=%s:%d realm=%s path=%s metadata=%x]\n",
host, port, realm, path, metadata));
if (!mDB) {
rv = Init();
@ -111,28 +116,28 @@ nsHttpAuthCache::SetCredentials(const char *host,
}
nsCAutoString key;
nsEntryList *list = LookupEntryList(host, port, key);
nsHttpAuthNode *node = LookupAuthNode(host, port, key);
if (!list) {
// only create a new list if we have a real entry
if (!creds)
if (!node) {
// only create a new node if we have a real entry
if (!creds && !user && !pass && !challenge)
return NS_OK;
// create a new entry list and set the given entry
list = new nsEntryList();
if (!list)
// create a new entry node and set the given entry
node = new nsHttpAuthNode();
if (!node)
return NS_ERROR_OUT_OF_MEMORY;
rv = list->SetCredentials(path, realm, creds);
rv = node->SetAuthEntry(path, realm, creds, user, pass, challenge, metadata);
if (NS_FAILED(rv))
delete list;
delete node;
else
PL_HashTableAdd(mDB, PL_strdup(key.get()), list);
PL_HashTableAdd(mDB, nsCRT::strdup(key.get()), node);
return rv;
}
rv = list->SetCredentials(path, realm, creds);
if (NS_SUCCEEDED(rv) && (list->Count() == 0)) {
// the list has no longer has any entries
rv = node->SetAuthEntry(path, realm, creds, user, pass, challenge, metadata);
if (NS_SUCCEEDED(rv) && (node->EntryCount() == 0)) {
// the node has no longer has any entries
PL_HashTableRemove(mDB, key.get());
}
@ -155,8 +160,8 @@ nsHttpAuthCache::ClearAll()
// nsHttpAuthCache <private>
//-----------------------------------------------------------------------------
nsHttpAuthCache::nsEntryList *
nsHttpAuthCache::LookupEntryList(const char *host, PRInt32 port, nsAFlatCString &key)
nsHttpAuthNode *
nsHttpAuthCache::LookupAuthNode(const char *host, PRInt32 port, nsAFlatCString &key)
{
char buf[32];
@ -169,7 +174,7 @@ nsHttpAuthCache::LookupEntryList(const char *host, PRInt32 port, nsAFlatCString
key.Append(':');
key.Append(buf);
return (nsEntryList *) PL_HashTableLookup(mDB, key.get());
return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
}
void *
@ -201,8 +206,8 @@ nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, PRUintn flag)
}
else if (flag == HT_FREE_ENTRY) {
// three wonderful flavors of freeing memory ;-)
delete (nsEntryList *) he->value;
PL_strfree((char *) he->key);
delete (nsHttpAuthNode *) he->value;
nsCRT::free((char *) he->key);
free(he);
}
}
@ -216,69 +221,60 @@ PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
};
//-----------------------------------------------------------------------------
// nsHttpAuthCache::nsEntry
// nsHttpAuthEntry
//-----------------------------------------------------------------------------
nsHttpAuthCache::
nsEntry::nsEntry(const char *path, const char *realm, const char *creds)
: mPath(PL_strdup(path))
, mRealm(PL_strdup(realm))
, mCreds(PL_strdup(creds))
nsHttpAuthEntry::nsHttpAuthEntry(const char *path, const char *realm,
const char *creds, const PRUnichar *user,
const PRUnichar *pass, const char *challenge,
nsISupports *metadata)
: mPath(strdup_if(path))
, mRealm(strdup_if(realm))
, mCreds(strdup_if(creds))
, mUser(strdup_if(user))
, mPass(strdup_if(pass))
, mChallenge(strdup_if(challenge))
, mMetaData(metadata)
{
LOG(("Creating nsHttpAuthCache::nsEntry @%x\n", this));
}
nsHttpAuthCache::
nsEntry::~nsEntry()
nsHttpAuthEntry::~nsHttpAuthEntry()
{
LOG(("Destroying nsHttpAuthCache::nsEntry @%x\n", this));
PL_strfree(mPath);
PL_strfree(mRealm);
PL_strfree(mCreds);
}
void nsHttpAuthCache::
nsEntry::SetPath(const char *path)
{
PL_strfree(mPath);
mPath = PL_strdup(path);
}
void nsHttpAuthCache::
nsEntry::SetCreds(const char *creds)
{
PL_strfree(mCreds);
mCreds = PL_strdup(creds);
CRTFREEIF(mPath);
CRTFREEIF(mRealm);
CRTFREEIF(mCreds);
CRTFREEIF(mUser);
CRTFREEIF(mPass);
CRTFREEIF(mChallenge);
}
//-----------------------------------------------------------------------------
// nsHttpAuthCache::nsEntryList
// nsHttpAuthNode
//-----------------------------------------------------------------------------
nsHttpAuthCache::
nsEntryList::nsEntryList()
nsHttpAuthNode::nsHttpAuthNode()
{
LOG(("Creating nsHttpAuthCache::nsEntryList @%x\n", this));
LOG(("Creating nsHttpAuthNode @%x\n", this));
}
nsHttpAuthCache::
nsEntryList::~nsEntryList()
nsHttpAuthNode::~nsHttpAuthNode()
{
LOG(("Destroying nsHttpAuthCache::nsEntryList @%x\n", this));
LOG(("Destroying nsHttpAuthNode @%x\n", this));
PRInt32 i;
for (i=0; i<mList.Count(); ++i)
delete (nsEntry *) mList[i];
delete (nsHttpAuthEntry *) mList[i];
mList.Clear();
}
nsresult nsHttpAuthCache::
nsEntryList::GetCredentialsForPath(const char *path,
nsACString &realm,
nsACString &creds)
nsresult
nsHttpAuthNode::GetAuthEntryForPath(const char *path,
nsHttpAuthEntry **entry)
{
nsEntry *entry = nsnull;
*entry = nsnull;
// it's permissible to specify a null path, in which case we just treat
// this as an empty string.
@ -290,72 +286,67 @@ nsEntryList::GetCredentialsForPath(const char *path,
// directory of an existing entry.
PRInt32 i;
for (i=0; i<mList.Count(); ++i) {
entry = (nsEntry *) mList[i];
if (!PL_strncmp(path, entry->Path(), PL_strlen(entry->Path())))
*entry = (nsHttpAuthEntry *) mList[i];
if (!nsCRT::strncmp(path, (*entry)->Path(), strlen((*entry)->Path())))
break;
entry = nsnull;
*entry = nsnull;
}
if (!entry)
return NS_ERROR_NOT_AVAILABLE;
realm.Assign(entry->Realm());
creds.Assign(entry->Creds());
return NS_OK;
return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
nsresult nsHttpAuthCache::
nsEntryList::GetCredentialsForRealm(const char *realm,
nsACString &creds)
nsresult
nsHttpAuthNode::GetAuthEntryForRealm(const char *realm,
nsHttpAuthEntry **entry)
{
NS_ENSURE_ARG_POINTER(realm);
nsEntry *entry = nsnull;
*entry = nsnull;
// look for an entry that matches this realm
PRInt32 i;
for (i=0; i<mList.Count(); ++i) {
entry = (nsEntry *) mList[i];
if (!PL_strcmp(realm, entry->Realm()))
*entry = (nsHttpAuthEntry *) mList[i];
if (!nsCRT::strcmp(realm, (*entry)->Realm()))
break;
entry = nsnull;
*entry = nsnull;
}
if (!entry)
return NS_ERROR_NOT_AVAILABLE;
creds.Assign(entry->Creds());
return NS_OK;
return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
nsresult nsHttpAuthCache::
nsEntryList::SetCredentials(const char *path,
nsresult
nsHttpAuthNode::SetAuthEntry(const char *path,
const char *realm,
const char *creds)
const char *creds,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
nsISupports *metadata)
{
NS_ENSURE_ARG_POINTER(realm);
nsEntry *entry = nsnull;
nsHttpAuthEntry *entry = nsnull;
// look for an entry with a matching realm
PRInt32 i;
for (i=0; i<mList.Count(); ++i) {
entry = (nsEntry *) mList[i];
if (!PL_strcmp(realm, entry->Realm()))
entry = (nsHttpAuthEntry *) mList[i];
if (!nsCRT::strcmp(realm, entry->Realm()))
break;
entry = nsnull;
}
if (!entry) {
if (creds) {
entry = new nsEntry(path, realm, creds);
if (creds || user || pass || challenge) {
entry = new nsHttpAuthEntry(path, realm, creds, user, pass, challenge, metadata);
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
mList.AppendElement(entry);
}
// else, nothing to do
}
else if (!creds) {
else if (!creds && !user && !pass && !challenge) {
mList.RemoveElementAt(i);
delete entry;
}
@ -363,12 +354,16 @@ nsEntryList::SetCredentials(const char *path,
// update the entry...
if (path) {
// we should hold onto the top-most of the two path
PRUint32 len1 = PL_strlen(path);
PRUint32 len2 = PL_strlen(entry->Path());
PRUint32 len1 = strlen(path);
PRUint32 len2 = strlen(entry->Path());
if (len1 < len2)
entry->SetPath(path);
}
entry->SetCreds(creds);
entry->SetUser(user);
entry->SetPass(pass);
entry->SetChallenge(challenge);
entry->SetMetaData(metadata);
}
return NS_OK;

Просмотреть файл

@ -40,13 +40,98 @@
#ifndef nsHttpAuthCache_h__
#define nsHttpAuthCache_h__
#include "nsHttp.h"
#include "nsError.h"
#include "nsVoidArray.h"
#include "nsAString.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "plhash.h"
//-----------------------------------------------------------------------------
// nsHttpAuthEntry
//-----------------------------------------------------------------------------
class nsHttpAuthEntry
{
public:
const char *Path() { return mPath; }
const char *Realm() { return mRealm; }
const char *Creds() { return mCreds; }
const PRUnichar *User() { return mUser; }
const PRUnichar *Pass() { return mPass; }
const char *Challenge() { return mChallenge; }
nsISupports *MetaData() { return mMetaData; }
private:
nsHttpAuthEntry(const char *path,
const char *realm,
const char *creds,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
nsISupports *metadata);
~nsHttpAuthEntry();
void SetPath(const char *v) { CRTFREEIF(mPath); mPath = strdup_if(v); }
void SetCreds(const char *v) { CRTFREEIF(mCreds); mCreds = strdup_if(v); }
void SetUser(const PRUnichar *v) { CRTFREEIF(mUser); mUser = strdup_if(v); }
void SetPass(const PRUnichar *v) { CRTFREEIF(mPass); mPass = strdup_if(v); }
void SetChallenge(const char *v) { CRTFREEIF(mChallenge); mChallenge = strdup_if(v); }
void SetMetaData(nsISupports *v) { mMetaData = v; }
private:
char *mPath;
char *mRealm;
char *mCreds;
PRUnichar *mUser;
PRUnichar *mPass;
char *mChallenge;
nsCOMPtr<nsISupports> mMetaData;
friend class nsHttpAuthCache;
friend class nsHttpAuthNode;
};
//-----------------------------------------------------------------------------
// nsHttpAuthNode
//-----------------------------------------------------------------------------
class nsHttpAuthNode
{
private:
nsHttpAuthNode();
~nsHttpAuthNode();
// path can be null, in which case we'll search for an entry
// with a null path.
nsresult GetAuthEntryForPath(const char *path,
nsHttpAuthEntry **entry);
// realm must not be null
nsresult GetAuthEntryForRealm(const char *realm,
nsHttpAuthEntry **entry);
// if a matching entry is found, then credentials will be changed.
nsresult SetAuthEntry(const char *path,
const char *realm,
const char *credentials,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
nsISupports *metadata);
PRUint32 EntryCount() { return (PRUint32) mList.Count(); }
private:
nsVoidArray mList; // list of nsHttpAuthEntry objects
friend class nsHttpAuthCache;
};
//-----------------------------------------------------------------------------
// nsHttpAuthCache
// (holds a hash table from host:port to nsHttpAuthNode)
//-----------------------------------------------------------------------------
class nsHttpAuthCache
@ -57,86 +142,42 @@ public:
nsresult Init();
// host and port are required
// path can be null
nsresult GetCredentialsForPath(const char *host,
// |host| and |port| are required
// |path| can be null
// |entry| is either null or a weak reference
nsresult GetAuthEntryForPath(const char *host,
PRInt32 port,
const char *path,
nsACString &realm,
nsACString &credentials);
nsHttpAuthEntry **entry);
// host and port are required
// realm must not be null
nsresult GetCredentialsForDomain(const char *host,
// |host| and |port| are required
// |realm| must not be null
// |entry| is either null or a weak reference
nsresult GetAuthEntryForDomain(const char *host,
PRInt32 port,
const char *realm,
nsACString &credentials);
nsHttpAuthEntry **entry);
// host and port are required
// path can be null
// realm must not be null
// credentials, if null, clears the entry
nsresult SetCredentials(const char *host,
// |host| and |port| are required
// |path| can be null
// |realm| must not be null
// if |credentials|, |user|, |pass|, and |challenge| are each
// null, then the entry is deleted.
nsresult SetAuthEntry(const char *host,
PRInt32 port,
const char *directory,
const char *realm,
const char *credentials);
const char *credentials,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
nsISupports *metadata);
// Expire all existing auth list entries including proxy auths.
// expire all existing auth list entries including proxy auths.
nsresult ClearAll();
public: /* internal */
class nsEntry
{
public:
nsEntry(const char *path,
const char *realm,
const char *creds);
~nsEntry();
const char *Path() { return mPath; }
const char *Realm() { return mRealm; }
const char *Creds() { return mCreds; }
void SetPath(const char *);
void SetCreds(const char *);
private:
char *mPath;
char *mRealm;
char *mCreds;
};
class nsEntryList
{
public:
nsEntryList();
~nsEntryList();
// path can be null, in which case we'll search for an entry
// with a null path.
nsresult GetCredentialsForPath(const char *path,
nsACString &realm,
nsACString &creds);
// realm must not be null
nsresult GetCredentialsForRealm(const char *realm,
nsACString &creds);
// if a matching entry is found, then credentials will be changed.
nsresult SetCredentials(const char *path,
const char *realm,
const char *credentials);
PRUint32 Count() { return (PRUint32) mList.Count(); }
private:
nsVoidArray mList;
};
private:
nsEntryList *LookupEntryList(const char *host, PRInt32 port, nsAFlatCString &key);
nsHttpAuthNode *LookupAuthNode(const char *host, PRInt32 port, nsAFlatCString &key);
// hash table allocation functions
static void* PR_CALLBACK AllocTable(void *, PRSize size);
@ -147,7 +188,7 @@ private:
static PLHashAllocOps gHashAllocOps;
private:
PLHashTable *mDB; // "host:port" --> nsEntryList
PLHashTable *mDB; // "host:port" --> nsHttpAuthNode
};
#endif // nsHttpAuthCache_h__

Просмотреть файл

@ -70,22 +70,23 @@ NS_IMPL_ISUPPORTS1(nsHttpBasicAuth, nsIHttpAuthenticator);
// nsHttpBasicAuth::nsIHttpAuthenticator
//-----------------------------------------------------------------------------
nsresult
NS_IMETHODIMP
nsHttpBasicAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
const char *challenge,
const PRUnichar *username,
const PRUnichar *password,
nsISupports *extra,
char **creds)
{
LOG(("nsHttpBasicAuth::GenerateCredentials [challenge=%s]\n", challenge));
NS_ENSURE_ARG_POINTER(creds);
// we only know how to deal with Basic auth for http.
PRBool isBasicAuth = !PL_strncasecmp(challenge, "basic", 5);
NS_ENSURE_TRUE(isBasicAuth, NS_ERROR_UNEXPECTED);
NS_ENSURE_ARG_POINTER(creds);
// we work with ASCII around here
nsCAutoString userpass;
userpass.AssignWithConversion(username);
@ -111,3 +112,17 @@ nsHttpBasicAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
PR_Free(b64userpass);
return NS_OK;
}
NS_IMETHODIMP
nsHttpBasicAuth::AreCredentialsReusable(PRBool *result)
{
*result = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsHttpBasicAuth::AllocateMetaData(nsISupports **result)
{
*result = nsnull;
return NS_OK;
}

Просмотреть файл

@ -64,7 +64,6 @@ nsHttpChannel::nsHttpChannel()
, mRequestTime(0)
, mIsPending(PR_FALSE)
, mApplyConversion(PR_TRUE)
, mTriedCredentialsFromPrehost(PR_FALSE)
, mFromCacheOnly(PR_FALSE)
, mCachedContentIsValid(PR_FALSE)
, mResponseHeadersModified(PR_FALSE)
@ -192,8 +191,7 @@ nsHttpChannel::Init(nsIURI *uri,
if (NS_FAILED(rv)) return rv;
// check to see if authorization headers should be included
rv = AddAuthorizationHeaders();
if (NS_FAILED(rv)) return rv;
AddAuthorizationHeaders();
// Notify nsIHttpNotify implementations
rv = nsHttpHandler::get()->OnModifyRequest(this);
@ -1313,7 +1311,6 @@ nsHttpChannel::GetCredentials(const char *challenges,
PRBool proxyAuth,
nsAFlatCString &creds)
{
nsAutoString user, pass;
nsresult rv;
LOG(("nsHttpChannel::GetCredentials [this=%x proxyAuth=%d challenges=%s]\n",
@ -1323,12 +1320,11 @@ nsHttpChannel::GetCredentials(const char *challenges,
if (!authCache)
return NS_ERROR_NOT_INITIALIZED;
// proxy auth's never in prehost
if (!mTriedCredentialsFromPrehost && !proxyAuth) {
rv = GetUserPassFromURI(user, pass);
if (NS_FAILED(rv)) return rv;
mTriedCredentialsFromPrehost = PR_TRUE;
}
// proxy auth's never in prehost. only take user:pass from URL if this
// is the first 401 response (mUser and mPass hold previously attempted
// username and password).
if (!proxyAuth && mUser.IsEmpty() && mPass.IsEmpty())
GetUserPassFromURI(getter_Copies(mUser), getter_Copies(mPass));
// figure out which challenge we can handle and which authenticator to use.
nsCAutoString challenge;
@ -1345,20 +1341,23 @@ nsHttpChannel::GetCredentials(const char *challenges,
rv = ParseRealm(challenge.get(), realm);
if (NS_FAILED(rv)) return rv;
const char *triedCreds = nsnull;
const char *host;
nsXPIDLCString path;
nsXPIDLString *user;
nsXPIDLString *pass;
PRInt32 port;
if (proxyAuth) {
host = mConnectionInfo->ProxyHost();
port = mConnectionInfo->ProxyPort();
triedCreds = mRequestHead.PeekHeader(nsHttp::Proxy_Authorization);
user = &mProxyUser;
pass = &mProxyPass;
}
else {
host = mConnectionInfo->Host();
port = mConnectionInfo->Port();
triedCreds = mRequestHead.PeekHeader(nsHttp::Authorization);
user = &mUser;
pass = &mPass;
rv = GetCurrentPath(getter_Copies(path));
if (NS_FAILED(rv)) return rv;
@ -1370,42 +1369,72 @@ nsHttpChannel::GetCredentials(const char *challenges,
// in the cache have changed, in which case we'd want to give them a
// try instead.
//
authCache->GetCredentialsForDomain(host, port, realm.get(), creds);
if (triedCreds && !PL_strcmp(triedCreds, creds.get())) {
// ok.. clear the credentials from the cache
authCache->SetCredentials(host, port, nsnull, realm.get(), nsnull);
creds.Truncate(0);
nsHttpAuthEntry *entry = nsnull;
authCache->GetAuthEntryForDomain(host, port, realm.get(), &entry);
if (entry) {
if (!nsCRT::strcmp(user->get(), entry->User()) &&
!nsCRT::strcmp(pass->get(), entry->Pass())) {
LOG(("clearing bad credentials from the auth cache\n"));
// ok, we've already tried this user:pass combo, so clear the
// corresponding entry from the auth cache.
authCache->SetAuthEntry(host, port, nsnull, realm.get(),
nsnull, nsnull, nsnull, nsnull, nsnull);
entry = nsnull;
user->Adopt(0);
pass->Adopt(0);
}
// otherwise, let's try the credentials we got from the cache
if (!creds.IsEmpty()) {
else {
LOG(("taking user:pass from auth cache\n"));
user->Adopt(nsCRT::strdup(entry->User()));
pass->Adopt(nsCRT::strdup(entry->Pass()));
if (entry->Creds()) {
LOG(("using cached credentials!\n"));
creds.Assign(entry->Creds());
return NS_OK;
}
}
}
if (user.IsEmpty()) {
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(), user, pass);
rv = PromptForUserPass(host, port, proxyAuth, realm.get(),
getter_Copies(*user),
getter_Copies(*pass));
if (NS_FAILED(rv)) return rv;
}
// talk to the authenticator to get credentials for this user/pass combo.
// ask the auth cache for a container for any meta data it might want to
// store in the auth cache.
nsCOMPtr<nsISupports> metadata;
rv = auth->AllocateMetaData(getter_AddRefs(metadata));
if (NS_FAILED(rv)) return rv;
// get credentials for the given user:pass
nsXPIDLCString result;
rv = auth->GenerateCredentials(this,
challenge.get(),
user.get(),
pass.get(),
rv = auth->GenerateCredentials(this, challenge.get(),
user->get(), pass->get(), metadata,
getter_Copies(result));
if (NS_FAILED(rv)) return rv;
creds.Assign(result);
// find out if this authenticator allows reuse of credentials
PRBool reusable;
rv = auth->AreCredentialsReusable(&reusable);
if (NS_FAILED(rv)) return rv;
// store these credentials in the cache. we do this even though we don't
// yet know that these credentials are valid b/c we need to avoid prompting
// the user more than once in case the credentials are valid.
return authCache->SetCredentials(host, port, path, realm.get(), creds.get());
// let's try these credentials
creds = result;
// create a cache entry. we do this even though we don't yet know that
// these credentials are valid b/c we need to avoid prompting the user more
// than once in case the credentials are valid.
//
// if the credentials are not reusable, then we don't bother sticking them
// in the auth cache.
return authCache->SetAuthEntry(host, port, path, realm.get(),
reusable ? creds.get() : nsnull,
user->get(), pass->get(),
challenge.get(), metadata);
}
nsresult
@ -1460,35 +1489,24 @@ nsHttpChannel::GetAuthenticator(const char *scheme, nsIHttpAuthenticator **auth)
return NS_OK;
}
nsresult
nsHttpChannel::GetUserPassFromURI(nsAString &user,
nsAString &pass)
void
nsHttpChannel::GetUserPassFromURI(PRUnichar **user,
PRUnichar **pass)
{
LOG(("nsHttpChannel::GetUserPassFromURI [this=%x]\n", this));
// XXX should be a necko utility function
nsXPIDLCString prehost;
mURI->GetPreHost(getter_Copies(prehost));
if (prehost) {
nsresult rv;
nsXPIDLCString buf;
rv = nsStdUnescape((char*)prehost.get(), getter_Copies(buf));
if (NS_FAILED(rv)) return rv;
char *p = PL_strchr(buf, ':');
if (p) {
// user:pass
*p = 0;
user = NS_ConvertASCIItoUCS2(buf);
pass = NS_ConvertASCIItoUCS2(p+1);
*user = nsnull;
*pass = nsnull;
mURI->GetUsername(getter_Copies(buf));
if (buf) {
*user = ToNewUnicode(buf);
mURI->GetPassword(getter_Copies(buf));
if (buf)
*pass = ToNewUnicode(buf);
}
else {
// user
user = NS_ConvertASCIItoUCS2(buf);
}
}
return NS_OK;
}
nsresult
@ -1524,8 +1542,8 @@ nsHttpChannel::PromptForUserPass(const char *host,
PRInt32 port,
PRBool proxyAuth,
const char *realm,
nsAString &user,
nsAString &pass)
PRUnichar **user,
PRUnichar **pass)
{
LOG(("nsHttpChannel::PromptForUserPass [this=%x realm=%s]\n", this, realm));
@ -1581,64 +1599,83 @@ nsHttpChannel::PromptForUserPass(const char *host,
if (NS_FAILED(rv)) return rv;
// prompt the user...
nsXPIDLString userBuf, passBuf;
PRBool retval = PR_FALSE;
rv = authPrompt->PromptUsernameAndPassword(nsnull,
message.get(),
rv = authPrompt->PromptUsernameAndPassword(nsnull, message.get(),
NS_ConvertASCIItoUCS2(domain).get(),
nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
getter_Copies(userBuf),
getter_Copies(passBuf),
&retval);
user, pass, &retval);
if (NS_FAILED(rv))
return rv;
if (!retval)
return NS_ERROR_ABORT;
user.Assign(userBuf);
pass.Assign(passBuf);
return NS_OK;
}
nsresult
nsHttpChannel::AddAuthorizationHeaders()
void
nsHttpChannel::SetAuthorizationHeader(nsHttpAuthCache *authCache,
nsHttpAtom header,
const char *host,
PRInt32 port,
const char *path,
PRUnichar **user,
PRUnichar **pass)
{
LOG(("nsHttpChannel::AddAuthorizationHeaders [this=%x]\n", this));
nsHttpAuthCache *authCache = nsHttpHandler::get()->AuthCache();
if (authCache) {
nsCAutoString creds;
nsCAutoString realm;
nsCOMPtr<nsIHttpAuthenticator> auth;
nsHttpAuthEntry *entry = nsnull;
nsresult rv;
rv = authCache->GetAuthEntryForPath(host, port, path, &entry);
if (NS_SUCCEEDED(rv)) {
nsXPIDLCString temp;
const char *creds = entry->Creds();
if (!creds) {
nsCAutoString foo;
rv = SelectChallenge(entry->Challenge(), foo, getter_AddRefs(auth));
if (NS_SUCCEEDED(rv)) {
rv = auth->GenerateCredentials(this, entry->Challenge(),
entry->User(), entry->Pass(),
entry->MetaData(),
getter_Copies(temp));
if (NS_SUCCEEDED(rv)) {
creds = temp.get();
*user = nsCRT::strdup(entry->User());
*pass = nsCRT::strdup(entry->Pass());
}
}
}
if (creds) {
LOG((" adding \"%s\" request header\n", header.get()));
mRequestHead.SetHeader(header, creds);
}
}
}
void
nsHttpChannel::AddAuthorizationHeaders()
{
LOG(("nsHttpChannel::AddAuthorizationHeaders? [this=%x]\n", this));
nsHttpAuthCache *authCache = nsHttpHandler::get()->AuthCache();
if (authCache) {
// check if proxy credentials should be sent
const char *proxyHost = mConnectionInfo->ProxyHost();
if (proxyHost) {
rv = authCache->GetCredentialsForPath(proxyHost,
mConnectionInfo->ProxyPort(),
nsnull, realm, creds);
if (NS_SUCCEEDED(rv)) {
LOG(("adding Proxy-Authorization request header\n"));
mRequestHead.SetHeader(nsHttp::Proxy_Authorization, creds.get());
}
}
if (proxyHost)
SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization,
proxyHost, mConnectionInfo->ProxyPort(),
nsnull,
getter_Copies(mProxyUser),
getter_Copies(mProxyPass));
// check if server credentials should be sent
nsXPIDLCString path;
rv = GetCurrentPath(getter_Copies(path));
if (NS_FAILED(rv)) return rv;
rv = authCache->GetCredentialsForPath(mConnectionInfo->Host(),
if (NS_SUCCEEDED(GetCurrentPath(getter_Copies(path))))
SetAuthorizationHeader(authCache, nsHttp::Authorization,
mConnectionInfo->Host(),
mConnectionInfo->Port(),
path.get(),
realm,
creds);
if (NS_SUCCEEDED(rv)) {
LOG(("adding Authorization request header\n"));
mRequestHead.SetHeader(nsHttp::Authorization, creds.get());
getter_Copies(mUser),
getter_Copies(mPass));
}
}
return NS_OK;
}
nsresult
nsHttpChannel::GetCurrentPath(char **path)

Просмотреть файл

@ -46,6 +46,7 @@
class nsHttpTransaction;
class nsHttpResponseHead;
class nsHttpAuthCache;
class nsIHttpAuthenticator;
class nsIProxyInfo;
@ -109,10 +110,11 @@ private:
nsresult GetCredentials(const char *challenges, PRBool proxyAuth, nsAFlatCString &creds);
nsresult SelectChallenge(const char *challenges, nsAFlatCString &challenge, nsIHttpAuthenticator **);
nsresult GetAuthenticator(const char *scheme, nsIHttpAuthenticator **);
nsresult GetUserPassFromURI(nsAString &user, nsAString &pass);
void GetUserPassFromURI(PRUnichar **user, PRUnichar **pass);
nsresult ParseRealm(const char *challenge, nsACString &realm);
nsresult PromptForUserPass(const char *host, PRInt32 port, PRBool proxyAuth, const char *realm, nsAString &user, nsAString &pass);
nsresult AddAuthorizationHeaders();
nsresult PromptForUserPass(const char *host, PRInt32 port, PRBool proxyAuth, const char *realm, PRUnichar **user, PRUnichar **pass);
void SetAuthorizationHeader(nsHttpAuthCache *, nsHttpAtom header, const char *host, PRInt32 port, const char *path, PRUnichar **user, PRUnichar **pass);
void AddAuthorizationHeaders();
nsresult GetCurrentPath(char **);
static void *PR_CALLBACK AsyncRedirect_EventHandlerFunc(PLEvent *);
@ -155,9 +157,14 @@ private:
PRUint32 mPostID;
PRUint32 mRequestTime;
// auth specific data
nsXPIDLString mUser;
nsXPIDLString mPass;
nsXPIDLString mProxyUser;
nsXPIDLString mProxyPass;
PRPackedBool mIsPending;
PRPackedBool mApplyConversion;
PRPackedBool mTriedCredentialsFromPrehost;
PRPackedBool mFromCacheOnly;
PRPackedBool mCachedContentIsValid;
PRPackedBool mResponseHeadersModified;

Просмотреть файл

@ -0,0 +1,556 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Justin Bradford <jab@atdot.org> (original author of nsDigestAuth.cpp)
* An-Cheng Huang <pach@cs.cmu.edu>
* Darin Fisher <darin@netscape.com>
*/
#include <stdlib.h>
#include "nsHttp.h"
#include "nsHttpDigestAuth.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"
//-----------------------------------------------------------------------------
// nsHttpDigestAuth <public>
//-----------------------------------------------------------------------------
nsHttpDigestAuth::nsHttpDigestAuth()
{
NS_INIT_ISUPPORTS();
mVerifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID);
mGotVerifier = (mVerifier != nsnull);
#if defined(PR_LOGGING)
if (mGotVerifier) {
LOG(("nsHttpDigestAuth: Got signature_verifier\n"));
} else {
LOG(("nsHttpDigestAuth: No signature_verifier available\n"));
}
#endif
}
//-----------------------------------------------------------------------------
// nsHttpDigestAuth::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS1(nsHttpDigestAuth, nsIHttpAuthenticator);
//-----------------------------------------------------------------------------
// nsHttpDigestAuth <protected>
//-----------------------------------------------------------------------------
nsresult
nsHttpDigestAuth::MD5Hash(const char *buf, PRUint32 len)
{
if (!mGotVerifier)
return NS_ERROR_NOT_INITIALIZED;
nsresult rv;
HASHContextStr *hid;
unsigned char cbuf[DIGEST_LENGTH], *chash = cbuf;
PRUint32 clen;
rv = mVerifier->HashBegin(nsISignatureVerifier::MD5, &hid);
if (NS_FAILED(rv)) return rv;
rv = mVerifier->HashUpdate(hid, buf, len);
if (NS_FAILED(rv)) return rv;
rv = mVerifier->HashEnd(hid, &chash, &clen, DIGEST_LENGTH);
if (NS_FAILED(rv)) return rv;
nsCRT::memcpy(mHashBuf, chash, DIGEST_LENGTH);
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpDigestAuth::nsIHttpAuthenticator
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpDigestAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
const char *challenge,
const PRUnichar *username,
const PRUnichar *password,
nsISupports *extra,
char **creds)
{
LOG(("nsHttpDigestAuth::GenerateCredentials [challenge=%s]\n", challenge));
NS_ENSURE_ARG_POINTER(creds);
PRBool isDigestAuth = !PL_strncasecmp(challenge, "digest ", 7);
NS_ENSURE_TRUE(isDigestAuth, NS_ERROR_UNEXPECTED);
NS_ConvertUCS2toUTF8 cUser(username), cPass(password);
nsresult rv;
nsCOMPtr<nsIURI> uri;
nsXPIDLCString path;
nsXPIDLCString httpMethod;
rv = httpChannel->GetURI(getter_AddRefs(uri));
if (NS_FAILED(rv)) return rv;
rv = uri->GetPath(getter_Copies(path));
if (NS_FAILED(rv)) return rv;
rv = httpChannel->GetRequestMethod(getter_Copies(httpMethod));
if (NS_FAILED(rv)) return rv;
nsCAutoString realm, domain, nonce, opaque;
PRBool stale;
PRUint16 algorithm, qop;
rv = ParseChallenge(challenge, realm, domain, nonce, opaque,
&stale, &algorithm, &qop);
if (NS_FAILED(rv)) {
LOG(("nsHttpDigestAuth::GenerateCredentials [ParseChallenge failed rv=%x]\n", rv));
return rv;
}
char ha1_digest[EXPANDED_DIGEST_LENGTH+1];
char ha2_digest[EXPANDED_DIGEST_LENGTH+1];
char response_digest[EXPANDED_DIGEST_LENGTH+1];
char upload_data_digest[EXPANDED_DIGEST_LENGTH+1];
if (qop & QOP_AUTH_INT) {
// we do not support auth-int "quality of protection" currently
qop &= ~QOP_AUTH_INT;
NS_WARNING("no support for Digest authentication with data integrity quality of protection");
/* TODO: to support auth-int, we need to get an MD5 digest of
* TODO: the data uploaded with this request.
* TODO: however, i am not sure how to read in the file in without
* TODO: disturbing the channel''s use of it. do i need to copy it
* TODO: somehow?
*/
#if 0
if (http_channel != nsnull)
{
nsIInputStream * upload;
http_channel->GetUploadStream(&upload);
if (upload) {
char * upload_buffer;
int upload_buffer_length = 0;
//TODO: read input stream into buffer
const char * digest = (const char*)
nsNetwerkMD5Digest(upload_buffer, upload_buffer_length);
ExpandToHex(digest, upload_data_digest);
NS_RELEASE(upload);
}
}
#endif
}
if (!(algorithm & ALGO_MD5 || algorithm & ALGO_MD5_SESS)) {
// they asked only for algorithms that we do not support
NS_WARNING("unsupported algorithm requested by Digest authentication");
return NS_ERROR_NOT_IMPLEMENTED;
}
//
// the following are for increasing security. see RFC 2617 for more
// information.
//
// nonce_count allows the server to keep track of auth challenges (to help
// prevent spoofing). we increase this count every time.
//
char nonce_count[NONCE_COUNT_LENGTH+1] = "00000001"; // in hex
nsCOMPtr<nsISupportsPRUint32> v(do_QueryInterface(extra));
if (v) {
PRUint32 nc;
v->GetData(&nc);
PR_snprintf(nonce_count, sizeof(nonce_count), "%08x", ++nc);
v->SetData(nc);
}
LOG((" nonce_count=%s\n", nonce_count));
//
// this lets the client verify the server response (via a server
// returned Authentication-Info header). also used for session info.
//
nsCAutoString cnonce;
static const char hexChar[] = "0123456789abcdef";
for (int i=0; i<16; ++i) {
cnonce.Append(hexChar[(int)(15.0 * rand()/(RAND_MAX + 1.0))]);
}
LOG((" cnonce=%s\n", cnonce.get()));
//
// calculate credentials
//
rv = CalculateHA1(cUser, cPass, realm, algorithm, nonce, cnonce, ha1_digest);
if (NS_FAILED(rv)) return rv;
rv = CalculateHA2(httpMethod, path, qop, upload_data_digest, ha2_digest);
if (NS_FAILED(rv)) return rv;
rv = CalculateResponse(ha1_digest, ha2_digest, nonce, qop, nonce_count,
cnonce, response_digest);
if (NS_FAILED(rv)) return rv;
nsCAutoString authString("Digest ");
authString += "username=\"";
authString += cUser;
authString += NS_LITERAL_CSTRING("\", realm=\"");
authString += realm;
authString += NS_LITERAL_CSTRING("\", nonce=\"");
authString += nonce;
authString += NS_LITERAL_CSTRING("\", uri=\"");
authString += path;
if (algorithm & ALGO_SPECIFIED) {
authString += "\", algorithm=";
if (algorithm & ALGO_MD5_SESS)
authString += "MD5-sess";
else
authString += "MD5";
} else {
authString += "\"";
}
authString += ", response=\"";
authString += response_digest;
if (!opaque.IsEmpty()) {
authString += "\", opaque=\"";
authString += opaque;
}
if (qop) {
authString += "\", qop=";
if (qop & QOP_AUTH_INT)
authString += "auth-int";
else
authString += "auth";
authString += ", nc=";
authString += nonce_count;
authString += ", cnonce=\"";
authString += cnonce;
}
authString += "\"";
*creds = ToNewCString(authString);
return NS_OK;
}
NS_IMETHODIMP
nsHttpDigestAuth::AreCredentialsReusable(PRBool *result)
{
*result = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsHttpDigestAuth::AllocateMetaData(nsISupports **result)
{
nsresult rv;
nsCOMPtr<nsISupportsPRUint32> v(
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv));
NS_ADDREF(*result = v);
return rv;
}
nsresult
nsHttpDigestAuth::CalculateResponse(const char * ha1_digest,
const char * ha2_digest,
const nsAFlatCString & nonce,
PRUint16 qop,
const char * nonce_count,
const nsAFlatCString & cnonce,
char * result)
{
PRUint32 len = 2*EXPANDED_DIGEST_LENGTH + nonce.Length() + 2;
if (qop & QOP_AUTH || qop & QOP_AUTH_INT) {
len += cnonce.Length() + NONCE_COUNT_LENGTH + 3;
if (qop & QOP_AUTH_INT)
len += 8; // length of "auth-int"
else
len += 4; // length of "auth"
}
nsCAutoString contents;
contents.SetCapacity(len);
contents.Assign(ha1_digest, EXPANDED_DIGEST_LENGTH);
contents.Append(':');
contents.Append(nonce);
contents.Append(':');
if (qop & QOP_AUTH || qop & QOP_AUTH_INT) {
contents.Append(nonce_count, NONCE_COUNT_LENGTH);
contents.Append(':');
contents.Append(cnonce);
contents.Append(':');
if (qop & QOP_AUTH_INT)
contents.Append(NS_LITERAL_CSTRING("auth-int:"));
else
contents.Append(NS_LITERAL_CSTRING("auth:"));
}
contents.Append(ha2_digest, EXPANDED_DIGEST_LENGTH);
nsresult rv = MD5Hash(contents.get(), contents.Length());
if (NS_FAILED(rv)) return rv;
return ExpandToHex(mHashBuf, result);
}
nsresult
nsHttpDigestAuth::ExpandToHex(const char * digest, char * result)
{
PRInt16 index, value;
for (index = 0; index < DIGEST_LENGTH; index++) {
value = (digest[index] >> 4) & 0xf;
if (value < 10)
result[index*2] = value + '0';
else
result[index*2] = value - 10 + 'a';
value = digest[index] & 0xf;
if (value < 10)
result[(index*2)+1] = value + '0';
else
result[(index*2)+1] = value - 10 + 'a';
}
result[EXPANDED_DIGEST_LENGTH] = 0;
return NS_OK;
}
nsresult
nsHttpDigestAuth::CalculateHA1(const nsAFlatCString & username,
const nsAFlatCString & password,
const nsAFlatCString & realm,
PRUint16 algorithm,
const nsAFlatCString & nonce,
const nsAFlatCString & cnonce,
char * result)
{
PRInt16 len = username.Length() + password.Length() + realm.Length() + 2;
if (algorithm & ALGO_MD5_SESS) {
PRInt16 exlen = EXPANDED_DIGEST_LENGTH + nonce.Length() + cnonce.Length() + 2;
if (exlen > len)
len = exlen;
}
nsCAutoString contents;
contents.SetCapacity(len + 1);
contents.Assign(username);
contents.Append(':');
contents.Append(realm);
contents.Append(':');
contents.Append(password);
nsresult rv;
rv = MD5Hash(contents.get(), contents.Length());
if (NS_FAILED(rv))
return rv;
if (algorithm & ALGO_MD5_SESS) {
char part1[EXPANDED_DIGEST_LENGTH+1];
ExpandToHex(mHashBuf, part1);
contents.Assign(part1, EXPANDED_DIGEST_LENGTH);
contents.Append(':');
contents.Append(nonce);
contents.Append(':');
contents.Append(cnonce);
rv = MD5Hash(contents.get(), contents.Length());
if (NS_FAILED(rv))
return rv;
}
return ExpandToHex(mHashBuf, result);
}
nsresult
nsHttpDigestAuth::CalculateHA2(const nsAFlatCString & method,
const nsAFlatCString & path,
PRUint16 qop,
const char * bodyDigest,
char * result)
{
PRInt16 methodLen = method.Length();
PRInt16 pathLen = path.Length();
PRInt16 len = methodLen + pathLen + 1;
if (qop & QOP_AUTH_INT) {
len += EXPANDED_DIGEST_LENGTH + 1;
}
nsCAutoString contents;
contents.SetCapacity(len);
contents.Assign(method);
contents.Append(':');
contents.Append(path);
if (qop & QOP_AUTH_INT) {
contents.Append(':');
contents.Append(bodyDigest, EXPANDED_DIGEST_LENGTH);
}
nsresult rv = MD5Hash(contents.get(), contents.Length());
if (NS_FAILED(rv)) return rv;
return ExpandToHex(mHashBuf, result);
}
nsresult
nsHttpDigestAuth::ParseChallenge(const char * challenge,
nsACString & realm,
nsACString & domain,
nsACString & nonce,
nsACString & opaque,
PRBool * stale,
PRUint16 * algorithm,
PRUint16 * qop)
{
const char *p = challenge + 7; // first 7 characters are "Digest "
*stale = PR_FALSE;
*algorithm = ALGO_MD5; // default is MD5
*qop = 0;
for (;;) {
while (*p && (*p == ',' || nsCRT::IsAsciiSpace(*p)))
++p;
if (!*p)
break;
// name
PRInt16 nameStart = (p - challenge);
while (*p && !nsCRT::IsAsciiSpace(*p) && *p != '=')
++p;
if (!*p)
return NS_ERROR_INVALID_ARG;
PRInt16 nameLength = (p - challenge) - nameStart;
while (*p && (nsCRT::IsAsciiSpace(*p) || *p == '='))
++p;
if (!*p)
return NS_ERROR_INVALID_ARG;
PRBool quoted = PR_FALSE;
if (*p == '"') {
++p;
quoted = PR_TRUE;
}
// value
PRInt16 valueStart = (p - challenge);
PRInt16 valueLength = 0;
if (quoted) {
while (*p && *p != '"')
++p;
if (*p != '"')
return NS_ERROR_INVALID_ARG;
valueLength = (p - challenge) - valueStart;
++p;
} else {
while (*p && !nsCRT::IsAsciiSpace(*p) && *p != ',')
++p;
valueLength = (p - challenge) - valueStart;
}
// extract information
if (nameLength == 5 &&
nsCRT::strncasecmp(challenge+nameStart, "realm", 5) == 0)
{
realm.Assign(challenge+valueStart, valueLength);
}
else if (nameLength == 6 &&
nsCRT::strncasecmp(challenge+nameStart, "domain", 6) == 0)
{
domain.Assign(challenge+valueStart, valueLength);
}
else if (nameLength == 5 &&
nsCRT::strncasecmp(challenge+nameStart, "nonce", 5) == 0)
{
nonce.Assign(challenge+valueStart, valueLength);
}
else if (nameLength == 6 &&
nsCRT::strncasecmp(challenge+nameStart, "opaque", 6) == 0)
{
opaque.Assign(challenge+valueStart, valueLength);
}
else if (nameLength == 5 &&
nsCRT::strncasecmp(challenge+nameStart, "stale", 5) == 0)
{
if (nsCRT::strncasecmp(challenge+valueStart, "true", 4) == 0)
*stale = PR_TRUE;
else
*stale = PR_FALSE;
}
else if (nameLength == 9 &&
nsCRT::strncasecmp(challenge+nameStart, "algorithm", 9) == 0)
{
// we want to clear the default, so we use = not |= here
*algorithm = ALGO_SPECIFIED;
if (valueLength == 3 &&
nsCRT::strncasecmp(challenge+valueStart, "MD5", 3) == 0)
*algorithm |= ALGO_MD5;
else if (valueLength == 8 &&
nsCRT::strncasecmp(challenge+valueStart, "MD5-sess", 8) == 0)
*algorithm |= ALGO_MD5_SESS;
}
else if (nameLength == 3 &&
nsCRT::strncasecmp(challenge+nameStart, "qop", 3) == 0)
{
PRInt16 ipos = valueStart;
while (ipos < valueStart+valueLength) {
while (ipos < valueStart+valueLength &&
(nsCRT::IsAsciiSpace(challenge[ipos]) ||
challenge[ipos] == ','))
ipos++;
PRInt16 algostart = ipos;
while (ipos < valueStart+valueLength &&
!nsCRT::IsAsciiSpace(challenge[ipos]) &&
challenge[ipos] != ',')
ipos++;
if ((ipos - algostart) == 4 &&
nsCRT::strncasecmp(challenge+algostart, "auth", 4) == 0)
*qop |= QOP_AUTH;
else if ((ipos - algostart) == 8 &&
nsCRT::strncasecmp(challenge+algostart, "auth-int", 8) == 0)
*qop |= QOP_AUTH_INT;
}
}
}
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,100 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Justin Bradford <jab@atdot.org> (original author of nsDigestAuth.h)
* An-Cheng Huang <pach@cs.cmu.edu>
* Darin Fisher <darin@netscape.com>
*/
#ifndef nsDigestAuth_h__
#define nsDigestAuth_h__
#include "nsIHttpAuthenticator.h"
#include "nsISignatureVerifier.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#define ALGO_SPECIFIED 0x01
#define ALGO_MD5 0x02
#define ALGO_MD5_SESS 0x04
#define QOP_AUTH 0x01
#define QOP_AUTH_INT 0x02
#define DIGEST_LENGTH 16
#define EXPANDED_DIGEST_LENGTH 32
#define NONCE_COUNT_LENGTH 8
//-----------------------------------------------------------------------------
// nsHttpDigestAuth
//-----------------------------------------------------------------------------
class nsHttpDigestAuth : public nsIHttpAuthenticator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIHTTPAUTHENTICATOR
nsHttpDigestAuth();
virtual ~nsHttpDigestAuth() {}
protected:
nsresult ExpandToHex(const char * digest, char * result);
nsresult CalculateResponse(const char * ha1_digest,
const char * ha2_digest,
const nsAFlatCString & nonce,
PRUint16 qop,
const char * nonce_count,
const nsAFlatCString & cnonce,
char * result);
nsresult CalculateHA1(const nsAFlatCString & username,
const nsAFlatCString & password,
const nsAFlatCString & realm,
PRUint16 algorithm,
const nsAFlatCString & nonce,
const nsAFlatCString & cnonce,
char * result);
nsresult CalculateHA2(const nsAFlatCString & http_method,
const nsAFlatCString & http_uri_path,
PRUint16 qop,
const char * body_digest,
char * result);
nsresult ParseChallenge(const char * challenge,
nsACString & realm,
nsACString & domain,
nsACString & nonce,
nsACString & opaque,
PRBool * stale,
PRUint16 * algorithm,
PRUint16 * qop);
// result is in mHashBuf
nsresult MD5Hash(const char *buf, PRUint32 len);
protected:
nsCOMPtr<nsISignatureVerifier> mVerifier;
char mHashBuf[DIGEST_LENGTH];
PRBool mGotVerifier;
};
#endif // nsHttpDigestAuth_h__