From 6160106c7495509310b709ee320b55cea8a60928 Mon Sep 17 00:00:00 2001
From: Magnus Hagander <magnus@hagander.net>
Date: Tue, 10 Jul 2007 13:14:22 +0000
Subject: [PATCH] Add support for GSSAPI authentication.

Documentation still being written, will be committed later.

Henry B. Hotz and Magnus Hagander
---
 configure                                     | 334 +++++++++++++++++-
 configure.in                                  |  28 +-
 src/backend/libpq/auth.c                      | 273 +++++++++++++-
 src/backend/libpq/hba.c                       |   4 +-
 src/backend/libpq/pg_hba.conf.sample          |   2 +-
 src/backend/libpq/pqcomm.c                    |  12 +-
 src/backend/postmaster/postmaster.c           |  11 +-
 src/backend/utils/misc/guc.c                  |   4 +-
 src/backend/utils/misc/postgresql.conf.sample |   6 +-
 src/include/libpq/hba.h                       |   5 +-
 src/include/libpq/libpq-be.h                  |  31 +-
 src/include/libpq/pqcomm.h                    |   4 +-
 src/include/pg_config.h.in                    |   3 +
 src/interfaces/libpq/Makefile                 |   6 +-
 src/interfaces/libpq/fe-auth.c                | 209 ++++++++++-
 src/interfaces/libpq/fe-connect.c             |  65 +++-
 src/interfaces/libpq/libpq-int.h              |  21 +-
 17 files changed, 989 insertions(+), 29 deletions(-)

diff --git a/configure b/configure
index 4bfae7d163..84db9ee1c3 100755
--- a/configure
+++ b/configure
@@ -314,7 +314,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_zlib EGREP ELF_SYS LDFLAGS_SL AWK FLEX FLEXFLAGS LN_S LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB TAR STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB YACC YFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_zlib EGREP ELF_SYS LDFLAGS_SL AWK FLEX FLEXFLAGS LN_S LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB TAR STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB YACC YFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS'
 ac_subst_files=''
 
 # Initialize some variables set by options.
@@ -887,6 +887,7 @@ Optional Packages:
   --with-tclconfig=DIR    tclConfig.sh is in DIR
   --with-perl             build Perl modules (PL/Perl)
   --with-python           build Python modules (PL/Python)
+  --with-gssapi           build with GSSAPI support
   --with-krb5             build with Kerberos 5 support
   --with-krb-srvnam=NAME  default service principal name in Kerberos [postgres]
   --with-pam              build with PAM support
@@ -3919,6 +3920,50 @@ echo "$as_me:$LINENO: result: $with_python" >&5
 echo "${ECHO_T}$with_python" >&6
 
 
+#
+# GSSAPI
+#
+echo "$as_me:$LINENO: checking wether to build with GSSAPI support" >&5
+echo $ECHO_N "checking wether to build with GSSAPI support... $ECHO_C" >&6
+
+pgac_args="$pgac_args with_gssapi"
+
+
+# Check whether --with-gssapi or --without-gssapi was given.
+if test "${with_gssapi+set}" = set; then
+  withval="$with_gssapi"
+
+  case $withval in
+    yes)
+
+
+cat >>confdefs.h <<\_ACEOF
+#define ENABLE_GSS 1
+_ACEOF
+
+  krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab"
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      { { echo "$as_me:$LINENO: error: no argument expected for --with-gssapi option" >&5
+echo "$as_me: error: no argument expected for --with-gssapi option" >&2;}
+   { (exit 1); exit 1; }; }
+      ;;
+  esac
+
+else
+  with_gssapi=no
+
+fi;
+
+echo "$as_me:$LINENO: result: $with_gssapi" >&5
+echo "${ECHO_T}$with_gssapi" >&6
+
+
+
 #
 # Kerberos 5
 #
@@ -6718,6 +6763,143 @@ echo "$as_me: WARNING:
 *** Not using spinlocks will cause poor performance." >&2;}
 fi
 
