From: Magnus Hagander Date: Tue, 10 Jul 2007 13:14:22 +0000 (+0000) Subject: Add support for GSSAPI authentication. X-Git-Tag: REL9_0_0~5339 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=6160106c7495509310b709ee320b55cea8a60928;p=pg-rex%2Fsyncrep.git Add support for GSSAPI authentication. Documentation still being written, will be committed later. Henry B. Hotz and Magnus Hagander --- diff --git a/configure b/configure index 4bfae7d163..84db9ee1c3 100755 --- a/configure +++ b/configure @@ -314,7 +314,7 @@ ac_includes_default="\ # include #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 @@ -3920,6 +3921,50 @@ 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 # echo "$as_me:$LINENO: checking whether to build with Kerberos 5 support" >&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 @@ -10016,6 +10198,155 @@ 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 +_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 +_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 is required for GSSAPI" >&5 +echo "$as_me: error: header file is required for GSSAPI" >&2;} + { (exit 1); exit 1; }; } +fi + + +fi + if test "$with_krb5" = yes ; then if test "${ac_cv_header_krb5_h+set}" = set; then echo "$as_me:$LINENO: checking for krb5.h" >&5 @@ -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 @@ -463,6 +463,19 @@ 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 # AC_MSG_CHECKING([whether to build with Kerberos 5 support]) @@ -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 is required for GSSAPI])]) +fi + if test "$with_krb5" = yes ; then AC_CHECK_HEADER(krb5.h, [], [AC_MSG_ERROR([header file 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 #include +#include #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 + +#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 . * * 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 #endif +#ifdef ENABLE_GSS +#include +#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 + +#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 +#endif + #ifdef USE_SSL #include #include @@ -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 === */