LCOV - code coverage report
Current view: directory - extensions/auth - nsHttpNegotiateAuth.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 165 0 0.0 %
Date: 2012-06-02 Functions: 10 0 0.0 %

       1                 : /* vim:set ts=4 sw=4 sts=4 et cindent: */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Negotiateauth
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Daniel Kouril.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2003
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Daniel Kouril <kouril@ics.muni.cz> (original author)
      23                 :  *   Wyllys Ingersoll <wyllys.ingersoll@sun.com>
      24                 :  *   Christopher Nebergall <cneberg@sandia.gov>
      25                 :  *   Darin Fisher <darin@meer.net>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : //
      42                 : // HTTP Negotiate Authentication Support Module
      43                 : //
      44                 : // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
      45                 : // (formerly draft-brezak-spnego-http-04.txt)
      46                 : //
      47                 : // Also described here:
      48                 : // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
      49                 : //
      50                 : 
      51                 : #include <string.h>
      52                 : #include <stdlib.h>
      53                 : 
      54                 : #include "nsAuth.h"
      55                 : #include "nsHttpNegotiateAuth.h"
      56                 : 
      57                 : #include "nsIHttpAuthenticableChannel.h"
      58                 : #include "nsIProxiedChannel.h"
      59                 : #include "nsIAuthModule.h"
      60                 : #include "nsIServiceManager.h"
      61                 : #include "nsIPrefService.h"
      62                 : #include "nsIPrefBranch.h"
      63                 : #include "nsIProxyInfo.h"
      64                 : #include "nsIURI.h"
      65                 : #include "nsCOMPtr.h"
      66                 : #include "nsString.h"
      67                 : #include "nsNetCID.h"
      68                 : #include "plbase64.h"
      69                 : #include "plstr.h"
      70                 : #include "prprf.h"
      71                 : #include "prlog.h"
      72                 : #include "prmem.h"
      73                 : #include "prnetdb.h"
      74                 : 
      75                 : //-----------------------------------------------------------------------------
      76                 : 
      77                 : static const char kNegotiate[] = "Negotiate";
      78                 : static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
      79                 : static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
      80                 : static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies";
      81                 : static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn";
      82                 : static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
      83                 : 
      84                 : #define kNegotiateLen  (sizeof(kNegotiate)-1)
      85                 : 
      86                 : //-----------------------------------------------------------------------------
      87                 : 
      88                 : NS_IMETHODIMP
      89               0 : nsHttpNegotiateAuth::GetAuthFlags(PRUint32 *flags)
      90                 : {
      91                 :     //
      92                 :     // Negotiate Auth creds should not be reused across multiple requests.
      93                 :     // Only perform the negotiation when it is explicitly requested by the
      94                 :     // server.  Thus, do *NOT* use the "REUSABLE_CREDENTIALS" flag here.
      95                 :     //
      96                 :     // CONNECTION_BASED is specified instead of REQUEST_BASED since we need
      97                 :     // to complete a sequence of transactions with the server over the same
      98                 :     // connection.
      99                 :     //
     100               0 :     *flags = CONNECTION_BASED | IDENTITY_IGNORED; 
     101               0 :     return NS_OK;
     102                 : }
     103                 : 
     104                 : //
     105                 : // Always set *identityInvalid == FALSE here.  This 
     106                 : // will prevent the browser from popping up the authentication
     107                 : // prompt window.  Because GSSAPI does not have an API
     108                 : // for fetching initial credentials (ex: A Kerberos TGT),
     109                 : // there is no correct way to get the users credentials.
     110                 : // 
     111                 : NS_IMETHODIMP
     112               0 : nsHttpNegotiateAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel,
     113                 :                                        const char *challenge,
     114                 :                                        bool isProxyAuth,
     115                 :                                        nsISupports **sessionState,
     116                 :                                        nsISupports **continuationState,
     117                 :                                        bool *identityInvalid)
     118                 : {
     119               0 :     nsIAuthModule *module = (nsIAuthModule *) *continuationState;
     120                 : 
     121               0 :     *identityInvalid = false;
     122               0 :     if (module)
     123               0 :         return NS_OK;
     124                 : 
     125                 :     nsresult rv;
     126                 : 
     127               0 :     nsCOMPtr<nsIURI> uri;
     128               0 :     rv = authChannel->GetURI(getter_AddRefs(uri));
     129               0 :     if (NS_FAILED(rv))
     130               0 :         return rv;
     131                 : 
     132               0 :     PRUint32 req_flags = nsIAuthModule::REQ_DEFAULT;
     133               0 :     nsCAutoString service;
     134                 : 
     135               0 :     if (isProxyAuth) {
     136               0 :         if (!TestBoolPref(kNegotiateAuthAllowProxies)) {
     137               0 :             LOG(("nsHttpNegotiateAuth::ChallengeReceived proxy auth blocked\n"));
     138               0 :             return NS_ERROR_ABORT;
     139                 :         }
     140                 : 
     141               0 :         nsCOMPtr<nsIProxyInfo> proxyInfo;
     142               0 :         authChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
     143               0 :         NS_ENSURE_STATE(proxyInfo);
     144                 : 
     145               0 :         proxyInfo->GetHost(service);
     146                 :     }
     147                 :     else {
     148               0 :         bool allowed = TestNonFqdn(uri) ||
     149               0 :                        TestPref(uri, kNegotiateAuthTrustedURIs);
     150               0 :         if (!allowed) {
     151               0 :             LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n"));
     152               0 :             return NS_ERROR_ABORT;
     153                 :         }
     154                 : 
     155               0 :         bool delegation = TestPref(uri, kNegotiateAuthDelegationURIs);
     156               0 :         if (delegation) {
     157               0 :             LOG(("  using REQ_DELEGATE\n"));
     158               0 :             req_flags |= nsIAuthModule::REQ_DELEGATE;
     159                 :         }
     160                 : 
     161               0 :         rv = uri->GetAsciiHost(service);
     162               0 :         if (NS_FAILED(rv))
     163               0 :             return rv;
     164                 :     }
     165                 : 
     166               0 :     LOG(("  service = %s\n", service.get()));
     167                 : 
     168                 :     //
     169                 :     // The correct service name for IIS servers is "HTTP/f.q.d.n", so
     170                 :     // construct the proper service name for passing to "gss_import_name".
     171                 :     //
     172                 :     // TODO: Possibly make this a configurable service name for use
     173                 :     // with non-standard servers that use stuff like "khttp/f.q.d.n" 
     174                 :     // instead.
     175                 :     //
     176               0 :     service.Insert("HTTP@", 0);
     177                 : 
     178                 :     const char *contractID;
     179               0 :     if (TestBoolPref(kNegotiateAuthSSPI)) {
     180               0 :            LOG(("  using negotiate-sspi\n"));
     181               0 :            contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-sspi";
     182                 :     }
     183                 :     else {
     184               0 :            LOG(("  using negotiate-gss\n"));
     185               0 :            contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-gss";
     186                 :     }
     187                 : 
     188               0 :     rv = CallCreateInstance(contractID, &module);
     189                 : 
     190               0 :     if (NS_FAILED(rv)) {
     191               0 :         LOG(("  Failed to load Negotiate Module \n"));
     192               0 :         return rv;
     193                 :     }
     194                 : 
     195               0 :     rv = module->Init(service.get(), req_flags, nsnull, nsnull, nsnull);
     196                 : 
     197               0 :     if (NS_FAILED(rv)) {
     198               0 :         NS_RELEASE(module);
     199               0 :         return rv;
     200                 :     }
     201                 : 
     202               0 :     *continuationState = module;
     203               0 :     return NS_OK;
     204                 : }
     205                 : 
     206               0 : NS_IMPL_ISUPPORTS1(nsHttpNegotiateAuth, nsIHttpAuthenticator)
     207                 :    
     208                 : //
     209                 : // GenerateCredentials
     210                 : //
     211                 : // This routine is responsible for creating the correct authentication
     212                 : // blob to pass to the server that requested "Negotiate" authentication.
     213                 : //
     214                 : NS_IMETHODIMP
     215               0 : nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
     216                 :                                          const char *challenge,
     217                 :                                          bool isProxyAuth,
     218                 :                                          const PRUnichar *domain,
     219                 :                                          const PRUnichar *username,
     220                 :                                          const PRUnichar *password,
     221                 :                                          nsISupports **sessionState,
     222                 :                                          nsISupports **continuationState,
     223                 :                                          PRUint32 *flags,
     224                 :                                          char **creds)
     225                 : {
     226                 :     // ChallengeReceived must have been called previously.
     227               0 :     nsIAuthModule *module = (nsIAuthModule *) *continuationState;
     228               0 :     NS_ENSURE_TRUE(module, NS_ERROR_NOT_INITIALIZED);
     229                 : 
     230               0 :     *flags = USING_INTERNAL_IDENTITY;
     231                 : 
     232               0 :     LOG(("nsHttpNegotiateAuth::GenerateCredentials() [challenge=%s]\n", challenge));
     233                 : 
     234               0 :     NS_ASSERTION(creds, "null param");
     235                 : 
     236                 : #ifdef DEBUG
     237                 :     bool isGssapiAuth =
     238               0 :         !PL_strncasecmp(challenge, kNegotiate, kNegotiateLen);
     239               0 :     NS_ASSERTION(isGssapiAuth, "Unexpected challenge");
     240                 : #endif
     241                 : 
     242                 :     //
     243                 :     // If the "Negotiate:" header had some data associated with it,
     244                 :     // that data should be used as the input to this call.  This may
     245                 :     // be a continuation of an earlier call because GSSAPI authentication
     246                 :     // often takes multiple round-trips to complete depending on the
     247                 :     // context flags given.  We want to use MUTUAL_AUTHENTICATION which
     248                 :     // generally *does* require multiple round-trips.  Don't assume
     249                 :     // auth can be completed in just 1 call.
     250                 :     //
     251               0 :     unsigned int len = strlen(challenge);
     252                 : 
     253                 :     void *inToken, *outToken;
     254                 :     PRUint32 inTokenLen, outTokenLen;
     255                 : 
     256               0 :     if (len > kNegotiateLen) {
     257               0 :         challenge += kNegotiateLen;
     258               0 :         while (*challenge == ' ')
     259               0 :             challenge++;
     260               0 :         len = strlen(challenge);
     261                 : 
     262                 :         // strip off any padding (see bug 230351)
     263               0 :         while (challenge[len - 1] == '=')
     264               0 :             len--;
     265                 : 
     266               0 :         inTokenLen = (len * 3)/4;
     267               0 :         inToken = malloc(inTokenLen);
     268               0 :         if (!inToken)
     269               0 :             return (NS_ERROR_OUT_OF_MEMORY);
     270                 : 
     271                 :         //
     272                 :         // Decode the response that followed the "Negotiate" token
     273                 :         //
     274               0 :         if (PL_Base64Decode(challenge, len, (char *) inToken) == NULL) {
     275               0 :             free(inToken);
     276               0 :             return(NS_ERROR_UNEXPECTED);
     277                 :         }
     278                 :     }
     279                 :     else {
     280                 :         //
     281                 :         // Initializing, don't use an input token.
     282                 :         //
     283               0 :         inToken = nsnull;
     284               0 :         inTokenLen = 0;
     285                 :     }
     286                 : 
     287               0 :     nsresult rv = module->GetNextToken(inToken, inTokenLen, &outToken, &outTokenLen);
     288                 : 
     289               0 :     free(inToken);
     290                 : 
     291               0 :     if (NS_FAILED(rv))
     292               0 :         return rv;
     293                 : 
     294               0 :     if (outTokenLen == 0) {
     295               0 :         LOG(("  No output token to send, exiting"));
     296               0 :         return NS_ERROR_FAILURE;
     297                 :     }
     298                 : 
     299                 :     //
     300                 :     // base64 encode the output token.
     301                 :     //
     302               0 :     char *encoded_token = PL_Base64Encode((char *)outToken, outTokenLen, nsnull);
     303                 : 
     304               0 :     nsMemory::Free(outToken);
     305                 : 
     306               0 :     if (!encoded_token)
     307               0 :         return NS_ERROR_OUT_OF_MEMORY;
     308                 : 
     309               0 :     LOG(("  Sending a token of length %d\n", outTokenLen));
     310                 : 
     311                 :     // allocate a buffer sizeof("Negotiate" + " " + b64output_token + "\0")
     312               0 :     *creds = (char *) nsMemory::Alloc(kNegotiateLen + 1 + strlen(encoded_token) + 1);
     313               0 :     if (NS_UNLIKELY(!*creds))
     314               0 :         rv = NS_ERROR_OUT_OF_MEMORY;
     315                 :     else
     316               0 :         sprintf(*creds, "%s %s", kNegotiate, encoded_token);
     317                 : 
     318               0 :     PR_Free(encoded_token);
     319               0 :     return rv;
     320                 : }
     321                 : 
     322                 : bool
     323               0 : nsHttpNegotiateAuth::TestBoolPref(const char *pref)
     324                 : {
     325               0 :     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     326               0 :     if (!prefs)
     327               0 :         return false;
     328                 : 
     329                 :     bool val;
     330               0 :     nsresult rv = prefs->GetBoolPref(pref, &val);
     331               0 :     if (NS_FAILED(rv))
     332               0 :         return false;
     333                 : 
     334               0 :     return val;
     335                 : }
     336                 : 
     337                 : bool
     338               0 : nsHttpNegotiateAuth::TestNonFqdn(nsIURI *uri)
     339                 : {
     340               0 :     nsCAutoString host;
     341                 :     PRNetAddr addr;
     342                 : 
     343               0 :     if (!TestBoolPref(kNegotiateAuthAllowNonFqdn))
     344               0 :         return false;
     345                 : 
     346               0 :     if (NS_FAILED(uri->GetAsciiHost(host)))
     347               0 :         return false;
     348                 : 
     349                 :     // return true if host does not contain a dot and is not an ip address
     350               0 :     return !host.IsEmpty() && host.FindChar('.') == kNotFound &&
     351               0 :            PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS;
     352                 : }
     353                 : 
     354                 : bool
     355               0 : nsHttpNegotiateAuth::TestPref(nsIURI *uri, const char *pref)
     356                 : {
     357               0 :     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     358               0 :     if (!prefs)
     359               0 :         return false;
     360                 : 
     361               0 :     nsCAutoString scheme, host;
     362                 :     PRInt32 port;
     363                 : 
     364               0 :     if (NS_FAILED(uri->GetScheme(scheme)))
     365               0 :         return false;
     366               0 :     if (NS_FAILED(uri->GetAsciiHost(host)))
     367               0 :         return false;
     368               0 :     if (NS_FAILED(uri->GetPort(&port)))
     369               0 :         return false;
     370                 : 
     371                 :     char *hostList;
     372               0 :     if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
     373               0 :         return false;
     374                 : 
     375                 :     // pseudo-BNF
     376                 :     // ----------
     377                 :     //
     378                 :     // url-list       base-url ( base-url "," LWS )*
     379                 :     // base-url       ( scheme-part | host-part | scheme-part host-part )
     380                 :     // scheme-part    scheme "://"
     381                 :     // host-part      host [":" port]
     382                 :     //
     383                 :     // for example:
     384                 :     //   "https://, http://office.foo.com"
     385                 :     //
     386                 : 
     387               0 :     char *start = hostList, *end;
     388               0 :     for (;;) {
     389                 :         // skip past any whitespace
     390               0 :         while (*start == ' ' || *start == '\t')
     391               0 :             ++start;
     392               0 :         end = strchr(start, ',');
     393               0 :         if (!end)
     394               0 :             end = start + strlen(start);
     395               0 :         if (start == end)
     396               0 :             break;
     397               0 :         if (MatchesBaseURI(scheme, host, port, start, end))
     398               0 :             return true;
     399               0 :         if (*end == '\0')
     400               0 :             break;
     401               0 :         start = end + 1;
     402                 :     }
     403                 :     
     404               0 :     nsMemory::Free(hostList);
     405               0 :     return false;
     406                 : }
     407                 : 
     408                 : bool
     409               0 : nsHttpNegotiateAuth::MatchesBaseURI(const nsCSubstring &matchScheme,
     410                 :                                     const nsCSubstring &matchHost,
     411                 :                                     PRInt32             matchPort,
     412                 :                                     const char         *baseStart,
     413                 :                                     const char         *baseEnd)
     414                 : {
     415                 :     // check if scheme://host:port matches baseURI
     416                 : 
     417                 :     // parse the base URI
     418               0 :     const char *hostStart, *schemeEnd = strstr(baseStart, "://");
     419               0 :     if (schemeEnd) {
     420                 :         // the given scheme must match the parsed scheme exactly
     421               0 :         if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
     422               0 :             return false;
     423               0 :         hostStart = schemeEnd + 3;
     424                 :     }
     425                 :     else
     426               0 :         hostStart = baseStart;
     427                 : 
     428                 :     // XXX this does not work for IPv6-literals
     429               0 :     const char *hostEnd = strchr(hostStart, ':');
     430               0 :     if (hostEnd && hostEnd < baseEnd) {
     431                 :         // the given port must match the parsed port exactly
     432               0 :         int port = atoi(hostEnd + 1);
     433               0 :         if (matchPort != (PRInt32) port)
     434               0 :             return false;
     435                 :     }
     436                 :     else
     437               0 :         hostEnd = baseEnd;
     438                 : 
     439                 : 
     440                 :     // if we didn't parse out a host, then assume we got a match.
     441               0 :     if (hostStart == hostEnd)
     442               0 :         return true;
     443                 : 
     444               0 :     PRUint32 hostLen = hostEnd - hostStart;
     445                 : 
     446                 :     // matchHost must either equal host or be a subdomain of host
     447               0 :     if (matchHost.Length() < hostLen)
     448               0 :         return false;
     449                 : 
     450               0 :     const char *end = matchHost.EndReading();
     451               0 :     if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
     452                 :         // if matchHost ends with host from the base URI, then make sure it is
     453                 :         // either an exact match, or prefixed with a dot.  we don't want
     454                 :         // "foobar.com" to match "bar.com"
     455               0 :         if (matchHost.Length() == hostLen ||
     456               0 :             *(end - hostLen) == '.' ||
     457               0 :             *(end - hostLen - 1) == '.')
     458               0 :             return true;
     459                 :     }
     460                 : 
     461               0 :     return false;
     462                 : }

Generated by: LCOV version 1.7