+if test "$with_gssapi" = yes ; then
+  if test "$PORTNAME" != "win32"; then
+  	echo "$as_me:$LINENO: checking for library containing gss_init_sec_context" >&5
+echo $ECHO_N "checking for library containing gss_init_sec_context... $ECHO_C" >&6
+if test "${ac_cv_search_gss_init_sec_context+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_gss_init_sec_context=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char gss_init_sec_context ();
+int
+main ()
+{
+gss_init_sec_context ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_gss_init_sec_context="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_gss_init_sec_context" = no; then
+  for ac_lib in gssapi_krb5; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char gss_init_sec_context ();
+int
+main ()
+{
+gss_init_sec_context ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_gss_init_sec_context="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_gss_init_sec_context" >&5
+echo "${ECHO_T}$ac_cv_search_gss_init_sec_context" >&6
+if test "$ac_cv_search_gss_init_sec_context" != no; then
+  test "$ac_cv_search_gss_init_sec_context" = "none required" || LIBS="$ac_cv_search_gss_init_sec_context $LIBS"
+
+else
+  { { echo "$as_me:$LINENO: error: could not find function 'gss_init_sec_context' required for GSSAPI" >&5
+echo "$as_me: error: could not find function 'gss_init_sec_context' required for GSSAPI" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+  else
+    LIBS="$LIBS -lgssapi32"
+  fi
+fi
+
 if test "$with_krb5" = yes ; then
   if test "$PORTNAME" != "win32"; then
      echo "$as_me:$LINENO: checking for library containing com_err" >&5
@@ -10014,6 +10196,155 @@ Use --without-zlib to disable zlib support." >&2;}
 fi
 
 
+fi
+
+if test "$with_gssapi" = yes ; then
+  if test "${ac_cv_header_gssapi_gssapi_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for gssapi/gssapi.h" >&5
+echo $ECHO_N "checking for gssapi/gssapi.h... $ECHO_C" >&6
+if test "${ac_cv_header_gssapi_gssapi_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_gssapi_gssapi_h" >&5
+echo "${ECHO_T}$ac_cv_header_gssapi_gssapi_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking gssapi/gssapi.h usability" >&5
+echo $ECHO_N "checking gssapi/gssapi.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <gssapi/gssapi.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking gssapi/gssapi.h presence" >&5
+echo $ECHO_N "checking gssapi/gssapi.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <gssapi/gssapi.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: gssapi/gssapi.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: gssapi/gssapi.h: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------- ##
+## Report this to pgsql-bugs@postgresql.org ##
+## ---------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for gssapi/gssapi.h" >&5
+echo $ECHO_N "checking for gssapi/gssapi.h... $ECHO_C" >&6
+if test "${ac_cv_header_gssapi_gssapi_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_gssapi_gssapi_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_gssapi_gssapi_h" >&5
+echo "${ECHO_T}$ac_cv_header_gssapi_gssapi_h" >&6
+
+fi
+if test $ac_cv_header_gssapi_gssapi_h = yes; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: header file <gssapi/gssapi.h> is required for GSSAPI" >&5
+echo "$as_me: error: header file <gssapi/gssapi.h> is required for GSSAPI" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
 fi
 
 if test "$with_krb5" = yes ; then
@@ -24618,6 +24949,7 @@ s,@enable_thread_safety@,$enable_thread_safety,;t t
 s,@with_tcl@,$with_tcl,;t t
 s,@with_perl@,$with_perl,;t t
 s,@with_python@,$with_python,;t t
+s,@with_gssapi@,$with_gssapi,;t t
 s,@with_krb5@,$with_krb5,;t t
 s,@krb_srvtab@,$krb_srvtab,;t t
 s,@with_pam@,$with_pam,;t t
diff --git a/configure.in b/configure.in
index c44da58b96..ffe4b2bf06 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $PostgreSQL: pgsql/configure.in,v 1.516 2007/06/29 16:18:43 tgl Exp $
+dnl $PostgreSQL: pgsql/configure.in,v 1.517 2007/07/10 13:14:20 mha Exp $
 dnl
 dnl Developers, please strive to achieve this order:
 dnl
@@ -462,6 +462,19 @@ PGAC_ARG_BOOL(with, python, no, [  --with-python           build Python modules
 AC_MSG_RESULT([$with_python])
 AC_SUBST(with_python)
 
+#
+# GSSAPI
+#
+AC_MSG_CHECKING([wether to build with GSSAPI support])
+PGAC_ARG_BOOL(with, gssapi, no, [  --with-gssapi           build with GSSAPI support],
+[
+  AC_DEFINE(ENABLE_GSS, 1, [Define to build with GSSAPI support. (--with-gssapi)])
+  krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab"
+])
+AC_MSG_RESULT([$with_gssapi])
+AC_SUBST(with_gssapi)
+
+
 #
 # Kerberos 5
 #
@@ -753,6 +766,15 @@ else
 *** Not using spinlocks will cause poor performance.])
 fi
 
+if test "$with_gssapi" = yes ; then
+  if test "$PORTNAME" != "win32"; then
+  	AC_SEARCH_LIBS(gss_init_sec_context, [gssapi_krb5], [],
+		 		  [AC_MSG_ERROR([could not find function 'gss_init_sec_context' required for GSSAPI])])
+  else
+    LIBS="$LIBS -lgssapi32"
+  fi
+fi
+
 if test "$with_krb5" = yes ; then
   if test "$PORTNAME" != "win32"; then
      AC_SEARCH_LIBS(com_err, [krb5 'krb5 -lcrypto -ldes -lasn1 -lroken' com_err], [],
@@ -848,6 +870,10 @@ failure.  It is possible the compiler isn't looking in the proper directory.
 Use --without-zlib to disable zlib support.])])
 fi
 
+if test "$with_gssapi" = yes ; then
+  AC_CHECK_HEADER(gssapi/gssapi.h, [], [AC_MSG_ERROR([header file <gssapi/gssapi.h> is required for GSSAPI])])
+fi
+
 if test "$with_krb5" = yes ; then
   AC_CHECK_HEADER(krb5.h, [], [AC_MSG_ERROR([header file <krb5.h> is required for Kerberos 5])])
 fi
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 37bfa81abd..dafc965445 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.148 2007/02/08 04:52:18 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.149 2007/07/10 13:14:20 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #endif
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <unistd.h>
 
 #include "libpq/auth.h"
 #include "libpq/crypt.h"
@@ -295,6 +296,250 @@ pg_krb5_recvauth(Port *port)
 }
 #endif   /* KRB5 */
 
