mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Landing patch for bug 17578 "I want Kerberos authentication and TGT forwarding"
This patch enables preliminary support for the SPNEGO protocol (Microsoft's Negotiate protocol) via the GSSAPI library. This means that only UNIX-like operating systems (including Linux and OSX) are supported. A SSPI-based solution is needed for WIN32. Only HTTP authentication is currently supported. Mail protocols will follow. Patch by: Daniel Kouril <kouril@ics.muni.cz> (original author) Wyllys Ingersoll <wyllys.ingersoll@sun.com> Christopher Nebergall <cneberg@sandia.gov> Darin Fisher <darin@meer.net> r=dougt sr=bryner a=chofmann
This commit is contained in:
parent
468b473dca
commit
9b6c795f0e
@ -1215,6 +1215,9 @@ for extension in $MOZ_EXTENSIONS; do
|
||||
finger ) MAKEFILES_extensions="$MAKEFILES_extensions
|
||||
extensions/finger/Makefile
|
||||
" ;;
|
||||
negotiateauth ) MAKEFILES_extensions="$MAKEFILES_extensions
|
||||
extensions/negotiateauth/Makefile
|
||||
" ;;
|
||||
gnomevfs ) MAKEFILES_extensions="$MAKEFILES_extensions
|
||||
extensions/gnomevfs/Makefile
|
||||
" ;;
|
||||
|
@ -415,6 +415,10 @@ HAVE_XIE=@HAVE_XIE@
|
||||
MOZ_MOVEMAIL=@MOZ_MOVEMAIL@
|
||||
MOZ_PSM=@MOZ_PSM@
|
||||
|
||||
# Gssapi (krb5) libraries and headers for the Negotiate auth method
|
||||
GSSAPI_INCLUDES = @GSSAPI_INCLUDES@
|
||||
GSSAPI_LIBS = @GSSAPI_LIBS@
|
||||
|
||||
# for Qt build
|
||||
MOC=@MOC@
|
||||
|
||||
|
119
configure.in
119
configure.in
@ -3685,7 +3685,7 @@ dnl ========================================================
|
||||
dnl = Enable compilation of specific extension modules
|
||||
dnl ========================================================
|
||||
|
||||
MOZ_EXTENSIONS_DEFAULT=" cookie wallet content-packs xml-rpc xmlextras help p3p pref transformiix venkman inspector irc universalchardet typeaheadfind webservices spellcheck gnomevfs"
|
||||
MOZ_EXTENSIONS_DEFAULT=" cookie wallet content-packs xml-rpc xmlextras help p3p pref transformiix venkman inspector irc universalchardet typeaheadfind webservices spellcheck gnomevfs negotiateauth"
|
||||
|
||||
MOZ_EXTENSIONS_ALL="$MOZ_EXTENSIONS_DEFAULT xmlterm datetime finger cview layout-debug tasks"
|
||||
|
||||
@ -3741,6 +3741,123 @@ if test `echo "$MOZ_EXTENSIONS" | grep -c tridentprofile` -ne 0; then
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl Check for GSSAPI libraries and headers needed for
|
||||
dnl integrated windows authentication support (disable
|
||||
dnl negotiateauth extension if GSSAPI is not available).
|
||||
dnl ========================================================
|
||||
|
||||
if test `echo "$MOZ_EXTENSIONS" | grep -c negotiateauth` -ne 0; then
|
||||
dnl
|
||||
dnl Check for alternative location for GSSAPI libraries.
|
||||
dnl
|
||||
MOZ_ARG_WITH_STRING(gssapi,
|
||||
[ --with-gssapi=PFX Location of GSSAPI libraries],
|
||||
GSSAPI_DIR=$withval)
|
||||
|
||||
dnl
|
||||
dnl If not specified, assume GSSAPI lives under /usr
|
||||
dnl
|
||||
if test -z "$GSSAPI_DIR"; then
|
||||
dnl
|
||||
dnl RedHat Linux 7, 8, and 9 install GSSAPI headers and libraries under
|
||||
dnl /usr/kerberos -- perhaps other platforms do the same. If not, then
|
||||
dnl we count on the default search paths. Fedora Core 1, at least,
|
||||
dnl puts the headers and libraries under /usr, but it still has
|
||||
dnl /usr/kerberos for the other KRB5 components.
|
||||
dnl
|
||||
if test -d "/usr/kerberos/include"; then
|
||||
GSSAPI_DIR="/usr/kerberos"
|
||||
else
|
||||
GSSAPI_DIR="/usr"
|
||||
fi
|
||||
fi
|
||||
|
||||
_SAVE_CPPFLAGS="$CPPFLAGS"
|
||||
_SAVE_LDFLAGS="$LDFLAGS"
|
||||
_SAVE_LIBS="$LIBS"
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -I$GSSAPI_DIR/include"
|
||||
LDFLAGS="$LDFLAGS -L$GSSAPI_DIR/lib"
|
||||
|
||||
dnl
|
||||
dnl Try to set GSSAPI_INCLUDES ...
|
||||
dnl
|
||||
AC_CHECK_HEADERS(gssapi.h,
|
||||
[GSSAPI_INCLUDES="-I$GSSAPI_DIR/include"])
|
||||
if test -z "$GSSAPI_INCLUDES" ; then
|
||||
AC_CHECK_HEADERS(gssapi/gssapi.h,
|
||||
[GSSAPI_INCLUDES="-I$GSSAPI_DIR/include"])
|
||||
fi
|
||||
CPPFLAGS="$_SAVE_CPPFLAGS $GSSAPI_INCLUDES"
|
||||
|
||||
dnl
|
||||
dnl Try to set GSSAPI_LIBS ...
|
||||
dnl
|
||||
AC_CHECK_LIB(gss, gss_init_sec_context,
|
||||
[GSSAPI_LIBS="-L$GSSAPI_DIR/lib -lgss"],)
|
||||
|
||||
dnl
|
||||
dnl If libgss was not found, look for the MIT Kerberos V5 GSSAPI
|
||||
dnl libraries instead.
|
||||
dnl
|
||||
if test -z "$GSSAPI_LIBS" ; then
|
||||
LIBS="$LIBS -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err"
|
||||
AC_CHECK_LIB(gssapi_krb5, gss_init_sec_context,
|
||||
[GSSAPI_LIBS="-L$GSSAPI_DIR/lib -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err"],)
|
||||
LIBS="$_SAVE_LIBS"
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl If GSS libs were STILL not found, try looking for Heimdal
|
||||
dnl Kerberos V5 libraries.
|
||||
dnl
|
||||
if test -z "$GSSAPI_LIBS" ; then
|
||||
if test -x "$GSSAPI_DIR/bin/krb5-config" ; then
|
||||
krb5cfg="$GSSAPI_DIR/bin/krb5-config"
|
||||
TMP_GSSAPI_LIBS=`$krb5cfg --libs gssapi 2>/dev/null`
|
||||
TMP_GSSAPI_INCLUDES=`$krb5cfg --cflags gssapi 2>/dev/null`
|
||||
|
||||
LIBS="$LIBS $TMP_GSSAPI_LIBS"
|
||||
AC_CHECK_LIB(gssapi, gss_init_sec_context,
|
||||
[GSSAPI_LIBS="$TMP_GSSAPI_LIBS"
|
||||
GSSAPI_INCLUDES="$TMP_GSSAPI_INCLUDES"],
|
||||
[GSSAPI_LIBS=""])
|
||||
|
||||
CPPFLAGS="$_SAVE_CPPFLAGS $GSSAPI_INCLUDES"
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl If GSSAPI libs were not found, remove extension from the list to be
|
||||
dnl built.
|
||||
dnl
|
||||
if test -z "$GSSAPI_LIBS" ; then
|
||||
AC_MSG_WARN([Cannot build negotiateauth without GSSAPI. Removing negotatiate from MOZ_EXTENSIONS.])
|
||||
MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|negotiateauth||'`
|
||||
GSSAPI_INCLUDES=
|
||||
else
|
||||
dnl
|
||||
dnl Try to determine if the GSS_C_NT_HOSTBASED_SERVICE value is
|
||||
dnl defined. Older MIT releases did not define this correctly.
|
||||
dnl
|
||||
AC_CHECK_HEADERS([gssapi/gssapi_generic.h])
|
||||
AC_TRY_COMPILE([ #include <gssapi/gssapi.h> ],
|
||||
[ gss_OID oid = GSS_C_NT_HOSTBASED_SERVICE; ],
|
||||
[AC_DEFINE(HAVE_GSS_C_NT_HOSTBASED_SERVICE)],[
|
||||
AC_MSG_WARN([GSS_C_NT_HOSTBASED_SERVICE not found])
|
||||
])
|
||||
fi
|
||||
|
||||
LIBS="$_SAVE_LIBS"
|
||||
LDFLAGS="$_SAVE_LDFLAGS"
|
||||
CPPFLAGS="$_SAVE_CPPFLAGS"
|
||||
|
||||
AC_SUBST(GSSAPI_INCLUDES)
|
||||
AC_SUBST(GSSAPI_LIBS)
|
||||
fi
|
||||
|
||||
|
||||
dnl Remove dupes
|
||||
MOZ_EXTENSIONS=`${PERL} ${srcdir}/build/unix/uniq.pl ${MOZ_EXTENSIONS}`
|
||||
|
||||
|
79
extensions/negotiateauth/Makefile.in
Normal file
79
extensions/negotiateauth/Makefile.in
Normal file
@ -0,0 +1,79 @@
|
||||
# vim:set ts=8 sw=8 sts=8 noet:
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# 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 negotiateauth.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Daniel Kouril <kouril@ics.muni.cz>
|
||||
#
|
||||
# Portions created by the Initial Developer are Copyright (C) 2003
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Daniel Kouril <kouril@ics.muni.cz> (original author)
|
||||
# Wyllys Ingersoll <wyllys.ingersoll@sun.com>
|
||||
# Christopher Nebergall <cneberg@sandia.gov>
|
||||
# Darin Fisher <darin@meer.net>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = negotiateauth
|
||||
LIBRARY_NAME = negotiateauth
|
||||
EXPORT_LIBRARY = 1
|
||||
IS_COMPONENT = 1
|
||||
|
||||
LOCAL_INCLUDES = $(GSSAPI_INCLUDES)
|
||||
|
||||
REQUIRES = \
|
||||
xpcom \
|
||||
string \
|
||||
necko \
|
||||
pref \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
nsHttpGssapiAuth.cpp \
|
||||
nsHttpGssapiAuthModule.cpp \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DSO_LDOPTS = \
|
||||
$(MOZ_COMPONENT_LIBS) \
|
||||
$(GSSAPI_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
# make sure this component is never statically linked into the main
|
||||
# application. this is necessary since we don't want to force users
|
||||
# to install GSSAPI libraries in order to use the rest of mozilla ;-)
|
||||
FORCE_SHARED_LIB= 1
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
610
extensions/negotiateauth/nsHttpGssapiAuth.cpp
Normal file
610
extensions/negotiateauth/nsHttpGssapiAuth.cpp
Normal file
@ -0,0 +1,610 @@
|
||||
/* vim:set ts=4 sw=4 sts=4 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 the Negotiateauth
|
||||
*
|
||||
* The Initial Developer of the Original Code is Daniel Kouril.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2003
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Daniel Kouril <kouril@ics.muni.cz> (original author)
|
||||
* Wyllys Ingersoll <wyllys.ingersoll@sun.com>
|
||||
* Christopher Nebergall <cneberg@sandia.gov>
|
||||
* Darin Fisher <darin@meer.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
//
|
||||
// GSSAPI Authentication Support Module
|
||||
//
|
||||
// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
|
||||
// (formerly draft-brezak-spnego-http-04.txt)
|
||||
//
|
||||
// Also described here:
|
||||
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
|
||||
//
|
||||
|
||||
// this #define must run before prlog.h is included
|
||||
#ifdef MOZ_LOGGING
|
||||
#define FORCE_PR_LOG 1
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(HAVE_GSSAPI_H)
|
||||
#include <gssapi.h>
|
||||
#elif defined(HAVE_GSSAPI_GSSAPI_H)
|
||||
#include <gssapi/gssapi.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_GSSAPI_GSSAPI_GENERIC_H)
|
||||
#include <gssapi/gssapi_generic.h>
|
||||
#endif
|
||||
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "plbase64.h"
|
||||
#include "plstr.h"
|
||||
#include "prprf.h"
|
||||
#include "prlog.h"
|
||||
#include "prmem.h"
|
||||
|
||||
#include "nsHttpGssapiAuth.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
//
|
||||
// in order to do logging, the following environment variables need to be set:
|
||||
//
|
||||
// set NSPR_LOG_MODULES=negotiateauth:4
|
||||
// set NSPR_LOG_FILE=negotiateauth.log
|
||||
//
|
||||
static PRLogModuleInfo* gNegotiateLog;
|
||||
|
||||
#define LOG(args) PR_LOG(gNegotiateLog, PR_LOG_DEBUG, args)
|
||||
|
||||
// Generate proper GSSAPI error messages from the major and
|
||||
// minor status codes.
|
||||
void
|
||||
LogGssError(OM_uint32 maj_stat, OM_uint32 min_stat, const char *prefix)
|
||||
{
|
||||
OM_uint32 new_stat;
|
||||
OM_uint32 msg_ctx = 0;
|
||||
gss_buffer_desc status1_string;
|
||||
gss_buffer_desc status2_string;
|
||||
OM_uint32 ret;
|
||||
nsCAutoString error(prefix);
|
||||
|
||||
error += ": ";
|
||||
do {
|
||||
ret = gss_display_status (&new_stat,
|
||||
maj_stat,
|
||||
GSS_C_GSS_CODE,
|
||||
GSS_C_NULL_OID,
|
||||
&msg_ctx,
|
||||
&status1_string);
|
||||
error += (const char *) status1_string.value;
|
||||
error += '\n';
|
||||
ret = gss_display_status (&new_stat,
|
||||
min_stat,
|
||||
GSS_C_MECH_CODE,
|
||||
GSS_C_NULL_OID,
|
||||
&msg_ctx,
|
||||
&status2_string);
|
||||
error += (const char *) status2_string.value;
|
||||
error += '\n';
|
||||
} while (!GSS_ERROR(ret) && msg_ctx != 0);
|
||||
|
||||
LOG(("%s\n", error.get()));
|
||||
}
|
||||
|
||||
#else /* PR_LOGGING */
|
||||
|
||||
#define LOG(args)
|
||||
#define LogGssError(x,y,z)
|
||||
|
||||
#endif /* PR_LOGGING */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static const char kNegotiate[] = "Negotiate";
|
||||
static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
|
||||
static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
|
||||
|
||||
#define kNegotiateLen (sizeof(kNegotiate)-1)
|
||||
|
||||
class nsGssapiContinuationState : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsGssapiContinuationState();
|
||||
~nsGssapiContinuationState() { Reset(); }
|
||||
|
||||
void Reset();
|
||||
|
||||
gss_OID GetOID() { return mech_oid; }
|
||||
|
||||
gss_ctx_id_t mCtx;
|
||||
private:
|
||||
gss_OID mech_oid;
|
||||
};
|
||||
|
||||
nsGssapiContinuationState::nsGssapiContinuationState()
|
||||
{
|
||||
OM_uint32 minstat, majstat;
|
||||
gss_OID_set mech_set;
|
||||
gss_OID item;
|
||||
unsigned int i;
|
||||
static gss_OID_desc gss_krb5_mech_oid_desc =
|
||||
{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
|
||||
static gss_OID_desc gss_spnego_mech_oid_desc =
|
||||
{6, (void *) "\x2b\x06\x01\x05\x05\x02"};
|
||||
|
||||
mCtx = GSS_C_NO_CONTEXT;
|
||||
mech_oid = &gss_krb5_mech_oid_desc;
|
||||
|
||||
int mech_found = 0;
|
||||
//
|
||||
// Now, look at the list of supported mechanisms,
|
||||
// if SPNEGO is found, then use it.
|
||||
// Otherwise, set the desired mechanism to
|
||||
// GSS_C_NO_OID and let the system try to use
|
||||
// the default mechanism.
|
||||
//
|
||||
// Using Kerberos directly (instead of negotiating
|
||||
// with SPNEGO) may work in some cases depending
|
||||
// on how smart the server side is.
|
||||
//
|
||||
majstat = gss_indicate_mechs(&minstat, &mech_set);
|
||||
if (GSS_ERROR(majstat))
|
||||
return;
|
||||
|
||||
for (i=0; i<mech_set->count && !mech_found; i++) {
|
||||
item = &mech_set->elements[i];
|
||||
if (item->length == gss_spnego_mech_oid_desc.length &&
|
||||
!memcmp(item->elements, gss_spnego_mech_oid_desc.elements,
|
||||
item->length)) {
|
||||
mech_found = 1;
|
||||
mech_oid = &gss_spnego_mech_oid_desc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gss_release_oid_set(&minstat, &mech_set);
|
||||
}
|
||||
|
||||
void
|
||||
nsGssapiContinuationState::Reset()
|
||||
{
|
||||
if (mCtx != GSS_C_NO_CONTEXT) {
|
||||
OM_uint32 minor_status;
|
||||
gss_delete_sec_context(&minor_status, &mCtx, GSS_C_NO_BUFFER);
|
||||
}
|
||||
mCtx = GSS_C_NO_CONTEXT;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS0(nsGssapiContinuationState)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsHttpGssapiAuth::nsHttpGssapiAuth()
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!gNegotiateLog)
|
||||
gNegotiateLog = PR_NewLogModule("negotiateauth");
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpGssapiAuth::GetAuthFlags(PRUint32 *flags)
|
||||
{
|
||||
//
|
||||
// GSSAPI creds should not be reused across multiple requests.
|
||||
// Only perform the negotiation when it is explicitly requested
|
||||
// by the server. Thus, do *NOT* use the "REUSABLE_CREDENTIALS"
|
||||
// flag here.
|
||||
//
|
||||
// CONNECTION_BASED is specified instead of REQUEST_BASED since
|
||||
// we need to complete a sequence of transactions with the server
|
||||
// over the same connection.
|
||||
//
|
||||
*flags = CONNECTION_BASED | IDENTITY_IGNORED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Always set *identityInvalid == FALSE here. This
|
||||
// will prevent the browser from popping up the authentication
|
||||
// prompt window. Because GSSAPI does not have an API
|
||||
// for fetching initial credentials (ex: A Kerberos TGT),
|
||||
// there is no correct way to get the users credentials.
|
||||
//
|
||||
NS_IMETHODIMP
|
||||
nsHttpGssapiAuth::ChallengeReceived(nsIHttpChannel *httpChannel,
|
||||
const char *challenge,
|
||||
PRBool isProxyAuth,
|
||||
nsISupports **sessionState,
|
||||
nsISupports **continuationState,
|
||||
PRBool *identityInvalid)
|
||||
{
|
||||
nsGssapiContinuationState *state = (nsGssapiContinuationState *) *continuationState;
|
||||
|
||||
*identityInvalid = PR_FALSE;
|
||||
|
||||
// proxy auth not supported
|
||||
if (isProxyAuth)
|
||||
return NS_ERROR_ABORT;
|
||||
|
||||
PRBool allowed = TestPref(httpChannel, kNegotiateAuthTrustedURIs);
|
||||
if (!allowed) {
|
||||
LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n"));
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
//
|
||||
// Use this opportunity to instantiate the state object
|
||||
// that gets used later when we generate the credentials.
|
||||
//
|
||||
|
||||
if (!state) {
|
||||
state = new nsGssapiContinuationState();
|
||||
if (!state)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(*continuationState = state);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsHttpGssapiAuth, nsIHttpAuthenticator);
|
||||
|
||||
//
|
||||
// GenerateCredentials
|
||||
//
|
||||
// This routine is responsible for creating the correct authentication
|
||||
// blob to pass to the server that requested "Negotiate" authentication.
|
||||
//
|
||||
NS_IMETHODIMP
|
||||
nsHttpGssapiAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
|
||||
const char *challenge,
|
||||
PRBool isProxyAuth,
|
||||
const PRUnichar *domain,
|
||||
const PRUnichar *username,
|
||||
const PRUnichar *password,
|
||||
nsISupports **sessionState,
|
||||
nsISupports **continuationState,
|
||||
char **creds)
|
||||
{
|
||||
OM_uint32 major_status, minor_status;
|
||||
OM_uint32 req_flags = GSS_C_MUTUAL_FLAG;
|
||||
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
|
||||
gss_buffer_t in_token_ptr = GSS_C_NO_BUFFER;
|
||||
gss_name_t server;
|
||||
nsGssapiContinuationState *state = (nsGssapiContinuationState *) *continuationState;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv;
|
||||
nsCString service;
|
||||
|
||||
LOG(("nsHttpGssapiAuth::GenerateCredentials() [challenge=%s]\n", challenge));
|
||||
|
||||
NS_ASSERTION(creds, "null param");
|
||||
|
||||
PRBool isGssapiAuth =
|
||||
!PL_strncasecmp(challenge, kNegotiate, kNegotiateLen);
|
||||
NS_ENSURE_TRUE(isGssapiAuth, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// proxy auth not supported
|
||||
if (isProxyAuth)
|
||||
return NS_ERROR_ABORT;
|
||||
|
||||
PRBool delegation = TestPref(httpChannel, kNegotiateAuthDelegationURIs);
|
||||
if (delegation) {
|
||||
LOG((" using GSS_C_DELEG_FLAG\n"));
|
||||
req_flags |= GSS_C_DELEG_FLAG;
|
||||
}
|
||||
|
||||
rv = httpChannel->GetURI(getter_AddRefs(uri));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = uri->GetAsciiHost(service);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
LOG((" hostname = %s\n", service.get()));
|
||||
|
||||
//
|
||||
// The correct service name for IIS servers is "HTTP/f.q.d.n", so
|
||||
// construct the proper service name for passing to "gss_import_name".
|
||||
//
|
||||
// TODO: Possibly make this a configurable service name for use
|
||||
// with non-standard servers that use stuff like "khttp/f.q.d.n"
|
||||
// instead.
|
||||
//
|
||||
service.Insert("HTTP@", 0);
|
||||
|
||||
input_token.value = (void *)service.get();
|
||||
input_token.length = service.Length() + 1;
|
||||
|
||||
major_status = gss_import_name(&minor_status,
|
||||
&input_token,
|
||||
#ifdef HAVE_GSS_C_NT_HOSTBASED_SERVICE
|
||||
GSS_C_NT_HOSTBASED_SERVICE,
|
||||
#else
|
||||
gss_nt_service_name,
|
||||
#endif
|
||||
&server);
|
||||
input_token.value = NULL;
|
||||
input_token.length = 0;
|
||||
if (GSS_ERROR(major_status)) {
|
||||
LogGssError(major_status, minor_status, "gss_import_name() failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//
|
||||
// If the "Negotiate:" header had some data associated with it,
|
||||
// that data should be used as the input to this call. This may
|
||||
// be a continuation of an earlier call because GSSAPI authentication
|
||||
// often takes multiple round-trips to complete depending on the
|
||||
// context flags given. We want to use MUTUAL_AUTHENTICATION which
|
||||
// generally *does* require multiple round-trips. Don't assume
|
||||
// auth can be completed in just 1 call.
|
||||
//
|
||||
unsigned int len = strlen(challenge);
|
||||
|
||||
if (len > kNegotiateLen) {
|
||||
challenge += kNegotiateLen;
|
||||
while (*challenge == ' ') challenge++;
|
||||
len = strlen(challenge);
|
||||
|
||||
input_token.length = (len * 3)/4;
|
||||
input_token.value = malloc(input_token.length);
|
||||
if (!input_token.value)
|
||||
return (NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
//
|
||||
// Decode the response that followed the "Negotiate" token
|
||||
//
|
||||
if (PL_Base64Decode(challenge, len, (char *) input_token.value) == NULL) {
|
||||
free(input_token.value);
|
||||
return(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
in_token_ptr = &input_token;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Starting over, clear out any existing context and don't
|
||||
// use an input token.
|
||||
//
|
||||
state->Reset();
|
||||
in_token_ptr = GSS_C_NO_BUFFER;
|
||||
}
|
||||
|
||||
major_status = gss_init_sec_context(&minor_status,
|
||||
GSS_C_NO_CREDENTIAL,
|
||||
&state->mCtx,
|
||||
server,
|
||||
state->GetOID(),
|
||||
req_flags,
|
||||
GSS_C_INDEFINITE,
|
||||
GSS_C_NO_CHANNEL_BINDINGS,
|
||||
in_token_ptr,
|
||||
nsnull,
|
||||
&output_token,
|
||||
nsnull,
|
||||
nsnull);
|
||||
|
||||
if (GSS_ERROR(major_status)) {
|
||||
LogGssError(major_status, minor_status, "gss_init_sec_context() failed");
|
||||
gss_release_name(&minor_status, &server);
|
||||
state->Reset();
|
||||
if (input_token.length > 0 && input_token.value != NULL)
|
||||
gss_release_buffer(&minor_status, &input_token);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (major_status == GSS_S_COMPLETE) {
|
||||
//
|
||||
// We are done with this authentication, reset the context.
|
||||
//
|
||||
state->Reset();
|
||||
}
|
||||
else if (major_status == GSS_S_CONTINUE_NEEDED) {
|
||||
//
|
||||
// The important thing is that we do NOT reset the
|
||||
// context here because it will be needed on the
|
||||
// next call.
|
||||
//
|
||||
}
|
||||
|
||||
// We don't need the input token data anymore.
|
||||
if (input_token.length > 0 && input_token.value != NULL)
|
||||
gss_release_buffer(&minor_status, &input_token);
|
||||
|
||||
if (output_token.length == 0) {
|
||||
LOG((" No GSS output token to send, exiting"));
|
||||
gss_release_name(&minor_status, &server);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//
|
||||
// The token output from the gss_init_sec_context call is
|
||||
// encoded and used as the Authentication response for the
|
||||
// server.
|
||||
//
|
||||
char *encoded_token = PL_Base64Encode((char *)output_token.value,
|
||||
output_token.length,
|
||||
nsnull);
|
||||
if (!encoded_token) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
LOG((" Sending a token of length %d\n", output_token.length));
|
||||
|
||||
// allocate a buffer sizeof("Negotiate" + " " + b64output_token + "\0")
|
||||
*creds = (char *) nsMemory::Alloc (kNegotiateLen + 1 + strlen(encoded_token) + 1);
|
||||
if (!(*creds)) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
sprintf(*creds, "%s %s", kNegotiate, encoded_token);
|
||||
rv = NS_OK;
|
||||
|
||||
end:
|
||||
if (encoded_token)
|
||||
PR_Free(encoded_token);
|
||||
|
||||
gss_release_buffer(&minor_status, &output_token);
|
||||
gss_release_name(&minor_status, &server);
|
||||
|
||||
LOG((" returning the call"));
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpGssapiAuth::TestPref(nsIHttpChannel *httpChannel, const char *pref)
|
||||
{
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (!prefs)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
httpChannel->GetURI(getter_AddRefs(uri));
|
||||
if (!uri)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCAutoString scheme, host;
|
||||
PRInt32 port;
|
||||
|
||||
if (NS_FAILED(uri->GetScheme(scheme)))
|
||||
return PR_FALSE;
|
||||
if (NS_FAILED(uri->GetAsciiHost(host)))
|
||||
return PR_FALSE;
|
||||
if (NS_FAILED(uri->GetPort(&port)))
|
||||
return PR_FALSE;
|
||||
|
||||
char *hostList;
|
||||
if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
|
||||
return PR_FALSE;
|
||||
|
||||
// pseudo-BNF
|
||||
// ----------
|
||||
//
|
||||
// url-list base-url ( base-url "," LWS )*
|
||||
// base-url ( scheme-part | host-part | scheme-part host-part )
|
||||
// scheme-part scheme "://"
|
||||
// host-part host [":" port]
|
||||
//
|
||||
// for example:
|
||||
// "https://, http://office.foo.com"
|
||||
//
|
||||
|
||||
char *start = hostList, *end;
|
||||
for (;;) {
|
||||
// skip past any whitespace
|
||||
while (*start == ' ' || *start == '\t')
|
||||
++start;
|
||||
end = strchr(start, ',');
|
||||
if (!end)
|
||||
end = start + strlen(start);
|
||||
if (start == end)
|
||||
break;
|
||||
if (MatchesBaseURI(scheme, host, port, start, end))
|
||||
return PR_TRUE;
|
||||
if (*end == '\0')
|
||||
break;
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
nsMemory::Free(hostList);
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpGssapiAuth::MatchesBaseURI(const nsCSubstring &matchScheme,
|
||||
const nsCSubstring &matchHost,
|
||||
PRInt32 matchPort,
|
||||
const char *baseStart,
|
||||
const char *baseEnd)
|
||||
{
|
||||
// check if scheme://host:port matches baseURI
|
||||
|
||||
// parse the base URI
|
||||
const char *hostStart, *schemeEnd = strstr(baseStart, "://");
|
||||
if (schemeEnd) {
|
||||
// the given scheme must match the parsed scheme exactly
|
||||
if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
|
||||
return PR_FALSE;
|
||||
hostStart = schemeEnd + 3;
|
||||
}
|
||||
else
|
||||
hostStart = baseStart;
|
||||
|
||||
// XXX this does not work for IPv6-literals
|
||||
const char *hostEnd = strchr(hostStart, ':');
|
||||
if (hostEnd) {
|
||||
// the given port must match the parsed port exactly
|
||||
int port = atoi(hostEnd + 1);
|
||||
if (matchPort != (PRInt32) port)
|
||||
return PR_FALSE;
|
||||
}
|
||||
else
|
||||
hostEnd = baseEnd;
|
||||
|
||||
|
||||
// if we didn't parse out a host, then assume we got a match.
|
||||
if (hostStart == hostEnd)
|
||||
return PR_TRUE;
|
||||
|
||||
PRUint32 hostLen = hostEnd - hostStart;
|
||||
|
||||
// now, allow host to be a subdomain of matchHost
|
||||
if (matchHost.Length() > hostLen)
|
||||
return PR_FALSE;
|
||||
|
||||
const char *end = matchHost.EndReading();
|
||||
if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
|
||||
// if matchHost ends with host from the base URI, then make sure it is
|
||||
// either an exact match, or prefixed with a dot. we don't want
|
||||
// "foobar.com" to match "bar.com"
|
||||
if (matchHost.Length() == hostLen ||
|
||||
*(end - hostLen) == '.' ||
|
||||
*(end - hostLen - 1) == '.')
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
77
extensions/negotiateauth/nsHttpGssapiAuth.h
Normal file
77
extensions/negotiateauth/nsHttpGssapiAuth.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* vim:set ts=4 sw=4 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 the Negotiateauth
|
||||
*
|
||||
* The Initial Developer of the Original Code is Daniel Kouril.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2003
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Daniel Kouril <kouril@ics.muni.cz> (original author)
|
||||
* Wyllys Ingersoll <wyllys.ingersoll@sun.com>
|
||||
* Christopher Nebergall <cneberg@sandia.gov>
|
||||
* Darin Fisher <darin@meer.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsHttpGssapiAuth_h__
|
||||
#define nsHttpGssapiAuth_h__
|
||||
|
||||
#include "nsIHttpAuthenticator.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsSubstring.h"
|
||||
|
||||
#define NS_HTTPGSSAPIAUTH_CID \
|
||||
{ /* 75c80fd0-accb-432c-af59-ec60668c3990 */ \
|
||||
0x75c80fd0, \
|
||||
0xaccb, \
|
||||
0x432c, \
|
||||
{0xaf, 0x59, 0xec, 0x60, 0x66, 0x8c, 0x39, 0x90} \
|
||||
}
|
||||
|
||||
// The nsGssapiAuth class provides responses for the GSS-API Negotiate method
|
||||
// as specified by Microsoft in draft-brezak-spnego-http-04.txt
|
||||
|
||||
class nsHttpGssapiAuth : public nsIHttpAuthenticator
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIHTTPAUTHENTICATOR
|
||||
|
||||
nsHttpGssapiAuth();
|
||||
|
||||
private:
|
||||
// returns true if channel is accepted by the list of hosts in the pref
|
||||
PRBool TestPref(nsIHttpChannel *, const char *pref);
|
||||
|
||||
PRBool MatchesBaseURI(const nsCSubstring &scheme,
|
||||
const nsCSubstring &host,
|
||||
PRInt32 port,
|
||||
const char *baseStart,
|
||||
const char *baseEnd);
|
||||
};
|
||||
#endif /* nsHttpGssapiAuth_h__ */
|
55
extensions/negotiateauth/nsHttpGssapiAuthModule.cpp
Normal file
55
extensions/negotiateauth/nsHttpGssapiAuthModule.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* 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 the Negotiateauth
|
||||
*
|
||||
* The Initial Developer of the Original Code is Daniel Kouril.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2003
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Daniel Kouril <kouril@ics.muni.cz> (original author)
|
||||
* Wyllys Ingersoll <wyllys.ingersoll@sun.com>
|
||||
* Christopher Nebergall <cneberg@sandia.gov>
|
||||
* Darin Fisher <darin@meer.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "nsHttpGssapiAuth.h"
|
||||
|
||||
// macro expansion defines our factory constructor method
|
||||
// used by the components[] array below.
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpGssapiAuth)
|
||||
|
||||
static nsModuleComponentInfo components[] = {
|
||||
{ "nsHttpGssapiAuth",
|
||||
NS_HTTPGSSAPIAUTH_CID,
|
||||
NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "negotiate",
|
||||
nsHttpGssapiAuthConstructor,
|
||||
},
|
||||
};
|
||||
|
||||
NS_IMPL_NSGETMODULE(nsHttpGssapiAuthModule, components)
|
@ -532,6 +532,10 @@ pref("network.http.proxy.pipelining", false);
|
||||
pref("network.http.pipelining.maxrequests" , 4);
|
||||
|
||||
pref("network.http.proxy.ssl.connect",true);
|
||||
|
||||
// Whether to warn if the user connects to a site with authentication
|
||||
// information that isn't required.
|
||||
pref("network.http.confirm_superfluous_auth",true);
|
||||
// </http>
|
||||
|
||||
// This preference controls whether or not internationalized domain names (IDN)
|
||||
@ -560,6 +564,20 @@ pref("network.dir.format", 2);
|
||||
// enables the prefetch service (i.e., prefetching of <link rel="next"> URLs).
|
||||
pref("network.prefetch-next", true);
|
||||
|
||||
|
||||
// The following prefs pertain to the negotiate-auth extension (see bug 17578),
|
||||
// which provides transparent Kerberos authentication using the SPNEGO protocol.
|
||||
// Each pref is a comma-separated list of keys, where each key has the format:
|
||||
// [scheme "://"] [host [":" port]]
|
||||
// For example, "foo.com" would match "http://www.foo.com/bar", etc.
|
||||
|
||||
// This list controls which URIs can support the negotiate auth protocol. This
|
||||
// list should be limited to the servers you know you'll need to login to.
|
||||
pref("network.negotiate-auth.trusted-uris", "https://");
|
||||
// This list controls which URIs can support delegation.
|
||||
pref("network.negotiate-auth.delegation-uris", "");
|
||||
|
||||
|
||||
// sspitzer: change this back to "news" when we get to beta.
|
||||
// for now, set this to news.mozilla.org because you can only
|
||||
// post to the server specified by this pref.
|
||||
|
Loading…
Reference in New Issue
Block a user