LCOV - code coverage report
Current view: directory - netwerk/protocol/http - nsHttpDigestAuth.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 323 264 81.7 %
Date: 2012-06-02 Functions: 16 16 100.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications Corporation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Justin Bradford <jab@atdot.org> (original author of nsDigestAuth.cpp)
      25                 :  *   An-Cheng Huang <pach@cs.cmu.edu>
      26                 :  *   Darin Fisher <darin@netscape.com>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      30                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include <stdlib.h>
      43                 : #include "nsHttp.h"
      44                 : #include "nsHttpDigestAuth.h"
      45                 : #include "nsIHttpAuthenticableChannel.h"
      46                 : #include "nsIServiceManager.h"
      47                 : #include "nsXPCOM.h"
      48                 : #include "nsISupportsPrimitives.h"
      49                 : #include "nsIURI.h"
      50                 : #include "nsString.h"
      51                 : #include "nsReadableUtils.h"
      52                 : #include "nsEscape.h"
      53                 : #include "nsNetCID.h"
      54                 : #include "plbase64.h"
      55                 : #include "plstr.h"
      56                 : #include "prprf.h"
      57                 : #include "prmem.h"
      58                 : #include "nsCRT.h"
      59                 : 
      60                 : //-----------------------------------------------------------------------------
      61                 : // nsHttpDigestAuth <public>
      62                 : //-----------------------------------------------------------------------------
      63                 : 
      64               1 : nsHttpDigestAuth::nsHttpDigestAuth()
      65               1 : {}
      66                 : 
      67               1 : nsHttpDigestAuth::~nsHttpDigestAuth()
      68               1 : {}
      69                 : 
      70                 : //-----------------------------------------------------------------------------
      71                 : // nsHttpDigestAuth::nsISupports
      72                 : //-----------------------------------------------------------------------------
      73                 : 
      74              29 : NS_IMPL_ISUPPORTS1(nsHttpDigestAuth, nsIHttpAuthenticator)
      75                 : 
      76                 : //-----------------------------------------------------------------------------
      77                 : // nsHttpDigestAuth <protected>
      78                 : //-----------------------------------------------------------------------------
      79                 : 
      80                 : nsresult
      81               6 : nsHttpDigestAuth::MD5Hash(const char *buf, PRUint32 len)
      82                 : {
      83                 :   nsresult rv;
      84                 : 
      85                 :   // Cache a reference to the nsICryptoHash instance since we'll be calling
      86                 :   // this function frequently.
      87               6 :   if (!mVerifier) {
      88               1 :     mVerifier = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
      89               1 :     if (NS_FAILED(rv)) {
      90               0 :       LOG(("nsHttpDigestAuth: no crypto hash!\n"));
      91               0 :       return rv;
      92                 :     }
      93                 :   }
      94                 : 
      95               6 :   rv = mVerifier->Init(nsICryptoHash::MD5);
      96               6 :   if (NS_FAILED(rv)) return rv;
      97                 : 
      98               6 :   rv = mVerifier->Update((unsigned char*)buf, len);
      99               6 :   if (NS_FAILED(rv)) return rv;
     100                 : 
     101              12 :   nsCAutoString hashString;
     102               6 :   rv = mVerifier->Finish(false, hashString);
     103               6 :   if (NS_FAILED(rv)) return rv;
     104                 : 
     105               6 :   NS_ENSURE_STATE(hashString.Length() == sizeof(mHashBuf));
     106               6 :   memcpy(mHashBuf, hashString.get(), hashString.Length());
     107                 : 
     108               6 :   return rv;
     109                 : }
     110                 : 
     111                 : nsresult
     112               2 : nsHttpDigestAuth::GetMethodAndPath(nsIHttpAuthenticableChannel *authChannel,
     113                 :                                    bool                         isProxyAuth,
     114                 :                                    nsCString                   &httpMethod,
     115                 :                                    nsCString                   &path)
     116                 : {
     117                 :   nsresult rv;
     118               4 :   nsCOMPtr<nsIURI> uri;
     119               2 :   rv = authChannel->GetURI(getter_AddRefs(uri));
     120               2 :   if (NS_SUCCEEDED(rv)) {
     121                 :     bool proxyMethodIsConnect;
     122               2 :     rv = authChannel->GetProxyMethodIsConnect(&proxyMethodIsConnect);
     123               2 :     if (NS_SUCCEEDED(rv)) {
     124               2 :       if (proxyMethodIsConnect && isProxyAuth) {
     125               0 :         httpMethod.AssignLiteral("CONNECT");
     126                 :         //
     127                 :         // generate hostname:port string. (unfortunately uri->GetHostPort
     128                 :         // leaves out the port if it matches the default value, so we can't
     129                 :         // just call it.)
     130                 :         //
     131                 :         PRInt32 port;
     132               0 :         rv  = uri->GetAsciiHost(path);
     133               0 :         rv |= uri->GetPort(&port);
     134               0 :         if (NS_SUCCEEDED(rv)) {
     135               0 :           path.Append(':');
     136               0 :           path.AppendInt(port < 0 ? NS_HTTPS_DEFAULT_PORT : port);
     137               0 :         }
     138                 :       }
     139                 :       else { 
     140               2 :         rv  = authChannel->GetRequestMethod(httpMethod);
     141               2 :         rv |= uri->GetPath(path);
     142               2 :         if (NS_SUCCEEDED(rv)) {
     143                 :           //
     144                 :           // strip any fragment identifier from the URL path.
     145                 :           //
     146               2 :           PRInt32 ref = path.RFindChar('#');
     147               2 :           if (ref != kNotFound)
     148               0 :             path.Truncate(ref);
     149                 :           //
     150                 :           // make sure we escape any UTF-8 characters in the URI path.  the
     151                 :           // digest auth uri attribute needs to match the request-URI.
     152                 :           //
     153                 :           // XXX we should really ask the HTTP channel for this string
     154                 :           // instead of regenerating it here.
     155                 :           //
     156               4 :           nsCAutoString buf;
     157               2 :           path = NS_EscapeURL(path, esc_OnlyNonASCII, buf);
     158                 :         }
     159                 :       }
     160                 :     }
     161                 :   }
     162               2 :   return rv;
     163                 : }
     164                 : 
     165                 : //-----------------------------------------------------------------------------
     166                 : // nsHttpDigestAuth::nsIHttpAuthenticator
     167                 : //-----------------------------------------------------------------------------
     168                 : 
     169                 : NS_IMETHODIMP
     170               3 : nsHttpDigestAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel,
     171                 :                                     const char *challenge,
     172                 :                                     bool isProxyAuth,
     173                 :                                     nsISupports **sessionState,
     174                 :                                     nsISupports **continuationState,
     175                 :                                     bool *result)
     176                 : {
     177               6 :   nsCAutoString realm, domain, nonce, opaque;
     178                 :   bool stale;
     179                 :   PRUint16 algorithm, qop;
     180                 : 
     181                 :   nsresult rv = ParseChallenge(challenge, realm, domain, nonce, opaque,
     182               3 :                                &stale, &algorithm, &qop);
     183               3 :   if (NS_FAILED(rv)) return rv;
     184                 : 
     185                 :   // if the challenge has the "stale" flag set, then the user identity is not
     186                 :   // necessarily invalid.  by returning FALSE here we can suppress username
     187                 :   // and password prompting that usually accompanies a 401/407 challenge.
     188               3 :   *result = !stale;
     189                 : 
     190                 :   // clear any existing nonce_count since we have a new challenge.
     191               3 :   NS_IF_RELEASE(*sessionState);
     192               3 :   return NS_OK;
     193                 : }
     194                 : 
     195                 : NS_IMETHODIMP
     196               2 : nsHttpDigestAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
     197                 :                                       const char *challenge,
     198                 :                                       bool isProxyAuth,
     199                 :                                       const PRUnichar *userdomain,
     200                 :                                       const PRUnichar *username,
     201                 :                                       const PRUnichar *password,
     202                 :                                       nsISupports **sessionState,
     203                 :                                       nsISupports **continuationState,
     204                 :                                       PRUint32 *aFlags,
     205                 :                                       char **creds)
     206                 : 
     207                 : {
     208               2 :   LOG(("nsHttpDigestAuth::GenerateCredentials [challenge=%s]\n", challenge));
     209                 : 
     210               2 :   NS_ENSURE_ARG_POINTER(creds);
     211                 : 
     212               2 :   *aFlags = 0;
     213                 : 
     214               2 :   bool isDigestAuth = !PL_strncasecmp(challenge, "digest ", 7);
     215               2 :   NS_ENSURE_TRUE(isDigestAuth, NS_ERROR_UNEXPECTED);
     216                 : 
     217                 :   // IIS implementation requires extra quotes
     218               2 :   bool requireExtraQuotes = false;
     219                 :   {
     220               4 :     nsCAutoString serverVal;
     221               2 :     authChannel->GetServerResponseHeader(serverVal);
     222               2 :     if (!serverVal.IsEmpty()) {
     223               2 :       requireExtraQuotes = !PL_strncasecmp(serverVal.get(), "Microsoft-IIS", 13);
     224                 :     }
     225                 :   }
     226                 : 
     227                 :   nsresult rv;
     228               4 :   nsCAutoString httpMethod;
     229               4 :   nsCAutoString path;
     230               2 :   rv = GetMethodAndPath(authChannel, isProxyAuth, httpMethod, path);
     231               2 :   if (NS_FAILED(rv)) return rv;
     232                 : 
     233               4 :   nsCAutoString realm, domain, nonce, opaque;
     234                 :   bool stale;
     235                 :   PRUint16 algorithm, qop;
     236                 : 
     237                 :   rv = ParseChallenge(challenge, realm, domain, nonce, opaque,
     238               2 :                       &stale, &algorithm, &qop);
     239               2 :   if (NS_FAILED(rv)) {
     240               0 :     LOG(("nsHttpDigestAuth::GenerateCredentials [ParseChallenge failed rv=%x]\n", rv));
     241               0 :     return rv;
     242                 :   }
     243                 : 
     244                 :   char ha1_digest[EXPANDED_DIGEST_LENGTH+1];
     245                 :   char ha2_digest[EXPANDED_DIGEST_LENGTH+1];
     246                 :   char response_digest[EXPANDED_DIGEST_LENGTH+1];
     247                 :   char upload_data_digest[EXPANDED_DIGEST_LENGTH+1];
     248                 : 
     249               2 :   if (qop & QOP_AUTH_INT) {
     250                 :     // we do not support auth-int "quality of protection" currently
     251               0 :     qop &= ~QOP_AUTH_INT;
     252                 : 
     253               0 :     NS_WARNING("no support for Digest authentication with data integrity quality of protection");
     254                 : 
     255                 :     /* TODO: to support auth-int, we need to get an MD5 digest of
     256                 :      * TODO: the data uploaded with this request.
     257                 :      * TODO: however, i am not sure how to read in the file in without
     258                 :      * TODO: disturbing the channel''s use of it. do i need to copy it 
     259                 :      * TODO: somehow?
     260                 :      */
     261                 : #if 0
     262                 :     if (http_channel != nsnull)
     263                 :     {
     264                 :       nsIInputStream * upload;
     265                 :       nsCOMPtr<nsIUploadChannel> uc = do_QueryInterface(http_channel);
     266                 :       NS_ENSURE_TRUE(uc, NS_ERROR_UNEXPECTED);
     267                 :       uc->GetUploadStream(&upload);
     268                 :       if (upload) {
     269                 :         char * upload_buffer;
     270                 :         int upload_buffer_length = 0;
     271                 :         //TODO: read input stream into buffer
     272                 :         const char * digest = (const char*)
     273                 :         nsNetwerkMD5Digest(upload_buffer, upload_buffer_length);
     274                 :         ExpandToHex(digest, upload_data_digest);
     275                 :         NS_RELEASE(upload);
     276                 :       }
     277                 :     }
     278                 : #endif
     279                 :   }
     280                 : 
     281               2 :   if (!(algorithm & ALGO_MD5 || algorithm & ALGO_MD5_SESS)) {
     282                 :     // they asked only for algorithms that we do not support
     283               0 :     NS_WARNING("unsupported algorithm requested by Digest authentication");
     284               0 :     return NS_ERROR_NOT_IMPLEMENTED;
     285                 :   }
     286                 : 
     287                 :   //
     288                 :   // the following are for increasing security.  see RFC 2617 for more
     289                 :   // information.
     290                 :   // 
     291                 :   // nonce_count allows the server to keep track of auth challenges (to help
     292                 :   // prevent spoofing). we increase this count every time.
     293                 :   //
     294               2 :   char nonce_count[NONCE_COUNT_LENGTH+1] = "00000001"; // in hex
     295               2 :   if (*sessionState) {
     296               0 :     nsCOMPtr<nsISupportsPRUint32> v(do_QueryInterface(*sessionState));
     297               0 :     if (v) {
     298                 :       PRUint32 nc;
     299               0 :       v->GetData(&nc);
     300               0 :       PR_snprintf(nonce_count, sizeof(nonce_count), "%08x", ++nc);
     301               0 :       v->SetData(nc);
     302                 :     }
     303                 :   }
     304                 :   else {
     305                 :     nsCOMPtr<nsISupportsPRUint32> v(
     306               4 :             do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID));
     307               2 :     if (v) {        
     308               2 :       v->SetData(1);
     309               2 :       NS_ADDREF(*sessionState = v);
     310                 :     }
     311                 :   }
     312               2 :   LOG(("   nonce_count=%s\n", nonce_count));
     313                 : 
     314                 :   //
     315                 :   // this lets the client verify the server response (via a server
     316                 :   // returned Authentication-Info header). also used for session info.
     317                 :   //
     318               4 :   nsCAutoString cnonce;
     319                 :   static const char hexChar[] = "0123456789abcdef"; 
     320              34 :   for (int i=0; i<16; ++i) {
     321              32 :     cnonce.Append(hexChar[(int)(15.0 * rand()/(RAND_MAX + 1.0))]);
     322                 :   }
     323               2 :   LOG(("   cnonce=%s\n", cnonce.get()));
     324                 : 
     325                 :   //
     326                 :   // calculate credentials
     327                 :   //
     328                 : 
     329               4 :   NS_ConvertUTF16toUTF8 cUser(username), cPass(password);
     330               2 :   rv = CalculateHA1(cUser, cPass, realm, algorithm, nonce, cnonce, ha1_digest);
     331               2 :   if (NS_FAILED(rv)) return rv;
     332                 : 
     333               2 :   rv = CalculateHA2(httpMethod, path, qop, upload_data_digest, ha2_digest);
     334               2 :   if (NS_FAILED(rv)) return rv;
     335                 : 
     336                 :   rv = CalculateResponse(ha1_digest, ha2_digest, nonce, qop, nonce_count,
     337               2 :                          cnonce, response_digest);
     338               2 :   if (NS_FAILED(rv)) return rv;
     339                 : 
     340                 :   //
     341                 :   // Values that need to match the quoted-string production from RFC 2616:
     342                 :   //
     343                 :   //    username
     344                 :   //    realm
     345                 :   //    nonce
     346                 :   //    opaque
     347                 :   //    cnonce
     348                 :   //
     349                 : 
     350               4 :   nsCAutoString authString;
     351                 : 
     352               2 :   authString.AssignLiteral("Digest username=");
     353               2 :   rv = AppendQuotedString(cUser, authString);
     354               2 :   NS_ENSURE_SUCCESS(rv, rv);
     355                 : 
     356               1 :   authString.AppendLiteral(", realm=");
     357               1 :   rv = AppendQuotedString(realm, authString);
     358               1 :   NS_ENSURE_SUCCESS(rv, rv);
     359                 : 
     360               1 :   authString.AppendLiteral(", nonce=");
     361               1 :   rv = AppendQuotedString(nonce, authString);
     362               1 :   NS_ENSURE_SUCCESS(rv, rv);
     363                 : 
     364               1 :   authString.AppendLiteral(", uri=\"");
     365               1 :   authString += path;
     366               1 :   if (algorithm & ALGO_SPECIFIED) {
     367               1 :     authString.AppendLiteral("\", algorithm=");
     368               1 :     if (algorithm & ALGO_MD5_SESS)
     369               0 :       authString.AppendLiteral("MD5-sess");
     370                 :     else
     371               1 :       authString.AppendLiteral("MD5");
     372                 :   } else {
     373               0 :     authString += '\"';
     374                 :   }
     375               1 :   authString.AppendLiteral(", response=\"");
     376               1 :   authString += response_digest;
     377               1 :   authString += '\"';
     378                 : 
     379               1 :   if (!opaque.IsEmpty()) {
     380               1 :     authString.AppendLiteral(", opaque=");
     381               1 :     rv = AppendQuotedString(opaque, authString);
     382               1 :     NS_ENSURE_SUCCESS(rv, rv);
     383                 :   }
     384                 : 
     385               1 :   if (qop) {
     386               1 :     authString.AppendLiteral(", qop=");
     387               1 :     if (requireExtraQuotes)
     388               0 :       authString += '\"';
     389               1 :     authString.AppendLiteral("auth");
     390               1 :     if (qop & QOP_AUTH_INT)
     391               0 :       authString.AppendLiteral("-int");
     392               1 :     if (requireExtraQuotes)
     393               0 :       authString += '\"';
     394               1 :     authString.AppendLiteral(", nc=");
     395               1 :     authString += nonce_count;
     396                 : 
     397               1 :     authString.AppendLiteral(", cnonce=");
     398               1 :     rv = AppendQuotedString(cnonce, authString);
     399               1 :     NS_ENSURE_SUCCESS(rv, rv);
     400                 :   }
     401                 : 
     402                 : 
     403               1 :   *creds = ToNewCString(authString);
     404               1 :   return NS_OK;
     405                 : }
     406                 : 
     407                 : NS_IMETHODIMP
     408               5 : nsHttpDigestAuth::GetAuthFlags(PRUint32 *flags)
     409                 : {
     410               5 :   *flags = REQUEST_BASED | REUSABLE_CHALLENGE | IDENTITY_ENCRYPTED;
     411                 :   //
     412                 :   // NOTE: digest auth credentials must be uniquely computed for each request,
     413                 :   //       so we do not set the REUSABLE_CREDENTIALS flag.
     414                 :   //
     415               5 :   return NS_OK;
     416                 : }
     417                 : 
     418                 : nsresult
     419               2 : nsHttpDigestAuth::CalculateResponse(const char * ha1_digest,
     420                 :                                     const char * ha2_digest,
     421                 :                                     const nsAFlatCString & nonce,
     422                 :                                     PRUint16 qop,
     423                 :                                     const char * nonce_count,
     424                 :                                     const nsAFlatCString & cnonce,
     425                 :                                     char * result)
     426                 : {
     427               2 :   PRUint32 len = 2*EXPANDED_DIGEST_LENGTH + nonce.Length() + 2;
     428                 : 
     429               2 :   if (qop & QOP_AUTH || qop & QOP_AUTH_INT) {
     430               2 :     len += cnonce.Length() + NONCE_COUNT_LENGTH + 3;
     431               2 :     if (qop & QOP_AUTH_INT)
     432               0 :       len += 8; // length of "auth-int"
     433                 :     else
     434               2 :       len += 4; // length of "auth"
     435                 :   }
     436                 : 
     437               4 :   nsCAutoString contents;
     438               2 :   contents.SetCapacity(len);
     439                 : 
     440               2 :   contents.Assign(ha1_digest, EXPANDED_DIGEST_LENGTH);
     441               2 :   contents.Append(':');
     442               2 :   contents.Append(nonce);
     443               2 :   contents.Append(':');
     444                 : 
     445               2 :   if (qop & QOP_AUTH || qop & QOP_AUTH_INT) {
     446               2 :     contents.Append(nonce_count, NONCE_COUNT_LENGTH);
     447               2 :     contents.Append(':');
     448               2 :     contents.Append(cnonce);
     449               2 :     contents.Append(':');
     450               2 :     if (qop & QOP_AUTH_INT)
     451               0 :       contents.AppendLiteral("auth-int:");
     452                 :     else
     453               2 :       contents.AppendLiteral("auth:");
     454                 :   }
     455                 : 
     456               2 :   contents.Append(ha2_digest, EXPANDED_DIGEST_LENGTH);
     457                 : 
     458               2 :   nsresult rv = MD5Hash(contents.get(), contents.Length());
     459               2 :   if (NS_SUCCEEDED(rv))
     460               2 :     rv = ExpandToHex(mHashBuf, result);
     461               2 :   return rv;
     462                 : }
     463                 : 
     464                 : nsresult
     465               6 : nsHttpDigestAuth::ExpandToHex(const char * digest, char * result)
     466                 : {
     467                 :   PRInt16 index, value;
     468                 : 
     469             102 :   for (index = 0; index < DIGEST_LENGTH; index++) {
     470              96 :     value = (digest[index] >> 4) & 0xf;
     471              96 :     if (value < 10)
     472              46 :       result[index*2] = value + '0';
     473                 :     else
     474              50 :       result[index*2] = value - 10 + 'a';
     475                 : 
     476              96 :     value = digest[index] & 0xf;
     477              96 :     if (value < 10)
     478              50 :       result[(index*2)+1] = value + '0';
     479                 :     else
     480              46 :       result[(index*2)+1] = value - 10 + 'a';
     481                 :   }
     482                 : 
     483               6 :   result[EXPANDED_DIGEST_LENGTH] = 0;
     484               6 :   return NS_OK;
     485                 : }
     486                 : 
     487                 : nsresult
     488               2 : nsHttpDigestAuth::CalculateHA1(const nsAFlatCString & username,
     489                 :                                const nsAFlatCString & password,
     490                 :                                const nsAFlatCString & realm,
     491                 :                                PRUint16 algorithm,
     492                 :                                const nsAFlatCString & nonce,
     493                 :                                const nsAFlatCString & cnonce,
     494                 :                                char * result)
     495                 : {
     496               2 :   PRInt16 len = username.Length() + password.Length() + realm.Length() + 2;
     497               2 :   if (algorithm & ALGO_MD5_SESS) {
     498               0 :     PRInt16 exlen = EXPANDED_DIGEST_LENGTH + nonce.Length() + cnonce.Length() + 2;
     499               0 :     if (exlen > len)
     500               0 :         len = exlen;
     501                 :   }
     502                 : 
     503               4 :   nsCAutoString contents;
     504               2 :   contents.SetCapacity(len + 1);
     505                 : 
     506               2 :   contents.Assign(username);
     507               2 :   contents.Append(':');
     508               2 :   contents.Append(realm);
     509               2 :   contents.Append(':');
     510               2 :   contents.Append(password);
     511                 : 
     512                 :   nsresult rv;
     513               2 :   rv = MD5Hash(contents.get(), contents.Length());
     514               2 :   if (NS_FAILED(rv))
     515               0 :     return rv;
     516                 : 
     517               2 :   if (algorithm & ALGO_MD5_SESS) {
     518                 :     char part1[EXPANDED_DIGEST_LENGTH+1];
     519               0 :     ExpandToHex(mHashBuf, part1);
     520                 : 
     521               0 :     contents.Assign(part1, EXPANDED_DIGEST_LENGTH);
     522               0 :     contents.Append(':');
     523               0 :     contents.Append(nonce);
     524               0 :     contents.Append(':');
     525               0 :     contents.Append(cnonce);
     526                 : 
     527               0 :     rv = MD5Hash(contents.get(), contents.Length());
     528               0 :     if (NS_FAILED(rv))
     529               0 :       return rv;
     530                 :   }
     531                 : 
     532               2 :   return ExpandToHex(mHashBuf, result);
     533                 : }
     534                 : 
     535                 : nsresult
     536               2 : nsHttpDigestAuth::CalculateHA2(const nsAFlatCString & method,
     537                 :                                const nsAFlatCString & path,
     538                 :                                PRUint16 qop,
     539                 :                                const char * bodyDigest,
     540                 :                                char * result)
     541                 : {
     542               2 :   PRInt16 methodLen = method.Length();
     543               2 :   PRInt16 pathLen = path.Length();
     544               2 :   PRInt16 len = methodLen + pathLen + 1;
     545                 : 
     546               2 :   if (qop & QOP_AUTH_INT) {
     547               0 :     len += EXPANDED_DIGEST_LENGTH + 1;
     548                 :   }
     549                 : 
     550               4 :   nsCAutoString contents;
     551               2 :   contents.SetCapacity(len);
     552                 : 
     553               2 :   contents.Assign(method);
     554               2 :   contents.Append(':');
     555               2 :   contents.Append(path);
     556                 : 
     557               2 :   if (qop & QOP_AUTH_INT) {
     558               0 :     contents.Append(':');
     559               0 :     contents.Append(bodyDigest, EXPANDED_DIGEST_LENGTH);
     560                 :   }
     561                 : 
     562               2 :   nsresult rv = MD5Hash(contents.get(), contents.Length());
     563               2 :   if (NS_SUCCEEDED(rv))
     564               2 :     rv = ExpandToHex(mHashBuf, result);
     565               2 :   return rv;
     566                 : }
     567                 : 
     568                 : nsresult
     569               5 : nsHttpDigestAuth::ParseChallenge(const char * challenge,
     570                 :                                  nsACString & realm,
     571                 :                                  nsACString & domain,
     572                 :                                  nsACString & nonce,
     573                 :                                  nsACString & opaque,
     574                 :                                  bool * stale,
     575                 :                                  PRUint16 * algorithm,
     576                 :                                  PRUint16 * qop)
     577                 : {
     578               5 :   const char *p = challenge + 7; // first 7 characters are "Digest "
     579                 : 
     580               5 :   *stale = false;
     581               5 :   *algorithm = ALGO_MD5; // default is MD5
     582               5 :   *qop = 0;
     583                 : 
     584              30 :   for (;;) {
     585             110 :     while (*p && (*p == ',' || nsCRT::IsAsciiSpace(*p)))
     586              40 :       ++p;
     587              35 :     if (!*p)
     588                 :       break;
     589                 : 
     590                 :     // name
     591              30 :     PRInt16 nameStart = (p - challenge);
     592             230 :     while (*p && !nsCRT::IsAsciiSpace(*p) && *p != '=') 
     593             170 :       ++p;
     594              30 :     if (!*p)
     595               0 :       return NS_ERROR_INVALID_ARG;
     596              30 :     PRInt16 nameLength = (p - challenge) - nameStart;
     597                 : 
     598              90 :     while (*p && (nsCRT::IsAsciiSpace(*p) || *p == '=')) 
     599              30 :       ++p;
     600              30 :     if (!*p) 
     601               0 :       return NS_ERROR_INVALID_ARG;
     602                 : 
     603              30 :     bool quoted = false;
     604              30 :     if (*p == '"') {
     605              20 :       ++p;
     606              20 :       quoted = true;
     607                 :     }
     608                 : 
     609                 :     // value
     610              30 :     PRInt16 valueStart = (p - challenge);
     611              30 :     PRInt16 valueLength = 0;
     612              30 :     if (quoted) {
     613             305 :       while (*p && *p != '"') 
     614             265 :         ++p;
     615              20 :       if (*p != '"') 
     616               0 :         return NS_ERROR_INVALID_ARG;
     617              20 :       valueLength = (p - challenge) - valueStart;
     618              20 :       ++p;
     619                 :     } else {
     620              55 :       while (*p && !nsCRT::IsAsciiSpace(*p) && *p != ',') 
     621              35 :         ++p; 
     622              10 :       valueLength = (p - challenge) - valueStart;
     623                 :     }
     624                 : 
     625                 :     // extract information
     626              40 :     if (nameLength == 5 &&
     627              10 :         nsCRT::strncasecmp(challenge+nameStart, "realm", 5) == 0)
     628                 :     {
     629               5 :       realm.Assign(challenge+valueStart, valueLength);
     630                 :     }
     631              35 :     else if (nameLength == 6 &&
     632              10 :         nsCRT::strncasecmp(challenge+nameStart, "domain", 6) == 0)
     633                 :     {
     634               5 :       domain.Assign(challenge+valueStart, valueLength);
     635                 :     }
     636              25 :     else if (nameLength == 5 &&
     637               5 :         nsCRT::strncasecmp(challenge+nameStart, "nonce", 5) == 0)
     638                 :     {
     639               5 :       nonce.Assign(challenge+valueStart, valueLength);
     640                 :     }
     641              20 :     else if (nameLength == 6 &&
     642               5 :         nsCRT::strncasecmp(challenge+nameStart, "opaque", 6) == 0)
     643                 :     {
     644               5 :       opaque.Assign(challenge+valueStart, valueLength);
     645                 :     }
     646              10 :     else if (nameLength == 5 &&
     647               0 :         nsCRT::strncasecmp(challenge+nameStart, "stale", 5) == 0)
     648                 :     {
     649               0 :       if (nsCRT::strncasecmp(challenge+valueStart, "true", 4) == 0)
     650               0 :         *stale = true;
     651                 :       else
     652               0 :         *stale = false;
     653                 :     }
     654              15 :     else if (nameLength == 9 &&
     655               5 :         nsCRT::strncasecmp(challenge+nameStart, "algorithm", 9) == 0)
     656                 :     {
     657                 :       // we want to clear the default, so we use = not |= here
     658               5 :       *algorithm = ALGO_SPECIFIED;
     659              10 :       if (valueLength == 3 &&
     660               5 :           nsCRT::strncasecmp(challenge+valueStart, "MD5", 3) == 0)
     661               5 :         *algorithm |= ALGO_MD5;
     662               0 :       else if (valueLength == 8 &&
     663               0 :           nsCRT::strncasecmp(challenge+valueStart, "MD5-sess", 8) == 0)
     664               0 :         *algorithm |= ALGO_MD5_SESS;
     665                 :     }
     666              10 :     else if (nameLength == 3 &&
     667               5 :         nsCRT::strncasecmp(challenge+nameStart, "qop", 3) == 0)
     668                 :     {
     669               5 :       PRInt16 ipos = valueStart;
     670              15 :       while (ipos < valueStart+valueLength) {
     671              20 :         while (ipos < valueStart+valueLength &&
     672               5 :                (nsCRT::IsAsciiSpace(challenge[ipos]) ||
     673               5 :                 challenge[ipos] == ',')) 
     674               0 :           ipos++;
     675               5 :         PRInt16 algostart = ipos;
     676              70 :         while (ipos < valueStart+valueLength &&
     677              20 :                !nsCRT::IsAsciiSpace(challenge[ipos]) &&
     678              20 :                challenge[ipos] != ',') 
     679              20 :           ipos++;
     680              10 :         if ((ipos - algostart) == 4 &&
     681               5 :             nsCRT::strncasecmp(challenge+algostart, "auth", 4) == 0)
     682               5 :           *qop |= QOP_AUTH;
     683               0 :         else if ((ipos - algostart) == 8 &&
     684               0 :             nsCRT::strncasecmp(challenge+algostart, "auth-int", 8) == 0)
     685               0 :           *qop |= QOP_AUTH_INT;
     686                 :       }
     687                 :     }
     688                 :   }
     689               5 :   return NS_OK;
     690                 : }
     691                 : 
     692                 : nsresult
     693               6 : nsHttpDigestAuth::AppendQuotedString(const nsACString & value,
     694                 :                                      nsACString & aHeaderLine)
     695                 : {
     696              12 :   nsCAutoString quoted;
     697               6 :   nsACString::const_iterator s, e;
     698               6 :   value.BeginReading(s);
     699               6 :   value.EndReading(e);
     700                 : 
     701                 :   //
     702                 :   // Encode string according to RFC 2616 quoted-string production
     703                 :   //
     704               6 :   quoted.Append('"');
     705              82 :   for ( ; s != e; ++s) {
     706                 :     //
     707                 :     // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
     708                 :     //
     709              77 :     if (*s <= 31 || *s == 127) {
     710               1 :       return NS_ERROR_FAILURE;
     711                 :     }
     712                 : 
     713                 :     // Escape two syntactically significant characters
     714              76 :     if (*s == '"' || *s == '\\') {
     715               0 :       quoted.Append('\\');
     716                 :     }
     717                 : 
     718              76 :     quoted.Append(*s);
     719                 :   }
     720                 :   // FIXME: bug 41489
     721                 :   // We should RFC2047-encode non-Latin-1 values according to spec
     722               5 :   quoted.Append('"');
     723               5 :   aHeaderLine.Append(quoted);
     724               5 :   return NS_OK;
     725                 : }
     726                 : 
     727                 : // vim: ts=2 sw=2

Generated by: LCOV version 1.7