+#ifdef ENABLE_GSS
+/*----------------------------------------------------------------
+ * GSSAPI authentication system
+ *----------------------------------------------------------------
+ */
+
+#include <gssapi/gssapi.h>
+
+#ifdef WIN32
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
+ {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+#endif
+
+
+static void
+pg_GSS_error(int severity, char *text, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+	gss_buffer_desc	gmsg;
+	OM_uint32		lmaj_s, lmin_s, msg_ctx;
+	char			localmsg1[128],
+					localmsg2[128];
+
+	/* Fetch major status message */
+	msg_ctx = 0;
+	lmaj_s = gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+			GSS_C_NO_OID, &msg_ctx, &gmsg);
+	strlcpy(localmsg1, gmsg.value, sizeof(localmsg1));
+	gss_release_buffer(&lmin_s, &gmsg);
+
+	if (msg_ctx)
+		/* More than one message available.
+		 * XXX: Should we loop and read all messages?
+		 * (same below)
+		 */
+		ereport(WARNING, 
+				(errmsg_internal("incomplete GSS error report")));
+
+	/* Fetch mechanism minor status message */
+	msg_ctx = 0;
+	lmaj_s = gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+			GSS_C_NO_OID, &msg_ctx, &gmsg);
+	strlcpy(localmsg2, gmsg.value, sizeof(localmsg2));
+	gss_release_buffer(&lmin_s, &gmsg);
+
+	if (msg_ctx)
+		ereport(WARNING,
+				(errmsg_internal("incomplete GSS minor error report")));
+
+	/* errmsg_internal, since translation of the first part must be
+	 * done before calling this function anyway. */
+	ereport(severity,
+			(errmsg_internal("%s:%s\n%s", text, localmsg1, localmsg2)));
+}
+
+static int
+pg_GSS_recvauth(Port *port)
+{
+	OM_uint32		maj_stat, min_stat, lmin_s, gflags;
+	char		   *kt_path;
+	int				mtype;
+	int				ret;
+	StringInfoData	buf;
+	gss_buffer_desc	gbuf;
+
+	if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+	{
+		/*
+		 * Set default Kerberos keytab file for the Krb5 mechanism.
+		 *
+		 * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0);
+		 *		except setenv() not always available.
+		 */
+		if (!getenv("KRB5_KTNAME"))
+		{
+			kt_path = palloc(PATH_MAX + 13);
+			snprintf(kt_path, PATH_MAX + 13,
+					"KRB5_KTNAME=%s", pg_krb_server_keyfile);
+			putenv(kt_path);
+		}
+	}
+
+	/*
+	 * We accept any service principal that's present in our
+	 * keytab. This increases interoperability between kerberos
+	 * implementations that see for example case sensitivity
+	 * differently, while not really opening up any vector
+	 * of attack.
+	 */
+	port->gss->cred = GSS_C_NO_CREDENTIAL;
+
+	/*
+	 * Initialize sequence with an empty context
+	 */
+	port->gss->ctx = GSS_C_NO_CONTEXT;
+
+	/*
+	 * Loop through GSSAPI message exchange. This exchange can consist
+	 * of multiple messags sent in both directions. First message is always
+	 * from the client. All messages from client to server are password
+	 * packets (type 'p').
+	 */
+	do 
+	{
+		mtype = pq_getbyte();
+		if (mtype != 'p')
+		{
+			/* Only log error if client didn't disconnect. */
+			if (mtype != EOF)
+				ereport(COMMERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("expected GSS response, got message type %d",
+							 mtype)));
+			return STATUS_ERROR;
+		}
+
+		/* Get the actual GSS token */
+		initStringInfo(&buf);
+		if (pq_getmessage(&buf, 2000))
+		{
+			/* EOF - pq_getmessage already logged error */
+			pfree(buf.data);
+			return STATUS_ERROR;
+		}
+
+		/* Map to GSSAPI style buffer */
+		gbuf.length = buf.len;
+		gbuf.value = buf.data;
+
+		ereport(DEBUG4,
+				(errmsg_internal("Processing received GSS token of length: %u",
+								 gbuf.length)));
+
+		maj_stat = gss_accept_sec_context(
+				&min_stat,
+				&port->gss->ctx,
+				port->gss->cred,
+				&gbuf,
+				GSS_C_NO_CHANNEL_BINDINGS,
+				&port->gss->name,
+				NULL,
+				&port->gss->outbuf,
+				&gflags,
+				NULL,
+				NULL);
+
+		/* gbuf no longer used */
+		pfree(buf.data);
+
+		ereport(DEBUG5,
+				(errmsg_internal("gss_accept_sec_context major: %i, "
+								 "minor: %i, outlen: %u, outflags: %x",
+								 maj_stat, min_stat, 
+								 port->gss->outbuf.length, gflags)));
+
+		if (port->gss->outbuf.length != 0)
+		{
+			/*
+			 * Negotiation generated data to be sent to the client.
+			 */
+			ereport(DEBUG4,
+					(errmsg_internal("sending GSS response token of length %u",
+									 port->gss->outbuf.length)));
+			sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+		}
+
+		if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+		{
+			OM_uint32	lmin_s;
+			gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
+			pg_GSS_error(ERROR, 
+					gettext_noop("accepting GSS security context failed"),
+					maj_stat, min_stat);
+		}
+
+		if (maj_stat == GSS_S_CONTINUE_NEEDED)
+			ereport(DEBUG4,
+					(errmsg_internal("GSS continue needed")));
+
+	} while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+	if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+	{
+		/*
+		 * Release service principal credentials
+		 */
+		gss_release_cred(&min_stat, port->gss->cred);
+	}
+
+	/*
+	 * GSS_S_COMPLETE indicates that authentication is now complete.
+	 *
+	 * Get the name of the user that authenticated, and compare it to the
+	 * pg username that was specified for the connection.
+	 */
+	maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
+	ereport(DEBUG1,
+			(errmsg("GSSAPI authenticated name: %s", (char *)gbuf.value)));
+
+	/*
+	 * Compare the part of the username that comes before the @
+	 * sign only (ignore realm). The GSSAPI libraries won't have 
+	 * authenticated the user if he's from an invalid realm.
+	 */
+	if (strchr(gbuf.value, '@'))
+	{
+		char *cp = strchr(gbuf.value, '@');
+		*cp = '\0';
+	}
+
+	if (pg_krb_caseins_users)
+		ret = pg_strcasecmp(port->user_name, gbuf.value);
+	else
+		ret = strcmp(port->user_name, gbuf.value);
+
+	if (ret)
+		/* GSS name and PGUSER are not equivalent */
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+				 errmsg("provided username and GSSAPI username don't match"),
+				 errdetail("provided: %s, GSSAPI: %s",
+					 port->user_name, (char *)gbuf.value)));
+	
+	gss_release_buffer(&lmin_s, &gbuf);
+
+	return STATUS_OK;
+}
+
+#else	/* no ENABLE_GSS */
+static int
+pg_GSS_recvauth(Port *port)
+{
+	ereport(LOG,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("GSSAPI not implemented on this server.")));
+	return STATUS_ERROR;
+}
+#endif	/* ENABLE_GSS */
+
 
 /*
  * Tell the user the authentication failed, but not (much about) why.
@@ -334,6 +579,9 @@ auth_failed(Port *port, int status)
 		case uaKrb5:
 			errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
 			break;
+		case uaGSS:
+			errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
+			break;
 		case uaTrust:
 			errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
 			break;
@@ -429,6 +677,11 @@ ClientAuthentication(Port *port)
 			status = pg_krb5_recvauth(port);
 			break;
 
+		case uaGSS:
+			sendAuthRequest(port, AUTH_REQ_GSS);
+			status = pg_GSS_recvauth(port);
+			break;
+
 		case uaIdent:
 
 			/*
@@ -518,6 +771,24 @@ sendAuthRequest(Port *port, AuthRequest areq)
 	else if (areq == AUTH_REQ_CRYPT)
 		pq_sendbytes(&buf, port->cryptSalt, 2);
 
+#ifdef ENABLE_GSS
+	/* Add the authentication data for the next step of
+	 * the GSSAPI negotiation. */
+	else if (areq == AUTH_REQ_GSS_CONT)
+	{
+		if (port->gss->outbuf.length > 0)
+		{
+			OM_uint32	lmin_s;
+
+			ereport(DEBUG4, 
+					(errmsg_internal("sending GSS token of length %u",
+									 port->gss->outbuf.length)));
+			pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
+			gss_release_buffer(&lmin_s, &port->gss->outbuf);
+		}
+	}
+#endif
+
 	pq_endmessage(&buf);
 
 	/*
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index a3accd47c5..9f917b1666 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.160 2007/02/10 14:58:54 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.161 2007/07/10 13:14:20 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -602,6 +602,8 @@ parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
 		*userauth_p = uaPassword;
 	else if (strcmp(token, "krb5") == 0)
 		*userauth_p = uaKrb5;
+	else if (strcmp(token, "gss") == 0)
+		*userauth_p = uaGSS;
 	else if (strcmp(token, "reject") == 0)
 		*userauth_p = uaReject;
 	else if (strcmp(token, "md5") == 0)
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index f52d1bfe81..ebdf76904a 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -34,7 +34,7 @@
 # the number of significant bits in the mask.  Alternatively, you can write
 # an IP address and netmask in separate columns to specify the set of hosts.
 #
-# METHOD can be "trust", "reject", "md5", "crypt", "password",
+# METHOD can be "trust", "reject", "md5", "crypt", "password", "gss",
 # "krb5", "ident", "pam" or "ldap".  Note that "password" sends passwords
 # in clear text; "md5" is preferred since it sends encrypted passwords.
 #
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index ee590be1dd..a40e6a6cb2 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -30,7 +30,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.192 2007/06/04 11:59:20 mha Exp $
+ *	$PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.193 2007/07/10 13:14:20 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -173,6 +173,16 @@ pq_close(int code, Datum arg)
 {
 	if (MyProcPort != NULL)
 	{
+#ifdef ENABLE_GSS
+		OM_uint32	min_s;
+		/* Shutdown GSSAPI layer */
+		if (MyProcPort->gss->ctx)
+			gss_delete_sec_context(&min_s, MyProcPort->gss->ctx, NULL);
+
+		if (MyProcPort->gss->cred)
+			gss_release_cred(&min_s, MyProcPort->gss->cred);
+#endif
+
 		/* Cleanly shut down SSL layer */
 		secure_close(MyProcPort);
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index b0dfb6cdbe..feb3db6402 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.530 2007/07/01 18:28:41 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.531 2007/07/10 13:14:21 mha Exp $
  *
  * NOTES
  *
@@ -1727,6 +1727,13 @@ ConnCreate(int serverFd)
 		RandomSalt(port->cryptSalt, port->md5Salt);
 	}
 
+	/*
+     * Allocate GSSAPI specific state struct
+	 */
+#ifdef ENABLE_GSS
+	port->gss = (pg_gssinfo *)calloc(1, sizeof(pg_gssinfo));
+#endif
+
 	return port;
 }
 
@@ -1740,6 +1747,8 @@ ConnFree(Port *conn)
 #ifdef USE_SSL
 	secure_close(conn);
 #endif
+	if (conn->gss)
+		free(conn->gss);
 	free(conn);
 }
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a542a22565..feb5354940 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.404 2007/06/30 19:12:02 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.405 2007/07/10 13:14:21 mha Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -1040,7 +1040,7 @@ static struct config_bool ConfigureNamesBool[] =
 
 	{
 		{"krb_caseins_users", PGC_POSTMASTER, CONN_AUTH_SECURITY,
-			gettext_noop("Sets whether Kerberos user names should be treated as case-insensitive."),
+			gettext_noop("Sets whether Kerberos and GSSAPI user names should be treated as case-insensitive."),
 			NULL
 		},
 		&pg_krb_caseins_users,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9729eb2ce7..738eb9a5f7 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -79,11 +79,11 @@
 #password_encryption = on
 #db_user_namespace = off
 
-# Kerberos
+# Kerberos and GSSAPI
 #krb_server_keyfile = ''		# (change requires restart)
-#krb_srvname = 'postgres'		# (change requires restart)
+#krb_srvname = 'postgres'		# (change requires restart, kerberos only)
 #krb_server_hostname = ''		# empty string matches any keytab entry
-					# (change requires restart)
+					# (change requires restart, kerberos only)
 #krb_caseins_users = off		# (change requires restart)
 
 # - TCP Keepalives -
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 155db7314d..b68aa5899b 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -4,7 +4,7 @@
  *	  Interface to hba.c
  *
  *
- * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.45 2006/11/05 22:42:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.46 2007/07/10 13:14:21 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,7 +22,8 @@ typedef enum UserAuth
 	uaIdent,
 	uaPassword,
 	uaCrypt,
-	uaMD5
+	uaMD5,
+	uaGSS
 #ifdef USE_PAM
 	,uaPAM
 #endif   /* USE_PAM */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 5e18b7ff2b..095ac91957 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.58 2007/01/05 22:19:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.59 2007/07/10 13:14:21 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,10 @@
 #include <netinet/tcp.h>
 #endif
 
+#ifdef ENABLE_GSS
+#include <gssapi/gssapi.h>
+#endif
+
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
 #include "utils/timestamp.h"
@@ -39,6 +43,20 @@ typedef enum CAC_state
 	CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY
 } CAC_state;
 
+
+/*
+ * GSSAPI specific state information
+ */
+#ifdef ENABLE_GSS
+typedef struct
+{
+	gss_cred_id_t	cred;		/* GSSAPI connection cred's */
+	gss_ctx_id_t	ctx;		/* GSSAPI connection context */
+	gss_name_t		name;		/* GSSAPI client name */
+	gss_buffer_desc	outbuf;		/* GSSAPI output token buffer */
+} pg_gssinfo;
+#endif
+
 /*
  * This is used by the postmaster in its communication with frontends.	It
  * contains all state information needed during this communication before the
@@ -98,6 +116,17 @@ typedef struct Port
 	int			keepalives_interval;
 	int			keepalives_count;
 
+#ifdef ENABLE_GSS
+	/*
+	 * If GSSAPI is supported, store GSSAPI information.
+	 * Oterwise, store a NULL pointer to make sure offsets
+	 * in the struct remain the same.
+	 */
+	pg_gssinfo *gss;
+#else
+	void	   *gss;
+#endif
+
 	/*
 	 * SSL structures (keep these last so that USE_SSL doesn't affect
 	 * locations of other fields)
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 50118e4d94..8e66aaa968 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/pqcomm.h,v 1.104 2007/07/08 18:28:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/pqcomm.h,v 1.105 2007/07/10 13:14:21 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -156,6 +156,8 @@ extern bool Db_user_namespace;
 #define AUTH_REQ_CRYPT		4	/* crypt password */
 #define AUTH_REQ_MD5		5	/* md5 password */
 #define AUTH_REQ_SCM_CREDS	6	/* transfer SCM credentials */
+#define AUTH_REQ_GSS		7	/* GSSAPI without wrap() */
+#define AUTH_REQ_GSS_CONT	8	/* Continue GSS exchanges */
 
 typedef uint32 AuthRequest;
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index a25311fef3..64b4473ea3 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -568,6 +568,9 @@
 /* Define to the appropriate snprintf format for 64-bit ints, if any. */
 #undef INT64_FORMAT
 
+/* Define to build with GSSAPI support. (--with-gssapi) */
+#undef ENABLE_GSS
+
 /* Define to build with Kerberos 5 support. (--with-krb5) */
 #undef KRB5
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 2b33e766b9..7d5ede01cc 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -5,7 +5,7 @@
 # Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.154 2007/01/07 08:49:31 petere Exp $
+# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.155 2007/07/10 13:14:21 mha Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -57,9 +57,9 @@ endif
 # shared library link.  (The order in which you list them here doesn't
 # matter.)
 ifneq ($(PORTNAME), win32)
-SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
 else
-SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
 endif
 ifeq ($(PORTNAME), win32)
 SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS))
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 82d7394457..8e6dca6fb0 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -10,7 +10,7 @@
  * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.123 2007/02/10 14:58:55 petere Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.124 2007/07/10 13:14:21 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -313,6 +313,182 @@ pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *s
 }
 #endif   /* KRB5 */
 
+#ifdef ENABLE_GSS
+/*
+ * GSSAPI authentication system.
+ */
+#include <gssapi/gssapi.h>
+
+#ifdef WIN32
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
+ {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
+#endif
+
+/*
+ * Fetch all errors of a specific type that fit into a buffer
+ * and append them.
+ */
+static void
+pg_GSS_error_int(char *mprefix, char *msg, int msglen,
+                 OM_uint32 stat, int type)
+{
+	int				curlen = 0;
+	OM_uint32		lmaj_s, lmin_s;
+	gss_buffer_desc	lmsg;
+	OM_uint32		msg_ctx = 0;
+
+	do 
+	{
+		lmaj_s = gss_display_status(&lmin_s, stat, type, 
+				GSS_C_NO_OID, &msg_ctx, &lmsg);
+
+		if (curlen < msglen)
+		{
+			snprintf(msg + curlen, msglen - curlen, "%s: %s\n",
+					mprefix, (char *)lmsg.value);
+			curlen += lmsg.length;
+		}
+		gss_release_buffer(&lmin_s, &lmsg);
+	} while (msg_ctx);
+}
+
+/*
+ * GSSAPI errors contains two parts. Put as much as possible of
+ * both parts into the string.
+ */
+void
+pg_GSS_error(char *mprefix, char *msg, int msglen,
+	OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+	int mlen;
+
+	/* Fetch major error codes */
+	pg_GSS_error_int(mprefix, msg, msglen, maj_stat, GSS_C_GSS_CODE);
+	mlen = strlen(msg);
+
+	/* If there is room left, try to add the minor codes as well */
+	if (mlen < msglen-1)
+		pg_GSS_error_int(mprefix, msg + mlen, msglen - mlen,
+				min_stat, GSS_C_MECH_CODE);
+}
+
+/* 
+ * Continue GSS authentication with next token as needed.
+ */
+static int
+pg_GSS_continue(char *PQerrormsg, PGconn *conn)
+{
+	OM_uint32	maj_stat, min_stat, lmin_s;
+
+	maj_stat = gss_init_sec_context(&min_stat,
+			GSS_C_NO_CREDENTIAL,
+			&conn->gctx,
+			conn->gtarg_nam,
+			GSS_C_NO_OID,
+			conn->gflags,
+			0,
+			GSS_C_NO_CHANNEL_BINDINGS,
+			(conn->gctx==GSS_C_NO_CONTEXT)?GSS_C_NO_BUFFER:&conn->ginbuf,
+			NULL,
+			&conn->goutbuf,
+			NULL,
+			NULL);
+
+	if (conn->gctx != GSS_C_NO_CONTEXT)
+	{
+		free(conn->ginbuf.value);
+		conn->ginbuf.value = NULL;
+		conn->ginbuf.length = 0;
+	}
+
+	if (conn->goutbuf.length != 0)
+	{
+		/*
+		 * GSS generated data to send to the server. We don't care if it's
+		 * the first or subsequent packet, just send the same kind of
+		 * password packet.
+		 */
+		if (pqPacketSend(conn, 'p',
+					conn->goutbuf.value, conn->goutbuf.length)
+				!= STATUS_OK)
+		{
+			gss_release_buffer(&lmin_s, &conn->goutbuf);
+			return STATUS_ERROR;
+		}
+	}
+	gss_release_buffer(&lmin_s, &conn->goutbuf);
+
+	if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
+				PQerrormsg, PQERRORMSG_LENGTH,
+				maj_stat, min_stat);
+		gss_release_name(&lmin_s, &conn->gtarg_nam);
+		if (conn->gctx)
+			gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
+		return STATUS_ERROR;
+	}
+
+	if (maj_stat == GSS_S_COMPLETE)
+		gss_release_name(&lmin_s, &conn->gtarg_nam);
+
+	return STATUS_OK;
+}
+
+/* 
+ * Send initial GSS authentication token
+ */
+static int
+pg_GSS_startup(char *PQerrormsg, PGconn *conn)
+{
+	OM_uint32	maj_stat, min_stat;
+	int			maxlen;
+	gss_buffer_desc	temp_gbuf;
+
+	if (conn->gctx)
+	{
+		snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+				libpq_gettext("duplicate GSS auth request\n"));
+		return STATUS_ERROR;
+	}
+
+	/*
+	 * Import service principal name so the proper ticket can be
+	 * acquired by the GSSAPI system.
+	 */
+	maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
+	temp_gbuf.value = (char*)malloc(maxlen);
+	snprintf(temp_gbuf.value, maxlen, "%s@%s", 
+			conn->krbsrvname, conn->pghost);
+	temp_gbuf.length = strlen(temp_gbuf.value);
+
+	maj_stat = gss_import_name(&min_stat, &temp_gbuf,
+			GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
+	free(temp_gbuf.value);
+
+	if (maj_stat != GSS_S_COMPLETE)
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI name import error"), 
+				PQerrormsg, PQERRORMSG_LENGTH,
+				maj_stat, min_stat);
+		return STATUS_ERROR;
+	}
+
+	/*
+	 * Initial packet is the same as a continuation packet with
+	 * no initial context.
+	 */
+	conn->gctx = GSS_C_NO_CONTEXT;
+
+	return pg_GSS_continue(PQerrormsg, conn);
+}
+#endif
 
 /*
  * Respond to AUTH_REQ_SCM_CREDS challenge.
@@ -479,6 +655,37 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
 			return STATUS_ERROR;
 #endif
 
+#ifdef ENABLE_GSS
+		case AUTH_REQ_GSS:
+			pglock_thread();
+			if (pg_GSS_startup(PQerrormsg, conn) != STATUS_OK)
+			{
+				/* PQerrormsg already filled in. */
+				pgunlock_thread();
+				return STATUS_ERROR;
+			}
+			pgunlock_thread();
+			break;
+
+		case AUTH_REQ_GSS_CONT:
+			pglock_thread();
+			if (pg_GSS_continue(PQerrormsg, conn) != STATUS_OK)
+			{
+				/* PQerrormsg already filled in. */
+				pgunlock_thread();
+				return STATUS_ERROR;
+			}
+			pgunlock_thread();
+			break;
+
+#else
+		case AUTH_REQ_GSS:
+		case AUTH_REQ_GSS_CONT:
+			snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+					libpq_gettext("GSSAPI authentication not supported\n"));
+			return STATUS_ERROR;
+#endif
+
 		case AUTH_REQ_MD5:
 		case AUTH_REQ_CRYPT:
 		case AUTH_REQ_PASSWORD:
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5263099ba4..25768f1964 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.347 2007/07/08 18:28:55 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.348 2007/07/10 13:14:21 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -181,8 +181,8 @@ static const PQconninfoOption PQconninfoOptions[] = {
 	{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
 	"SSL-Mode", "", 8},			/* sizeof("disable") == 8 */
 
-#ifdef KRB5
-	/* Kerberos authentication supports specifying the service name */
+#if defined(KRB5) || defined(ENABLE_GSS)
+	/* Kerberos and GSSAPI authentication support specifying the service name */
 	{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
 	"Kerberos-service-name", "", 20},
 #endif
@@ -412,7 +412,7 @@ connectOptions1(PGconn *conn, const char *conninfo)
 		conn->sslmode = strdup("require");
 	}
 #endif
-#ifdef KRB5
+#if defined(KRB5) || defined(ENABLE_GSS)
 	tmp = conninfo_getval(connOptions, "krbsrvname");
 	conn->krbsrvname = tmp ? strdup(tmp) : NULL;
 #endif
@@ -1496,12 +1496,13 @@ keep_going:						/* We will come back to here until there is
 
 				/*
 				 * Try to validate message length before using it.
-				 * Authentication requests can't be very large.  Errors can be
+				 * Authentication requests can't be very large, although GSS
+				 * auth requests may not be that small.  Errors can be
 				 * a little larger, but not huge.  If we see a large apparent
 				 * length in an error, it means we're really talking to a
 				 * pre-3.0-protocol server; cope.
 				 */
-				if (beresp == 'R' && (msgLength < 8 || msgLength > 100))
+				if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
 				{
 					printfPQExpBuffer(&conn->errorMessage,
 									  libpq_gettext(
@@ -1660,6 +1661,43 @@ keep_going:						/* We will come back to here until there is
 						return PGRES_POLLING_READING;
 					}
 				}
+#ifdef ENABLE_GSS
+				/*
+				 * AUTH_REQ_GSS provides no input data
+				 * Just set the request flags 
+				 */
+				if (areq == AUTH_REQ_GSS)
+					conn->gflags = GSS_C_MUTUAL_FLAG;
+
+				/*
+				 * Read GSSAPI data packets
+				 */
+				if (areq == AUTH_REQ_GSS_CONT)
+				{
+					/* Continue GSSAPI authentication */
+					int llen = msgLength - 4;
+					
+					/*
+					 * We can be called repeatedly for the same buffer.
+					 * Avoid re-allocating the buffer in this case - 
+					 * just re-use the old buffer.
+					 */
+					if (llen != conn->ginbuf.length)
+					{
+						if (conn->ginbuf.value)
+							free(conn->ginbuf.value);
+
+						conn->ginbuf.length = llen;
+						conn->ginbuf.value = malloc(llen);
+					}
+
+					if (pqGetnchar(conn->ginbuf.value, llen, conn))
+					{
+						/* We'll come back when there is more data. */
+						return PGRES_POLLING_READING;
+					}
+				}
+#endif
 
 				/*
 				 * OK, we successfully read the message; mark data consumed
@@ -1957,7 +1995,7 @@ freePGconn(PGconn *conn)
 		free(conn->pgpass);
 	if (conn->sslmode)
 		free(conn->sslmode);
-#ifdef KRB5
+#if defined(KRB5) || defined(ENABLE_GSS)
 	if (conn->krbsrvname)
 		free(conn->krbsrvname);
 #endif
@@ -1973,6 +2011,19 @@ freePGconn(PGconn *conn)
 		notify = notify->next;
 		free(prev);
 	}
+#ifdef ENABLE_GSS
+	{
+		OM_uint32	min_s;
+		if (conn->gctx)
+			gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
+		if (conn->gtarg_nam)
+			gss_release_name(&min_s, &conn->gtarg_nam);
+		if (conn->ginbuf.length)
+			gss_release_buffer(&min_s, &conn->ginbuf);
+		if (conn->goutbuf.length)
+			gss_release_buffer(&min_s, &conn->goutbuf);
+	}
+#endif
 	pstatus = conn->pstatus;
 	while (pstatus != NULL)
 	{
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 98080f2350..cbefdd8e0f 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.121 2007/07/08 18:28:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.122 2007/07/10 13:14:22 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,10 @@
 /* include stuff found in fe only */
 #include "pqexpbuffer.h"
 
+#ifdef ENABLE_GSS
+#include <gssapi/gssapi.h>
+#endif
+
 #ifdef USE_SSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
@@ -268,7 +272,7 @@ struct pg_conn
 	char	   *pguser;			/* Postgres username and password, if any */
 	char	   *pgpass;
 	char	   *sslmode;		/* SSL mode (require,prefer,allow,disable) */
-#ifdef KRB5
+#if defined(KRB5) || defined(ENABLE_GSS)
 	char	   *krbsrvname;		/* Kerberos service name */
 #endif
 
@@ -350,6 +354,14 @@ struct pg_conn
 	char		peer_cn[SM_USER + 1];	/* peer common name */
 #endif
 
+#ifdef ENABLE_GSS
+	gss_ctx_id_t	gctx;		/* GSS context */
+	gss_name_t		gtarg_nam;	/* GSS target name */
+	OM_uint32		gflags;		/* GSS service request flags */
+	gss_buffer_desc	ginbuf;		/* GSS input token */
+	gss_buffer_desc	goutbuf;	/* GSS output token */
+#endif
+
 	/* Buffer for current error message */
 	PQExpBufferData errorMessage;		/* expansible string */
 
@@ -399,6 +411,11 @@ extern pgthreadlock_t pg_g_threadlock;
 #define pgunlock_thread()	((void) 0)
 #endif
 
+/* === in fe-auth.c === */
+#ifdef ENABLE_GSS
+extern void pg_GSS_error(char *mprefix, char *msg, int msglen,
+        OM_uint32 maj_stat, OM_uint32 min_stat);
+#endif
 
 /* === in fe-exec.c === */
 
-- 
2.40.0