Feature: Add SCRAM and Certificate authentication support
authorMuhammad Usama <m.usama@gmail.com>
Thu, 16 Aug 2018 16:34:32 +0000 (21:34 +0500)
committerMuhammad Usama <m.usama@gmail.com>
Thu, 16 Aug 2018 16:34:32 +0000 (21:34 +0500)
New feature to add scram and cert authentication method support in Pgpool-II.
Apart from supporting the new authentication methods the commit also includes
the following enhancements and changes in the authentication framework
of Pgpool-II

Different auth methods for frontend and backend for user session
================================================================
Now it possible to use different authentication method for client
application and backend PostgreSQL servers.
For example, a client application can use scram-sha-256 to connect to Pgpool-II
which in turn can use trust or md5 authentication to connect to
PostgreSQL backend for the same session.

Use MD5 and SCRAM without pool_passwd
=====================================
New configuration parameter allow_clear_text_frontend_auth, enables the Pgpool-II
to use clear-text-password authentication with frontend clients when pool_passwd
file does not contains the password for the connecting user.
For example: suppose PostgreSQL servers has a user named "some_user" which can
connect to database using SCRAM authentication, Now for this "some_user" to
connect to PostgreSQL using SCRAM through Pgpool-II we must have the some_user's
password stored in the pool_passwd file, but if in some case when pool_passwd does
not have the entry of "some_user" and allow_clear_text_frontend_auth is enabled
in the pgpool.conf then Pgpool-II will ask the connecting frontend to use
clear-text-password auth method for authentication, and after receiving the
password from the client, Pgpool-II will use that password to authenticate with
backend using the required SCRAM auth.

Note: allow_clear_text_frontend_auth only works when pool_hba.conf is not enabled.

Encrypted passwords in pool_passwd file
=======================================
Since the SCRAM authentication method explicitly guards against the
man-in-middle type attacks, so to use such authentication methods Pgpool-II
requires the PostgreSQL user password to authenticate with the backend.
But as storing the clear text password in the "pool_passwd" file is never a good
idea, so now you can store the AES256-CBC encrypted password in the "pool_passwd".
To store the AES encrypted password in the "pool_passwd" the password is first
encrypted using the AES256 encryption with the user provided key and then the
encrypted password is base64 encoded and AES prefix is added to
the encoded string.

New pg_enc utility to create encrypted passwords
================================================
A new utility pg_enc is added to create AES encrypted passwords. The utility
works similar in most ways as pg_md5 utility, with a some small differences,
pg_enc also requires the key for encrypting the password entries. later that
same key is required by Pgpool-II to decrypt the passwords to be used for
authentication.

Note: Pgpool-II must be build with ssl (--with-openssl) support to use
this encrypted password feature.

Providing encryption key to Pgpool-II
=====================================
If you have AES encrypted passwords stored in the pool_passwd file, then
Pgpool-II will require the decryption key to decrypt the passwords before
using them, Pgpool-II tries to read the decryption key at startup from
the pgpoolkey file.
By default the Pgpool-II will look for the pgpoolkey file in user's home
directory or the file referenced by environment variable PGPOOLKEYFILE.
You can also specify the key file using the (-k, --key-file=KEY_FILE)
command line argument to the Pgpool-II binary.

Encrypted Passwords in pgpool.conf
==================================
The commit also allows to specify the AES encrypted password in the pgpool.conf
file for healh_check_user, sr_check_user, wd_lifecheck_user and recovery_user
users, Additionally if the password field for any of these users is left blank
in pgpool conf then Pgpool-II will first try to get the password for that user
from pool_passwd file before using the empty password for the connection.
So now pgpool.conf can be made password free and single pool_passwd file can be
used to store all passwords for internal and external user connection

Documentation updates and regression test cases for the
feature are also part of the commit.
Thanks to jesperpedersen <jesper.pedersen@redhat.com> for helping
in documentation and testing for the feature

80 files changed:
Makefile.in
aclocal.m4
configure
configure.ac
doc.ja/Makefile.in
doc.ja/src/Makefile.in
doc.ja/src/sgml/Makefile.in
doc/Makefile.in
doc/src/Makefile.in
doc/src/sgml/Makefile.in
doc/src/sgml/client-auth.sgml
doc/src/sgml/connection-settings.sgml
doc/src/sgml/healthcheck.sgml
doc/src/sgml/online-recovery.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/pg_enc.sgml [new file with mode: 0644]
doc/src/sgml/reference.sgml
doc/src/sgml/stream-check.sgml
doc/src/sgml/watchdog.sgml
src/Makefile.am
src/Makefile.in
src/auth/auth-scram.c [new file with mode: 0644]
src/auth/pool_auth.c
src/auth/pool_hba.c
src/auth/pool_passwd.c
src/config/pool_config_variables.c
src/include/Makefile.in
src/include/auth/md5.h
src/include/auth/pool_hba.h
src/include/auth/pool_passwd.h
src/include/auth/scram-common.h [new file with mode: 0644]
src/include/auth/scram.h [new file with mode: 0644]
src/include/config.h.in
src/include/pool.h
src/include/pool_config.h
src/include/pool_type.h
src/include/utils/base64.h [new file with mode: 0644]
src/include/utils/sha2.h [new file with mode: 0644]
src/include/utils/ssl_utils.h [new file with mode: 0644]
src/include/watchdog/wd_utils.h
src/libs/Makefile.in
src/libs/pcp/Makefile.in
src/main/health_check.c
src/main/main.c
src/main/pgpool_main.c
src/parser/Makefile.in
src/pcp_con/recovery.c
src/protocol/child.c
src/sample/pgpool.conf.sample
src/sample/pgpool.conf.sample-logical
src/sample/pgpool.conf.sample-master-slave
src/sample/pgpool.conf.sample-replication
src/sample/pgpool.conf.sample-stream
src/sample/pool_hba.conf.sample
src/streaming_replication/pool_worker_child.c
src/test/pgpool_setup
src/test/regression/tests/020.allow_clear_text_frontend_auth/test.sh [new file with mode: 0755]
src/test/regression/tests/021.pool_passwd_auth/pool_hba.conf [new file with mode: 0644]
src/test/regression/tests/021.pool_passwd_auth/test.sh [new file with mode: 0755]
src/test/regression/tests/022.pool_passwd_alternative_auth/pool_hba.conf [new file with mode: 0644]
src/test/regression/tests/022.pool_passwd_alternative_auth/test.sh [new file with mode: 0755]
src/tools/Makefile.am
src/tools/Makefile.in
src/tools/pcp/Makefile.in
src/tools/pgenc/Makefile.am [new file with mode: 0644]
src/tools/pgenc/Makefile.in [new file with mode: 0644]
src/tools/pgenc/pg_enc.c [new file with mode: 0644]
src/tools/pgmd5/Makefile.in
src/tools/pgmd5/pool_config.c [changed from file to symlink]
src/utils/base64.c [new file with mode: 0644]
src/utils/pool_process_reporting.c
src/utils/pool_ssl.c
src/utils/scram-common.c [new file with mode: 0644]
src/utils/sha2.c [new file with mode: 0644]
src/utils/ssl_utils.c [new file with mode: 0644]
src/watchdog/Makefile.in
src/watchdog/watchdog.c
src/watchdog/wd_json_data.c
src/watchdog/wd_lifecheck.c
src/watchdog/wd_utils.c

index 012e6a8c6c2cc948262145970c07b7516a0b5a6f..70c2e6b7d0edd7dc32e8caf10132ed0edc4211d8 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -88,6 +78,11 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = .
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/configure $(am__configure_deps) mkinstalldirs \
+       AUTHORS COPYING ChangeLog INSTALL NEWS README TODO compile \
+       config.guess config.sub depcomp install-sh missing ylwrap \
+       ltmain.sh
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -98,8 +93,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
-       $(am__configure_deps) $(am__DIST_COMMON)
 am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
  configure.lineno config.status.lineno
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
@@ -163,9 +156,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
-am__DIST_COMMON = $(srcdir)/Makefile.in AUTHORS COPYING ChangeLog \
-       INSTALL NEWS README TODO compile config.guess config.sub \
-       depcomp install-sh ltmain.sh missing mkinstalldirs ylwrap
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
@@ -336,7 +326,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -371,6 +360,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -586,16 +576,10 @@ dist-xz: distdir
        $(am__post_remove_distdir)
 
 dist-tarZ: distdir
-       @echo WARNING: "Support for distribution archives compressed with" \
-                      "legacy program 'compress' is deprecated." >&2
-       @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
        tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
        $(am__post_remove_distdir)
 
 dist-shar: distdir
-       @echo WARNING: "Support for shar distribution archives is" \
-                      "deprecated." >&2
-       @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
        shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
        $(am__post_remove_distdir)
 
@@ -630,17 +614,16 @@ distcheck: dist
        esac
        chmod -R a-w $(distdir)
        chmod u+w $(distdir)
-       mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
+       mkdir $(distdir)/_build $(distdir)/_inst
        chmod a-w $(distdir)
        test -d $(distdir)/_build || exit 0; \
        dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
          && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
          && am__cwd=`pwd` \
-         && $(am__cd) $(distdir)/_build/sub \
-         && ../../configure \
+         && $(am__cd) $(distdir)/_build \
+         && ../configure --srcdir=.. --prefix="$$dc_install_base" \
            $(AM_DISTCHECK_CONFIGURE_FLAGS) \
            $(DISTCHECK_CONFIGURE_FLAGS) \
-           --srcdir=../.. --prefix="$$dc_install_base" \
          && $(MAKE) $(AM_MAKEFLAGS) \
          && $(MAKE) $(AM_MAKEFLAGS) dvi \
          && $(MAKE) $(AM_MAKEFLAGS) check \
@@ -816,8 +799,6 @@ uninstall-am:
        mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
        ps ps-am tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 docs:
        $(MAKE) -C doc all && $(MAKE) -C doc.ja all
index 99efdd8336fee5c1e1203639bd71c8e970dc3329..27b03b797772dff1403990ac5da4a25b9ab43330 100644 (file)
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.15 -*- Autoconf -*-
+# generated automatically by aclocal 1.13.4 -*- Autoconf -*-
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -20,7 +20,7 @@ You have another version of autoconf.  It may work, but is not guaranteed to.
 If you have problems, you may need to regenerate the build system entirely.
 To do so, use the procedure documented by the package, typically 'autoreconf'.])])
 
-# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+# Copyright (C) 2002-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -32,10 +32,10 @@ To do so, use the procedure documented by the package, typically 'autoreconf'.])
 # generated from the m4 files accompanying Automake X.Y.
 # (This private macro should not be called outside this file.)
 AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.15'
+[am__api_version='1.13'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.15], [],
+m4_if([$1], [1.13.4], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.15])dnl
+[AM_AUTOMAKE_VERSION([1.13.4])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -103,14 +103,15 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 # configured tree to be moved without reconfiguration.
 
 AC_DEFUN([AM_AUX_DIR_EXPAND],
-[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
-# Expand $ac_aux_dir to an absolute path.
-am_aux_dir=`cd "$ac_aux_dir" && pwd`
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
 ])
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+# Copyright (C) 1997-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -143,7 +144,7 @@ fi])])
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -152,12 +153,6 @@ fi])])
 # This macro actually does too much.  Some checks are only needed if
 # your package does certain things.  But this isn't really a big deal.
 
-dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
-m4_define([AC_PROG_CC],
-m4_defn([AC_PROG_CC])
-[_AM_PROG_CC_C_O
-])
-
 # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
 # AM_INIT_AUTOMAKE([OPTIONS])
 # -----------------------------------------------
@@ -233,8 +228,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl
 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
 AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
-# We need awk for the "check" target (and possibly the TAP driver).  The
-# system "awk" is bad on some platforms.
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
 AC_REQUIRE([AC_PROG_AWK])dnl
 AC_REQUIRE([AC_PROG_MAKE_SET])dnl
 AC_REQUIRE([AM_SET_LEADING_DOT])dnl
@@ -266,51 +261,6 @@ dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
 AC_CONFIG_COMMANDS_PRE(dnl
 [m4_provide_if([_AM_COMPILER_EXEEXT],
   [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
-
-# POSIX will say in a future version that running "rm -f" with no argument
-# is OK; and we want to be able to make that assumption in our Makefile
-# recipes.  So use an aggressive probe to check that the usage we want is
-# actually supported "in the wild" to an acceptable degree.
-# See automake bug#10828.
-# To make any issue more visible, cause the running configure to be aborted
-# by default if the 'rm' program in use doesn't match our expectations; the
-# user can still override this though.
-if rm -f && rm -fr && rm -rf; then : OK; else
-  cat >&2 <<'END'
-Oops!
-
-Your 'rm' program seems unable to run without file operands specified
-on the command line, even when the '-f' option is present.  This is contrary
-to the behaviour of most rm programs out there, and not conforming with
-the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
-
-Please tell bug-automake@gnu.org about your system, including the value
-of your $PATH and any error possibly output before this message.  This
-can help us improve future automake versions.
-
-END
-  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
-    echo 'Configuration will proceed anyway, since you have set the' >&2
-    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
-    echo >&2
-  else
-    cat >&2 <<'END'
-Aborting the configuration process, to ensure you take notice of the issue.
-
-You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
-
-If you want to complete the configuration process using your problematic
-'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
-to "yes", and re-run configure.
-
-END
-    AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
-  fi
-fi
-dnl The trailing newline in this macro's definition is deliberate, for
-dnl backward compatibility and to allow trailing 'dnl'-style comments
-dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
 ])
 
 dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
@@ -319,6 +269,7 @@ dnl mangled by Autoconf and run in a shell conditional statement.
 m4_define([_AC_COMPILER_EXEEXT],
 m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
 
+
 # When config.status generates a header, we must update the stamp-h file.
 # This file resides in the same directory as the config header
 # that is generated.  The stamp files are numbered to have different names.
@@ -340,7 +291,7 @@ for _am_header in $config_headers :; do
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -351,7 +302,7 @@ echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_co
 # Define $install_sh.
 AC_DEFUN([AM_PROG_INSTALL_SH],
 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-if test x"${install_sh+set}" != xset; then
+if test x"${install_sh}" != xset; then
   case $am_aux_dir in
   *\ * | *\    *)
     install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -361,7 +312,7 @@ if test x"${install_sh+set}" != xset; then
 fi
 AC_SUBST([install_sh])])
 
-# Copyright (C) 2003-2014 Free Software Foundation, Inc.
+# Copyright (C) 2003-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -380,7 +331,7 @@ fi
 rmdir .tst 2>/dev/null
 AC_SUBST([am__leading_dot])])
 
-# Copyright (C) 1998-2014 Free Software Foundation, Inc.
+# Copyright (C) 1998-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -401,7 +352,7 @@ fi])
 # Add --enable-maintainer-mode option to configure.         -*- Autoconf -*-
 # From Jim Meyering
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -434,9 +385,41 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
 ]
 )
 
+# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_CC_C_O
+# --------------
+# Like AC_PROG_CC_C_O, but changed for automake.
+AC_DEFUN([AM_PROG_CC_C_O],
+[AC_REQUIRE([AC_PROG_CC_C_O])dnl
+AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+dnl Make sure AC_PROG_CC is never called again, or it will override our
+dnl setting of CC.
+m4_define([AC_PROG_CC],
+          [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])])
+])
+
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+# Copyright (C) 1997-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -477,7 +460,7 @@ fi
 # Obsolete and "removed" macros, that must however still report explicit
 # error messages when used, to smooth transition.
 #
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -504,7 +487,7 @@ AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES])
 
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -533,73 +516,9 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
-#
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# _AM_PROG_CC_C_O
-# ---------------
-# Like AC_PROG_CC_C_O, but changed for automake.  We rewrite AC_PROG_CC
-# to automatically call this.
-AC_DEFUN([_AM_PROG_CC_C_O],
-[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-AC_REQUIRE_AUX_FILE([compile])dnl
-AC_LANG_PUSH([C])dnl
-AC_CACHE_CHECK(
-  [whether $CC understands -c and -o together],
-  [am_cv_prog_cc_c_o],
-  [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
-  # Make sure it works both with $CC and with simple cc.
-  # Following AC_PROG_CC_C_O, we do the test twice because some
-  # compilers refuse to overwrite an existing .o file with -o,
-  # though they will create one.
-  am_cv_prog_cc_c_o=yes
-  for am_i in 1 2; do
-    if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
-         && test -f conftest2.$ac_objext; then
-      : OK
-    else
-      am_cv_prog_cc_c_o=no
-      break
-    fi
-  done
-  rm -f core conftest*
-  unset am_i])
-if test "$am_cv_prog_cc_c_o" != yes; then
-   # Losing compiler, so override with the script.
-   # FIXME: It is wrong to rewrite CC.
-   # But if we don't then we get into trouble of one sort or another.
-   # A longer-term fix would be to have automake use am__CC in this case,
-   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
-   CC="$am_aux_dir/compile $CC"
-fi
-AC_LANG_POP([C])])
-
-# For backward compatibility.
-AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
-#
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# AM_RUN_LOG(COMMAND)
-# -------------------
-# Run COMMAND, save the exit status in ac_status, and log it.
-# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
-AC_DEFUN([AM_RUN_LOG],
-[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
-   ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
-   ac_status=$?
-   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
-   (exit $ac_status); }])
-
 # Check to make sure that the build environment is sane.    -*- Autoconf -*-
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -680,7 +599,7 @@ AC_CONFIG_COMMANDS_PRE(
 rm -f conftest.file
 ])
 
-# Copyright (C) 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2009-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -740,7 +659,7 @@ AC_SUBST([AM_BACKSLASH])dnl
 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
 ])
 
-# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -768,7 +687,7 @@ fi
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006-2014 Free Software Foundation, Inc.
+# Copyright (C) 2006-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -787,7 +706,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004-2014 Free Software Foundation, Inc.
+# Copyright (C) 2004-2013 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
index 41c9dd0f7b5aada05b6c356d7c0141e27a3a7de6..f9fc91f5eeaf89e00aad5deb4c556871445e0324 100755 (executable)
--- a/configure
+++ b/configure
@@ -754,7 +754,6 @@ infodir
 docdir
 oldincludedir
 includedir
-runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -849,7 +848,6 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
-runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1102,15 +1100,6 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
-  -runstatedir | --runstatedir | --runstatedi | --runstated \
-  | --runstate | --runstat | --runsta | --runst | --runs \
-  | --run | --ru | --r)
-    ac_prev=runstatedir ;;
-  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
-  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
-  | --run=* | --ru=* | --r=*)
-    runstatedir=$ac_optarg ;;
-
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1248,7 +1237,7 @@ fi
 for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
                datadir sysconfdir sharedstatedir localstatedir includedir \
                oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-               libdir localedir mandir runstatedir
+               libdir localedir mandir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1401,7 +1390,6 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
-  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -3359,6 +3347,9 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
+
+am__api_version='1.13'
+
 ac_aux_dir=
 for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
   if test -f "$ac_dir/install-sh"; then
@@ -3388,71 +3379,6 @@ ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
 ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
 
 
-# Expand $ac_aux_dir to an absolute path.
-am_aux_dir=`cd "$ac_aux_dir" && pwd`
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
-$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
-if ${am_cv_prog_cc_c_o+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-  # Make sure it works both with $CC and with simple cc.
-  # Following AC_PROG_CC_C_O, we do the test twice because some
-  # compilers refuse to overwrite an existing .o file with -o,
-  # though they will create one.
-  am_cv_prog_cc_c_o=yes
-  for am_i in 1 2; do
-    if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
-   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
-   ac_status=$?
-   echo "$as_me:$LINENO: \$? = $ac_status" >&5
-   (exit $ac_status); } \
-         && test -f conftest2.$ac_objext; then
-      : OK
-    else
-      am_cv_prog_cc_c_o=no
-      break
-    fi
-  done
-  rm -f core conftest*
-  unset am_i
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
-$as_echo "$am_cv_prog_cc_c_o" >&6; }
-if test "$am_cv_prog_cc_c_o" != yes; then
-   # Losing compiler, so override with the script.
-   # FIXME: It is wrong to rewrite CC.
-   # But if we don't then we get into trouble of one sort or another.
-   # A longer-term fix would be to have automake use am__CC in this case,
-   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
-   CC="$am_aux_dir/compile $CC"
-fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-
-
-am__api_version='1.15'
-
 # Find a good install program.  We prefer a C program (faster),
 # so one script is as good as another.  But avoid the broken or
 # incompatible versions:
@@ -3623,6 +3549,9 @@ test "$program_suffix" != NONE &&
 ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
 program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
 
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
 if test x"${MISSING+set}" != xset; then
   case $am_aux_dir in
   *\ * | *\    *)
@@ -3640,7 +3569,7 @@ else
 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
 fi
 
-if test x"${install_sh+set}" != xset; then
+if test x"${install_sh}" != xset; then
   case $am_aux_dir in
   *\ * | *\    *)
     install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -3968,8 +3897,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
 # <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
 mkdir_p='$(MKDIR_P)'
 
-# We need awk for the "check" target (and possibly the TAP driver).  The
-# system "awk" is bad on some platforms.
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
 # Always define AMTAR for backward compatibility.  Yes, it's still used
 # in the wild :-(  We should find a proper way to deprecate it ...
 AMTAR='$${TAR-tar}'
@@ -3985,48 +3914,6 @@ am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
 
 
 
-# POSIX will say in a future version that running "rm -f" with no argument
-# is OK; and we want to be able to make that assumption in our Makefile
-# recipes.  So use an aggressive probe to check that the usage we want is
-# actually supported "in the wild" to an acceptable degree.
-# See automake bug#10828.
-# To make any issue more visible, cause the running configure to be aborted
-# by default if the 'rm' program in use doesn't match our expectations; the
-# user can still override this though.
-if rm -f && rm -fr && rm -rf; then : OK; else
-  cat >&2 <<'END'
-Oops!
-
-Your 'rm' program seems unable to run without file operands specified
-on the command line, even when the '-f' option is present.  This is contrary
-to the behaviour of most rm programs out there, and not conforming with
-the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
-
-Please tell bug-automake@gnu.org about your system, including the value
-of your $PATH and any error possibly output before this message.  This
-can help us improve future automake versions.
-
-END
-  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
-    echo 'Configuration will proceed anyway, since you have set the' >&2
-    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
-    echo >&2
-  else
-    cat >&2 <<'END'
-Aborting the configuration process, to ensure you take notice of the issue.
-
-You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
-
-If you want to complete the configuration process using your problematic
-'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
-to "yes", and re-run configure.
-
-END
-    as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
-  fi
-fi
-
 
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
 done
 test -n "$YACC" || YACC="yacc"
 
+if test "x$CC" != xcc; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC and cc understand -c and -o together" >&5
+$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether cc understands -c and -o together" >&5
+$as_echo_n "checking whether cc understands -c and -o together... " >&6; }
+fi
+set dummy $CC; ac_cc=`$as_echo "$2" |
+                     sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+if eval \${ac_cv_prog_cc_${ac_cc}_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+# Make sure it works both with $CC and with simple cc.
+# We do the test twice because some compilers refuse to overwrite an
+# existing .o file with -o, though they will create one.
+ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+rm -f conftest2.*
+if { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } &&
+   test -f conftest2.$ac_objext && { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; };
+then
+  eval ac_cv_prog_cc_${ac_cc}_c_o=yes
+  if test "x$CC" != xcc; then
+    # Test first that cc exists at all.
+    if { ac_try='cc -c conftest.$ac_ext >&5'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+      ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+      rm -f conftest2.*
+      if { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } &&
+        test -f conftest2.$ac_objext && { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; };
+      then
+       # cc works too.
+       :
+      else
+       # cc exists but doesn't like -o.
+       eval ac_cv_prog_cc_${ac_cc}_c_o=no
+      fi
+    fi
+  fi
+else
+  eval ac_cv_prog_cc_${ac_cc}_c_o=no
+fi
+rm -f core conftest*
+
+fi
+if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+$as_echo "#define NO_MINUS_C_MINUS_O 1" >>confdefs.h
+
+fi
+
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+
 
 
 
@@ -14892,7 +14904,7 @@ $as_echo "enable cassert = $enable_cassert" >&6; }
 ac_config_headers="$ac_config_headers src/include/config.h"
 
 
-ac_config_files="$ac_config_files Makefile doc/Makefile doc/src/Makefile doc/src/sgml/Makefile doc.ja/Makefile doc.ja/src/Makefile doc.ja/src/sgml/Makefile src/Makefile src/include/Makefile src/parser/Makefile src/libs/Makefile src/libs/pcp/Makefile src/tools/Makefile src/tools/pgmd5/Makefile src/tools/pcp/Makefile src/watchdog/Makefile"
+ac_config_files="$ac_config_files Makefile doc/Makefile doc/src/Makefile doc/src/sgml/Makefile doc.ja/Makefile doc.ja/src/Makefile doc.ja/src/sgml/Makefile src/Makefile src/include/Makefile src/parser/Makefile src/libs/Makefile src/libs/pcp/Makefile src/tools/Makefile src/tools/pgmd5/Makefile src/tools/pgenc/Makefile src/tools/pcp/Makefile src/watchdog/Makefile"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -15922,6 +15934,7 @@ do
     "src/libs/pcp/Makefile") CONFIG_FILES="$CONFIG_FILES src/libs/pcp/Makefile" ;;
     "src/tools/Makefile") CONFIG_FILES="$CONFIG_FILES src/tools/Makefile" ;;
     "src/tools/pgmd5/Makefile") CONFIG_FILES="$CONFIG_FILES src/tools/pgmd5/Makefile" ;;
+    "src/tools/pgenc/Makefile") CONFIG_FILES="$CONFIG_FILES src/tools/pgenc/Makefile" ;;
     "src/tools/pcp/Makefile") CONFIG_FILES="$CONFIG_FILES src/tools/pcp/Makefile" ;;
     "src/watchdog/Makefile") CONFIG_FILES="$CONFIG_FILES src/watchdog/Makefile" ;;
 
index b1cc2476e89f7b9f8d77c52100679d926c1d1dd9..4a68327460ab4548290685f6109de9fa9acfb41c 100644 (file)
@@ -428,4 +428,4 @@ AC_MSG_RESULT([enable cassert = $enable_cassert])
 
 AM_CONFIG_HEADER(src/include/config.h)
 
-AC_OUTPUT([Makefile doc/Makefile  doc/src/Makefile doc/src/sgml/Makefile doc.ja/Makefile  doc.ja/src/Makefile doc.ja/src/sgml/Makefile src/Makefile src/include/Makefile src/parser/Makefile src/libs/Makefile src/libs/pcp/Makefile src/tools/Makefile src/tools/pgmd5/Makefile src/tools/pcp/Makefile src/watchdog/Makefile])
+AC_OUTPUT([Makefile doc/Makefile  doc/src/Makefile doc/src/sgml/Makefile doc.ja/Makefile  doc.ja/src/Makefile doc.ja/src/sgml/Makefile src/Makefile src/include/Makefile src/parser/Makefile src/libs/Makefile src/libs/pcp/Makefile src/tools/Makefile src/tools/pgmd5/Makefile src/tools/pgenc/Makefile src/tools/pcp/Makefile src/watchdog/Makefile])
index b4f7f065ebd5db1fd3f1e64436fe56001fa07050..8cb6a97779fd93dba9eac5e2026760bb073fb23e 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -88,6 +78,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = doc.ja
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -98,7 +90,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -160,7 +151,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -317,7 +307,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -345,6 +334,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps doc.ja/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps doc.ja/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -639,8 +629,6 @@ uninstall-am:
        mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
        ps ps-am tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 50f7cad9d9087fbbca4d9f88c85439b0d3d4af0b..86485c08758b65246f9ad2f9b8daa9a3182b84f8 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -88,6 +78,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = doc.ja/src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -98,7 +90,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -160,7 +151,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -317,7 +307,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -348,6 +337,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps doc.ja/src/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps doc.ja/src/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -642,8 +632,6 @@ uninstall-am:
        mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
        ps ps-am tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index f694be45337b9f16f6a15850111b5b1240fbf6ca..0d28a186274406ef4817ce8afb8281e6972a8f28 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -90,6 +80,8 @@ host_triplet = @host@
 @STYLE_ENV_SET_TRUE@am__append_1 = -V website-stylesheet
 @STYLE_ENV_SET_TRUE@am__append_2 = --param website.stylesheet 1
 subdir = doc.ja/src/sgml
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -100,7 +92,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -127,7 +118,6 @@ am__can_run_installinfo = \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -259,7 +249,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -307,6 +296,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps doc.ja/src/sgml/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps doc.ja/src/sgml/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -480,8 +470,6 @@ uninstall-am:
        mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
        tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 all: html man1 man8
 
index 2a08207198e8ca1c8ace5eded78596c240f06c32..cfadaedd3744aa1f848edc431844d249bf1e10bf 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -88,6 +78,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = doc
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -98,7 +90,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -160,7 +151,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -317,7 +307,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -345,6 +334,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps doc/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps doc/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -639,8 +629,6 @@ uninstall-am:
        mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
        ps ps-am tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index fefb66f7443d861841c9b46790ab7f78f3414693..5a63874c908d9d29dd8bdb0ee044250541121f50 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -88,6 +78,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = doc/src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -98,7 +90,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -160,7 +151,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -317,7 +307,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -350,6 +339,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps doc/src/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps doc/src/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -644,8 +634,6 @@ uninstall-am:
        mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
        ps ps-am tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 9daf4493b089dbba6076b4485e5c09ec1d10352e..af322706dd38e8d7b6e0de9aef0838ec98720be6 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -90,6 +80,8 @@ host_triplet = @host@
 @STYLE_ENV_SET_TRUE@am__append_1 = -V website-stylesheet
 @STYLE_ENV_SET_TRUE@am__append_2 = --param website.stylesheet 1
 subdir = doc/src/sgml
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -100,7 +92,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -127,7 +118,6 @@ am__can_run_installinfo = \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -259,7 +249,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -306,6 +295,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps doc/src/sgml/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps doc/src/sgml/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -479,8 +469,6 @@ uninstall-am:
        mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
        tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 all: html man1 man8
 
index 7fe6f79788448a71aa9636ebdc26f0b2c1eb7416..c3a9a5b6632e9523922d96d59dd1aa86371640be 100644 (file)
                        <note>
                          <para>
                            To use <literal>md5</literal> authentication, you need to register
-                           the user name and password in <filename>"pool_passwd"</filename>.
+                           the user name and password in <xref linkend="guc-pool-passwd"> file.
                            See <xref linkend="auth-md5"> for more details.
                          </para>
                        </note>
                    </listitem>
                  </varlistentry>
 
+          <varlistentry>
+                   <term><literal>scram-sha-256</literal></term>
+                   <listitem>
+                     <para>
+              Perform SCRAM-SHA-256 authentication to verify the user's password.
+                       <note>
+                         <para>
+                           To use <literal>scram-sha-256</literal> authentication, you need to register
+                           the user name and password in <xref linkend="guc-pool-passwd"> file.
+                           See <xref linkend="auth-scram"> for more details.
+                         </para>
+                       </note>
+                     </para>
+                   </listitem>
+                 </varlistentry>
+
+          <varlistentry>
+                   <term><literal>cert</literal></term>
+                   <listitem>
+                     <para>
+                Authenticate using SSL client certificates.
+                           See <xref linkend="auth-cert"> for more details.
+                     </para>
+                   </listitem>
+                 </varlistentry>
+
                  <varlistentry>
                    <term><literal>pam</literal></term>
                    <listitem>
        <para>
          If <productname>Pgpool-II</productname> is operated in raw
          mode or there's only 1 backend configured, you don't need to
-         setup <literal>pool_passwd</literal>.
+         setup <xref linkend="guc-pool-passwd">.
        </para>
       </note>
 
        <title>Authentication file format</title>
 
        <para>
-         This <literal>pool_passwd</literal> file should contain lines in the following format:
+         <xref linkend="guc-pool-passwd"> file should contain lines in the following format:
          <programlisting>
             "username:encrypted_passwd"
          </programlisting>
          1- Login as the database's operating system user and type
          <command>"pg_md5 --md5auth --username=username password"</command> user name
          and <literal>md5</literal> encrypted password are registered
-         into pool_passwd.  If pool_passwd does not exist yet, pg_md5
+         into <xref linkend="guc-pool-passwd">.  If pool_passwd does not exist yet, pg_md5
          command will automatically create it for you.
          <note>
             <para>
 
     </sect2>
 
+    <sect2 id="auth-scram">
+      <title>scram-sha-256 Authentication</title>
+
+      <indexterm zone="auth-scram">
+       <primary>SCRAM</primary>
+      </indexterm>
+
+      <para>
+      This authentication method also known as SCRAM is a
+      challenge-response based authentication that prevents the
+      password sniffing on untrusted connections.
+      Since <productname>Pgpool-II</productname> does not has the
+      visibility of <productname>PostgreSQL</productname>'s database user
+      password, so <literal>SCRAM</literal> authentication is supported using the
+         <xref linkend="guc-pool-passwd"> authentication file.
+      </para>
+
+      <sect3 id="scram-authentication-file-format">
+       <title>Authentication file entry for SCRAM</title>
+
+       <para>
+      To use the <literal>SCRAM</literal> authentication
+      <xref linkend="guc-pool-passwd"> authentication file
+      must contain the user password in either plain text
+      or <literal>AES</literal> encrypted format.
+
+         <programlisting>
+            "username:plain_text_passwd"
+         </programlisting>
+         <programlisting>
+            "username:AES_encrypted_passwd"
+         </programlisting>
+         <note>
+            <para>
+              <literal>md5</literal> type user passwords in
+              <xref linkend="guc-pool-passwd"> file can't be used for
+              <literal>scram</literal> authentication
+            </para>
+         </note>
+       </para>
+      </sect3>
+
+      <sect3 id="setting-scram-sha-256-authentication">
+       <title>Setting scram-sha-256 Authentication</title>
+       <indexterm zone="setting-scram-sha-256-authentication">
+         <primary>SCRAM</primary>
+       </indexterm>
+
+       <para>
+         Here are the steps to enable <literal>scram-sha-256</literal>
+         authentication:
+       </para>
+       <para>
+         1- Create <xref linkend="guc-pool-passwd"> file entry
+      for database user and password in plain text or <literal>AES</literal>
+      encrypted format.
+      <xref linkend="PG-ENC"> ustility comes with <productname>Pgpool-II</productname>
+      can be used to create the <literal>AES</literal> encrypted password
+      entries in <xref linkend="guc-pool-passwd"> file.
+         <note>
+            <para>
+              User name and password must be identical to those registered
+              in <productname>PostgreSQL</productname> server.
+            </para>
+         </note>
+       </para>
+
+       <para>
+         2- Add an appropriate scram-sha-256 entry to <filename>pool_hba.conf</filename>.
+         See <xref linkend="auth-pool-hba-conf"> for more details.
+       </para>
+       <para>
+         3- After changing SCRAM password (in both pool_passwd
+         and <productname>PostgreSQL</productname> of course), reload
+         the pgpool configurations.
+       </para>
+      </sect3>
+
+    </sect2>
+
+    <sect2 id="auth-cert">
+      <title>Certificate Authentication</title>
+
+      <indexterm zone="auth-cert">
+       <primary>Certificate</primary>
+      </indexterm>
+
+      <para>
+      This authentication method uses <literal>SSL</literal> client certificates
+      to perform authentication. It is therefore only available for SSL connections.
+      When using this authentication method, the <productname>Pgpool-II</productname>
+      will require that the client provide a valid certificate.
+      No password prompt will be sent to the client.
+      The <literal>cn</literal> (Common Name) attribute of the certificate will be
+      compared to the requested database user name, and if they match the login will
+      be allowed.
+      </para>
+
+      <note>
+       <para>
+         The certificate authentication works between client and
+      <productname>Pgpool-II</productname>, for the
+         backend authentication you can use any other authentication method
+       </para>
+      </note>
+
+    </sect2>
+
     <sect2 id="auth-pam">
       <title>PAM Authentication</title>
 
     </sect2>
   </sect1>
 
+  <sect1 id="auth-different-auth-method">
+    <title>Using different methods for frontend and backend authentication</title>
+
+    <indexterm zone="auth-different-auth-method">
+         <primary>AUTH</primary>
+    </indexterm>
+
+    <para>
+    Since <productname>Pgpool-II</productname><emphasis>V4.0</emphasis>
+    it possible to use different authentication for client application
+    and backend <productname>PostgreSQL</productname> servers.
+    For example, a client application can use <literal>scram-sha-256</literal>
+    to connect to <productname>Pgpool-II</productname> which
+    in turn can use <literal>trust</literal> or <literal>md5</literal>
+    authentication to connect to <productname>PostgreSQL</productname>
+    backend for the same session.
+
+    </para>
+  </sect1>
+
+  <sect1 id="auth-aes-encrypted-password">
+    <title>Using AES256 encrypted passwords in <xref linkend="guc-pool-passwd"> file</title>
+
+    <indexterm zone="auth-aes-encrypted-password">
+         <primary>AUTH</primary>
+    </indexterm>
+
+    <para>
+    Since the <literal>SCRAM</literal> authentication method explicitly
+    guards against the man-in-middle type attacks, so to use such authentication
+    methods <productname>Pgpool-II</productname> requires the
+    <productname>PostgreSQL</productname> user password to
+    authenticate with the backend.
+    </para>
+
+    <para>
+    But as storing the clear text password in the <xref linkend="guc-pool-passwd">
+    file is never a good idea, so you can store the AES256-CBC encrypted password
+    in the <xref linkend="guc-pool-passwd">. To store the AES encrypted password
+    in the <xref linkend="guc-pool-passwd"> the password is first encrypted using
+    the AES256-CBC encryption with the user provided key and then the encrypted password
+    is <literal>base64</literal> encoded and <literal>AES</literal> prefix is added
+    to the encoded string.
+    <note>
+      <para>
+        You can use the <xref linkend="PG-ENC"> utility to create the properly
+        formatted AES encrypted passwords.
+      </para>
+    </note>
+    </para>
+
+      <sect2 id="auth-create-aes-passwords">
+        <title>Creating encrypted password entries</title>
+        <para>
+          <xref linkend="PG-ENC"> can be used to create <literal>AES</literal>
+          encrypted password entries in <xref linkend="guc-pool-passwd"> file.
+          <xref linkend="PG-ENC"> requires the key for encrypting the password entries.
+          later that same key will be required by the <productname>Pgpool-II</productname>
+          to decrypt the passwords to use for authentication.
+          <note>
+            <para>
+            <productname>Pgpool-II</productname> must be build with ssl
+            (--with-openssl) support to use this encrypted password feature.
+            </para>
+          </note>
+       </para>
+      </sect2>
+
+      <sect2 id="auth-aes-decryption-key">
+        <title>Providing decryption key to <productname>Pgpool-II</productname></title>
+
+        <para>
+          If you have <literal>AES</literal> encrypted passwords stored in the
+          <xref linkend="guc-pool-passwd"> file, then <productname>Pgpool-II</productname>
+          will require the decryption key to decrypt the passwords before using them,
+          <productname>Pgpool-II</productname> tries to read the decryption key at
+          startup from the <filename>pgpoolkey</filename> file.
+        </para>
+        <para>
+          By default the <productname>Pgpool-II</productname> will look for the
+          <filename>pgpoolkey</filename> file in user's home directory or the file
+          referenced by environment variable <literal>PGPOOLKEYFILE</literal>.
+          You can also specify the key file using the (-k, --key-file=KEY_FILE)
+          command line argument to the <productname>Pgpool-II</productname> binary.
+       </para>
+      </sect2>
+
+    </sect1>
+
 </chapter>
index 5bc156a3677954e9bf845d39a13980b6c830e17d..c3bc63cad92b0db1bef4c5ef10906c27b32cfe68 100644 (file)
        </listitem>
       </varlistentry>
 
+      <varlistentry id="guc-allow-clear-text-frontend-auth" xreflabel="allow_clear_text_frontend_auth">
+       <term><varname>allow_clear_text_frontend_auth</varname> (<type>boolean</type>)
+         <indexterm>
+           <primary><varname>allow_clear_text_frontend_auth</varname> configuration parameter</primary>
+         </indexterm>
+       </term>
+       <listitem>
+         <para>
+        If <productname>PostgreSQL</productname> backend servers require
+        <literal>md5</literal> or <literal> SCRAM</literal> authentication for some
+        user's authentication but the password for that user is not present in the
+        <filename>"pool_passwd"</filename> file, then enabling
+        <literal>allow_clear_text_frontend_auth</literal> will allow the
+        <productname>Pgpool-II</productname> to use clear-text-password
+        authentication with frontend clients to get the password in palin text form
+        from the client and use it for backend authentication.
+         </para>
+         <para>
+             Default is <literal>false</literal>
+         </para>
+         <para>
+           This parameter can be changed by reloading
+           the <productname>Pgpool-II</productname> configurations.
+         </para>
+      <note>
+        <para>
+          <literal>allow_clear_text_frontend_auth</literal> only works when <xref linkend="guc-enable-pool-hba"> is not enabled
+        </para>
+      </note>
+       </listitem>
+      </varlistentry>
+
       <varlistentry id="guc-authentication-timeout" xreflabel="authentication_timeout">
        <term><varname>authentication_timeout</varname> (<type>integer</type>)
          <indexterm>
index 316720867e5b0af9f85425eb40723a5161460bf4..d75d3514590a0af7be1cc64f948a593f7fea2c0f 100644 (file)
         The user and password must be same in all the <productname>PostgreSQL</> backends.
         Otherwise, health check results in an error.
       </para>
+      <para>
+        If <varname>health_check_password</varname> is left blank <productname>Pgpool-II</productname>
+        will first try to get the password for <xref linkend="guc-health-check-user"> from
+        <xref linkend="guc-pool-passwd"> file before using the empty password.
+      </para>
+      <para>
+        You can also specify AES256-CBC encrypted password in <varname>health_check_password</varname> field.
+        To specify the <literal>AES</literal> encrypted password, password string must be prefixed with
+        <literal>AES</literal> after encrypting (using <literal>aes-256-cbc</literal> algorithm) and
+        encoding to <literal>base64</literal>.
+      </para>
+      <para>
+        You can also use <xref linkend="PG-ENC"> utility to create the correctly formatted
+        <literal>AES</literal> encrypted password strings.
+        <note>
+            <para>
+                <productname>Pgpool-II</productname> will require a valid decryption key at the
+                startup to use the encrypted passwords.
+                see <xref linkend="auth-aes-decryption-key"> for more details on providing the
+                decryption key to <productname>Pgpool-II</productname>
+            </para>
+        </note>
+      </para>
       <para>
         This parameter can be changed by reloading the <productname>Pgpool-II</> configurations.
       </para>
index bc89c38a7b8687cb83bbf39ddc492cd7f2cc6f1b..6329b7bcd1d521975ccf91172062c4b1a7f23c45 100644 (file)
           Specifies the password for the <productname>PostgreSQL</> user name configured in
           <xref linkend="guc-recovery-user"> to perform online recovery.
         </para>
+        <para>
+            If <varname>recovery_password</varname> is left blank <productname>Pgpool-II</productname>
+            will first try to get the password for <xref linkend="guc-recovery-user"> from
+            <xref linkend="guc-pool-passwd"> file before using the empty password.
+        </para>
+        <para>
+            You can also specify AES256-CBC encrypted password in <varname>recovery_password</varname> field.
+            To specify the <literal>AES</literal> encrypted password, password string must be prefixed with
+            <literal>AES</literal> after encrypting (using <literal>aes-256-cbc</literal> algorithm) and
+            encoding to <literal>base64</literal>.
+        </para>
+        <para>
+            You can also use <xref linkend="PG-ENC"> utility to create the correctly formatted
+            <literal>AES</literal> encrypted password strings.
+            <note>
+                <para>
+                    <productname>Pgpool-II</productname> will require a valid decryption key at the
+                    startup to use the encrypted passwords.
+                    see <xref linkend="auth-aes-decryption-key"> for more details on providing the
+                    decryption key to <productname>Pgpool-II</productname>
+                </para>
+            </note>
+        </para>
+
         <para>
           This parameter can be changed by reloading the <productname>Pgpool-II</> configurations.
         </para>
index d20fde677d4ab4f437f12c0852b315c8732aed6e..27ec7d06853c5e3ada67b9843aa33df6072be420 100644 (file)
@@ -17,6 +17,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pcpStopPgpool       SYSTEM "pcp_stop_pgpool.sgml">
 <!ENTITY pcpRecoveryNode     SYSTEM "pcp_recovery_node.sgml">
 <!ENTITY pgMd5               SYSTEM "pg_md5.sgml">
+<!ENTITY pgEnc               SYSTEM "pg_enc.sgml">
 <!ENTITY pgpool              SYSTEM "pgpool.sgml">
 <!ENTITY pgpoolSetup         SYSTEM "pgpool_setup.sgml">
 <!ENTITY watchdoglSetup      SYSTEM "watchdog_setup.sgml">
diff --git a/doc/src/sgml/ref/pg_enc.sgml b/doc/src/sgml/ref/pg_enc.sgml
new file mode 100644 (file)
index 0000000..77e6f74
--- /dev/null
@@ -0,0 +1,165 @@
+<!--
+doc/src/sgml/ref/pg_enc.sgml
+Pgpool-II documentation
+-->
+
+<refentry id="PG-ENC">
+ <indexterm zone="pg-enc">
+  <primary>pg_enc</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>pg_enc</refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>Other Commands</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>pg_enc</refname>
+  <refpurpose>
+    password encryption utility
+  </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pg_enc</command>
+   <arg rep="repeat"><replaceable>option</replaceable></arg>
+   <arg choice="plain"><replaceable>-p</replaceable></arg>
+  </cmdsynopsis>
+  <cmdsynopsis>
+   <command>pg_enc</command>
+   <arg rep="repeat"><replaceable>option</replaceable></arg>
+   <arg choice="plain"><replaceable>password</replaceable></arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-PG-ENC-1">
+  <title>Description</title>
+  <para>
+  <command>pg_enc</command>
+  password encryption utility.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Options</title>
+   <para>
+    <variablelist>
+
+     <varlistentry>
+      <term><option>-k <replaceable class="parameter">KEY_FILE</replaceable></option></term>
+      <term><option>--key-file=<replaceable class="parameter">KEY_FILE</replaceable></option></term>
+      <listitem>
+       <para>
+        Set the path to the encryption key file. Default is the <literal>.pgpoolkey</literal> file
+         located in the users home directory.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-K <replaceable class="parameter">ENCRYPTION_KEY</replaceable></option></term>
+      <term><option>--enc-key=<replaceable class="parameter">ENCRYPTION_KEY</replaceable></option></term>
+      <listitem>
+       <para>
+        Encryption key to be used for encrypting database passwords.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-f <replaceable class="parameter">CONFIG_FILE</replaceable></option></term>
+      <term><option>--config-file=<replaceable class="parameter">CONFIG_FILE</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies the <literal>pgpool.conf</literal> file.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-p</option></term>
+      <term><option>--prompt</option></term>
+      <listitem>
+       <para>
+        Prompt for database password using standard input.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-P</option></term>
+      <term><option>--prompt-for-key</option></term>
+      <listitem>
+       <para>
+        Prompt for encryption key using standard input.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-m</option></term>
+      <term><option>--update-pass</option></term>
+      <listitem>
+       <para>
+        Create encrypted password entry in the pool_passwd file.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-u <replaceable class="parameter">your_username</replaceable></option></term>
+      <term><option>--username=<replaceable class="parameter">your_username</replaceable></option></term>
+      <listitem>
+       <para>
+        Creates the <literal>pool_passwd</literal> entry for the database user called
+         <literal>your_username</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-h</option></term>
+      <term><option>--help</option></term>
+      <listitem>
+       <para>
+        Prints the help for <literal>pg_enc</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Example</title>
+   <para>
+       Here is an example output:
+    <programlisting>
+pg_enc -p
+db password: [your password]
+    </programlisting>
+  </para>
+  <para>
+  or
+  </para>
+  <programlisting>
+./pg_enc foo
+trying to read key from file /home/pgpool/.pgpoolkey
+
+jglid1QRgiCl/vfhHUDyVA==
+pool_passwd string: AESjglid1QRgiCl/vfhHUDyVA==
+  </programlisting>
+  <para>
+    <literal>pg_enc</literal> can be used for <literal>pool_passwd</literal> passwords with:
+    <programlisting>
+pg_enc -m -f /path/to/pgpool.conf -u username -p
+db password: [your password]
+    </programlisting>
+    which will add an entry for <literal>username</literal> with the password given.
+  </para>
+ </refsect1>
+
+</refentry>
index a53149aa53ea5ee7521f88831ad5026e96ece035..e9c101441cf0d704ab91c33f8854d28789eb3fee 100644 (file)
     </partintro>
 
     &pgMd5;
+    &pgEnc;
     &pgpoolSetup;
     &watchdoglSetup;
 
index 403e86f94e11e6d1b8ec6fade2d8445d755f983d..1e16092e2767cf76406a65a7c97c973f96070435 100644 (file)
           to perform the streaming replication checks.
           Use <literal>''</literal> (empty string) if the user does not requires a password.
         </para>
+        <para>
+            If <varname>sr_check_password</varname> is left blank <productname>Pgpool-II</productname>
+            will first try to get the password for <xref linkend="guc-sr-check-user"> from
+            <xref linkend="guc-pool-passwd"> file before using the empty password.
+        </para>
+        <para>
+            You can also specify AES256-CBC encrypted password in <varname>sr_check_password</varname> field.
+            To specify the <literal>AES</literal> encrypted password, password string must be prefixed with
+            <literal>AES</literal> after encrypting (using <literal>aes-256-cbc</literal> algorithm) and
+            encoding to <literal>base64</literal>.
+        </para>
+        <para>
+            You can also use <xref linkend="PG-ENC"> utility to create the correctly formatted
+            <literal>AES</literal> encrypted password strings.
+            <note>
+                <para>
+                    <productname>Pgpool-II</productname> will require a valid decryption key at the
+                    startup to use the encrypted passwords.
+                    see <xref linkend="auth-aes-decryption-key"> for more details on providing the
+                    decryption key to <productname>Pgpool-II</productname>
+                </para>
+            </note>
+        </para>
 
         <para>
           This parameter can be changed by reloading the <productname>Pgpool-II</> configurations.
index 014059341f1cb23b42d5fba63fe4b38e21fd88dd..965fd2872b5e9ae1f4e7bedcc35d92bb063a5f40 100644 (file)
   <listitem>
    <para>
       Specifies the password for the user used for the life check of remote <productname>Pgpool-II</productname>.
-      Default is <literal>''</literal>(empty).
+   </para>
+   <para>
+        If <varname>wd_lifecheck_password</varname> is left blank <productname>Pgpool-II</productname>
+        will first try to get the password for <xref linkend="guc-wd-lifecheck-user"> from
+        <xref linkend="guc-pool-passwd"> file before using the empty password.
+    </para>
+    <para>
+        You can also specify AES256-CBC encrypted password in <varname>wd_lifecheck_password</varname> field.
+        To specify the <literal>AES</literal> encrypted password, password string must be prefixed with
+        <literal>AES</literal> after encrypting (using <literal>aes-256-cbc</literal> algorithm) and
+        encoding to <literal>base64</literal>.
+    </para>
+    <para>
+        You can also use <xref linkend="PG-ENC"> utility to create the correctly formatted
+        <literal>AES</literal> encrypted password strings.
+        <note>
+            <para>
+                <productname>Pgpool-II</productname> will require a valid decryption key at the
+                startup to use the encrypted passwords.
+                see <xref linkend="auth-aes-decryption-key"> for more details on providing the
+                decryption key to <productname>Pgpool-II</productname>
+            </para>
+        </note>
    </para>
    <para>
       <varname>wd_lifecheck_password</varname> is only applicable if the
     <para>
       This parameter can only be set at server start.
     </para>
+    <para>
+      Default is <literal>''</literal>(empty).
+    </para>
     </listitem>
     </varlistentry>
 
index fe401d03eb69545dab514c093be27225ec7981b2..941c943f33e3b164ed1b0ae0efa5a1e150a417ca 100644 (file)
@@ -17,6 +17,7 @@ pgpool_SOURCES = main/main.c \
        auth/pool_auth.c \
        auth/pool_passwd.c \
        auth/pool_hba.c \
+       auth/auth-scram.c \
        protocol/pool_proto2.c \
        protocol/child.c \
        protocol/pool_process_query.c \
@@ -53,6 +54,10 @@ pgpool_SOURCES = main/main.c \
        utils/regex_array.c \
        utils/json_writer.c \
        utils/json.c \
+       utils/scram-common.c \
+       utils/base64.c \
+       utils/sha2.c \
+       utils/ssl_utils.c \
     utils/statistics.c
 
 DEFS = @DEFS@ \
index 93b645f13fd0a7b5c23af70d9ece932343db8c18..235f91a78376f39cf2bedbe79537c5503072d785 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -91,6 +81,9 @@ build_triplet = @build@
 host_triplet = @host@
 bin_PROGRAMS = pgpool$(EXEEXT)
 subdir = src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs config/pool_config.c \
+       $(top_srcdir)/ylwrap
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -101,7 +94,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -117,8 +109,9 @@ am_pgpool_OBJECTS = main/main.$(OBJEXT) main/pool_globals.$(OBJEXT) \
        pcp_con/pcp_child.$(OBJEXT) pcp_con/pcp_worker.$(OBJEXT) \
        pcp_con/recovery.$(OBJEXT) auth/md5.$(OBJEXT) \
        auth/pool_auth.$(OBJEXT) auth/pool_passwd.$(OBJEXT) \
-       auth/pool_hba.$(OBJEXT) protocol/pool_proto2.$(OBJEXT) \
-       protocol/child.$(OBJEXT) protocol/pool_process_query.$(OBJEXT) \
+       auth/pool_hba.$(OBJEXT) auth/auth-scram.$(OBJEXT) \
+       protocol/pool_proto2.$(OBJEXT) protocol/child.$(OBJEXT) \
+       protocol/pool_process_query.$(OBJEXT) \
        protocol/pool_connection_pool.$(OBJEXT) \
        protocol/pool_proto_modules.$(OBJEXT) \
        query_cache/pool_memqcache.$(OBJEXT) \
@@ -140,7 +133,9 @@ am_pgpool_OBJECTS = main/main.$(OBJEXT) main/pool_globals.$(OBJEXT) \
        utils/mmgr/aset.$(OBJEXT) utils/error/elog.$(OBJEXT) \
        utils/error/assert.$(OBJEXT) utils/pcp/pcp_stream.$(OBJEXT) \
        utils/regex_array.$(OBJEXT) utils/json_writer.$(OBJEXT) \
-       utils/json.$(OBJEXT) utils/statistics.$(OBJEXT)
+       utils/json.$(OBJEXT) utils/scram-common.$(OBJEXT) \
+       utils/base64.$(OBJEXT) utils/sha2.$(OBJEXT) \
+       utils/ssl_utils.$(OBJEXT) utils/statistics.$(OBJEXT)
 pgpool_OBJECTS = $(am_pgpool_OBJECTS)
 pgpool_DEPENDENCIES = parser/libsql-parser.a parser/nodes.o \
        watchdog/lib-watchdog.a
@@ -264,8 +259,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs \
-       $(top_srcdir)/ylwrap config/pool_config.c
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -423,7 +416,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -449,6 +441,7 @@ pgpool_SOURCES = main/main.c \
        auth/pool_auth.c \
        auth/pool_passwd.c \
        auth/pool_hba.c \
+       auth/auth-scram.c \
        protocol/pool_proto2.c \
        protocol/child.c \
        protocol/pool_process_query.c \
@@ -485,6 +478,10 @@ pgpool_SOURCES = main/main.c \
        utils/regex_array.c \
        utils/json_writer.c \
        utils/json.c \
+       utils/scram-common.c \
+       utils/base64.c \
+       utils/sha2.c \
+       utils/ssl_utils.c \
     utils/statistics.c
 
 sysconf_DATA = sample/pgpool.conf.sample \
@@ -586,6 +583,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -677,6 +675,7 @@ auth/md5.$(OBJEXT): auth/$(am__dirstamp)
 auth/pool_auth.$(OBJEXT): auth/$(am__dirstamp)
 auth/pool_passwd.$(OBJEXT): auth/$(am__dirstamp)
 auth/pool_hba.$(OBJEXT): auth/$(am__dirstamp)
+auth/auth-scram.$(OBJEXT): auth/$(am__dirstamp)
 protocol/$(am__dirstamp):
        @$(MKDIR_P) protocol
        @: > protocol/$(am__dirstamp)
@@ -741,6 +740,10 @@ utils/pcp/pcp_stream.$(OBJEXT): utils/pcp/$(am__dirstamp)
 utils/regex_array.$(OBJEXT): utils/$(am__dirstamp)
 utils/json_writer.$(OBJEXT): utils/$(am__dirstamp)
 utils/json.$(OBJEXT): utils/$(am__dirstamp)
+utils/scram-common.$(OBJEXT): utils/$(am__dirstamp)
+utils/base64.$(OBJEXT): utils/$(am__dirstamp)
+utils/sha2.$(OBJEXT): utils/$(am__dirstamp)
+utils/ssl_utils.$(OBJEXT): utils/$(am__dirstamp)
 utils/statistics.$(OBJEXT): utils/$(am__dirstamp)
 
 pgpool$(EXEEXT): $(pgpool_OBJECTS) $(pgpool_DEPENDENCIES) $(EXTRA_pgpool_DEPENDENCIES) 
@@ -1119,8 +1122,6 @@ uninstall-am: uninstall-binPROGRAMS uninstall-pkgdataDATA \
        tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \
        uninstall-pkgdataDATA uninstall-sysconfDATA
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/auth/auth-scram.c b/src/auth/auth-scram.c
new file mode 100644 (file)
index 0000000..3d755b6
--- /dev/null
@@ -0,0 +1,1653 @@
+/* -*-pgsql-c-*- */
+/*
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2018     PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * auth_scram.c: SCRAM authentication stuff
+ * borrowed from PostgreSQL source src/backend/libpq/auth-scram.c
+ *
+ */
+/*-------------------------------------------------------------------------
+ *
+ * auth-scram.c
+ *       Server-side implementation of the SASL SCRAM-SHA-256 mechanism.
+ *
+ * See the following RFCs for more details:
+ * - RFC 5802: https://tools.ietf.org/html/rfc5802
+ * - RFC 5803: https://tools.ietf.org/html/rfc5803
+ * - RFC 7677: https://tools.ietf.org/html/rfc7677
+ *
+ * Here are some differences:
+ *
+ * - Username from the authentication exchange is not used. The client
+ *      should send an empty string as the username.
+ *
+ * - If the password isn't valid UTF-8, or contains characters prohibited
+ *      by the SASLprep profile, we skip the SASLprep pre-processing and use
+ *      the raw bytes in calculating the hash.
+ *
+ * - Channel binding is not supported yet.
+ *
+ *
+ * The password stored in pg_authid consists of the iteration count, salt,
+ * StoredKey and ServerKey.
+ *
+ * SASLprep usage
+ * --------------
+ *
+ * One notable difference to the SCRAM specification is that while the
+ * specification dictates that the password is in UTF-8, and prohibits
+ * certain characters, we are more lenient.  If the password isn't a valid
+ * UTF-8 string, or contains prohibited characters, the raw bytes are used
+ * to calculate the hash instead, without SASLprep processing.  This is
+ * because PostgreSQL supports other encodings too, and the encoding being
+ * used during authentication is undefined (client_encoding isn't set until
+ * after authentication).  In effect, we try to interpret the password as
+ * UTF-8 and apply SASLprep processing, but if it looks invalid, we assume
+ * that it's in some other encoding.
+ *
+ * In the worst case, we misinterpret a password that's in a different
+ * encoding as being Unicode, because it happens to consists entirely of
+ * valid UTF-8 bytes, and we apply Unicode normalization to it.  As long
+ * as we do that consistently, that will not lead to failed logins.
+ * Fortunately, the UTF-8 byte sequences that are ignored by SASLprep
+ * don't correspond to any commonly used characters in any of the other
+ * supported encodings, so it should not lead to any significant loss in
+ * entropy, even if the normalization is incorrectly applied to a
+ * non-UTF-8 password.
+ *
+ * Error handling
+ * --------------
+ *
+ * Don't reveal user information to an unauthenticated client.  We don't
+ * want an attacker to be able to probe whether a particular username is
+ * valid.  In SCRAM, the server has to read the salt and iteration count
+ * from the user's password verifier, and send it to the client.  To avoid
+ * revealing whether a user exists, when the client tries to authenticate
+ * with a username that doesn't exist, or doesn't have a valid SCRAM
+ * verifier in pg_authid, we create a fake salt and iteration count
+ * on-the-fly, and proceed with the authentication with that.  In the end,
+ * we'll reject the attempt, as if an incorrect password was given.  When
+ * we are performing a "mock" authentication, the 'doomed' flag in
+ * scram_state is set.
+ *
+ * In the error messages, avoid printing strings from the client, unless
+ * you check that they are pure ASCII.  We don't want an unauthenticated
+ * attacker to be able to spam the logs with characters that are not valid
+ * to the encoding being used, whatever that is.  We cannot avoid that in
+ * general, after logging in, but let's do what we can here.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/libpq/auth-scram.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "pool.h"
+#include "auth/scram-common.h"
+#include "utils/sha2.h"
+#include "auth/pool_passwd.h"
+#include "auth/scram.h"
+#include "utils/base64.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
+
+#include "parser/stringinfo.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+
+#define MOCK_AUTH_NONCE_LEN            32
+
+typedef enum
+{
+       FE_SCRAM_INIT,
+       FE_SCRAM_NONCE_SENT,
+       FE_SCRAM_PROOF_SENT,
+       FE_SCRAM_FINISHED
+} fe_scram_state_enum;
+
+typedef struct
+{
+       fe_scram_state_enum state;
+
+       /* These are supplied by the user */
+       const char *username;
+       char       *password;
+
+       /* We construct these */
+       uint8           SaltedPassword[SCRAM_KEY_LEN];
+       char       *client_nonce;
+       char       *client_first_message_bare;
+       char       *client_final_message_without_proof;
+
+       /* These come from the server-first message */
+       char       *server_first_message;
+       char       *salt;
+       int                     saltlen;
+       int                     iterations;
+       char       *nonce;
+
+       /* These come from the server-final message */
+       char       *server_final_message;
+       char            ServerSignature[SCRAM_KEY_LEN];
+} fe_scram_state;
+
+
+/*
+ * Status data for a SCRAM authentication exchange.  This should be kept
+ * internal to this file.
+ */
+typedef enum
+{
+       SCRAM_AUTH_INIT,
+       SCRAM_AUTH_SALT_SENT,
+       SCRAM_AUTH_FINISHED
+} scram_state_enum;
+
+typedef struct
+{
+       scram_state_enum state;
+
+       const char *username;           /* username from startup packet */
+
+       char            cbind_flag;
+
+       int                     iterations;
+       char       *salt;                       /* base64-encoded */
+       uint8           StoredKey[SCRAM_KEY_LEN];
+       uint8           ServerKey[SCRAM_KEY_LEN];
+
+       /* Fields of the first message from client */
+       char       *client_first_message_bare;
+       char       *client_username;
+       char       *client_nonce;
+
+       /* Fields from the last message from client */
+       char       *client_final_message_without_proof;
+       char       *client_final_nonce;
+       char            ClientProof[SCRAM_KEY_LEN];
+
+       /* Fields generated in the server */
+       char       *server_first_message;
+       char       *server_nonce;
+
+       /*
+        * If something goes wrong during the authentication, or we are performing
+        * a "mock" authentication (see comments at top of file), the 'doomed'
+        * flag is set.  A reason for the failure, for the server log, is put in
+        * 'logdetail'.
+        */
+       bool            doomed;
+       char       *logdetail;
+} scram_state;
+
+
+static bool read_server_first_message(fe_scram_state *state, char *input);
+static bool read_server_final_message(fe_scram_state *state, char *input);
+static char *build_client_first_message(fe_scram_state *state);
+static char *build_client_final_message(fe_scram_state *state);
+static bool verify_server_signature(fe_scram_state *state);
+static void calculate_client_proof(fe_scram_state *state,
+                                                                  const char *client_final_message_without_proof,
+                                                                  uint8 *result);
+
+static void read_client_first_message(scram_state *state, char *input);
+static void read_client_final_message(scram_state *state, char *input);
+static char *build_server_first_message(scram_state *state);
+static char *build_server_final_message(scram_state *state);
+static bool verify_client_proof(scram_state *state);
+static bool verify_final_nonce(scram_state *state);
+static bool parse_scram_verifier(const char *verifier, int *iterations,
+                                        char **salt, uint8 *stored_key, uint8 *server_key);
+static void mock_scram_verifier(const char *username, int *iterations,
+                                       char **salt, uint8 *stored_key, uint8 *server_key);
+static bool is_scram_printable(char *p);
+static char *sanitize_char(char c);
+static char *GetMockAuthenticationNonce(void);
+static char *scram_mock_salt(const char *username);
+
+/*
+ * pg_be_scram_init
+ *
+ * Initialize a new SCRAM authentication exchange status tracker.  This
+ * needs to be called before doing any exchange.  It will be filled later
+ * after the beginning of the exchange with verifier data.
+ *
+ * 'username' is the username provided by the client in the startup message.
+ * 'shadow_pass' is the role's password verifier, from pg_authid.rolpassword.
+ * If 'shadow_pass' is NULL, we still perform an authentication exchange, but
+ * it will fail, as if an incorrect password was given.
+ */
+void *
+pg_be_scram_init(const char *username, const char *shadow_pass)
+{
+       scram_state *state;
+       bool            got_verifier;
+
+       state = (scram_state *) palloc0(sizeof(scram_state));
+       state->state = SCRAM_AUTH_INIT;
+       state->username = username;
+
+       /*
+        * Parse the stored password verifier.
+        */
+       if (shadow_pass)
+       {
+               int                     password_type = get_password_type(shadow_pass);
+
+               if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
+               {
+                       if (parse_scram_verifier(shadow_pass, &state->iterations, &state->salt,
+                                                                        state->StoredKey, state->ServerKey))
+                               got_verifier = true;
+                       else
+                       {
+                               /*
+                                * The password looked like a SCRAM verifier, but could not be
+                                * parsed.
+                                */
+                               ereport(LOG,
+                                               (errmsg("invalid SCRAM verifier for user \"%s\"",
+                                                               username)));
+                               got_verifier = false;
+                       }
+               }
+               else
+               {
+                       /*
+                        * The user doesn't have SCRAM verifier. (You cannot do SCRAM
+                        * authentication with an MD5 hash.)
+                        */
+                       state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM verifier."),
+                                                                               state->username);
+                       got_verifier = false;
+               }
+       }
+       else
+       {
+               /*
+                * The caller requested us to perform a dummy authentication.  This is
+                * considered normal, since the caller requested it, so don't set log
+                * detail.
+                */
+               got_verifier = false;
+       }
+
+       /*
+        * If the user did not have a valid SCRAM verifier, we still go through
+        * the motions with a mock one, and fail as if the client supplied an
+        * incorrect password.  This is to avoid revealing information to an
+        * attacker.
+        */
+       if (!got_verifier)
+       {
+               mock_scram_verifier(username, &state->iterations, &state->salt,
+                                                       state->StoredKey, state->ServerKey);
+               state->doomed = true;
+       }
+
+       return state;
+}
+
+/*
+ * Continue a SCRAM authentication exchange.
+ *
+ * 'input' is the SCRAM payload sent by the client.  On the first call,
+ * 'input' contains the "Initial Client Response" that the client sent as
+ * part of the SASLInitialResponse message, or NULL if no Initial Client
+ * Response was given.  (The SASL specification distinguishes between an
+ * empty response and non-existing one.)  On subsequent calls, 'input'
+ * cannot be NULL.  For convenience in this function, the caller must
+ * ensure that there is a null terminator at input[inputlen].
+ *
+ * The next message to send to client is saved in 'output', for a length
+ * of 'outputlen'.  In the case of an error, optionally store a palloc'd
+ * string at *logdetail that will be sent to the postmaster log (but not
+ * the client).
+ */
+int
+pg_be_scram_exchange(void *opaq, char *input, int inputlen,
+                                        char **output, int *outputlen, char **logdetail)
+{
+       scram_state *state = (scram_state *) opaq;
+       int                     result;
+
+       *output = NULL;
+
+       /*
+        * If the client didn't include an "Initial Client Response" in the
+        * SASLInitialResponse message, send an empty challenge, to which the
+        * client will respond with the same data that usually comes in the
+        * Initial Client Response.
+        */
+       if (input == NULL)
+       {
+               Assert(state->state == SCRAM_AUTH_INIT);
+
+               *output = pstrdup("");
+               *outputlen = 0;
+               return SASL_EXCHANGE_CONTINUE;
+       }
+
+       /*
+        * Check that the input length agrees with the string length of the input.
+        * We can ignore inputlen after this.
+        */
+       if (inputlen == 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("The message is empty.")));
+       if (inputlen != strlen(input))
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Message length does not match input length.")));
+
+       switch (state->state)
+       {
+               case SCRAM_AUTH_INIT:
+
+                       /*
+                        * Initialization phase.  Receive the first message from client
+                        * and be sure that it parsed correctly.  Then send the challenge
+                        * to the client.
+                        */
+                       read_client_first_message(state, input);
+
+                       /* prepare message to send challenge */
+                       *output = build_server_first_message(state);
+
+                       state->state = SCRAM_AUTH_SALT_SENT;
+                       result = SASL_EXCHANGE_CONTINUE;
+                       break;
+
+               case SCRAM_AUTH_SALT_SENT:
+
+                       /*
+                        * Final phase for the server.  Receive the response to the
+                        * challenge previously sent, verify, and let the client know that
+                        * everything went well (or not).
+                        */
+                       read_client_final_message(state, input);
+
+                       if (!verify_final_nonce(state))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                                errmsg("invalid SCRAM response"),
+                                                errdetail("Nonce does not match.")));
+
+                       /*
+                        * Now check the final nonce and the client proof.
+                        *
+                        * If we performed a "mock" authentication that we knew would fail
+                        * from the get go, this is where we fail.
+                        *
+                        * The SCRAM specification includes an error code,
+                        * "invalid-proof", for authentication failure, but it also allows
+                        * erroring out in an application-specific way.  We choose to do
+                        * the latter, so that the error message for invalid password is
+                        * the same for all authentication methods.  The caller will call
+                        * ereport(), when we return SASL_EXCHANGE_FAILURE with no output.
+                        *
+                        * NB: the order of these checks is intentional.  We calculate the
+                        * client proof even in a mock authentication, even though it's
+                        * bound to fail, to thwart timing attacks to determine if a role
+                        * with the given name exists or not.
+                        */
+                       if (!verify_client_proof(state) || state->doomed)
+                       {
+                               result = SASL_EXCHANGE_FAILURE;
+                               break;
+                       }
+
+                       /* Build final message for client */
+                       *output = build_server_final_message(state);
+
+                       /* Success! */
+                       result = SASL_EXCHANGE_SUCCESS;
+                       state->state = SCRAM_AUTH_FINISHED;
+                       break;
+
+               default:
+                       elog(ERROR, "invalid SCRAM exchange state");
+                       result = SASL_EXCHANGE_FAILURE;
+       }
+
+       if (result == SASL_EXCHANGE_FAILURE && state->logdetail && logdetail)
+               *logdetail = state->logdetail;
+
+       if (*output)
+               *outputlen = strlen(*output);
+
+       return result;
+}
+
+/*
+ * Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
+ *
+ * The result is palloc'd, so caller is responsible for freeing it.
+ */
+char *
+pg_be_scram_build_verifier(const char *password)
+{
+       char            saltbuf[SCRAM_DEFAULT_SALT_LEN];
+       char       *result;
+
+       /* Generate random salt */
+       pool_random(saltbuf, SCRAM_DEFAULT_SALT_LEN);
+
+       result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
+                                                                 SCRAM_DEFAULT_ITERATIONS, password);
+
+
+       return result;
+}
+
+/*
+ * Verify a plaintext password against a SCRAM verifier.  This is used when
+ * performing plaintext password authentication for a user that has a SCRAM
+ * verifier stored in pg_authid.
+ */
+bool
+scram_verify_plain_password(const char *username, const char *password,
+                                                       const char *verifier)
+{
+       char       *encoded_salt;
+       char       *salt;
+       int                     saltlen;
+       int                     iterations;
+       uint8           salted_password[SCRAM_KEY_LEN];
+       uint8           stored_key[SCRAM_KEY_LEN];
+       uint8           server_key[SCRAM_KEY_LEN];
+       uint8           computed_key[SCRAM_KEY_LEN];
+       char       *prep_password = NULL;
+
+       if (!parse_scram_verifier(verifier, &iterations, &encoded_salt,
+                                                         stored_key, server_key))
+       {
+               /*
+                * The password looked like a SCRAM verifier, but could not be parsed.
+                */
+               ereport(LOG,
+                               (errmsg("invalid SCRAM verifier for user \"%s\"", username)));
+               return false;
+       }
+
+       salt = palloc(pg_b64_dec_len(strlen(encoded_salt)));
+       saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt);
+       if (saltlen == -1)
+       {
+               ereport(LOG,
+                               (errmsg("invalid SCRAM verifier for user \"%s\"", username)));
+               return false;
+       }
+
+       /* Compute Server Key based on the user-supplied plaintext password */
+       scram_SaltedPassword(password, salt, saltlen, iterations, salted_password);
+       scram_ServerKey(salted_password, computed_key);
+
+       if (prep_password)
+               pfree(prep_password);
+
+       /*
+        * Compare the verifier's Server Key with the one computed from the
+        * user-supplied password.
+        */
+       return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
+}
+
+
+/*
+ * Parse and validate format of given SCRAM verifier.
+ *
+ * Returns true if the SCRAM verifier has been parsed, and false otherwise.
+ */
+static bool
+parse_scram_verifier(const char *verifier, int *iterations, char **salt,
+                                        uint8 *stored_key, uint8 *server_key)
+{
+       char       *v;
+       char       *p;
+       char       *scheme_str;
+       char       *salt_str;
+       char       *iterations_str;
+       char       *storedkey_str;
+       char       *serverkey_str;
+       int                     decoded_len;
+       char       *decoded_salt_buf;
+
+       /*
+        * The verifier is of form:
+        *
+        * SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
+        */
+       v = pstrdup(verifier);
+       if ((scheme_str = strtok(v, "$")) == NULL)
+               goto invalid_verifier;
+       if ((iterations_str = strtok(NULL, ":")) == NULL)
+               goto invalid_verifier;
+       if ((salt_str = strtok(NULL, "$")) == NULL)
+               goto invalid_verifier;
+       if ((storedkey_str = strtok(NULL, ":")) == NULL)
+               goto invalid_verifier;
+       if ((serverkey_str = strtok(NULL, "")) == NULL)
+               goto invalid_verifier;
+
+       /* Parse the fields */
+       if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
+               goto invalid_verifier;
+
+       errno = 0;
+       *iterations = strtol(iterations_str, &p, 10);
+       if (*p || errno != 0)
+               goto invalid_verifier;
+
+       /*
+        * Verify that the salt is in Base64-encoded format, by decoding it,
+        * although we return the encoded version to the caller.
+        */
+       decoded_salt_buf = palloc(pg_b64_dec_len(strlen(salt_str)));
+       decoded_len = pg_b64_decode(salt_str, strlen(salt_str), decoded_salt_buf);
+       if (decoded_len < 0)
+               goto invalid_verifier;
+       *salt = pstrdup(salt_str);
+
+       /*
+        * Decode StoredKey and ServerKey.
+        */
+       if (pg_b64_dec_len(strlen(storedkey_str) != SCRAM_KEY_LEN))
+               goto invalid_verifier;
+       decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str),
+                                                               (char *) stored_key);
+       if (decoded_len != SCRAM_KEY_LEN)
+               goto invalid_verifier;
+
+       if (pg_b64_dec_len(strlen(serverkey_str) != SCRAM_KEY_LEN))
+               goto invalid_verifier;
+       decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str),
+                                                               (char *) server_key);
+       if (decoded_len != SCRAM_KEY_LEN)
+               goto invalid_verifier;
+
+       return true;
+
+invalid_verifier:
+       pfree(v);
+       *salt = NULL;
+       return false;
+}
+
+static void
+mock_scram_verifier(const char *username, int *iterations, char **salt,
+                                       uint8 *stored_key, uint8 *server_key)
+{
+       char       *raw_salt;
+       char       *encoded_salt;
+       int                     encoded_len;
+
+       /* Generate deterministic salt */
+       raw_salt = scram_mock_salt(username);
+
+       encoded_salt = (char *) palloc(pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN) + 1);
+       encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt);
+       encoded_salt[encoded_len] = '\0';
+
+       *salt = encoded_salt;
+       *iterations = SCRAM_DEFAULT_ITERATIONS;
+
+       /* StoredKey and ServerKey are not used in a doomed authentication */
+       memset(stored_key, 0, SCRAM_KEY_LEN);
+       memset(server_key, 0, SCRAM_KEY_LEN);
+}
+
+/*
+ * Read the value in a given SCRAM exchange message for given attribute.
+ */
+static char *
+read_attr_value(char **input, char attr)
+{
+       char       *begin = *input;
+       char       *end;
+
+       if (*begin != attr)
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Expected attribute \"%c\" but found \"%s\".",
+                                                  attr, sanitize_char(*begin))));
+       begin++;
+
+       if (*begin != '=')
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
+       begin++;
+
+       end = begin;
+       while (*end && *end != ',')
+               end++;
+
+       if (*end)
+       {
+               *end = '\0';
+               *input = end + 1;
+       }
+       else
+               *input = end;
+
+       return begin;
+}
+
+static bool
+is_scram_printable(char *p)
+{
+       /*------
+        * Printable characters, as defined by SCRAM spec: (RFC 5802)
+        *
+        *      printable               = %x21-2B / %x2D-7E
+        *                                        ;; Printable ASCII except ",".
+        *                                        ;; Note that any "printable" is also
+        *                                        ;; a valid "value".
+        *------
+        */
+       for (; *p; p++)
+       {
+               if (*p < 0x21 || *p > 0x7E || *p == 0x2C /* comma */ )
+                       return false;
+       }
+       return true;
+}
+
+/*
+ * Convert an arbitrary byte to printable form.  For error messages.
+ *
+ * If it's a printable ASCII character, print it as a single character.
+ * otherwise, print it in hex.
+ *
+ * The returned pointer points to a static buffer.
+ */
+static char *
+sanitize_char(char c)
+{
+       static char buf[5];
+
+       if (c >= 0x21 && c <= 0x7E)
+               snprintf(buf, sizeof(buf), "'%c'", c);
+       else
+               snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c);
+       return buf;
+}
+
+/*
+ * Read the next attribute and value in a SCRAM exchange message.
+ *
+ * Returns NULL if there is attribute.
+ */
+static char *
+read_any_attr(char **input, char *attr_p)
+{
+       char       *begin = *input;
+       char       *end;
+       char            attr = *begin;
+
+       /*------
+        * attr-val                = ALPHA "=" value
+        *                                       ;; Generic syntax of any attribute sent
+        *                                       ;; by server or client
+        *------
+        */
+       if (!((attr >= 'A' && attr <= 'Z') ||
+                 (attr >= 'a' && attr <= 'z')))
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Attribute expected, but found invalid character \"%s\".",
+                                                  sanitize_char(attr))));
+       if (attr_p)
+               *attr_p = attr;
+       begin++;
+
+       if (*begin != '=')
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
+       begin++;
+
+       end = begin;
+       while (*end && *end != ',')
+               end++;
+
+       if (*end)
+       {
+               *end = '\0';
+               *input = end + 1;
+       }
+       else
+               *input = end;
+
+       return begin;
+}
+
+/*
+ * Read and parse the first message from client in the context of a SCRAM
+ * authentication exchange message.
+ *
+ * At this stage, any errors will be reported directly with ereport(ERROR).
+ */
+static void
+read_client_first_message(scram_state *state, char *input)
+{
+       input = pstrdup(input);
+
+       /*------
+        * The syntax for the client-first-message is: (RFC 5802)
+        *
+        * saslname                = 1*(value-safe-char / "=2C" / "=3D")
+        *                                       ;; Conforms to <value>.
+        *
+        * authzid                 = "a=" saslname
+        *                                       ;; Protocol specific.
+        *
+        * cb-name                 = 1*(ALPHA / DIGIT / "." / "-")
+        *                                        ;; See RFC 5056, Section 7.
+        *                                        ;; E.g., "tls-server-end-point" or
+        *                                        ;; "tls-unique".
+        *
+        * gs2-cbind-flag  = ("p=" cb-name) / "n" / "y"
+        *                                       ;; "n" -> client doesn't support channel binding.
+        *                                       ;; "y" -> client does support channel binding
+        *                                       ;;                but thinks the server does not.
+        *                                       ;; "p" -> client requires channel binding.
+        *                                       ;; The selected channel binding follows "p=".
+        *
+        * gs2-header      = gs2-cbind-flag "," [ authzid ] ","
+        *                                       ;; GS2 header for SCRAM
+        *                                       ;; (the actual GS2 header includes an optional
+        *                                       ;; flag to indicate that the GSS mechanism is not
+        *                                       ;; "standard", but since SCRAM is "standard", we
+        *                                       ;; don't include that flag).
+        *
+        * username                = "n=" saslname
+        *                                       ;; Usernames are prepared using SASLprep.
+        *
+        * reserved-mext  = "m=" 1*(value-char)
+        *                                       ;; Reserved for signaling mandatory extensions.
+        *                                       ;; The exact syntax will be defined in
+        *                                       ;; the future.
+        *
+        * nonce                   = "r=" c-nonce [s-nonce]
+        *                                       ;; Second part provided by server.
+        *
+        * c-nonce                 = printable
+        *
+        * client-first-message-bare =
+        *                                       [reserved-mext ","]
+        *                                       username "," nonce ["," extensions]
+        *
+        * client-first-message =
+        *                                       gs2-header client-first-message-bare
+        *
+        * For example:
+        * n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
+        *
+        * The "n,," in the beginning means that the client doesn't support
+        * channel binding, and no authzid is given.  "n=user" is the username.
+        * However, in PostgreSQL the username is sent in the startup packet, and
+        * the username in the SCRAM exchange is ignored.  libpq always sends it
+        * as an empty string.  The last part, "r=fyko+d2lbbFgONRv9qkxdawL" is
+        * the client nonce.
+        *------
+        */
+
+       /* read gs2-cbind-flag */
+       state->cbind_flag = *input;
+       switch (*input)
+       {
+               case 'n':
+                       /* Client does not support channel binding */
+                       input++;
+                       break;
+               case 'y':
+                       /* Client supports channel binding, but we're not doing it today */
+                       input++;
+                       break;
+               case 'p':
+
+                       /*
+                        * Client requires channel binding.  We don't support it.
+                        *
+                        * RFC 5802 specifies a particular error code,
+                        * e=server-does-support-channel-binding, for this.  But it can
+                        * only be sent in the server-final message, and we don't want to
+                        * go through the motions of the authentication, knowing it will
+                        * fail, just to send that error message.
+                        */
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("client requires SCRAM channel binding, but it is not supported")));
+               default:
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                        errmsg("malformed SCRAM message"),
+                                        errdetail("Unexpected channel-binding flag \"%s\".",
+                                                          sanitize_char(*input))));
+       }
+       if (*input != ',')
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Comma expected, but found character \"%s\".",
+                                                  sanitize_char(*input))));
+       input++;
+
+       /*
+        * Forbid optional authzid (authorization identity).  We don't support it.
+        */
+       if (*input == 'a')
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("client uses authorization identity, but it is not supported")));
+       if (*input != ',')
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Unexpected attribute \"%s\" in client-first-message.",
+                                                  sanitize_char(*input))));
+       input++;
+
+       state->client_first_message_bare = pstrdup(input);
+
+       /*
+        * Any mandatory extensions would go here.  We don't support any.
+        *
+        * RFC 5802 specifies error code "e=extensions-not-supported" for this,
+        * but it can only be sent in the server-final message.  We prefer to fail
+        * immediately (which the RFC also allows).
+        */
+       if (*input == 'm')
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("client requires an unsupported SCRAM extension")));
+
+       /*
+        * Read username.  Note: this is ignored.  We use the username from the
+        * startup message instead, still it is kept around if provided as it
+        * proves to be useful for debugging purposes.
+        */
+       state->client_username = read_attr_value(&input, 'n');
+
+       /* read nonce and check that it is made of only printable characters */
+       state->client_nonce = read_attr_value(&input, 'r');
+       if (!is_scram_printable(state->client_nonce))
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("non-printable characters in SCRAM nonce")));
+
+       /*
+        * There can be any number of optional extensions after this.  We don't
+        * support any extensions, so ignore them.
+        */
+       while (*input != '\0')
+               read_any_attr(&input, NULL);
+
+       /* success! */
+}
+
+/*
+ * Verify the final nonce contained in the last message received from
+ * client in an exchange.
+ */
+static bool
+verify_final_nonce(scram_state *state)
+{
+       int                     client_nonce_len = strlen(state->client_nonce);
+       int                     server_nonce_len = strlen(state->server_nonce);
+       int                     final_nonce_len = strlen(state->client_final_nonce);
+
+       if (final_nonce_len != client_nonce_len + server_nonce_len)
+               return false;
+       if (memcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
+               return false;
+       if (memcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
+               return false;
+
+       return true;
+}
+
+/*
+ * Verify the client proof contained in the last message received from
+ * client in an exchange.
+ */
+static bool
+verify_client_proof(scram_state *state)
+{
+       uint8           ClientSignature[SCRAM_KEY_LEN];
+       uint8           ClientKey[SCRAM_KEY_LEN];
+       uint8           client_StoredKey[SCRAM_KEY_LEN];
+       scram_HMAC_ctx ctx;
+       int                     i;
+
+       /* calculate ClientSignature */
+       scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN);
+       scram_HMAC_update(&ctx,
+                                         state->client_first_message_bare,
+                                         strlen(state->client_first_message_bare));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         state->server_first_message,
+                                         strlen(state->server_first_message));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         state->client_final_message_without_proof,
+                                         strlen(state->client_final_message_without_proof));
+       scram_HMAC_final(ClientSignature, &ctx);
+
+       /* Extract the ClientKey that the client calculated from the proof */
+       for (i = 0; i < SCRAM_KEY_LEN; i++)
+               ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
+
+       /* Hash it one more time, and compare with StoredKey */
+       scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey);
+
+       if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
+               return false;
+
+       return true;
+}
+
+/*
+ * Build the first server-side message sent to the client in a SCRAM
+ * communication exchange.
+ */
+static char *
+build_server_first_message(scram_state *state)
+{
+       /*------
+        * The syntax for the server-first-message is: (RFC 5802)
+        *
+        * server-first-message =
+        *                                       [reserved-mext ","] nonce "," salt ","
+        *                                       iteration-count ["," extensions]
+        *
+        * nonce                   = "r=" c-nonce [s-nonce]
+        *                                       ;; Second part provided by server.
+        *
+        * c-nonce                 = printable
+        *
+        * s-nonce                 = printable
+        *
+        * salt                    = "s=" base64
+        *
+        * iteration-count = "i=" posit-number
+        *                                       ;; A positive number.
+        *
+        * Example:
+        *
+        * r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
+        *------
+        */
+
+       /*
+        * Per the spec, the nonce may consist of any printable ASCII characters.
+        * For convenience, however, we don't use the whole range available,
+        * rather, we generate some random bytes, and base64 encode them.
+        */
+       char            raw_nonce[SCRAM_RAW_NONCE_LEN];
+       int                     encoded_len;
+
+       pool_random(raw_nonce, SCRAM_RAW_NONCE_LEN);
+
+       state->server_nonce = palloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1);
+       encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->server_nonce);
+       state->server_nonce[encoded_len] = '\0';
+
+       state->server_first_message =
+               psprintf("r=%s%s,s=%s,i=%u",
+                                state->client_nonce, state->server_nonce,
+                                state->salt, state->iterations);
+
+       return pstrdup(state->server_first_message);
+}
+
+
+/*
+ * Read and parse the final message received from client.
+ */
+static void
+read_client_final_message(scram_state *state, char *input)
+{
+       char            attr;
+       char       *channel_binding;
+       char       *value;
+       char       *begin,
+                          *proof;
+       char       *p;
+       char       *client_proof;
+
+       begin = p = pstrdup(input);
+
+       /*------
+        * The syntax for the server-first-message is: (RFC 5802)
+        *
+        * gs2-header      = gs2-cbind-flag "," [ authzid ] ","
+        *                                       ;; GS2 header for SCRAM
+        *                                       ;; (the actual GS2 header includes an optional
+        *                                       ;; flag to indicate that the GSS mechanism is not
+        *                                       ;; "standard", but since SCRAM is "standard", we
+        *                                       ;; don't include that flag).
+        *
+        * cbind-input   = gs2-header [ cbind-data ]
+        *                                       ;; cbind-data MUST be present for
+        *                                       ;; gs2-cbind-flag of "p" and MUST be absent
+        *                                       ;; for "y" or "n".
+        *
+        * channel-binding = "c=" base64
+        *                                       ;; base64 encoding of cbind-input.
+        *
+        * proof                   = "p=" base64
+        *
+        * client-final-message-without-proof =
+        *                                       channel-binding "," nonce [","
+        *                                       extensions]
+        *
+        * client-final-message =
+        *                                       client-final-message-without-proof "," proof
+        *------
+        */
+
+       /*
+        * Read channel-binding.  We don't support channel binding, so it's
+        * expected to always be "biws", which is "n,,", base64-encoded, or
+        * "eSws", which is "y,,".  We also have to check whether the flag is
+        * the same one that the client originally sent.
+        */
+       channel_binding = read_attr_value(&p, 'c');
+       if (!(strcmp(channel_binding, "biws") == 0 && state->cbind_flag == 'n') &&
+               !(strcmp(channel_binding, "eSws") == 0 && state->cbind_flag == 'y'))
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                (errmsg("unexpected SCRAM channel-binding attribute in client-final-message"))));
+       state->client_final_nonce = read_attr_value(&p, 'r');
+
+       /* ignore optional extensions */
+       do
+       {
+               proof = p - 1;
+               value = read_any_attr(&p, &attr);
+       } while (attr != 'p');
+
+       client_proof = palloc(pg_b64_dec_len(strlen(value)));
+       if (pg_b64_decode(value, strlen(value), client_proof) != SCRAM_KEY_LEN)
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Malformed proof in client-final-message.")));
+       memcpy(state->ClientProof, client_proof, SCRAM_KEY_LEN);
+       pfree(client_proof);
+
+       if (*p != '\0')
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("malformed SCRAM message"),
+                                errdetail("Garbage found at the end of client-final-message.")));
+
+       state->client_final_message_without_proof = palloc(proof - begin + 1);
+       memcpy(state->client_final_message_without_proof, input, proof - begin);
+       state->client_final_message_without_proof[proof - begin] = '\0';
+}
+
+/*
+ * Build the final server-side message of an exchange.
+ */
+static char *
+build_server_final_message(scram_state *state)
+{
+       uint8           ServerSignature[SCRAM_KEY_LEN];
+       char       *server_signature_base64;
+       int                     siglen;
+       scram_HMAC_ctx ctx;
+
+       /* calculate ServerSignature */
+       scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN);
+       scram_HMAC_update(&ctx,
+                                         state->client_first_message_bare,
+                                         strlen(state->client_first_message_bare));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         state->server_first_message,
+                                         strlen(state->server_first_message));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         state->client_final_message_without_proof,
+                                         strlen(state->client_final_message_without_proof));
+       scram_HMAC_final(ServerSignature, &ctx);
+
+       server_signature_base64 = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
+       siglen = pg_b64_encode((const char *) ServerSignature,
+                                                  SCRAM_KEY_LEN, server_signature_base64);
+       server_signature_base64[siglen] = '\0';
+
+       /*------
+        * The syntax for the server-final-message is: (RFC 5802)
+        *
+        * verifier                = "v=" base64
+        *                                       ;; base-64 encoded ServerSignature.
+        *
+        * server-final-message = (server-error / verifier)
+        *                                       ["," extensions]
+        *
+        *------
+        */
+       return psprintf("v=%s", server_signature_base64);
+}
+
+
+/*
+ * Determinisitcally generate salt for mock authentication, using a SHA256
+ * hash based on the username and a cluster-level secret key.  Returns a
+ * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN.
+ */
+static char *
+GetMockAuthenticationNonce(void)
+{
+       static char mockNonce[MOCK_AUTH_NONCE_LEN] = "pgpool-II random nonce string";
+       return mockNonce;
+}
+static char *
+scram_mock_salt(const char *username)
+{
+       pg_sha256_ctx ctx;
+       static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH];
+       char       *mock_auth_nonce = GetMockAuthenticationNonce();
+
+       /*
+        * Generate salt using a SHA256 hash of the username and the cluster's
+        * mock authentication nonce.  (This works as long as the salt length is
+        * not larger the SHA256 digest length. If the salt is smaller, the caller
+        * will just ignore the extra data.)
+        */
+       StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
+                                        "salt length greater than SHA256 digest length");
+
+       pg_sha256_init(&ctx);
+       pg_sha256_update(&ctx, (uint8 *) username, strlen(username));
+       pg_sha256_update(&ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN);
+       pg_sha256_final(&ctx, sha_digest);
+
+       return (char *) sha_digest;
+}
+
+/* scram client auth code */
+
+/*
+ * Initialize SCRAM exchange status.
+ */
+void *
+pg_fe_scram_init(const char *username, const char *password)
+{
+       fe_scram_state *state;
+
+       state = (fe_scram_state *) palloc(sizeof(fe_scram_state));
+       if (!state)
+               return NULL;
+       memset(state, 0, sizeof(fe_scram_state));
+       state->state = FE_SCRAM_INIT;
+       state->username = username;
+
+       state->password = pstrdup(password);
+
+       return state;
+}
+
+/*
+ * Free SCRAM exchange status
+ */
+void
+pg_fe_scram_free(void *opaq)
+{
+       fe_scram_state *state = (fe_scram_state *) opaq;
+
+       if (state->password)
+               pfree(state->password);
+
+       /* client messages */
+       if (state->client_nonce)
+               pfree(state->client_nonce);
+       if (state->client_first_message_bare)
+               pfree(state->client_first_message_bare);
+       if (state->client_final_message_without_proof)
+               pfree(state->client_final_message_without_proof);
+
+       /* first message from server */
+       if (state->server_first_message)
+               pfree(state->server_first_message);
+       if (state->salt)
+               pfree(state->salt);
+       if (state->nonce)
+               pfree(state->nonce);
+
+       /* final message from server */
+       if (state->server_final_message)
+               pfree(state->server_final_message);
+
+       pfree(state);
+}
+
+/*
+ * Exchange a SCRAM message with backend.
+ */
+void
+pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
+                                        char **output, int *outputlen,
+                                        bool *done, bool *success)
+{
+       fe_scram_state *state = (fe_scram_state *) opaq;
+
+       *done = false;
+       *success = false;
+       *output = NULL;
+       *outputlen = 0;
+
+       /*
+        * Check that the input length agrees with the string length of the input.
+        * We can ignore inputlen after this.
+        */
+       if (state->state != FE_SCRAM_INIT)
+       {
+               if (inputlen == 0)
+               {
+                       ereport(ERROR,
+                                       (errmsg("malformed SCRAM message from server (empty message)")));
+                       goto error;
+               }
+               if (inputlen != strlen(input))
+               {
+                       ereport(ERROR,
+                                       (errmsg("malformed SCRAM message (length mismatch)")));
+
+                       goto error;
+               }
+       }
+
+       switch (state->state)
+       {
+               case FE_SCRAM_INIT:
+                       /* Begin the SCRAM handshake, by sending client nonce */
+                       *output = build_client_first_message(state);
+                       if (*output == NULL)
+                               goto error;
+
+                       *outputlen = strlen(*output);
+                       *done = false;
+                       state->state = FE_SCRAM_NONCE_SENT;
+                       break;
+
+               case FE_SCRAM_NONCE_SENT:
+                       /* Receive salt and server nonce, send response. */
+                       if (!read_server_first_message(state, input))
+                               goto error;
+
+                       *output = build_client_final_message(state);
+                       if (*output == NULL)
+                               goto error;
+
+                       *outputlen = strlen(*output);
+                       *done = false;
+                       state->state = FE_SCRAM_PROOF_SENT;
+                       break;
+
+               case FE_SCRAM_PROOF_SENT:
+                       /* Receive server signature */
+                       if (!read_server_final_message(state, input))
+                               goto error;
+
+                       /*
+                        * Verify server signature, to make sure we're talking to the
+                        * genuine server.  XXX: A fake server could simply not require
+                        * authentication, though.  There is currently no option in libpq
+                        * to reject a connection, if SCRAM authentication did not happen.
+                        */
+                       if (verify_server_signature(state))
+                               *success = true;
+                       else
+                       {
+                               *success = false;
+                               ereport(ERROR,
+                                               (errmsg("incorrect server signature")));
+                       }
+                       *done = true;
+                       state->state = FE_SCRAM_FINISHED;
+                       break;
+
+               default:
+                       /* shouldn't happen */
+                       ereport(ERROR,
+                                       (errmsg("invalid SCRAM exchange state")));
+                       goto error;
+       }
+       return;
+
+error:
+       *done = true;
+       *success = false;
+       return;
+}
+
+/*
+ * Build the first exchange message sent by the client.
+ */
+static char *
+build_client_first_message(fe_scram_state *state)
+{
+       char            raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
+       char       *buf;
+       char            buflen;
+       int                     encoded_len;
+
+       /*
+        * Generate a "raw" nonce.  This is converted to ASCII-printable form by
+        * base64-encoding it.
+        */
+       pool_random(raw_nonce, SCRAM_RAW_NONCE_LEN);
+
+       state->client_nonce = palloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1);
+
+       encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->client_nonce);
+       state->client_nonce[encoded_len] = '\0';
+
+       /*
+        * Generate message.  The username is left empty as the backend uses the
+        * value provided by the startup packet.  Also, as this username is not
+        * prepared with SASLprep, the message parsing would fail if it includes
+        * '=' or ',' characters.
+        */
+       buflen = 8 + strlen(state->client_nonce) + 1;
+       buf = palloc(buflen);
+
+       snprintf(buf, buflen, "n,,n=,r=%s", state->client_nonce);
+
+       state->client_first_message_bare = pstrdup(buf + 3);
+
+       return buf;
+}
+
+/*
+ * Build the final exchange message sent from the client.
+ */
+static char *
+build_client_final_message(fe_scram_state *state)
+{
+       StringInfoData buf;
+       uint8           client_proof[SCRAM_KEY_LEN];
+       char       *result;
+
+       initStringInfo(&buf);
+
+       /*
+        * Construct client-final-message-without-proof.  We need to remember it
+        * for verifying the server proof in the final step of authentication.
+        */
+       appendStringInfo(&buf, "c=biws,r=%s", state->nonce);
+
+       state->client_final_message_without_proof = pstrdup(buf.data);
+
+       /* Append proof to it, to form client-final-message. */
+       calculate_client_proof(state,
+                                                  state->client_final_message_without_proof,
+                                                  client_proof);
+       /////
+       appendStringInfoString(&buf, ",p=");
+
+       /*
+       char *encoded_data = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
+       int len = encoded_data = pg_b64_encode((char *) client_proof,
+                                                                                  SCRAM_KEY_LEN,
+                                                                                  encoded_data);
+       encoded_data[len] = '\0';
+       appendStringInfoString(&buf, encoded_data);
+        */
+
+       enlargeStringInfo(&buf, pg_b64_enc_len(SCRAM_KEY_LEN));
+       buf.len += pg_b64_encode((char *) client_proof,
+                                                        SCRAM_KEY_LEN,
+                                                        buf.data + buf.len);
+       buf.data[buf.len] = '\0';
+
+       result = pstrdup(buf.data);
+
+       pfree(buf.data);
+       return result;
+}
+
+/*
+ * Read the first exchange message coming from the server.
+ */
+static bool
+read_server_first_message(fe_scram_state *state, char *input)
+{
+       char       *iterations_str;
+       char       *endptr;
+       char       *encoded_salt;
+       char       *nonce;
+
+       state->server_first_message = pstrdup(input);
+
+       /* parse the message */
+       nonce = read_attr_value(&input, 'r');
+       if (nonce == NULL)
+       {
+               /* read_attr_value() has generated an error string */
+               return false;
+       }
+
+       /* Verify immediately that the server used our part of the nonce */
+       if (strlen(nonce) < strlen(state->client_nonce) ||
+               memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
+       {
+               ereport(ERROR,
+                               (errmsg("invalid SCRAM response from server(nonce mismatch)")));
+               return false;
+       }
+
+       state->nonce = pstrdup(nonce);
+
+       encoded_salt = read_attr_value(&input, 's');
+       if (encoded_salt == NULL)
+       {
+               /* read_attr_value() has generated an error string */
+               return false;
+       }
+       state->salt = palloc(pg_b64_dec_len(strlen(encoded_salt)));
+
+       state->saltlen = pg_b64_decode(encoded_salt,
+                                                                  strlen(encoded_salt),
+                                                                  state->salt);
+
+       iterations_str = read_attr_value(&input, 'i');
+       if (iterations_str == NULL)
+       {
+               /* read_attr_value() has generated an error string */
+               return false;
+       }
+       state->iterations = strtol(iterations_str, &endptr, 10);
+       if (*endptr != '\0' || state->iterations < 1)
+       {
+               ereport(ERROR,
+                               (errmsg("malformed SCRAM message from server (invalid iteration count)")));
+               return false;
+       }
+
+       if (*input != '\0')
+               ereport(WARNING,
+                               (errmsg("malformed SCRAM message from server (garbage at end of server-first-message)")));
+
+       return true;
+}
+
+/*
+ * Read the final exchange message coming from the server.
+ */
+static bool
+read_server_final_message(fe_scram_state *state, char *input)
+{
+       char       *encoded_server_signature;
+       int                     server_signature_len;
+
+       state->server_final_message = pstrdup(input);
+
+       /* Check for error result. */
+       if (*input == 'e')
+       {
+               char       *err = read_attr_value(&input, 'e');
+
+               ereport(ERROR,
+                               (errmsg("error received from server in SCRAM exchange: %s",err)));
+               return false;
+       }
+
+       /* Parse the message. */
+       encoded_server_signature = read_attr_value(&input, 'v');
+       if (encoded_server_signature == NULL)
+       {
+               /* read_attr_value() has generated an error message */
+               return false;
+       }
+
+       if (*input != '\0')
+               ereport(WARNING,
+                               (errmsg("malformed SCRAM message (garbage at end of server-final-message)")));
+
+       server_signature_len = pg_b64_decode(encoded_server_signature,
+                                                                                strlen(encoded_server_signature),
+                                                                                state->ServerSignature);
+       if (server_signature_len != SCRAM_KEY_LEN)
+       {
+               ereport(ERROR,
+                               (errmsg("malformed SCRAM message from server (invalid server signature)")));
+               return false;
+       }
+
+       return true;
+}
+
+/*
+ * Calculate the client proof, part of the final exchange message sent
+ * by the client.
+ */
+static void
+calculate_client_proof(fe_scram_state *state,
+                                          const char *client_final_message_without_proof,
+                                          uint8 *result)
+{
+       uint8           StoredKey[SCRAM_KEY_LEN];
+       uint8           ClientKey[SCRAM_KEY_LEN];
+       uint8           ClientSignature[SCRAM_KEY_LEN];
+       int                     i;
+       scram_HMAC_ctx ctx;
+
+       /*
+        * Calculate SaltedPassword, and store it in 'state' so that we can reuse
+        * it later in verify_server_signature.
+        */
+       scram_SaltedPassword(state->password, state->salt, state->saltlen,
+                                                state->iterations, state->SaltedPassword);
+
+       scram_ClientKey(state->SaltedPassword, ClientKey);
+       scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
+
+       scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN);
+       scram_HMAC_update(&ctx,
+                                         state->client_first_message_bare,
+                                         strlen(state->client_first_message_bare));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         state->server_first_message,
+                                         strlen(state->server_first_message));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         client_final_message_without_proof,
+                                         strlen(client_final_message_without_proof));
+       scram_HMAC_final(ClientSignature, &ctx);
+
+       for (i = 0; i < SCRAM_KEY_LEN; i++)
+               result[i] = ClientKey[i] ^ ClientSignature[i];
+}
+
+/*
+ * Validate the server signature, received as part of the final exchange
+ * message received from the server.
+ */
+static bool
+verify_server_signature(fe_scram_state *state)
+{
+       uint8           expected_ServerSignature[SCRAM_KEY_LEN];
+       uint8           ServerKey[SCRAM_KEY_LEN];
+       scram_HMAC_ctx ctx;
+
+       scram_ServerKey(state->SaltedPassword, ServerKey);
+
+       /* calculate ServerSignature */
+       scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN);
+       scram_HMAC_update(&ctx,
+                                         state->client_first_message_bare,
+                                         strlen(state->client_first_message_bare));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         state->server_first_message,
+                                         strlen(state->server_first_message));
+       scram_HMAC_update(&ctx, ",", 1);
+       scram_HMAC_update(&ctx,
+                                         state->client_final_message_without_proof,
+                                         strlen(state->client_final_message_without_proof));
+       scram_HMAC_final(expected_ServerSignature, &ctx);
+
+       if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
+               return false;
+
+       return true;
+}
+
+/*
+ * Build a new SCRAM verifier.
+ */
+char *
+pg_fe_scram_build_verifier(const char *password)
+{
+       char            saltbuf[SCRAM_DEFAULT_SALT_LEN];
+       char       *result;
+
+       /* Generate a random salt */
+       pool_random(saltbuf, SCRAM_DEFAULT_SALT_LEN);
+
+       result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
+                                                                 SCRAM_DEFAULT_ITERATIONS, password);
+
+       return result;
+}
+
index b8597815b6afb2e4295849a79e0e01a52a07e1bb..772e10d0044111d4ce96f21f795d1e71f4c90beb 100644 (file)
 #include "pool_config.h"
 #include "auth/pool_hba.h"
 #include "auth/pool_passwd.h"
+#include "auth/scram.h"
 #include "utils/elog.h"
 #include "utils/palloc.h"
 #include "utils/memutils.h"
+#include "auth/md5.h"
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
 #ifdef HAVE_PARAM_H
 #include <param.h>
 #endif
+#ifdef USE_SSL
+#include <openssl/rand.h>
+#endif
+
 #include <errno.h>
 #include <string.h>
 #include <stdlib.h>
 
 #define AUTHFAIL_ERRORCODE "28000"
+#define MAX_SASL_PAYLOAD_LEN 1024
+
 
 static POOL_STATUS pool_send_backend_key_data(POOL_CONNECTION *frontend, int pid, int key, int protoMajor);
 static int do_clear_text_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
 static void pool_send_auth_fail(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp);
 static int do_crypt(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
-static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
-static int send_md5auth_request(POOL_CONNECTION *frontend, int protoMajor, char *salt);
+static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor,
+                                 char *storedPassword, PasswordType passwordType);
+static int do_md5_single_backend(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
+static void send_md5auth_request(POOL_CONNECTION *frontend, int protoMajor, char *salt);
 static int read_password_packet(POOL_CONNECTION *frontend, int protoMajor,     char *password, int *pwdSize);
 static int send_password_packet(POOL_CONNECTION *backend, int protoMajor, char *password);
 static int send_auth_ok(POOL_CONNECTION *frontend, int protoMajor);
-
+static void sendAuthRequest(POOL_CONNECTION *frontend, int protoMajor, int32 auth_req_type, char *extradata, int extralen);
 static long PostmasterRandom(void);
 
+static int pg_SASL_continue(POOL_CONNECTION *backend, char *payload, int payloadlen, void *sasl_state, bool final);
+static void* pg_SASL_init(POOL_CONNECTION *backend, char *payload, int payloadlen, char *username, char* storedPassword);
+static bool do_SCRAM(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, int protoMajor, int message_length,
+                                        char *username, char *storedPassword, PasswordType passwordType);
+static void authenticate_frontend_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor);
+static void authenticate_frontend_cert(POOL_CONNECTION *frontend);
+static void authenticate_frontend_SCRAM(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth);
+static void authenticate_frontend_clear_text(POOL_CONNECTION *frontend);
+static bool get_auth_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth,
+                                                         char** password, PasswordType *passwordType);
+
+/*
+ * Do authentication. Assuming the only caller is
+ * make_persistent_db_connection().
+ */
+void connection_do_auth(POOL_CONNECTION_POOL_SLOT *cp, char *password)
+{
+       char kind;
+       int length;
+       int auth_kind;
+       char state;
+       char *p;
+       int pid, key;
+       bool keydata_done;
+
+       /*
+        * read kind expecting 'R' packet (authentication response)
+        */
+       pool_read_with_error(cp->con, &kind, sizeof(kind),
+                                                "authentication message response type");
+
+       if (kind != 'R')
+       {
+               char *msg;
+               int sts = 0;
+
+               if (kind == 'E' || kind == 'N')
+               {
+                       sts =  pool_extract_error_message(false, cp->con, cp->sp->major, false, &msg);
+               }
+
+               if (sts == 1)   /* succeeded in extracting error/notice message */
+               {
+                       ereport(ERROR,
+                                       (errmsg("failed to authenticate"),
+                                        errdetail("%s", msg)));
+                       pfree(msg);
+               }
+               ereport(ERROR,
+                       (errmsg("failed to authenticate"),
+                                errdetail("invalid authentication message response type, Expecting 'R' and received '%c'",kind)));
+       }
+
+       /* read message length */
+       pool_read_with_error(cp->con, &length, sizeof(length),
+                                                "authentication message response length");
+
+       length = ntohl(length);
+
+       /* read auth kind */
+       pool_read_with_error(cp->con, &auth_kind, sizeof(auth_kind),
+                                                "authentication method from response");
+       auth_kind = ntohl(auth_kind);
+       ereport(DEBUG1,
+                       (errmsg("authenticate kind = %d",auth_kind)));
+
+       if (auth_kind == AUTH_REQ_OK)   /* trust authentication? */
+       {
+               cp->con->auth_kind = AUTH_REQ_OK;
+       }
+       else if (auth_kind == AUTH_REQ_PASSWORD) /* clear text password? */
+       {
+               kind = send_password_packet(cp->con, PROTO_MAJOR_V3, password);
+               if (kind != AUTH_REQ_OK)
+                       ereport(ERROR,
+                               (errmsg("password authentication failed for user:%s",cp->sp->user),
+                                        errdetail("backend replied with invalid kind")));
+
+               cp->con->auth_kind = AUTH_REQ_OK;
+               return;
+       }
+       else if (auth_kind == AUTH_REQ_CRYPT) /* crypt password? */
+       {
+               char salt[3];
+               char *crypt_password;
+
+               pool_read_with_error(cp->con, &salt, 2,"crypt salt");
+               salt[2] = '\0';
+
+               crypt_password = crypt(password, salt);
+               if (crypt_password == NULL)
+                       ereport(ERROR,
+                               (errmsg("crypt authentication failed for user:%s",cp->sp->user),
+                                        errdetail("failed to encrypt the password")));
+
+               /* Send password packet to backend and receive auth response */
+               kind = send_password_packet(cp->con, PROTO_MAJOR_V3, crypt_password);
+               if (kind != AUTH_REQ_OK)
+                       ereport(ERROR,
+                                       (errmsg("crypt authentication failed for user:%s",cp->sp->user),
+                                        errdetail("backend replied with invalid kind")));
+
+               cp->con->auth_kind = AUTH_REQ_OK;
+               return;
+       }
+       else if (auth_kind == AUTH_REQ_MD5) /* md5 password? */
+       {
+               char salt[4];
+               char *buf, *buf1;
+
+               pool_read_with_error(cp->con, &salt, sizeof(salt),"authentication md5 salt");
+
+               buf = palloc0(2 * (MD5_PASSWD_LEN + 4)); /* hash + "md5" + '\0' */
+
+               /* build md5 password */
+               buf1 = buf + MD5_PASSWD_LEN + 4;
+               pool_md5_encrypt(password, cp->sp->user, strlen(cp->sp->user), buf1);
+               pool_md5_encrypt(buf1, salt, 4, buf + 3);
+               memcpy(buf, "md5", 3);
+
+               /* Send password packet to backend and receive auth response */
+               kind = send_password_packet(cp->con, PROTO_MAJOR_V3, buf);
+               pfree(buf);
+               if (kind != AUTH_REQ_OK)
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed for user:%s",cp->sp->user),
+                                        errdetail("backend replied with invalid kind")));
+
+               cp->con->auth_kind = AUTH_REQ_OK;
+               return;
+       }
+       else if (auth_kind == AUTH_REQ_SASL)
+       {
+               if (do_SCRAM(NULL, cp->con, PROTO_MAJOR_V3, length, cp->sp->user, password, PASSWORD_TYPE_PLAINTEXT) == false)
+               {
+                       ereport(ERROR,
+                               (errmsg("failed to authenticate with backend"),
+                                        errdetail("SCRAM authentication failed for user:%s",cp->sp->user)));
+               }
+               ereport(DEBUG1,
+                               (errmsg("SCRAM authentication successful for user:%s",cp->sp->user)));
+
+       }
+       else
+       {
+               ereport(ERROR,
+                               (errmsg("failed to authenticate"),
+                                errdetail("auth kind %d is not yet supported", auth_kind)));
+       }
+
+       /*
+        * Read backend key data and wait until Ready for query arriving or
+        * error happens.
+        */
+
+       keydata_done = false;
+
+       for (;;)
+       {
+               pool_read_with_error(cp->con, &kind, sizeof(kind),
+                                                        "authentication message kind");
+
+               switch (kind)
+               {
+                       case 'K':       /* backend key data */
+                               keydata_done = true;
+                               ereport(DEBUG1,
+                                               (errmsg("authenticate backend: key data received")));
+
+                               /* read message length */
+                               pool_read_with_error(cp->con, &length, sizeof(length),"message length for authentication kind 'K'");
+                               if (ntohl(length) != 12)
+                               {
+                                       ereport(ERROR,
+                                                       (errmsg("failed to authenticate"),
+                                                        errdetail("invalid backend key data length. received %d bytes when expecting 12 bytes"
+                                                                          , ntohl(length))));
+                               }
+
+                               /* read pid */
+                               pool_read_with_error(cp->con, &pid, sizeof(pid),"pid for authentication kind 'K'");
+                               cp->pid = pid;
+
+                               /* read key */
+                               pool_read_with_error(cp->con, &key, sizeof(key),
+                                                                        "key for authentication kind 'K'");
+                               cp->key = key;
+                               break;
+
+                       case 'Z':       /* Ready for query */
+                               /* read message length */
+                               pool_read_with_error(cp->con, &length, sizeof(length),
+                                                                        "message length for authentication kind 'Z'");
+                               length = ntohl(length);
+
+                               /* read transaction state */
+                               pool_read_with_error(cp->con, &state, sizeof(state),
+                                                                        "transaction state  for authentication kind 'Z'");
+
+                               ereport(DEBUG1,
+                                               (errmsg("authenticate backend: transaction state: %c", state)));
+
+                               cp->con->tstate = state;
+
+                               if (!keydata_done)
+                               {
+                                       ereport(ERROR,
+                                                       (errmsg("failed to authenticate"),
+                                                        errdetail("ready for query arrived before receiving keydata")));
+                               }
+                               return;
+                               break;
+
+                       case 'S':       /* parameter status */
+                       case 'N':       /* notice response */
+                       case 'E':       /* error response */
+                               /* Just throw away data */
+                               pool_read_with_error(cp->con, &length, sizeof(length),
+                                                                        "backend message length");
+
+                               length = ntohl(length);
+                               length -= 4;
+
+                               p = pool_read2(cp->con, length);
+                               if (p == NULL)
+                                       ereport(ERROR,
+                                                       (errmsg("failed to authenticate"),
+                                                        errdetail("unable to read data from socket")));
+
+                               break;
+
+                       default:
+                               ereport(ERROR,
+                                               (errmsg("failed to authenticate"),
+                                                errdetail("unknown authentication message response received '%c'",kind)));
+                               break;
+               }
+       }
+       ereport(ERROR,
+                       (errmsg("failed to authenticate")));
+}
+
 /*
  * After sending the start up packet to the backend, do the
  * authentication against backend. if success return 0 otherwise non
@@ -70,6 +327,7 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        int length;
        int authkind;
        int i;
+       int message_length = 0;
        StartupPacket *sp;
        
 
@@ -105,14 +363,17 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        /*
         * message length (v3 only)
         */
-       if (protoMajor == PROTO_MAJOR_V3 && pool_read_message_length(cp) < 0)
-               ereport(ERROR,
-                       (errmsg("invalid authentication packet from backend"),
-                               errdetail("failed to get the authentication packet length"),
-                                       errhint("This is likely caused by the inconsistency of auth method among DB nodes. \
-                                                       Please check the previous error messages (hint: length field) \
-                                                       from pool_read_message_length and recheck the pg_hba.conf settings.")));
-
+       if (protoMajor == PROTO_MAJOR_V3)
+       {
+               message_length = pool_read_message_length(cp);
+               if (message_length <= 0)
+                       ereport(ERROR,
+                               (errmsg("invalid authentication packet from backend"),
+                                       errdetail("failed to get the authentication packet length"),
+                                               errhint("This is likely caused by the inconsistency of auth method among DB nodes. \
+                                                               Please check the previous error messages (hint: length field) \
+                                                               from pool_read_message_length and recheck the pg_hba.conf settings.")));
+       }
 
        /*
         * read authentication request kind.
@@ -141,9 +402,8 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        ereport(DEBUG1,
                (errmsg("authentication backend"),
                         errdetail("auth kind:%d", authkind)));
-
        /* trust? */
-       if (authkind == 0)
+       if (authkind == AUTH_REQ_OK)
        {
                int msglen;
 
@@ -157,11 +417,11 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
 
                msglen = htonl(0);
                pool_write_and_flush(frontend, &msglen, sizeof(msglen));
-               MASTER(cp)->auth_kind = 0;
+               MASTER(cp)->auth_kind = AUTH_REQ_OK;
        }
 
        /* clear text password authentication? */
-       else if (authkind == 3)
+       else if (authkind == AUTH_REQ_PASSWORD)
        {
                for (i=0;i<NUM_BACKENDS;i++)
                {
@@ -188,7 +448,7 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        }
 
        /* crypt authentication? */
-       else if (authkind == 4)
+       else if (authkind == AUTH_REQ_CRYPT)
        {
                for (i=0;i<NUM_BACKENDS;i++)
                {
@@ -212,22 +472,49 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
        }
 
        /* md5 authentication? */
-       else if (authkind == 5)
+       else if (authkind == AUTH_REQ_MD5)
        {
-               /* If MD5 auth is not active in pool_hba.conf, it cannot be
-                * used with other than raw mode.
+               char *password;
+               PasswordType passwordType;
+
+               /*
+                * check if we can use md5 authentication.
                 */
-               if ((frontend->pool_hba == NULL || frontend->pool_hba->auth_method != uaMD5) && !RAW_MODE && NUM_BACKENDS > 1)
+               if (NUM_BACKENDS >  1)
                {
-                       pool_send_error_message(frontend, protoMajor, AUTHFAIL_ERRORCODE,
-                                                                       "MD5 authentication is unsupported in replication and master-slave modes.",
-                                                                       "",
-                                                                       "check pg_hba.conf",
-                                                                       __FILE__, __LINE__);
-                       ereport(ERROR,
-                               (errmsg("failed to authenticate with backend"),
-                                       errdetail("MD5 authentication is not supported in replication and master-slave modes."),
-                                               errhint("check pg_hba.conf settings on backend node")));
+                       if (get_auth_password(MASTER(cp), frontend, 0,
+                                                                 &password, &passwordType) == false)
+                       {
+                               /*
+                                * We do not have any passeord,
+                                * we can still get the password from client using plain text authentication
+                                * if it is allowed by user
+                                */
+                               if (frontend->pool_hba == NULL && pool_config->allow_clear_text_frontend_auth)
+                               {
+                                       ereport(LOG,
+                                               (errmsg("using clear text authentication with frontend"),
+                                                       errdetail("backend will still use md5 auth"),
+                                                        errhint("you can disable this behavior by setting allow_clear_text_frontend_auth to off")));
+                                       authenticate_frontend_clear_text(frontend);
+                                       /* now check again if we have a password now */
+                                       if (get_auth_password(MASTER(cp), frontend, 0,
+                                                                                 &password, &passwordType) == false)
+                                       {
+                                               ereport(ERROR,
+                                                       (errmsg("failed to authenticate with backend using md5"),
+                                                                errdetail("unable to get the password")));
+                                       }
+                               }
+                       }
+                       /* we have a password to use, validate the password type */
+                       if (passwordType != PASSWORD_TYPE_PLAINTEXT && passwordType != PASSWORD_TYPE_MD5
+                               && passwordType != PASSWORD_TYPE_AES)
+                       {
+                               ereport(ERROR,
+                                       (errmsg("failed to authenticate with backend using md5"),
+                                                errdetail("valid password not found")));
+                       }
                }
 
                for (i=0;i<NUM_BACKENDS;i++)
@@ -236,10 +523,10 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
                                continue;
 
                        ereport(DEBUG1,
-                               (errmsg("authentication backend"),
+                                       (errmsg("authentication backend: %d",i),
                                         errdetail("trying md5 authentication")));
 
-                       authkind = do_md5(CONNECTION(cp, i), frontend, 0, protoMajor);
+                       authkind = do_md5(CONNECTION(cp, i), frontend, 0, protoMajor, password, passwordType);
 
                        if (authkind < 0)
                        {
@@ -251,6 +538,86 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
                }
        }
 
+       /* SCRAM authentication? */
+       else if (authkind == AUTH_REQ_SASL)
+       {
+               char *password;
+               PasswordType passwordType = PASSWORD_TYPE_UNKNOWN;
+
+               if (get_auth_password(MASTER(cp), frontend, 0,
+                                                         &password, &passwordType) == false)
+               {
+                       /*
+                        * We do not have any password,
+                        * we can still get the password from client using plain text authentication
+                        * if it is allowed by user
+                        */
+                       if (frontend->pool_hba == NULL && pool_config->allow_clear_text_frontend_auth)
+                       {
+                               ereport(LOG,
+                                       (errmsg("using clear text authentication with frontend"),
+                                                errdetail("backend will still use SCRAM auth"),
+                                                errhint("you can disable this behavior by setting allow_clear_text_frontend_auth to off")));
+                               authenticate_frontend_clear_text(frontend);
+                               /* now check again if we have a password now */
+                               if (get_auth_password(MASTER(cp), frontend, 0,
+                                                                         &password, &passwordType) == false)
+                               {
+                                       ereport(ERROR,
+                                                       (errmsg("failed to authenticate with backend using SCRAM"),
+                                                        errdetail("unable to get the password")));
+                               }
+                       }
+               }
+               /*
+                * if we have encrypted password,
+                * Decrypt it before going any further
+                */
+               if (passwordType == PASSWORD_TYPE_AES)
+               {
+                       password = get_decrypted_password(password);
+                       if (password == NULL)
+                               ereport(ERROR,
+                                               (errmsg("SCRAM authentication failed"),
+                                                errdetail("unable to decrypt password from pool_passwd"),
+                                                errhint("verify the valid pool_key exists")));
+                       /* we have converted the password to plain text */
+                       passwordType = PASSWORD_TYPE_PLAINTEXT;
+               }
+
+               /* we have a password to use, validate the password type */
+               if (passwordType != PASSWORD_TYPE_PLAINTEXT)
+               {
+                       ereport(ERROR,
+                                       (errmsg("failed to authenticate with backend using SCRAM"),
+                                        errdetail("valid password not found")));
+               }
+
+               for (i=0;i<NUM_BACKENDS;i++)
+               {
+                       if (!VALID_BACKEND(i))
+                               continue;
+
+                       ereport(DEBUG1,
+                               (errmsg("authentication backend %d",i),
+                                        errdetail("trying SCRAM authentication")));
+
+                       if (do_SCRAM(frontend, CONNECTION(cp, i), protoMajor, message_length, frontend->username, password, passwordType) == false)
+                       {
+                               pool_send_auth_fail(frontend, cp);
+                               ereport(ERROR,
+                                               (errmsg("failed to authenticate with backend"),
+                                                errdetail("SCRAM authentication failed in slot [%d].",i)));
+                       }
+                       ereport(DEBUG1,
+                               (errmsg("SCRAM authentication successful for backend %d",i)));
+
+               }
+
+               send_auth_ok(frontend, protoMajor);
+               authkind = 0;
+       }
+
        else
        {
                ereport(ERROR,
@@ -437,35 +804,42 @@ int pool_do_reauth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
     int msglen;
 
        protoMajor = MAJOR(cp);
-
-       switch(MASTER(cp)->auth_kind)
+       /*
+        * if hba is enabled we would already have passed authentication
+        */
+       if (!frontend->frontend_authenticated)
        {
-               case 0:
-                       /* trust */
-                       break;
-
-               case 3:
-                       /* clear text password */
-                       do_clear_text_password(MASTER(cp), frontend, 1, protoMajor);
-                       break;
-
-               case 4:
-                       /* crypt password */
-                       do_crypt(MASTER(cp), frontend, 1, protoMajor);
-                       break;
-
-               case 5:
-                       /* md5 password */
-                       do_md5(MASTER(cp), frontend, 1, protoMajor);
-                       break;
-
-               default:
-            ereport(ERROR,
-                (errmsg("authentication failed"),
-                     errdetail("unknown authentication request code %d",MASTER(cp)->auth_kind)));
+               switch(MASTER(cp)->auth_kind)
+               {
+                       case AUTH_REQ_OK:
+                               /* trust */
+                               break;
+
+                       case AUTH_REQ_PASSWORD:
+                               /* clear text password */
+                               do_clear_text_password(MASTER(cp), frontend, 1, protoMajor);
+                               break;
+
+                       case AUTH_REQ_CRYPT:
+                               /* crypt password */
+                               do_crypt(MASTER(cp), frontend, 1, protoMajor);
+                               break;
+
+                       case AUTH_REQ_MD5:
+                               /* md5 password */
+                               authenticate_frontend_md5(MASTER(cp), frontend, 1, protoMajor);
+                               break;
+                       case AUTH_REQ_SASL:
+                               /* SCRAM */
+                               authenticate_frontend_SCRAM(MASTER(cp), frontend, 1);
+                               break;
+                       default:
+                               ereport(ERROR,
+                                       (errmsg("authentication failed"),
+                                                errdetail("unknown authentication request code %d",MASTER(cp)->auth_kind)));
+               }
        }
 
-
     pool_write(frontend, "R", 1);
 
     if (protoMajor == PROTO_MAJOR_V3)
@@ -530,131 +904,168 @@ static POOL_STATUS pool_send_backend_key_data(POOL_CONNECTION *frontend, int pid
        return 0;
 }
 
+static void authenticate_frontend_clear_text(POOL_CONNECTION *frontend)
+{
+       static int size;
+       char password[MAX_PASSWORD_SIZE];
+       char userPassword[MAX_PASSWORD_SIZE];
+       char *storedPassword = NULL;
+       char *userPwd = NULL;
+
+       sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_PASSWORD, NULL, 0);
+
+       /* Read password packet */
+       read_password_packet(frontend, frontend->protoVersion, password, &size);
+
+       /* save the password in frontend */
+       frontend->auth_kind = AUTH_REQ_PASSWORD;
+       frontend->pwd_size = size;
+       memcpy(frontend->password, password, frontend->pwd_size);
+       frontend->password[size] = 0; /* Null terminate the password string */
+       frontend->passwordType = PASSWORD_TYPE_PLAINTEXT;
+
+       if (!frontend->passwordMapping)
+       {
+               /*
+                * if the password is not present in pool_passwd
+                * just bail out from here
+                */
+               return;
+       }
+
+       storedPassword = frontend->passwordMapping->pgpoolUser.password;
+       userPwd = password;
+
+       /*
+        * If we have md5 password stored in pool_passwd, convert the user
+        * supplied password to md5 for comparison
+        */
+       if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_MD5)
+       {
+               pg_md5_encrypt(password,
+                                          frontend->username,
+                                          strlen(frontend->username), userPassword);
+               userPwd = userPassword;
+               size = strlen (userPwd);
+       }
+       else if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_AES)
+       {
+               /*
+                * decrypt the stored AES password
+                * for comparing it
+                */
+               storedPassword = get_decrypted_password(storedPassword);
+               if (storedPassword == NULL)
+                       ereport(ERROR,
+                               (errmsg("clear text authentication failed"),
+                                        errdetail("unable to decrypt password from pool_passwd"),
+                                        errhint("verify the valid pool_key exists")));
+       }
+       else if (frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_PLAINTEXT)
+       {
+               ereport(ERROR,
+                       (errmsg("clear text authentication failed"),
+                                errdetail("password type stored in pool_passwd can't be used for clear text authentication")));
+       }
+
+       if (memcmp(userPwd, storedPassword, size))
+       {
+               /* Password does not match */
+               ereport(ERROR,
+                       (errmsg("clear text authentication failed"),
+                                errdetail("password does not match")));
+       }
+       ereport(LOG,
+                       (errmsg("clear text authentication successful with frontend")));
+
+       if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_AES)
+               pfree(storedPassword);
+
+       frontend->frontend_authenticated = true;
+}
+
 /*
  * perform clear text password authentication
  */
 static int do_clear_text_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
 {
        static int size;
-       static char password[MAX_PASSWORD_SIZE];
-       char response;
+       char *pwd = NULL;
        int kind;
-       int len;
 
-       /* master? */
-       if (IS_MASTER_NODE_ID(backend->db_node_id))
+       if (reauth && frontend->frontend_authenticated)
        {
-               pool_write(frontend, "R", 1);   /* authentication */
-               if (protoMajor == PROTO_MAJOR_V3)
-               {
-                       len = htonl(8);
-                       pool_write(frontend, &len, sizeof(len));
-               }
-               kind = htonl(3);                /* clear text password authentication */
-               pool_write_and_flush(frontend, &kind, sizeof(kind));    /* indicating clear text password authentication */
+               /* frontend and backend are both authenticated already */
+               return 0;
+       }
 
-               /* read password packet */
-               if (protoMajor == PROTO_MAJOR_V2)
+       /* get the password */
+       if (frontend->pwd_size > 0)
+       {
+               pwd = frontend->password;
+               size = frontend->pwd_size;
+       }
+       else if (frontend->passwordMapping)
+       {
+               if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_PLAINTEXT)
                {
-                       pool_read(frontend, &size, sizeof(size));
+                       pwd = frontend->passwordMapping->pgpoolUser.password;
+                       size = pwd? strlen(pwd): 0;
                }
-               else
+               else if (frontend->passwordMapping->pgpoolUser.passwordType == PASSWORD_TYPE_AES)
                {
-                       char k;
-
-                       pool_read(frontend, &k, sizeof(k));
-                       if (k != 'p')
-                ereport(ERROR,
-                        (errmsg("clear text password authentication failed"),
-                         errdetail("invalid password packet. Packet does not starts with \"p\"")));
-                       pool_read(frontend, &size, sizeof(size));
+                       /*
+                        * decrypt the stored AES password
+                        * for comparing it
+                        */
+                       pwd = get_decrypted_password(frontend->passwordMapping->pgpoolUser.password);
+                       if (pwd == NULL)
+                               ereport(ERROR,
+                                       (errmsg("clear text password authentication failed"),
+                                                errdetail("unable to decrypt password from pool_passwd"),
+                                                errhint("verify the valid pool_key exists")));
+                       size = strlen(pwd);
                }
-
-               if ((ntohl(size) - 4) > sizeof(password))
-            ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
-                     errdetail("password is too long. password size = %d", ntohl(size) - 4)));
-
-               pool_read(frontend, password, ntohl(size) - 4);
+       }
+       if (pwd == NULL)
+       {
+               /* If we still do not have a password. we can't proceed */
+               ereport(ERROR,
+                       (errmsg("clear text password authentication failed"),
+                                errdetail("unable to get the password")));
+               return 0;
        }
 
        /* connection reusing? */
        if (reauth)
        {
-               if ((ntohl(size) - 4) != backend->pwd_size)
+               if (size != backend->pwd_size)
             ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
+                               (errmsg("clear text password authentication failed"),
                      errdetail("password size does not match")));
 
-               if (memcmp(password, backend->password, backend->pwd_size) != 0)
+               if (memcmp(pwd, backend->password, backend->pwd_size) != 0)
             ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
+                               (errmsg("clear text password authentication failed"),
                      errdetail("password does not match")));
 
                return 0;
        }
 
-       /* send password packet to backend */
-       if (protoMajor == PROTO_MAJOR_V3)
-               pool_write(backend, "p", 1);
-       pool_write(backend, &size, sizeof(size));
-       pool_write_and_flush(backend, password, ntohl(size) -4);
-       
-    pool_read(backend, &response, sizeof(response));
-
-       if (response != 'R')
-       {
-               if(response == 'E') /* Backend has thrown an error instead */
-               {
-                       char* message = NULL;
-                       if (pool_extract_error_message(false, backend, protoMajor, false, &message) == 1)
-                       {
-                               ereport(ERROR,
-                                       (errmsg("clear text password authentication failed"),
-                                        errdetail("%s",message?message:"backend throws authentication error")));
-                       }
-                       if (message)
-                               pfree(message);
-               }
-        ereport(ERROR,
-            (errmsg("clear text password authentication failed"),
-                 errdetail("invalid packet from backend. backend does not return R while processing clear text password authentication")));
-       }
-       if (protoMajor == PROTO_MAJOR_V3)
-       {
-               pool_read(backend, &len, sizeof(len));
-
-               if (ntohl(len) != 8)
-            ereport(ERROR,
-                    (errmsg("clear text password authentication failed"),
-                     errdetail("invalid packet from backend. incorrect authentication packet size (%d)", ntohl(len))));
-       }
-
-       /* expect to read "Authentication OK" response. kind should be 0... */
-       pool_read(backend, &kind, sizeof(kind));
+       kind = send_password_packet(backend, protoMajor, pwd);
 
        /* if authenticated, save info */
-       if (!reauth && kind == 0)
+       if (!reauth && kind == AUTH_REQ_OK)
        {
                if (IS_MASTER_NODE_ID(backend->db_node_id))
                {
-                       int msglen;
-
-                       pool_write(frontend, "R", 1);
-
-                       if (protoMajor == PROTO_MAJOR_V3)
-                       {
-                               msglen = htonl(8);
-                               pool_write(frontend, &msglen, sizeof(msglen));
-                       }
-
-                       msglen = htonl(0);
-                       pool_write_and_flush(frontend, &msglen, sizeof(msglen));
+                       send_auth_ok(frontend, protoMajor);
                }
 
-               backend->auth_kind = 3;
-               backend->pwd_size = ntohl(size) - 4;
-               memcpy(backend->password, password, backend->pwd_size);
+               backend->auth_kind = AUTH_REQ_PASSWORD;
+               backend->pwd_size = size;
+               memcpy(backend->password, pwd, backend->pwd_size);
+               backend->passwordType = PASSWORD_TYPE_PLAINTEXT;
        }
        return kind;
 }
@@ -808,171 +1219,632 @@ static int do_crypt(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int rea
 }
 
 /*
- * perform MD5 authentication
+ * Do the SCRAM authentication with the frontend using the stored
+ * password in the pool_passwd file.
  */
-static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
+static void
+authenticate_frontend_SCRAM(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth)
 {
-       char salt[4];
-       static int size;
-       static char password[MAX_PASSWORD_SIZE];
-       int kind;
-       char encbuf[POOL_PASSWD_LEN+1];
-       char *pool_passwd = NULL;
+       void       *scram_opaq;
+       char       *output = NULL;
+       int                     outputlen = 0;
+       char       *input;
+       int                     inputlen;
+       int                     result;
+       bool            initial;
+       char            *logdetail = NULL;
+       char            *shadow_pass;
+
+       PasswordType storedPasswordType = PASSWORD_TYPE_UNKNOWN;
+       char *storedPassword = NULL;
+
+       if (!frontend->passwordMapping)
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
+
+       if (!frontend->passwordMapping)
+       {
+               /* see if we have a password stored in the backend for this */
+               if (reauth && backend->pwd_size)
+               {
+                       storedPasswordType = backend->passwordType;
+                       storedPassword = backend->password;
+               }
+               else
+               {
+                       ereport(FATAL,
+                               (return_code(2),
+                                        errmsg("SCRAM authentication failed"),
+                                        errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+               }
+       }
+       else
+       {
+               storedPasswordType = frontend->passwordMapping->pgpoolUser.passwordType;
+               storedPassword = frontend->passwordMapping->pgpoolUser.password;
+       }
 
-       if (!RAW_MODE && NUM_BACKENDS > 1)
+       if (storedPasswordType == PASSWORD_TYPE_AES)
        {
-               /* Read password entry from pool_passwd */
-               pool_passwd = pool_get_passwd(frontend->username);
-               if (!pool_passwd)
-            ereport(ERROR,
-                               (errmsg("md5 authentication failed"),
-                     errdetail("username \"%s\" does not exist in pool_passwd",frontend->username)));
+               /*
+                * decrypt the stored AES password
+                * for comparing it
+                */
+               storedPassword = get_decrypted_password(storedPassword);
+               if (storedPassword == NULL)
+                       ereport(ERROR,
+                                       (errmsg("SCRAM authentication failed"),
+                                        errdetail("unable to decrypt password from pool_passwd"),
+                                        errhint("verify the valid pool_key exists")));
+               /* we have converted the password to plain text */
+               storedPasswordType = PASSWORD_TYPE_PLAINTEXT;
+       }
+
+       if (storedPasswordType != PASSWORD_TYPE_PLAINTEXT)
+       {
+               ereport(ERROR,
+                       (errmsg("SCRAM authentication failed"),
+                                errdetail("username \"%s\" has invalid password type",frontend->username)));
+       }
 
+       shadow_pass = pg_be_scram_build_verifier(storedPassword);
+       if (!shadow_pass)
+               ereport(ERROR,
+                       (errmsg("authentication failed"),
+                                errdetail("faild to build the scram verifier")));
 
-               /* master? */
-               if (IS_MASTER_NODE_ID(backend->db_node_id))
-               {
-                       /* Send md5 auth request to frontend with my own salt */
-                       pool_random_salt(salt);
-                       send_md5auth_request(frontend, protoMajor, salt);
+       /*
+        * SASL auth is not supported for protocol versions before 3, because it
+        * relies on the overall message length word to determine the SASL payload
+        * size in AuthenticationSASLContinue and PasswordMessage messages.  (We
+        * used to have a hard rule that protocol messages must be parsable
+        * without relying on the length word, but we hardly care about older
+        * protocol version anymore.)
+        */
+       if (frontend->protoVersion < PROTO_MAJOR_V3)
+               ereport(FATAL,
+                               (errmsg("SASL authentication is not supported in protocol version 2")));
 
-                       /* Read password packet */
-                       read_password_packet(frontend, protoMajor, password, &size);
+       /*
+        * Send the SASL authentication request to user.  It includes the list of
+        * authentication mechanisms (which is trivial, because we only support
+        * SCRAM-SHA-256 at the moment).  The extra "\0" is for an empty string to
+        * terminate the list.
+        */
 
-                       /* Check the password using my salt + pool_passwd */
-                       pg_md5_encrypt(pool_passwd+strlen("md5"), salt, sizeof(salt), encbuf);
-                       if (strcmp(password, encbuf))
+       sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_SASL, SCRAM_SHA_256_NAME "\0",
+                                       strlen(SCRAM_SHA_256_NAME) + 2);
+
+       /*
+        * Initialize the status tracker for message exchanges.
+        *
+        * If the user doesn't exist, or doesn't have a valid password, or it's
+        * expired, we still go through the motions of SASL authentication, but
+        * tell the authentication method that the authentication is "doomed".
+        * That is, it's going to fail, no matter what.
+        *
+        * This is because we don't want to reveal to an attacker what usernames
+        * are valid, nor which users have a valid password.
+        */
+       scram_opaq = pg_be_scram_init(frontend->username, shadow_pass);
+
+       /*
+        * Loop through SASL message exchange.  This exchange can consist of
+        * multiple messages sent in both directions.  First message is always
+        * from the client.  All messages from client to server are password
+        * packets (type 'p').
+        */
+       initial = true;
+       do
+       {
+               static int size;
+               static char data[MAX_PASSWORD_SIZE];
+
+               /* Read password packet */
+               read_password_packet(frontend, frontend->protoVersion, data, &size);
+
+               ereport(DEBUG4,
+                               (errmsg("Processing received SASL response of length %d", size)));
+
+               /*
+                * The first SASLInitialResponse message is different from the others.
+                * It indicates which SASL mechanism the client selected, and contains
+                * an optional Initial Client Response payload.  The subsequent
+                * SASLResponse messages contain just the SASL payload.
+                */
+               if (initial)
+               {
+                       const char *selected_mech;
+                       char *ptr = data;
+
+                       /*
+                        * We only support SCRAM-SHA-256 at the moment, so anything else
+                        * is an error.
+                        */
+                       selected_mech = data;
+                       if (strcmp(selected_mech, SCRAM_SHA_256_NAME) != 0)
                        {
-                               /* Password does not match */
-                ereport(ERROR,
-                    (errmsg("md5 authentication failed"),
-                         errdetail("password does not match")));
+                               ereport(ERROR,
+                                               (errmsg("client selected an invalid SASL authentication mechanism")));
                        }
+                       /* get the length of trailing optional data */
+                       ptr += strlen(selected_mech) + 1;
+                       memcpy(&inputlen,ptr,sizeof(int));
+                       inputlen = ntohl(inputlen);
+                       if (inputlen == -1)
+                               input = NULL;
+                       else
+                               input = ptr + 4;
+
+                       initial = false;
+               }
+               else
+               {
+                       inputlen = size;
+                       input = data;
                }
-               kind = 0;
+               Assert(input == NULL || input[inputlen] == '\0');
 
-               if (!reauth)
+               /*
+                * we pass 'logdetail' as NULL when doing a mock authentication,
+                * because we should already have a better error message in that case
+                */
+               result = pg_be_scram_exchange(scram_opaq, input, inputlen,
+                                                                         &output, &outputlen,
+                                                                         &logdetail);
+
+               /* input buffer no longer used */
+
+               if (output)
                {
                        /*
-                        * If ok, authenticate against backends using pool_passwd
+                        * Negotiation generated data to be sent to the client.
                         */
-                       /* Read salt */
-                       pool_read(backend, salt, sizeof(salt));
+                       ereport(DEBUG4,
+                               (errmsg("sending SASL challenge of length %u", outputlen)));
 
-                       ereport(DEBUG1,
-                               (errmsg("performing md5 authentication"),
-                                       errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
-                                                  salt[0], salt[1], salt[2], salt[3])));
+                       if (result == SASL_EXCHANGE_SUCCESS)
+                               sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_SASL_FIN, output, outputlen);
+                       else
+                               sendAuthRequest(frontend, frontend->protoVersion, AUTH_REQ_SASL_CONT, output, outputlen);
 
-                       /* Encrypt password in pool_passwd using the salt */
-                       pg_md5_encrypt(pool_passwd+strlen("md5"), salt, sizeof(salt), encbuf);
+                       pfree(output);
+               }
+       } while (result == SASL_EXCHANGE_CONTINUE);
 
-                       /* Send password packet to backend and receive auth response */
-                       kind = send_password_packet(backend, protoMajor, encbuf);
-                       if (kind < 0)
-                ereport(ERROR,
-                    (errmsg("md5 authentication failed"),
-                         errdetail("backend replied with invalid kind")));
+       /* Oops, Something bad happened */
+       if (result != SASL_EXCHANGE_SUCCESS)
+       {
+               ereport(ERROR,
+                       (errmsg("authentication failed"),
+                                errdetail("username \"%s\" or password does not exist in backend",frontend->username)));
+       }
+       frontend->frontend_authenticated = true;
+}
+
+void authenticate_frontend(POOL_CONNECTION *frontend)
+{
+       switch (frontend->pool_hba->auth_method)
+       {
+               case uaMD5:
+                       authenticate_frontend_md5(NULL,frontend,0,frontend->protoVersion);
+                       break;
+               case uaCert:
+                       authenticate_frontend_cert(frontend);
+                       break;
+               case uaSCRAM:
+                       authenticate_frontend_SCRAM(NULL, frontend, 0);
+                       break;
+               case uaPassword:
+                       authenticate_frontend_clear_text(frontend);
+                       break;
+               case uaImplicitReject:
+               case uaReject:
+                       ereport(ERROR,
+                                       (errmsg("authentication with pgpool failed for user \"%s\" rejected",frontend->username)));
+                       break;
+               case uaTrust:
+                       frontend->frontend_authenticated = true;
+       }
+}
+
+#ifdef USE_SSL
+static void authenticate_frontend_cert(POOL_CONNECTION *frontend)
+{
+       if (frontend->client_cert_loaded == true && frontend->cert_cn)
+       {
+               ereport(DEBUG1,
+                               (errmsg("connecting user is \"%s\" and ssl certificate CN is \"%s\"",frontend->username,frontend->cert_cn)));
+               if (strcasecmp(frontend->username,frontend->cert_cn) == 0)
+               {
+                       frontend->frontend_authenticated = true;
+                       ereport(LOG,
+                                       (errmsg("SSL certificate authentication for user \"%s\" with Pgpool-II is successful",frontend->username)));
+                       return;
                }
+               else
+               {
+                       frontend->frontend_authenticated = false;
+                       ereport(LOG,
+                                       (errmsg("SSL certificate authentication for user \"%s\" failed",frontend->username)));
+               }
+       }
+       ereport(ERROR,
+               (errmsg("CERT authentication failed"),
+                        errdetail("no valid certificate presented")));
 
-               if (!reauth && kind == 0)
+}
+#else
+static void authenticate_frontend_cert(POOL_CONNECTION *frontend)
+{
+       ereport(ERROR,
+               (errmsg("CERT authentication failed"),
+                        errdetail("CERT authentication is not supported without SSL")));
+}
+#endif
+
+static void authenticate_frontend_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
+{
+       char salt[4];
+       static int size;
+       char password[MAX_PASSWORD_SIZE];
+       char userPassword[MAX_PASSWORD_SIZE];
+       char encbuf[POOL_PASSWD_LEN+1];
+       char *md5;
+
+       PasswordType storedPasswordType = PASSWORD_TYPE_UNKNOWN;
+       char *storedPassword = NULL;
+
+       if (RAW_MODE || NUM_BACKENDS == 1)
+       {
+               if (backend)
+                       do_md5_single_backend(backend, frontend, reauth, protoMajor);
+               return; /* This will be handled later */
+       }
+       if (!frontend->passwordMapping)
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
+
+       if (!frontend->passwordMapping)
+       {
+               /* see if we have a password stored in the backend for this */
+               if (reauth && backend->pwd_size)
                {
-                       if (IS_MASTER_NODE_ID(backend->db_node_id))
-                       {
-                               /* Send auth ok to frontend */
-                               send_auth_ok(frontend, protoMajor);
-                       }
+                       storedPasswordType = backend->passwordType;
+                       storedPassword = backend->password;
+               }
+               else
+               {
+                       ereport(FATAL,
+                               (return_code(2),
+                                       errmsg("md5 authentication failed"),
+                                        errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+               }
+       }
+       else
+       {
+               storedPasswordType = frontend->passwordMapping->pgpoolUser.passwordType;
+               storedPassword = frontend->passwordMapping->pgpoolUser.password;
+       }
+
+       pool_random_salt(salt);
+       send_md5auth_request(frontend, frontend->protoVersion, salt);
 
-                       /* Save the auth info */
-                       backend->auth_kind = 5;
+       /* Read password packet */
+       read_password_packet(frontend, frontend->protoVersion, password, &size);
+
+       /*If we have clear text password stored in pool_passwd, convert it to md5 */
+       if (storedPasswordType == PASSWORD_TYPE_PLAINTEXT || storedPasswordType == PASSWORD_TYPE_AES)
+       {
+               char *pwd;
+               if (storedPasswordType == PASSWORD_TYPE_AES)
+               {
+                       pwd = get_decrypted_password(storedPassword);
+                       if (pwd == NULL)
+                               ereport(ERROR,
+                                               (errmsg("md5 authentication failed"),
+                                                errdetail("unable to decrypt password from pool_passwd"),
+                                                errhint("verify the valid pool_key exists")));
+               }
+               else
+               {
+                       pwd = storedPassword;
                }
-               return kind;
+               pg_md5_encrypt(pwd,
+                                          frontend->username,
+                                          strlen(frontend->username), userPassword);
+
+               if (storedPasswordType == PASSWORD_TYPE_AES)
+                       pfree(pwd);
+
+               md5 = userPassword;
+       }
+       else if (storedPasswordType == PASSWORD_TYPE_MD5)
+       {
+               md5 = storedPassword;
+       }
+       else
+       {
+               ereport(FATAL,
+                       (return_code(2),
+                               errmsg("md5 authentication failed"),
+                                errdetail("unable to get the password for \"%s\"",frontend->username)));
        }
 
-       /*
-        * Followings are NUM_BACKEND == 1 case.
-        */
+       /* Check the password using my salt + pool_passwd */
+       pg_md5_encrypt(md5+strlen("md5"), salt, sizeof(salt), encbuf);
+       if (strcmp(password, encbuf))
+       {
+               /* Password does not match */
+               ereport(ERROR,
+                       (errmsg("md5 authentication failed"),
+                                errdetail("password does not match")));
+       }
+       ereport(LOG,
+                       (errmsg("md5 authentication successful with frontend")));
+
+       frontend->frontend_authenticated = true;
+}
+
+/*
+ * perform MD5 authentication
+ */
+static int do_md5_single_backend(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor)
+{
+       char salt[4];
+       static int size;
+       static char password[MAX_PASSWORD_SIZE];
+       int kind;
+
        if (!reauth)
        {
-               /* read salt */
+               /* read salt from backend */
                pool_read(backend, salt, sizeof(salt));
                ereport(DEBUG1,
-                       (errmsg("performing md5 authentication"),
-                               errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
-                                          salt[0], salt[1], salt[2], salt[3])));
+                               (errmsg("performing md5 authentication"),
+                                errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
+                                                  salt[0], salt[1], salt[2], salt[3])));
        }
        else
        {
+               /* Use the saved salt */
                memcpy(salt, backend->salt, sizeof(salt));
        }
 
-       /* master? */
-       if (IS_MASTER_NODE_ID(backend->db_node_id))
-       {
-               /* Send md5 auth request to frontend */
-               send_md5auth_request(frontend, protoMajor, salt);
+       /* Send md5 auth request to frontend */
+       send_md5auth_request(frontend, protoMajor, salt);
 
-               /* Read password packet */
-               read_password_packet(frontend, protoMajor, password, &size);
-       }
+       /* Read password packet */
+       read_password_packet(frontend, protoMajor, password, &size);
 
-       /* connection reusing? */
+       /* connection reusing? compare it with saved password */
        if (reauth)
        {
+               if (backend->passwordType != PASSWORD_TYPE_MD5)
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("invalid password type")));
+
                if (size != backend->pwd_size)
-            ereport(ERROR,
-                               (errmsg("md5 authentication failed"),
-                     errdetail("password does not match")));
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("password does not match")));
 
                if (memcmp(password, backend->password, backend->pwd_size) != 0)
-            ereport(ERROR,
-                               (errmsg("md5 authentication failed"),
-                     errdetail("password does not match")));
-
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("password does not match")));
                return 0;
        }
+       else
+       {
+               /* Send password packet to backend and receive auth response */
+               kind = send_password_packet(backend, protoMajor, password);
+               if (kind < 0)
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("backend replied with invalid kind")));
 
-       /* Send password packet to backend and receive auth response */
-       kind = send_password_packet(backend, protoMajor, password);
-       if (kind < 0)
-        ereport(ERROR,
+               /* If authenticated, reply back to frontend and save info */
+               if (kind == AUTH_REQ_OK)
+               {
+                       send_auth_ok(frontend, protoMajor);
+                       backend->passwordType = PASSWORD_TYPE_MD5;
+                       backend->auth_kind = AUTH_REQ_MD5;
+                       backend->pwd_size = size;
+                       memcpy(backend->password, password, backend->pwd_size);
+                       memcpy(backend->salt, salt, sizeof(salt));
+               }
+       }
+       return kind;
+}
+
+static bool get_auth_password(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth,
+                                                         char** password, PasswordType *passwordType)
+{
+       /* First preference is to use the pool_passwd file */
+       if (frontend->passwordMapping == NULL)
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
+
+       if (frontend->passwordMapping == NULL)
+       {
+               /*
+                * check if we have password stored in the
+                * frontend connection.
+                * that could come by using the clear text auth
+                */
+               if (frontend->pwd_size > 0 && frontend->passwordType == PASSWORD_TYPE_PLAINTEXT)
+               {
+                       *password = frontend->password;
+                       *passwordType = frontend->passwordType;
+                       return true;
+               }
+               else if (reauth && backend && backend->pwd_size > 0)
+               {
+                       *password = backend->password;
+                       *passwordType = backend->passwordType;
+                       return true;
+               }
+       }
+       else
+       {
+               *password = frontend->passwordMapping->pgpoolUser.password;
+               *passwordType = frontend->passwordMapping->pgpoolUser.passwordType;
+               return true;
+       }
+       return false;
+}
+
+/*
+ * perform MD5 authentication
+ */
+static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int reauth, int protoMajor,
+                                 char *storedPassword, PasswordType passwordType)
+{
+       char salt[4];
+       static char userPassword[MAX_PASSWORD_SIZE];
+       int kind;
+       bool password_decrypted = false;
+       char encbuf[POOL_PASSWD_LEN+1];
+       char *pool_passwd = NULL;
+
+       if (NUM_BACKENDS == 1)
+               return do_md5_single_backend(backend, frontend, reauth, protoMajor);
+
+       if (passwordType == PASSWORD_TYPE_AES)
+       {
+               /*
+                * decrypt the stored AES password
+                * for comparing it
+                */
+               storedPassword = get_decrypted_password(storedPassword);
+               if (storedPassword == NULL)
+                       ereport(ERROR,
+                                       (errmsg("md5 authentication failed"),
+                                        errdetail("unable to decrypt password from pool_passwd"),
+                                        errhint("verify the valid pool_key exists")));
+               /* we have converted the password to plain text */
+               passwordType = PASSWORD_TYPE_PLAINTEXT;
+               password_decrypted = true;
+       }
+       if (passwordType == PASSWORD_TYPE_PLAINTEXT)
+       {
+               pg_md5_encrypt(storedPassword,
+                                          frontend->username,
+                                          strlen(frontend->username), userPassword);
+               pool_passwd = userPassword;
+       }
+       else if (passwordType == PASSWORD_TYPE_MD5)
+       {
+               pool_passwd = storedPassword;
+       }
+       else
+       {
+               ereport(ERROR,
                        (errmsg("md5 authentication failed"),
-                 errdetail("backend replied with invalid kind")));
+                                errdetail("unable to get the password")));
+       }
+
+       /* master? */
+       if (IS_MASTER_NODE_ID(backend->db_node_id) && frontend->frontend_authenticated == false )
+       {
+               /*
+                * If frontend is not authenticated and do it it first.
+                * but if we have already received the password from frontend
+                * using the clear text auth, we may not need to authenticate it
+                */
+               if (pool_config->allow_clear_text_frontend_auth &&
+                       frontend->auth_kind == AUTH_REQ_PASSWORD &&
+                       frontend->pwd_size > 0 &&
+                       frontend->passwordType == PASSWORD_TYPE_PLAINTEXT)
+               {
+                       ereport(DEBUG2,
+                                       (errmsg("MD5 authentication using the password from frontend")));
+                       /* save this password in backend for the re-auth */
+                       backend->pwd_size = frontend->pwd_size;
+                       memcpy(backend->password, frontend->password, frontend->pwd_size);
+                       backend->password[backend->pwd_size] = 0; /* null terminate */
+                       backend->passwordType = frontend->passwordType;
+               }
+               else
+               {
+                       authenticate_frontend_md5(backend, frontend, reauth, protoMajor);
+               }
+       }
+
+       if (!reauth)
+       {
+               /*
+                * now authenticate the backend
+                */
+
+               /* Read salt */
+               pool_read(backend, salt, sizeof(salt));
+
+               ereport(DEBUG2,
+                       (errmsg("performing md5 authentication"),
+                               errdetail("DB node id: %d salt: %hhx%hhx%hhx%hhx", backend->db_node_id,
+                                          salt[0], salt[1], salt[2], salt[3])));
+
+               /* Encrypt password in pool_passwd using the salt */
+               pg_md5_encrypt(pool_passwd+strlen("md5"), salt, sizeof(salt), encbuf);
+
+               /* Send password packet to backend and receive auth response */
+               kind = send_password_packet(backend, protoMajor, encbuf);
+               if (kind < 0)
+                       ereport(ERROR,
+                               (errmsg("md5 authentication failed"),
+                                        errdetail("backend replied with invalid kind")));
+       }
 
-       /* If authenticated, reply back to frontend and save info */
        if (!reauth && kind == 0)
        {
-               send_auth_ok(frontend, protoMajor);
+               if (IS_MASTER_NODE_ID(backend->db_node_id))
+               {
+                       /* Send auth ok to frontend */
+                       send_auth_ok(frontend, protoMajor);
+               }
 
-               backend->auth_kind = 5;
-               backend->pwd_size = size;
-               memcpy(backend->password, password, backend->pwd_size);
-               memcpy(backend->salt, salt, sizeof(salt));
+               /* Save the auth info */
+               backend->auth_kind = AUTH_REQ_MD5;
        }
-       return kind;
+
+       if (password_decrypted && storedPassword)
+               pfree(storedPassword);
+
+       return 0;
 }
 
 /*
- * Send md5 authentication request packet to frontend
+ * Send an authentication request packet to the frontend.
  */
-static int send_md5auth_request(POOL_CONNECTION *frontend, int protoMajor, char *salt)
+static void
+sendAuthRequest(POOL_CONNECTION *frontend, int protoMajor, int32 auth_req_type, char *extradata, int extralen)
 {
-       int len;
-       int kind;
+       int kind = htonl(auth_req_type);
 
        pool_write(frontend, "R", 1);   /* authentication */
        if (protoMajor == PROTO_MAJOR_V3)
        {
-               len = htonl(12);
+               int len = 8 + extralen;
+               len = htonl(len);
                pool_write(frontend, &len, sizeof(len));
        }
-       kind = htonl(5);
-       pool_write(frontend, &kind, sizeof(kind));      /* indicating MD5 */
-       pool_write_and_flush(frontend, salt, 4);                /* salt */
+       pool_write(frontend, &kind, sizeof(kind));
+       if (extralen > 0)
+               pool_write_and_flush(frontend, extradata, extralen);
+       else
+               pool_flush(frontend);
+}
 
-       return 0;
+/*
+ * Send md5 authentication request packet to frontend
+ */
+static void
+send_md5auth_request(POOL_CONNECTION *frontend, int protoMajor, char *salt)
+{
+       sendAuthRequest(frontend, protoMajor, AUTH_REQ_MD5, salt, 4);
 }
 
+
 /*
  * Read password packet from frontend
  */
@@ -1283,20 +2155,32 @@ int pool_read_int(POOL_CONNECTION_POOL *cp)
        return data;
 }
 
+void
+pool_random(void *buf, size_t len)
+{
+       int ret = 0;
+#ifdef USE_SSL
+       ret = RAND_bytes(buf, len);
+#endif
+       /* if RND_bytes fails or not present use the old technique */
+       if ( ret == 0)
+       {
+               int i;
+               char *ptr = buf;
+               for (i = 0; i < len; i++)
+               {
+                       long rand = PostmasterRandom();
+                       ptr[i] = (rand % 255) + 1;
+               }
+       }
+}
+
 /*
  *  pool_random_salt
  */
 void pool_random_salt(char *md5Salt)
 {
-       long rand = PostmasterRandom();
-
-       md5Salt[0] = (rand % 255) + 1;
-       rand = PostmasterRandom();
-       md5Salt[1] = (rand % 255) + 1;
-       rand = PostmasterRandom();
-       md5Salt[2] = (rand % 255) + 1;
-       rand = PostmasterRandom();
-       md5Salt[3] = (rand % 255) + 1;
+       pool_random(md5Salt,4);
 }
 
 /*
@@ -1336,3 +2220,281 @@ PostmasterRandom(void)
 
        return random();
 }
+
+
+static bool do_SCRAM(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, int protoMajor, int message_length,
+                                        char *username, char *storedPassword, PasswordType passwordType)
+{
+       /* read the packet first */
+       void *sasl_state = NULL;
+       int payload_len = message_length - 4 - 4;
+       int  auth_kind = AUTH_REQ_SASL;
+       char payload[MAX_SASL_PAYLOAD_LEN];
+
+       if (passwordType != PASSWORD_TYPE_PLAINTEXT)
+       {
+               ereport(ERROR,
+                       (errmsg("SCRAM authentication failed"),
+                                errdetail("invalid password type")));
+       }
+       if (storedPassword == NULL)
+       {
+               ereport(ERROR,
+                       (errmsg("SCRAM authentication failed"),
+                                errdetail("password not found")));
+       }
+
+       /* master? */
+       if (frontend && IS_MASTER_NODE_ID(backend->db_node_id) && frontend->frontend_authenticated == false )
+       {
+               /*
+                * If frontend is not authenticated and do it it first.
+                * but if we have already received the password from frontend
+                * using the clear text auth, we may not need to authenticate it
+                */
+               if (pool_config->allow_clear_text_frontend_auth &&
+                       frontend->auth_kind == AUTH_REQ_PASSWORD &&
+                       frontend->pwd_size > 0 &&
+                       frontend->passwordType == PASSWORD_TYPE_PLAINTEXT)
+               {
+                       ereport(DEBUG2,
+                                       (errmsg("SCRAM authentication using the password from frontend")));
+                       /* save this password in backend for the re-auth */
+                       backend->pwd_size = frontend->pwd_size;
+                       memcpy(backend->password, frontend->password, frontend->pwd_size);
+                       backend->password[backend->pwd_size] = 0; /* null terminate */
+                       backend->passwordType = frontend->passwordType;
+               }
+               else
+               {
+                       authenticate_frontend_SCRAM(backend, frontend, 0);
+               }
+       }
+
+       for(;;)
+       {
+               char kind;
+               int len;
+
+               /* at this point we have already read kind, message length and authkind */
+               if (payload_len > MAX_SASL_PAYLOAD_LEN)
+                       ereport(ERROR,
+                                       (errmsg("invalid authentication data too big")));
+
+               pool_read(backend, payload, payload_len);
+
+               switch (auth_kind) {
+                       case AUTH_REQ_OK:
+                               /* Save the auth info in backend */
+                               backend->auth_kind = AUTH_REQ_SASL;
+                               return true;
+                               break;
+                       case AUTH_REQ_SASL:
+                               /*
+                                * The request contains the name (as assigned by IANA) of the
+                                * authentication mechanism.
+                                */
+                               sasl_state = pg_SASL_init(backend, payload, payload_len, username, storedPassword);
+                               if (!sasl_state)
+                               {
+                                       ereport(ERROR,
+                                                       (errmsg("invalid authentication request from server")));
+                                       return false;
+                               }
+                               break;
+
+                       case AUTH_REQ_SASL_CONT:
+                       case AUTH_REQ_SASL_FIN:
+                               if (sasl_state == NULL)
+                               {
+                                       ereport(ERROR,
+                                                       (errmsg("invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL")));
+                                       return false;
+                               }
+                               if (pg_SASL_continue(backend, payload, payload_len, sasl_state, (auth_kind == AUTH_REQ_SASL_FIN))!= 0)
+                               {
+                                       /* Use error message, if set already */
+                                       ereport(ERROR,
+                                                       (errmsg("error in SASL authentication")));
+
+                               }
+                               break;
+                       default:
+                               ereport(ERROR,
+                                               (errmsg("invalid authentication request from server: unknown auth kind %d",auth_kind)));
+                       }
+               /* Read next packend */
+               pool_read(backend, &kind, sizeof(kind));
+               pool_read(backend, &len, sizeof(len));
+               if (kind != 'R')
+                       ereport(ERROR,
+                                       (errmsg("backend authentication failed"),
+                                        errdetail("backend response with kind \'%c\' when expecting \'R\'",kind)));
+               message_length = ntohl(len);
+               if (len <= 8)
+                       ereport(ERROR,
+                                       (errmsg("backend authentication failed"),
+                                        errdetail("backend response with no data ")));
+
+               pool_read(backend, &auth_kind, sizeof(auth_kind));
+               auth_kind = ntohl(auth_kind);
+               payload_len = message_length - 4 - 4;
+       }
+       return false;
+}
+
+static void*
+pg_SASL_init(POOL_CONNECTION *backend, char *payload, int payloadlen, char *username, char* storedPassword)
+{
+       char       *initialresponse = NULL;
+       int                     initialresponselen;
+       bool            done;
+       bool            success;
+       const char *selected_mechanism;
+       char    *mechanism_buf = payload;
+       void    *sasl_state = NULL;
+       int     size;
+       int     send_msg_len;
+
+       /*
+        * Parse the list of SASL authentication mechanisms in the
+        * AuthenticationSASL message, and select the best mechanism that we
+        * support.  (Only SCRAM-SHA-256 is supported at the moment.)
+        */
+       selected_mechanism = NULL;
+       for (;;)
+       {
+               /* An empty string indicates end of list */
+               if (mechanism_buf[0] == '\0')
+                       break;
+
+               /*
+                * If we have already selected a mechanism, just skip through the rest
+                * of the list.
+                */
+               if (selected_mechanism)
+                       continue;
+
+               /*
+                * Do we support this mechanism?
+                */
+               if (strcmp(mechanism_buf, SCRAM_SHA_256_NAME) == 0)
+               {
+                       /* This is the password which we need to send to the PG backend
+                        * for authentication.
+                        * It is stored in the file
+                        */
+                       if (storedPassword == NULL || storedPassword[0] == '\0')
+                       {
+                               ereport(ERROR,
+                                               (errmsg("password not found")));
+                       }
+
+                       sasl_state = pg_fe_scram_init(username, storedPassword);
+                       if (!sasl_state)
+                               ereport(ERROR,
+                                               (errmsg("SASL authentication error\n")));
+                       selected_mechanism = SCRAM_SHA_256_NAME;
+               }
+               mechanism_buf += strlen(mechanism_buf) +1;
+       }
+
+       if (!selected_mechanism)
+       {
+               ereport(ERROR,
+                               (errmsg("none of the server's SASL authentication mechanisms are supported\n")));
+       }
+
+       /* Get the mechanism-specific Initial Client Response, if any */
+       pg_fe_scram_exchange(sasl_state,
+                                                NULL, -1,
+                                                &initialresponse, &initialresponselen,
+                                                &done, &success);
+
+       if (done && !success)
+               ereport(ERROR,
+                               (errmsg("SASL authentication error")));
+
+       send_msg_len = strlen(selected_mechanism) + 1;
+       if (initialresponse)
+       {
+               send_msg_len += 4;
+               send_msg_len += initialresponselen;
+       }
+
+       size = htonl(send_msg_len + 4);
+
+       pool_write(backend, "p", 1);
+       pool_write(backend, &size, sizeof(size));
+       pool_write(backend, (void*)selected_mechanism, strlen(selected_mechanism)+1);
+       if (initialresponse)
+       {
+               size = htonl(initialresponselen);
+               pool_write(backend, &size, sizeof(size));
+               pool_write(backend, initialresponse, initialresponselen);
+       }
+       pool_flush(backend);
+
+       if (initialresponse)
+               pfree(initialresponse);
+
+       return sasl_state;
+}
+
+/*
+ * Exchange a message for SASL communication protocol with the backend.
+ * This should be used after calling pg_SASL_init to set up the status of
+ * the protocol.
+ */
+static int
+pg_SASL_continue(POOL_CONNECTION *backend, char *payload, int payloadlen, void *sasl_state, bool final)
+{
+       char       *output;
+       int                     outputlen;
+       bool            done;
+       bool            success;
+       char       *challenge;
+
+       /* Read the SASL challenge from the AuthenticationSASLContinue message. */
+       challenge = palloc(payloadlen + 1);
+       memcpy(challenge,payload,payloadlen);
+       challenge[payloadlen] = '\0';
+
+       /* For safety and convenience, ensure the buffer is NULL-terminated. */
+
+       pg_fe_scram_exchange(sasl_state,
+                                                challenge, payloadlen,
+                                                &output, &outputlen,
+                                                &done, &success);
+       pfree(challenge);                       /* don't need the input anymore */
+
+       if (final && !done)
+       {
+               if (outputlen != 0)
+                       pfree(output);
+
+               ereport(ERROR,
+                               (errmsg("AuthenticationSASLFinal received from server, but SASL authentication was not completed")));
+               return -1;
+       }
+       if (outputlen != 0)
+       {
+               /*
+                * Send the SASL response to the server.
+                */
+               int size = htonl(outputlen + 4);
+
+               pool_write(backend, "p", 1);
+               pool_write(backend, &size, sizeof(size));
+               pool_write_and_flush(backend, output, outputlen);
+               pfree(output);
+
+               return 0;
+       }
+
+       if (done && !success)
+               return -1;
+
+       return 0;
+}
+
index dd398db55f711835dd8ed2a1d81c93f6320e1455..9e559a6d61beb875266093985e0055bed4ad0605 100644 (file)
@@ -134,7 +134,7 @@ next_token(char **lineptr, char *buf, int bufsz,
 static List *
 next_field_expand(const char *filename, char **lineptr,
                                  int elevel, char **err_msg);
-static POOL_STATUS CheckMd5Auth(char *username);
+static POOL_STATUS CheckUserExist(char *username);
 
 #ifdef USE_PAM
 #ifdef HAVE_PAM_PAM_APPL_H
@@ -615,8 +615,14 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
                parsedline->auth_method = uaTrust;
        else if (strcmp(token->string, "reject") == 0)
                parsedline->auth_method = uaReject;
+       else if (strcmp(token->string, "cert") == 0)
+               parsedline->auth_method = uaCert;
+       else if (strcmp(token->string, "password") == 0)
+               parsedline->auth_method = uaPassword;
        else if (strcmp(token->string, "md5") == 0)
                parsedline->auth_method = uaMD5;
+       else if (strcmp(token->string, "scram-sha-256") == 0)
+               parsedline->auth_method = uaSCRAM;
 #ifdef USE_PAM
        else if (strcmp(token->string, "pam") == 0)
                parsedline->auth_method = uaPAM;
@@ -767,7 +773,11 @@ void ClientAuthentication(POOL_CONNECTION *frontend)
                         errdetail("missing or erroneous pool_hba.conf file"),
                             errhint("see pgpool log for details")));
 
-
+               /*
+                * Get the password for the user if it is stored
+                * in the pool_password file
+                */
+               frontend->passwordMapping = pool_get_user_credentials(frontend->username);
         switch (frontend->pool_hba->auth_method)
         {
                        case uaImplicitReject:
@@ -816,14 +826,55 @@ void ClientAuthentication(POOL_CONNECTION *frontend)
             case uaKrb5:
             case uaIdent:
             case uaCrypt:
+                        */
             case uaPassword:
-                break; 
-            */
+                               ereport(DEBUG1,
+                                               (errmsg("password authentication required")));
+                               status = POOL_CONTINUE;
+                break;
+                       case uaCert:
+                               ereport(DEBUG1,
+                                               (errmsg("SSL certificate authentication required")));
+                               status = POOL_CONTINUE;
+                               break;
 
             case uaMD5:
-                status = CheckMd5Auth(frontend->username);
+                               status = POOL_CONTINUE;
+
+                               if (NUM_BACKENDS <= 1)
+                                       break;
+
+                               if (!frontend->passwordMapping)
+                                       ereport(FATAL,
+                                               (return_code(2),
+                                                       errmsg("md5 authentication failed"),
+                               errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+                               if (frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_PLAINTEXT &&
+                                       frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_MD5 &&
+                                       frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_AES)
+                                       ereport(FATAL,
+                                               (return_code(2),
+                           errmsg("md5 authentication failed"),
+                                                        errdetail("pool_passwd file does not contain valid md5 entry for \"%s\"",frontend->username)));
                 break;
 
+                       case uaSCRAM:
+                               if (!frontend->passwordMapping)
+                                       ereport(FATAL,
+                                               (return_code(2),
+                                                       errmsg("SCRAM authentication failed"),
+                                                        errdetail("pool_passwd file does not contain an entry for \"%s\"",frontend->username)));
+                               if (frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_PLAINTEXT &&
+                                       frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_SCRAM_SHA_256 &&
+                                       frontend->passwordMapping->pgpoolUser.passwordType != PASSWORD_TYPE_AES)
+                                       ereport(FATAL,
+                                               (return_code(2),
+                                                       errmsg("SCRAM authentication failed"),
+                                                        errdetail("pool_passwd file does not contain valid SCRAM entry for \"%s\"",frontend->username)));
+
+                               status = POOL_CONTINUE;
+                               break;
+
 
     #ifdef USE_PAM
             case uaPAM:
@@ -845,7 +896,10 @@ void ClientAuthentication(POOL_CONNECTION *frontend)
     PG_END_TRY();
 
        if (status == POOL_CONTINUE)
+       {
                sendAuthRequest(frontend, AUTH_REQ_OK);
+               authenticate_frontend(frontend);
+       }
        else
                auth_failed(frontend);
 }
@@ -989,6 +1043,18 @@ static void auth_failed(POOL_CONNECTION *frontend)
                                         frontend->username);
                        break;
 
+               case uaSCRAM:
+                       snprintf(errmessage, messagelen,
+                                        "\"SCRAM\" authentication with pgpool failed for user \"%s\"",
+                                        frontend->username);
+                       break;
+
+               case uaCert:
+                       snprintf(errmessage, messagelen,
+                                        "\"CERT\" authentication with pgpool failed for user \"%s\"",
+                                        frontend->username);
+                       break;
+
 /*             case uaCrypt: */
 /*             case uaPassword: */
 /*                     snprintf(errmessage, messagelen, */
@@ -1983,7 +2049,9 @@ static POOL_STATUS CheckPAMAuth(POOL_CONNECTION *frontend, char *user, char *pas
 
 #endif   /* USE_PAM */
 
-static POOL_STATUS CheckMd5Auth(char *username)
+
+
+static POOL_STATUS CheckUserExist(char *username)
 {
        char *passwd;
 
@@ -1991,12 +2059,7 @@ static POOL_STATUS CheckMd5Auth(char *username)
        passwd = pool_get_passwd(username);
 
        if (passwd == NULL)
-        ereport(FATAL,
-            (return_code(2),
-                 errmsg("md5 authentication failed"),
-                    errdetail("pool_passwd file does not contain an entry for \"%s\"",username)));
-
-
+               return POOL_ERROR;
        /*
         * Ok for now. Actual authentication will be performed later.
         */
index 660a6c5f9f99d2a94a2bd645c3d2a825a335b6bd..051e756a731487659cea574b40dbe067f2842d2d 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL 
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2015     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
 #include "pool.h"
 #include "auth/pool_passwd.h"
 #include "auth/md5.h"
+#include "utils/ssl_utils.h"
+#include "utils/base64.h"
 #ifndef POOL_PRIVATE
 #include "utils/elog.h"
 #else
 #include "utils/fe_ports.h"
 #endif
+#include <sys/stat.h>
+
 
 static FILE *passwd_fd = NULL; /* File descriptor for pool_passwd */
 static char saved_passwd_filename[POOLMAXPATHLEN+1];
@@ -101,7 +105,7 @@ int pool_create_passwdent(char *username, char *passwd)
                                (errmsg("pool_create_passwdent should be called with pool_passwd opened with read/write mode")));
 
        len = strlen(passwd);
-       if (len != POOL_PASSWD_LEN)
+       if (len <= 0)
                ereport(ERROR,
                                (errmsg("error updating password, invalid password length:%d",len)));
 
@@ -233,6 +237,176 @@ char *pool_get_passwd(char *username)
        return NULL;
 }
 
+/*
+ * return the next token if the current token matches the
+ * user
+ */
+static char *
+getNextToken(char *buf, char **token)
+{
+#define MAX_TOKEN_LEN 128
+       char    *tbuf,*p;
+       bool    bslash = false;
+       char    tok[MAX_TOKEN_LEN+1];
+       int readlen = 0;
+
+       *token = NULL;
+       if (buf == NULL)
+               return NULL;
+
+       tbuf = buf;
+       p = tok;
+       while (*tbuf != 0 && readlen < MAX_TOKEN_LEN)
+       {
+               if (*tbuf == '\\' && !bslash)
+               {
+                       tbuf++;
+                       bslash = true;
+               }
+
+               if (*tbuf == ':' && !bslash)
+               {
+                       *p = '\0';
+                       if(readlen)
+                               *token = pstrdup(tok);
+                       return tbuf + 1;
+               }
+               /*  just copy to the tok */
+               bslash = false;
+               *p++ = *tbuf++;
+               readlen++;
+       }
+       *p = '\0';
+       if(readlen)
+               *token = pstrdup(tok);
+       return NULL;
+}
+/*
+ * return the next token if the current token matches the
+ * user
+ */
+static char *
+userMatchesString(char *buf, char *user)
+{
+       char       *tbuf,
+       *ttok;
+       bool            bslash = false;
+
+       if (buf == NULL || user == NULL)
+               return NULL;
+       tbuf = buf;
+       ttok = user;
+       while (*tbuf != 0)
+       {
+               if (*tbuf == '\\' && !bslash)
+               {
+                       tbuf++;
+                       bslash = true;
+               }
+               if (*tbuf == ':' && *ttok == 0 && !bslash)
+                       return tbuf + 1;
+               bslash = false;
+               if (*ttok == 0)
+                       return NULL;
+               if (*tbuf == *ttok)
+               {
+                       tbuf++;
+                       ttok++;
+               }
+               else
+                       return NULL;
+       }
+       return NULL;
+}
+
+/*
+ * user:passwod[:user:password]
+ */
+PasswordMapping *pool_get_user_credentials(char *username)
+{
+       PasswordMapping *pwdMapping = NULL;
+       char        buf[1024];
+
+       if (!username)
+               ereport(ERROR,
+                               (errmsg("unable to get password, username is NULL")));
+
+       if (!passwd_fd)
+       {
+               ereport(WARNING,
+                               (errmsg("unable to get password, password file descriptor is NULL")));
+               return NULL;
+       }
+       rewind(passwd_fd);
+
+       while (!feof(passwd_fd) && !ferror(passwd_fd))
+       {
+               char    *t = buf;
+               char    *tok;
+               int     len;
+
+               if (fgets(buf, sizeof(buf), passwd_fd) == NULL)
+                       break;
+
+               len = strlen(buf);
+               if (len == 0)
+                       continue;
+
+               /* Remove trailing newline */
+               if (buf[len - 1] == '\n')
+                       buf[len - 1] = 0;
+
+               if ((t = userMatchesString(t, username)) == NULL)
+                       continue;
+               /* Get the password */
+               t = getNextToken(t, &tok);
+               if (tok)
+               {
+                       pwdMapping = palloc0(sizeof(PasswordMapping));
+                       pwdMapping->pgpoolUser.password = tok;
+                       pwdMapping->pgpoolUser.passwordType = get_password_type(pwdMapping->pgpoolUser.password);
+                       pwdMapping->pgpoolUser.userName = (char*) pstrdup(username);
+                       pwdMapping->mappedUser = false;
+               }
+               else
+                       continue;
+               /* Get backend user*/
+               t = getNextToken(t, &tok);
+               if (tok)
+               {
+                       /* check if we also have the password */
+                       char *pwd;
+                       t = getNextToken(t, &pwd);
+                       if (tok)
+                       {
+                               pwdMapping->backendUser.password = pwd;
+                               pwdMapping->backendUser.userName = tok;
+                               pwdMapping->backendUser.passwordType = get_password_type(pwdMapping->backendUser.password);
+                               pwdMapping->mappedUser = true;
+                       }
+               }
+               break;
+       }
+       return pwdMapping;
+}
+
+void delete_passwordMapping(PasswordMapping *pwdMapping)
+{
+       if (!pwdMapping)
+               return;
+       if (pwdMapping->pgpoolUser.password)
+               pfree(pwdMapping->pgpoolUser.password);
+       if (pwdMapping->pgpoolUser.userName)
+               pfree(pwdMapping->pgpoolUser.userName);
+
+       if (pwdMapping->backendUser.password)
+               pfree(pwdMapping->backendUser.password);
+       if (pwdMapping->backendUser.userName)
+               pfree(pwdMapping->backendUser.userName);
+
+       pfree(pwdMapping);
+}
+
 /*
  * Delete the entry by username. If specified entry does not exist,
  * does nothing.
@@ -258,3 +432,202 @@ void pool_reopen_passwd_file(void)
        pool_finish_pool_passwd();
        pool_init_pool_passwd(saved_passwd_filename, pool_passwd_mode);
 }
+
+/*
+ * function first uses the password in the argument, if the
+ * argument is empty string or NULL, it looks for the password
+ * for uset in pool_passwd file.
+ * The returned password is always in plain text and palloc'd (if not null)
+ */
+char *get_pgpool_config_user_password(char *username, char *password_in_config)
+{
+       PasswordType passwordType = PASSWORD_TYPE_UNKNOWN;
+       char *password = NULL;
+       PasswordMapping*  password_mapping = NULL;
+       /*
+        * if the password specified in confg is empty strin or NULL
+        * look for the password in pool_passwd file
+        */
+       if (password_in_config == NULL || strlen(password_in_config) == 0)
+       {
+               password_mapping = pool_get_user_credentials(username);
+               if (password_mapping == NULL)
+               {
+                       return NULL;
+               }
+               passwordType = password_mapping->pgpoolUser.passwordType;
+               password = password_mapping->pgpoolUser.password;
+       }
+       else
+       {
+               passwordType = get_password_type(password_in_config);
+               password = password_in_config;
+       }
+
+       if (passwordType == PASSWORD_TYPE_AES)
+       {
+               /*
+                * decrypt the stored AES password
+                * for comparing it
+                */
+               password = get_decrypted_password(password);
+               if (password == NULL)
+               {
+                       ereport(WARNING,
+                               (errmsg("could not get the password for user:%s",username),
+                                        errdetail("unable to decrypt password from pool_passwd"),
+                                        errhint("verify the valid pool_key exists")));
+               }
+               else
+               {
+                       delete_passwordMapping(password_mapping);
+                       /* the password returned by get_decrypted_password() is
+                        * already palloc'd */
+                       return password;
+               }
+       }
+
+       if (passwordType != PASSWORD_TYPE_PLAINTEXT)
+       {
+               ereport(WARNING,
+                               (errmsg("could not get the password for user:%s",username),
+                                errdetail("username \"%s\" has invalid password type",username)));
+               password = NULL;
+       }
+       if (password)
+               password = (char*)pstrdup(password);
+
+       delete_passwordMapping(password_mapping);
+
+       return password;
+}
+
+#ifndef POOL_PRIVATE
+char *get_decrypted_password(const char *shadow_pass)
+{
+       if (get_password_type(shadow_pass) == PASSWORD_TYPE_AES)
+       {
+               unsigned char b64_dec[MAX_PGPASS_LEN *2];
+               unsigned char plaintext[MAX_PGPASS_LEN];
+               int len;
+               char *pwd;
+               const char *enc_key = (const char*)get_pool_key();
+
+               if (enc_key == NULL)
+                       return NULL;
+
+               pwd = (char*)shadow_pass + 3;
+
+               if ((len = strlen(pwd)) == 0)
+                       return NULL;
+
+               if ((len = pg_b64_decode((const char*)pwd, len, (char*)b64_dec)) == 0)
+               {
+                       ereport(WARNING,
+                                       (errmsg("base64 decoding failed")));
+                       return NULL;
+               }
+               if ((len = aes_decrypt_with_password(b64_dec, len,
+                                                                               enc_key, plaintext)) <= 0)
+               {
+                       ereport(WARNING,
+                                       (errmsg("decryption failed")));
+                       return NULL;
+               }
+               plaintext[len] = 0;
+               return pstrdup((const char*)plaintext);
+       }
+       return NULL;
+}
+#else
+char *get_decrypted_password(const char *shadow_pass)
+{
+       ereport(ERROR,
+                       (errmsg("unable to decrypt password")));
+}
+#endif
+
+PasswordType
+get_password_type(const char *shadow_pass)
+{
+       if (strncmp(shadow_pass, PASSWORD_MD5_PREFIX, strlen(PASSWORD_MD5_PREFIX)) == 0)
+       {
+               if ( strlen(shadow_pass) == MD5_PASSWD_LEN + strlen(PASSWORD_MD5_PREFIX))
+                       return PASSWORD_TYPE_MD5;
+               return PASSWORD_TYPE_PLAINTEXT;
+       }
+       if (strncmp(shadow_pass, PASSWORD_AES_PREFIX, strlen(PASSWORD_AES_PREFIX)) == 0 )
+               return PASSWORD_TYPE_AES;
+       if (strncmp(shadow_pass, PASSWORD_SCRAM_PREFIX, strlen(PASSWORD_SCRAM_PREFIX)) == 0)
+               return PASSWORD_TYPE_SCRAM_SHA_256;
+
+       return PASSWORD_TYPE_PLAINTEXT;
+}
+
+/*
+ * Get a key from the pgpool key file. return the palloc'd.
+ * value
+ */
+char *read_pool_key(char *key_file_path)
+{
+       FILE        *fp;
+       struct stat stat_buf;
+       char *key = NULL;
+
+#define LINELEN MAX_POOL_KEY_LEN
+       char        buf[LINELEN];
+
+       if (strlen(key_file_path) == 0)
+               return NULL;
+
+       /* If password file cannot be opened, ignore it. */
+       if (stat(key_file_path, &stat_buf) != 0)
+               return NULL;
+
+       if (!S_ISREG(stat_buf.st_mode))
+       {
+               ereport(WARNING,
+                               (errmsg("pool key file \"%s\" is not a text file\n",key_file_path)));
+               return NULL;
+       }
+
+       /* If password file is insecure, alert the user. */
+       if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
+       {
+               ereport(WARNING,
+                               (errmsg("pool key file \"%s\" is not a text file\n",key_file_path)));
+
+               ereport(WARNING,
+                               (errmsg("pool key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n",
+                               key_file_path)));
+               /* do we want to allow unsecure pool key file ?*/
+               //return NULL;
+       }
+
+       fp = fopen(key_file_path, "r");
+       if (fp == NULL)
+               return NULL;
+
+       while (!feof(fp) && !ferror(fp))
+       {
+               int     len;
+
+               if (fgets(buf, sizeof(buf), fp) == NULL)
+                       break;
+
+               len = strlen(buf);
+               if (len == 0)
+                       continue;
+
+               /* Remove trailing newline */
+               if (buf[len - 1] == '\n')
+                       buf[len - 1] = 0;
+               key = pstrdup(buf);
+               break;
+       }
+
+       fclose(fp);
+       return key;
+
+#undef LINELEN
+}
index 8778d7b0c5c7911ff42e773e08b3e5e38f8add2c..a90d784c552c4c9754bfe8fcb2e7a4b86967a369 100644 (file)
@@ -516,6 +516,15 @@ static struct config_bool ConfigureNamesBool[] =
                false,
                NULL, NULL,NULL
        },
+       {
+               {"allow_clear_text_frontend_auth", CFGCXT_RELOAD, GENERAL_CONFIG,
+                       "allow to use clear text password authentication with clients, when pool_passwd does not contain the user password.",
+                       CONFIG_VAR_TYPE_BOOL,false, 0
+               },
+               &g_pool_config.allow_clear_text_frontend_auth,
+               false,
+               NULL, NULL,NULL
+       },
 
        /* End-of-list marker */
        EMPTY_CONFIG_BOOL
index 1eff8bc356ab25b5759a6a4ed2c559a5b20d69ee..6c22d56fea870a1243fb58624329ff72be4ddb33 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -88,6 +78,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/include
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(srcdir)/config.h.in $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -98,7 +90,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = config.h
 CONFIG_CLEAN_FILES =
@@ -144,8 +135,6 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
-       $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -277,7 +266,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -309,6 +297,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/include/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/include/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -328,8 +317,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
 $(am__aclocal_m4_deps):
 
 config.h: stamp-h1
-       @test -f $@ || rm -f stamp-h1
-       @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+       @if test ! -f $@; then rm -f stamp-h1; else :; fi
+       @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi
 
 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
        @rm -f stamp-h1
@@ -545,8 +534,6 @@ uninstall-am:
        mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
        tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 209a9d0db89c18705b70348f9faa6716e2f0c8cc..87d23769f9229cc39ae6b7ac5ed814e81c6fb230 100644 (file)
@@ -19,7 +19,6 @@
 #ifndef MD5_H
 #define MD5_H
 
-#define MAX_USER_NAME_LEN 128
 #define MD5_PASSWD_LEN 32
 
 #define WD_AUTH_HASH_LEN 64
index ddfbc788aebea1f37f75a64075d7c9d69815ac10..6a22afd1ca5b08aece580472cda6a6c90c5fba83 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2017     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -19,7 +19,7 @@
  * suitability of this software for any purpose.  It is provided "as
  * is" without express or implied warranty.
  *
- * pool_passwd.h: pool_passwd related definitions.
+ * pool_hba.h: pool_hba related definitions.
  *
  */
 
@@ -38,9 +38,11 @@ typedef enum UserAuth
        /*  uaKrb5, */
        uaTrust,
        /*  uaIdent, */
-       /*  uaPassword, */
+       uaPassword,
        /*  uaCrypt, */
-       uaMD5
+       uaCert,
+       uaMD5,
+       uaSCRAM
 #ifdef USE_PAM
        ,uaPAM
 #endif /* USE_PAM */
index 15b1c27d160b378719ba44a95cf953e5edc587e7..71f059379b805b2e550e74526cdcdbe3b1902bc6 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL 
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2015     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
 #ifndef POOL_PASSWD_H
 #define POOL_PASSWD_H
 
-#include "pool.h"
 
 #define POOL_PASSWD_FILENAME "pool_passwd"
 #define POOL_PASSWD_LEN 35
 
+#define MAX_USER_NAME_LEN      128
+#define MAX_PGPASS_LEN         128
+#define MAX_POOL_KEY_LEN       256
+
+
+#define PASSWORD_MD5_PREFIX    "md5"
+#define PASSWORD_AES_PREFIX    "AES"
+#define PASSWORD_SCRAM_PREFIX  "SCRAM-SHA-256$"
+#define PASSWORD_TEXT_PREFIX   "TEXT"
+
 typedef enum {
        POOL_PASSWD_R,          /* open pool_passwd in read only mode. used by pgpool-II child main process */
        POOL_PASSWD_RW,         /* open pool_passwd in read/write mode. used by pg_md5 command */
 } POOL_PASSWD_MODE;
 
+typedef enum PasswordType
+{
+       PASSWORD_TYPE_UNKNOWN = 0,
+       PASSWORD_TYPE_PLAINTEXT,
+       PASSWORD_TYPE_MD5,
+       PASSWORD_TYPE_AES,
+       PASSWORD_TYPE_SCRAM_SHA_256
+} PasswordType;
+
+typedef struct UserPassword
+{
+       char *userName;
+       char *password;
+       PasswordType passwordType;
+}UserPassword;
+
+typedef struct PasswordMapping
+{
+       UserPassword pgpoolUser;
+       UserPassword backendUser;
+       bool mappedUser;
+}PasswordMapping;
+
+extern PasswordMapping *pool_get_user_credentials(char *username);
+extern PasswordType get_password_type(const char *shadow_pass);
 extern void pool_init_pool_passwd(char *pool_passwd_filename, POOL_PASSWD_MODE mode);
 extern int pool_create_passwdent(char *username, char *passwd);
 extern char *pool_get_passwd(char *username);
 extern void pool_delete_passwdent(char *username);
 extern void pool_finish_pool_passwd(void);
 extern void pool_reopen_passwd_file(void);
-
+extern char *get_decrypted_password(const char *shadow_pass);
+extern char *read_pool_key(char *key_file_path);
+extern char *get_pgpool_config_user_password(char *username, char *password_in_config);
+extern void delete_passwordMapping(PasswordMapping *pwdMapping);
 #endif /* POOL_PASSWD_H */
diff --git a/src/include/auth/scram-common.h b/src/include/auth/scram-common.h
new file mode 100644 (file)
index 0000000..9852f48
--- /dev/null
@@ -0,0 +1,93 @@
+/* -*-pgsql-c-*- */
+/*
+ *
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2018     PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * scram-common.h: SCRAM auth related definitions.
+ * borrowed from PostgreSQL code src/include/common/scram-common.h
+ *
+ */
+
+/*-------------------------------------------------------------------------
+ *
+ * scram-common.h
+ *             Declarations for helper functions used for SCRAM authentication
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/common/scram-common.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SCRAM_COMMON_H
+#define SCRAM_COMMON_H
+
+#include "pool_type.h"
+#include "utils/sha2.h"
+
+/* Length of SCRAM keys (client and server) */
+#define SCRAM_KEY_LEN                          PG_SHA256_DIGEST_LENGTH
+
+/* length of HMAC */
+#define SHA256_HMAC_B                          PG_SHA256_BLOCK_LENGTH
+
+/*
+ * Size of random nonce generated in the authentication exchange.  This
+ * is in "raw" number of bytes, the actual nonces sent over the wire are
+ * encoded using only ASCII-printable characters.
+ */
+#define SCRAM_RAW_NONCE_LEN                    18
+
+/*
+ * Length of salt when generating new verifiers, in bytes.  (It will be stored
+ * and sent over the wire encoded in Base64.)  16 bytes is what the example in
+ * RFC 7677 uses.
+ */
+#define SCRAM_DEFAULT_SALT_LEN         16
+
+/*
+ * Default number of iterations when generating verifier.  Should be at least
+ * 4096 per RFC 7677.
+ */
+#define SCRAM_DEFAULT_ITERATIONS       4096
+
+/*
+ * Context data for HMAC used in SCRAM authentication.
+ */
+typedef struct
+{
+       pg_sha256_ctx sha256ctx;
+       uint8           k_opad[SHA256_HMAC_B];
+} scram_HMAC_ctx;
+
+extern void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen);
+extern void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen);
+extern void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx);
+
+extern void scram_SaltedPassword(const char *password, const char *salt,
+                                        int saltlen, int iterations, uint8 *result);
+extern void scram_H(const uint8 *str, int len, uint8 *result);
+extern void scram_ClientKey(const uint8 *salted_password, uint8 *result);
+extern void scram_ServerKey(const uint8 *salted_password, uint8 *result);
+
+extern char *scram_build_verifier(const char *salt, int saltlen, int iterations,
+                                        const char *password);
+
+#endif                                                 /* SCRAM_COMMON_H */
diff --git a/src/include/auth/scram.h b/src/include/auth/scram.h
new file mode 100644 (file)
index 0000000..71222d4
--- /dev/null
@@ -0,0 +1,65 @@
+/* -*-pgsql-c-*- */
+/*
+ *
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2018     PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * scram.h: SCRAM auth related definitions.
+ * borrowed from PostgreSQL code src/include/libpq/scram.h
+ *
+ */
+/*-------------------------------------------------------------------------
+ *
+ * scram.h
+ *       Interface to libpq/scram.c
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/libpq/scram.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SCRAM_H
+#define PG_SCRAM_H
+
+/* Name of SCRAM-SHA-256 per IANA */
+#define SCRAM_SHA_256_NAME "SCRAM-SHA-256"
+
+/* Status codes for message exchange */
+#define SASL_EXCHANGE_CONTINUE         0
+#define SASL_EXCHANGE_SUCCESS          1
+#define SASL_EXCHANGE_FAILURE          2
+
+/* Routines dedicated to authentication */
+extern void *pg_be_scram_init(const char *username, const char *shadow_pass);
+extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
+                                        char **output, int *outputlen, char **logdetail);
+
+/* Routines to handle and check SCRAM-SHA-256 verifier */
+extern char *pg_be_scram_build_verifier(const char *password);
+extern bool scram_verify_plain_password(const char *username,
+                                                       const char *password, const char *verifier);
+extern void *pg_fe_scram_init(const char *username, const char *password);
+extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
+                                        char **output, int *outputlen,
+                                                 bool *done, bool *success);
+extern void pg_fe_scram_free(void *opaq);
+extern char *pg_fe_scram_build_verifier(const char *password);
+
+#endif                                                 /* PG_SCRAM_H */
index af10a84fd6a6d7544ed654cfb1fd3a82ec8d8c35..0f539e0f11a35352f722dfc4637a58ae8c9c8ff6 100644 (file)
    */
 #undef LT_OBJDIR
 
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
 /* Name of package */
 #undef PACKAGE
 
index a8922f34b43a1c147f1927fde50353c26e0e1d19..4705d18231cc9fd9967ff56ef2fabb3f1213a8ed 100644 (file)
@@ -28,6 +28,7 @@
 #include "pool_type.h"
 #include "pcp/libpcp_ext.h"
 #include "utils/pool_signal.h"
+#include "auth/pool_passwd.h"
 #include "parser/nodes.h"
 #include <stdio.h>
 #include <time.h>
@@ -48,6 +49,9 @@
 
 #define POOLMAXPATHLEN 8192
 
+#define POOLKEYFILE    ".pgpoolkey"
+#define POOLKEYFILEENV "PGPOOLKEYFILE"
+
 /*
  * Brought from PostgreSQL's pg_config_manual.h.
  *
@@ -181,6 +185,13 @@ typedef struct {
 #ifdef USE_SSL
        SSL_CTX *ssl_ctx; /* SSL connection context */
        SSL *ssl;       /* SSL connection */
+       X509 *peer;
+       char *cert_cn;                  /* common in the ssl certificate
+                                                        * presented by frontend connection
+                                                        * Used for cert authentication
+                                                        */
+       bool client_cert_loaded;
+
 #endif
        int ssl_active; /* SSL is failed if < 0, off if 0, on if > 0 */
 
@@ -215,8 +226,9 @@ typedef struct {
         */
        int auth_kind;          /* 3: clear text password, 4: crypt password, 5: md5 password */
        int pwd_size;           /* password (sent back from frontend) size in host order */
-       char password[MAX_PASSWORD_SIZE];               /* password (sent back from frontend) */
+       char password[MAX_PASSWORD_SIZE +1 ];           /* password (sent back from frontend) */
        char salt[4];           /* password salt */
+       PasswordType    passwordType;
 
        /*
         * following are used to remember current session parameter status.
@@ -240,6 +252,8 @@ typedef struct {
        char *username;
        char *remote_hostname;
        int remote_hostname_resolv;
+       bool frontend_authenticated;
+       PasswordMapping *passwordMapping;
        ConnectionInfo *con_info; /* shared memory coninfo used
                                                   * for handling the query containing
                                                   * pg_terminate_backend*/
@@ -553,8 +567,10 @@ extern POOL_STATUS pool_process_query(POOL_CONNECTION *frontend,
                                                                          POOL_CONNECTION_POOL *backend,
                                                                          int reset_request);
 
+extern void connection_do_auth(POOL_CONNECTION_POOL_SLOT *cp, char *password);
 extern int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
 extern int pool_do_reauth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp);
+extern void authenticate_frontend(POOL_CONNECTION *frontend);
 
 extern bool is_backend_cache_empty(POOL_CONNECTION_POOL *backend);
 
@@ -565,6 +581,7 @@ extern void pool_ssl_close(POOL_CONNECTION *cp);
 extern int pool_ssl_read(POOL_CONNECTION *cp, void *buf, int size);
 extern int pool_ssl_write(POOL_CONNECTION *cp, const void *buf, int size);
 extern bool pool_ssl_pending(POOL_CONNECTION *cp);
+extern int SSL_ServerSide_init(void);
 
 extern POOL_STATUS ErrorResponse(POOL_CONNECTION *frontend, 
                                                                  POOL_CONNECTION_POOL *backend);
@@ -703,9 +720,11 @@ extern bool pool_push_pending_data(POOL_CONNECTION *backend);
 
 /* pool_auth.c */
 extern void pool_random_salt(char *md5Salt);
+extern void pool_random(void *buf, size_t len);
 
 /* main.c */
 extern void pool_sleep(unsigned int second);
+extern char *get_pool_key(void);
 
 /* pool_worker_child.c */
 extern void do_worker_child(void);
index 4d0d49cf7cefca64ea66dc7203ed3a0af49a83eb..0194f9172ba4e5b0525f9339c1abe0c2f49f6652 100644 (file)
@@ -153,7 +153,13 @@ typedef struct {
     int        connection_life_time;           /* if idle for this seconds, connection closes */
     int        child_max_connections;          /* if max_connections received, child exits */
        int client_idle_limit;                  /* If client_idle_limit is n (n > 0), the client is forced to be
-                                                                        *disconnected after n seconds idle */
+                                                                        * disconnected after n seconds idle */
+       bool allow_clear_text_frontend_auth;
+                                                                       /* enable Pgpool-II to use clear text password authentication
+                                                                        * between Pgpool and client to get the password
+                                                                        * when password for user does not exist in pool_password
+                                                                        * file.
+                                                                        */
        int authentication_timeout;             /* maximum time in seconds to complete client authentication */
     int        max_pool;                                       /* max # of connection pool per child */
     char *logdir;                                      /* logging directory */
index d0fcb87eec2f3a80f2e54cd3c8732c39a0d0ae2d..494f6300f21c5bbc902426cf5acfe0c14392d2b7 100644 (file)
@@ -179,12 +179,19 @@ SockAddr;
 
 
 #define AUTH_REQ_OK         0   /* User is authenticated  */
-#define AUTH_REQ_KRB4       1   /* Kerberos V4 */
-#define AUTH_REQ_KRB5       2   /* Kerberos V5 */
+#define AUTH_REQ_KRB4       1   /* Kerberos V4. Not supported any more. */
+#define AUTH_REQ_KRB5       2   /* Kerberos V5. Not supported any more. */
 #define AUTH_REQ_PASSWORD   3   /* Password */
-#define AUTH_REQ_CRYPT      4   /* crypt password */
+#define AUTH_REQ_CRYPT      4   /* crypt password. Not supported any more. */
 #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 */
+#define AUTH_REQ_SSPI       9   /* SSPI negotiate without wrap() */
+#define AUTH_REQ_SASL      10   /* Begin SASL authentication */
+#define AUTH_REQ_SASL_CONT 11   /* Continue SASL authentication */
+#define AUTH_REQ_SASL_FIN  12   /* Final SASL message */
+
 
 typedef unsigned int AuthRequest;
 
diff --git a/src/include/utils/base64.h b/src/include/utils/base64.h
new file mode 100644 (file)
index 0000000..7fe19bb
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * base64.h
+ *       Encoding and decoding routines for base64 without whitespace
+ *       support.
+ *
+ * Portions Copyright (c) 2001-2017, PostgreSQL Global Development Group
+ *
+ * src/include/common/base64.h
+ */
+#ifndef BASE64_H
+#define BASE64_H
+
+/* base 64 */
+extern int     pg_b64_encode(const char *src, int len, char *dst);
+extern int     pg_b64_decode(const char *src, int len, char *dst);
+extern int     pg_b64_enc_len(int srclen);
+extern int     pg_b64_dec_len(int srclen);
+
+#endif                                                 /* BASE64_H */
diff --git a/src/include/utils/sha2.h b/src/include/utils/sha2.h
new file mode 100644 (file)
index 0000000..85104b3
--- /dev/null
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * sha2.h
+ *       Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
+ *
+ * Portions Copyright (c) 2016-2017, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *               src/include/common/sha2.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */
+
+/*
+ * FILE:       sha2.h
+ * AUTHOR:     Aaron D. Gifford <me@aarongifford.com>
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ *       may be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#ifndef _PG_SHA2_H_
+#define _PG_SHA2_H_
+
+#include "pool_type.h"
+#ifdef USE_SSL_NO_USE
+#include <openssl/sha.h>
+#endif
+
+/*** SHA224/256/384/512 Various Length Definitions ***********************/
+#define PG_SHA224_BLOCK_LENGTH                 64
+#define PG_SHA224_DIGEST_LENGTH                        28
+#define PG_SHA224_DIGEST_STRING_LENGTH (PG_SHA224_DIGEST_LENGTH * 2 + 1)
+#define PG_SHA256_BLOCK_LENGTH                 64
+#define PG_SHA256_DIGEST_LENGTH                        32
+#define PG_SHA256_DIGEST_STRING_LENGTH (PG_SHA256_DIGEST_LENGTH * 2 + 1)
+#define PG_SHA384_BLOCK_LENGTH                 128
+#define PG_SHA384_DIGEST_LENGTH                        48
+#define PG_SHA384_DIGEST_STRING_LENGTH (PG_SHA384_DIGEST_LENGTH * 2 + 1)
+#define PG_SHA512_BLOCK_LENGTH                 128
+#define PG_SHA512_DIGEST_LENGTH                        64
+#define PG_SHA512_DIGEST_STRING_LENGTH (PG_SHA512_DIGEST_LENGTH * 2 + 1)
+
+/* Context Structures for SHA-1/224/256/384/512 */
+#ifdef USE_SSL_NO_USE
+typedef SHA256_CTX pg_sha256_ctx;
+typedef SHA512_CTX pg_sha512_ctx;
+typedef SHA256_CTX pg_sha224_ctx;
+typedef SHA512_CTX pg_sha384_ctx;
+#else
+typedef struct pg_sha256_ctx
+{
+       uint32          state[8];
+       uint64          bitcount;
+       uint8           buffer[PG_SHA256_BLOCK_LENGTH];
+} pg_sha256_ctx;
+typedef struct pg_sha512_ctx
+{
+       uint64          state[8];
+       uint64          bitcount[2];
+       uint8           buffer[PG_SHA512_BLOCK_LENGTH];
+} pg_sha512_ctx;
+typedef struct pg_sha256_ctx pg_sha224_ctx;
+typedef struct pg_sha512_ctx pg_sha384_ctx;
+#endif                                                 /* USE_SSL */
+
+/* Interface routines for SHA224/256/384/512 */
+extern void pg_sha224_init(pg_sha224_ctx *ctx);
+extern void pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *input0,
+                                size_t len);
+extern void pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest);
+
+extern void pg_sha256_init(pg_sha256_ctx *ctx);
+extern void pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *input0,
+                                size_t len);
+extern void pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest);
+
+extern void pg_sha384_init(pg_sha384_ctx *ctx);
+extern void pg_sha384_update(pg_sha384_ctx *ctx,
+                                const uint8 *, size_t len);
+extern void pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest);
+
+extern void pg_sha512_init(pg_sha512_ctx *ctx);
+extern void pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *input0,
+                                size_t len);
+extern void pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest);
+
+#endif                                                 /* _PG_SHA2_H_ */
diff --git a/src/include/utils/ssl_utils.h b/src/include/utils/ssl_utils.h
new file mode 100644 (file)
index 0000000..36b053d
--- /dev/null
@@ -0,0 +1,34 @@
+/* -*-pgsql-c-*- */
+/*
+ *
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2018     PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ *
+ */
+
+#ifndef SSL_UTILS_H
+#define SSL_UTILS_H
+
+extern void calculate_hmac_sha256(const char *data, int len, char *buf);
+extern int aes_decrypt_with_password(unsigned char *ciphertext, int ciphertext_len,
+                                                         const char *password, unsigned char *plaintext);
+extern int aes_encrypt_with_password(unsigned char *plaintext, int plaintext_len,
+                                                         const char *password, unsigned char *ciphertext);
+
+#endif
index 69bdbd0d029e8cf5de8aea92808a8372f30f971c..cd6567a29f18a8fe9b2318088281c17d1f118d6c 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2016     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -38,7 +38,10 @@ extern void wd_check_network_command_configurations(void);
 extern int watchdog_thread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
 extern char *string_replace(const char *string, const char *pattern, const char *replacement);
 extern void wd_calc_hash(const char *str, int len, char *buf);
-
+extern int aes_decrypt_with_password(unsigned char *ciphertext, int ciphertext_len,
+                                                         const char *password, unsigned char *plaintext);
+extern int aes_encrypt_with_password(unsigned char *plaintext, int plaintext_len,
+                                                         const char *password, unsigned char *ciphertext);
 /* wd_escalation.c */
 extern pid_t fork_escalation_process(void);
 extern pid_t fork_plunging_process(void);
index dd1cd3a36c86d3745c59398d8c150172d4437926..8c9c08381816e6f0b18de2e41f2641ca3d315a22 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 @SET_MAKE@
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -88,6 +78,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/libs
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -98,7 +90,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -160,7 +151,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -315,7 +305,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -340,6 +329,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/libs/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/libs/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -634,8 +624,6 @@ uninstall-am:
        mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
        ps ps-am tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 3776dd94f2d57df7a8d752d1cb3db43298918407..6d87693d0031532162c97ac572662ae73044658a 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -90,6 +80,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/libs/pcp
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs $(include_HEADERS)
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -100,8 +92,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
-       $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -210,7 +200,6 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -340,7 +329,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -383,6 +371,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/libs/pcp/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/libs/pcp/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -705,8 +694,6 @@ uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
        tags tags-am uninstall uninstall-am uninstall-includeHEADERS \
        uninstall-libLTLIBRARIES
 
-.PRECIOUS: Makefile
-
 
 md5.c: ../../../src/auth/md5.c
        rm -f $@ && ln -s $< .
index de3eda8e8218eee19fac1cd9126f378f97b94352..835970817c39febbb7c3baeec8ed28966c3b75d5 100644 (file)
@@ -245,6 +245,9 @@ static bool establish_persistent_connection(int node)
        {
                retry_cnt = pool_config->health_check_params[node].health_check_max_retries;
 
+               char *password = get_pgpool_config_user_password(pool_config->health_check_params[node].health_check_user,
+                                                                                                                pool_config->health_check_params[node].health_check_password);
+
                do
                {
                        /*
@@ -265,7 +268,7 @@ static bool establish_persistent_connection(int node)
                                                                                                                 bkinfo->backend_port,
                                                                                                                 pool_config->health_check_params[node].health_check_database,
                                                                                                                 pool_config->health_check_params[node].health_check_user,
-                                                                                                                pool_config->health_check_params[node].health_check_password, false);
+                                                                                                                password?password:"", false);
 
                        if (pool_config->health_check_params[node].health_check_timeout > 0)
                        {
@@ -297,6 +300,9 @@ static bool establish_persistent_connection(int node)
                                sleep(pool_config->health_check_params[node].health_check_retry_delay);
                        }
                } while (retry_cnt >= 0);
+
+               if (password)
+                       pfree(password);
        }
        return true;
 }
index a87fa2965df69076326be675643a44dfd04ff518..81a810d44256c4c3be569a337dcc7c8ba754e86f 100644 (file)
@@ -47,6 +47,9 @@
 #include "watchdog/wd_utils.h"
 #include "pool_config_variables.h"
 
+
+static bool get_pool_key_filename(char *poolKeyFile);
+
 static void daemonize(void);
 static char    *get_pid_file_path(void);
 static int read_pid_file(void);
@@ -66,7 +69,7 @@ int stop_sig = SIGTERM;               /* stopping signal default value */
 int myargc;
 char **myargv;
 int assert_enabled = 0;
-
+char *pool_key = NULL;
 int main(int argc, char **argv)
 {
        int opt;
@@ -78,11 +81,13 @@ int main(int argc, char **argv)
        char pcp_conf_file_path[POOLMAXPATHLEN+1];
        char conf_file_path[POOLMAXPATHLEN+1];
        char hba_file_path[POOLMAXPATHLEN+1];
+       char pool_passwd_key_file_path[POOLMAXPATHLEN+1];
 
        static struct option long_options[] = {
                {"hba-file", required_argument, NULL, 'a'},
                {"debug", no_argument, NULL, 'd'},
                {"config-file", required_argument, NULL, 'f'},
+               {"key-file", required_argument, NULL, 'k'},
                {"pcp-file", required_argument, NULL, 'F'},
                {"help", no_argument, NULL, 'h'},
                {"mode", required_argument, NULL, 'm'},
@@ -100,7 +105,9 @@ int main(int argc, char **argv)
        snprintf(conf_file_path, sizeof(conf_file_path), "%s/%s", DEFAULT_CONFIGDIR, POOL_CONF_FILE_NAME);
        snprintf(pcp_conf_file_path, sizeof(pcp_conf_file_path), "%s/%s", DEFAULT_CONFIGDIR, PCP_PASSWD_FILE_NAME);
        snprintf(hba_file_path, sizeof(hba_file_path), "%s/%s", DEFAULT_CONFIGDIR, HBA_CONF_FILE_NAME);
-    while ((opt = getopt_long(argc, argv, "a:df:F:hm:nDCxv", long_options, &optindex)) != -1)
+       pool_passwd_key_file_path[0] = 0;
+
+       while ((opt = getopt_long(argc, argv, "a:df:k:F:hm:nDCxv", long_options, &optindex)) != -1)
        {
                switch (opt)
                {
@@ -139,6 +146,15 @@ int main(int argc, char **argv)
                                strlcpy(pcp_conf_file_path, optarg, sizeof(pcp_conf_file_path));
                                break;
 
+                       case 'k':       /* specify key file for decrypt pool_password entries */
+                               if (!optarg)
+                               {
+                                       usage();
+                                       exit(1);
+                               }
+                               strlcpy(pool_passwd_key_file_path, optarg, sizeof(pool_passwd_key_file_path));
+                               break;
+
                        case 'h':
                                usage();
                                exit(0);
@@ -184,15 +200,15 @@ int main(int argc, char **argv)
                                exit(1);
                }
        }
-#ifdef USE_SSL
-       /* global ssl init */
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
-       OPENSSL_init_ssl(0, NULL);
-#else
-       SSL_library_init();
-#endif
-       SSL_load_error_strings();
-#endif /* USE_SSL */
+//#ifdef USE_SSL
+//     /* global ssl init */
+//#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+//     OPENSSL_init_ssl(0, NULL);
+//#else
+//     SSL_library_init();
+//#endif
+//     SSL_load_error_strings();
+//#endif /* USE_SSL */
 
        myargv = save_ps_display_args(myargc, myargv);
        /* create MemoryContexts */
@@ -284,6 +300,14 @@ int main(int argc, char **argv)
        if (pool_config->enable_pool_hba)
                load_hba(hba_file);
 
+#ifdef USE_SSL
+       /*
+        * If ssl is enabled, initialize the SSL context
+        */
+       if (pool_config->ssl)
+               SSL_ServerSide_init();
+#endif /* USE_SSL */
+
        /* check effective user id for watchdog */
        /* watchdog must be started under the privileged user */
        wd_check_network_command_configurations();
@@ -291,6 +315,13 @@ int main(int argc, char **argv)
        /* set signal masks */
        poolinitmask();
 
+       /* read the pool password key */
+       if (strlen(pool_passwd_key_file_path) == 0)
+       {
+               get_pool_key_filename(pool_passwd_key_file_path);
+       }
+       pool_key = read_pool_key(pool_passwd_key_file_path);
+
        if (not_detach)
                write_pid_file();
        else
@@ -328,6 +359,10 @@ static void show_version(void)
 
 static void usage(void)
 {
+       char    homedir[POOLMAXPATHLEN];
+       if (!get_home_directory(homedir, sizeof(homedir)))
+               strncpy(homedir,"USER-HOME-DIR",POOLMAXPATHLEN);
+
        fprintf(stderr, "%s version %s (%s),\n",        PACKAGE, VERSION, PGPOOLVERSION);
        fprintf(stderr, "  A generic connection pool/replication/load balance server for PostgreSQL\n\n");
        fprintf(stderr, "Usage:\n");
@@ -343,6 +378,10 @@ static void usage(void)
        fprintf(stderr, "  -f, --config-file=CONFIG_FILE\n");
        fprintf(stderr, "                      Set the path to the pgpool.conf configuration file\n");
        fprintf(stderr, "                      (default: %s/%s)\n",DEFAULT_CONFIGDIR, POOL_CONF_FILE_NAME);
+       fprintf(stderr, "  -k, --key-file=KEY_FILE\n");
+       fprintf(stderr, "                      Set the path to the pgpool key file\n");
+       fprintf(stderr, "                      (default: %s/%s)\n",homedir, POOLKEYFILE);
+       fprintf(stderr, "                      can be over ridden by %s environment variable\n",POOLKEYFILEENV);
        fprintf(stderr, "  -F, --pcp-file=PCP_CONFIG_FILE\n");
        fprintf(stderr, "                      Set the path to the pcp.conf configuration file\n");
        fprintf(stderr, "                      (default: %s/%s)\n",DEFAULT_CONFIGDIR, PCP_PASSWD_FILE_NAME);
@@ -363,6 +402,31 @@ static void usage(void)
        fprintf(stderr, "  immediate   the same mode as fast\n");
 }
 
+static bool
+get_pool_key_filename(char *poolKeyFile)
+{
+       char    *passfile_env;
+
+       if ((passfile_env = getenv(POOLKEYFILEENV)) != NULL)
+       {
+               /* use the literal path from the environment, if set */
+               strlcpy(poolKeyFile, passfile_env, POOLMAXPATHLEN);
+       }
+       else
+       {
+               char    homedir[POOLMAXPATHLEN];
+               if (!get_home_directory(homedir, sizeof(homedir)))
+                       return false;
+               snprintf(poolKeyFile, POOLMAXPATHLEN, "%s/%s", homedir, POOLKEYFILE);
+       }
+       return true;
+}
+
+
+char *get_pool_key(void)
+{
+       return pool_key;
+}
 /*
 * detach control ttys
 */
index 5b73dbf8d393d413f4f900e332254d764ccabdda..7329f0fb1d36ee81477e2ca6d840d5e7ca233ac9 100644 (file)
@@ -46,7 +46,6 @@
 
 #include <libgen.h>
 #include "utils/elog.h"
-#include "utils/palloc.h"
 
 #include "pool.h"
 #include "utils/palloc.h"
@@ -2302,7 +2301,8 @@ do_health_check(bool use_template_db, volatile int *health_check_node_id)
        static char *dbname;
        int i;
        bool all_nodes_healthy = false;
-
+       char *password = get_pgpool_config_user_password(pool_config->health_check_user,
+                                                                                                        pool_config->health_check_password);
        /* Do not execute health check during recovery */
        if (*InRecovery)
                return false;
@@ -2353,13 +2353,15 @@ do_health_check(bool use_template_db, volatile int *health_check_node_id)
                                                                                         bkinfo->backend_port,
                                                                                         dbname,
                                                                                         pool_config->health_check_user,
-                                                                                        pool_config->health_check_password, false);
+                                                                                        password?password:"", false);
 
                ereport(DEBUG1,
                        (errmsg("persistent DB connection to backend node %d having status %d is successful", i, bkinfo->backend_status)));
 
                discard_persistent_db_connection(slot);
        }
+       if (password)
+               pfree(password);
        return all_nodes_healthy;
 }
 #endif
@@ -3239,6 +3241,9 @@ static int find_primary_node(void)
        int i;
        POOL_NODE_STATUS *status;
        int primary = -1;
+       char *password = get_pgpool_config_user_password(pool_config->sr_check_user,
+                                                                                                        pool_config->sr_check_password);
+
 
        /* Streaming replication mode? */
        if (!SL_MODE)
@@ -3281,7 +3286,7 @@ static int find_primary_node(void)
                                                                                                                 bkinfo->backend_port,
                                                                                                                 pool_config->sr_check_database,
                                                                                                                 pool_config->sr_check_user,
-                                                                                                                pool_config->sr_check_password, true);
+                                                                                                                password?password:"", true);
                if (!slots[i])
                {
                        ereport(LOG,
@@ -3289,6 +3294,9 @@ static int find_primary_node(void)
                }
        }
 
+       if(password)
+               pfree(password);
+
        /* Verify backend status */
        status = verify_backend_node_status(slots);
 
index 097f0cd2de56da5c1fca2347f5993855f9142296..1176b3c35ec96dbeeffa267db74de36cc616be57 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -90,6 +80,8 @@ build_triplet = @build@
 host_triplet = @host@
 @use_repl_snprintf_TRUE@am__append_1 = snprintf.c
 subdir = src/parser
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs gram.h gram.c $(top_srcdir)/ylwrap
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -100,7 +92,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -204,8 +195,6 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs \
-       $(top_srcdir)/ylwrap gram.c gram.h
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -335,7 +324,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -372,6 +360,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/parser/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/parser/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -644,8 +633,6 @@ uninstall-am:
        mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
        tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 gram.c: gram.y scan.c
 scan.c: scan.l
index 67098a3caa9d523140b848f8e143a6a2de6eeafb..0d30e4a99e1799fe31a7eaec15ec99a8743a4df3 100644 (file)
@@ -331,6 +331,8 @@ static void check_postmaster_started(BackendInfo *backend)
        char port_str[16];
        PGconn *conn;
        char *dbname;
+       char *password = get_pgpool_config_user_password(pool_config->recovery_user,
+                                                                                                        pool_config->recovery_password);
 
        snprintf(port_str, sizeof(port_str),"%d", backend->backend_port);
 
@@ -353,13 +355,16 @@ static void check_postmaster_started(BackendInfo *backend)
                                                        NULL,
                                                        dbname,
                                                        pool_config->recovery_user,
-                                                       pool_config->recovery_password);
+                                                       password?password:NULL);
 
                r = PQstatus(conn);
                PQfinish(conn);
                if (r == CONNECTION_OK)
-                       return;
-
+               {
+                       if (password)
+                               pfree(password);
+                               return;
+               }
                ereport(LOG,
                        (errmsg("checking if postmaster is started"),
                                errdetail("failed to connect to postmaster on hostname:%s database:%s user:%s",
@@ -388,12 +393,16 @@ static void check_postmaster_started(BackendInfo *backend)
                                                        NULL,
                                                        dbname,
                                                        pool_config->recovery_user,
-                                                       pool_config->recovery_password);
+                                                       password?password:NULL);
 
                r = PQstatus(conn);
                PQfinish(conn);
                if (r == CONNECTION_OK)
+               {
+                       if (password)
+                               pfree(password);
                        return;
+               }
 
                ereport(LOG,
                        (errmsg("checking if postmaster is started"),
@@ -404,6 +413,9 @@ static void check_postmaster_started(BackendInfo *backend)
                        sleep(3);
        } while (i++ < WAIT_RETRY_COUNT);
 
+       if (password)
+               pfree(password);
+
        ereport(ERROR,
                (errmsg("recovery is checking if postmaster is started"),
                         errdetail("postmaster on hostname:\"%s\" database:\"%s\" user:\"%s\" failed to start in %d second",
@@ -414,6 +426,8 @@ static PGconn *connect_backend_libpq(BackendInfo *backend)
 {
        char port_str[16];
        PGconn *conn;
+       char *password = get_pgpool_config_user_password(pool_config->recovery_user,
+                                                                                                        pool_config->recovery_password);
 
        snprintf(port_str, sizeof(port_str),
                         "%d", backend->backend_port);
@@ -423,7 +437,10 @@ static PGconn *connect_backend_libpq(BackendInfo *backend)
                                                NULL,
                                                "template1",
                                                pool_config->recovery_user,
-                                               pool_config->recovery_password);
+                                               password?password:"");
+
+       if (password)
+               pfree(password);
 
        if (PQstatus(conn) != CONNECTION_OK)
        {
index b2669ee5a0ac2a36c4d0d4ff2659037dc6a548e0..e0f114ce6dabdd8f356316cdc2729cd2cbc25cf6 100644 (file)
 #include <stdlib.h>
 #include <sys/time.h>
 
-#ifdef HAVE_CRYPT_H
-#include <crypt.h>
-#endif
-
 #include "pool.h"
 #include "utils/palloc.h"
 #include "utils/memutils.h"
@@ -72,7 +68,6 @@ static RETSIGTYPE reload_config_handler(int sig);
 static RETSIGTYPE authentication_timeout(int sig);
 static void send_params(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
 static void send_frontend_exits(void);
-static void s_do_auth(POOL_CONNECTION_POOL_SLOT *cp, char *password);
 static void connection_count_up(void);
 static void connection_count_down(void);
 static bool connect_using_existing_connection(POOL_CONNECTION *frontend,
@@ -1294,7 +1289,7 @@ POOL_CONNECTION_POOL_SLOT *make_persistent_db_connection(
        PG_TRY();
        {
                send_startup_packet(cp);
-               s_do_auth(cp, password);
+               connection_do_auth(cp, password);
        }
        PG_CATCH();
        {
@@ -1388,226 +1383,6 @@ void discard_persistent_db_connection(POOL_CONNECTION_POOL_SLOT *cp)
        free_persisten_db_connection_memory(cp);
 }
 
-/*
- * Do authentication. Assuming the only caller is
- * *make_persistent_db_connection().
- */
-static void s_do_auth(POOL_CONNECTION_POOL_SLOT *cp, char *password)
-{
-       char kind;
-       int status;
-       int length;
-       int auth_kind;
-       char state;
-       char *p;
-       int pid, key;
-       bool keydata_done;
-
-       /*
-        * read kind expecting 'R' packet (authentication response)
-        */
-       pool_read_with_error(cp->con, &kind, sizeof(kind),
-                                  "authentication message response type");
-
-       if (kind != 'R')
-       {
-               char *msg;
-               int sts = 0;
-
-               if (kind == 'E' || kind == 'N')
-               {
-                       sts =  pool_extract_error_message(false, cp->con, cp->sp->major, false, &msg);
-               }
-
-               if (sts == 1)   /* succeeded in extracting error/notice message */
-               {
-                       ereport(ERROR,
-                                       (errmsg("failed to authenticate"),
-                                        errdetail("%s", msg)));
-                       pfree(msg);
-               }
-                       ereport(ERROR,
-                                       (errmsg("failed to authenticate"),
-                                        errdetail("invalid authentication message response type, Expecting 'R' and received '%c'",kind)));
-       }
-
-       /* read message length */
-       pool_read_with_error(cp->con, &length, sizeof(length),
-                         "authentication message response length");
-
-       length = ntohl(length);
-
-       /* read auth kind */
-       pool_read_with_error(cp->con, &auth_kind, sizeof(auth_kind),
-                       "authentication method from response");
-       auth_kind = ntohl(auth_kind);
-    ereport(DEBUG1,
-            (errmsg("authenticate kind = %d",auth_kind)));
-
-       if (auth_kind == 0)     /* trust authentication? */
-       {
-               cp->con->auth_kind = 0;
-       }
-       else if (auth_kind == 3) /* clear text password? */
-       {
-               int size = htonl(strlen(password) + 5);
-
-               pool_write(cp->con, "p", 1);
-               pool_write(cp->con, &size, sizeof(size));
-               pool_write_and_flush(cp->con, password, strlen(password) + 1);
-               pool_flush(cp->con);
-               s_do_auth(cp, password);
-               return;
-       }
-       else if (auth_kind == 4) /* crypt password? */
-       {
-               int size;
-               char salt[3];
-               char *crypt_password;
-
-               pool_read_with_error(cp->con, &salt, 2,"crypt salt");
-               salt[2] = '\0';
-
-               crypt_password = crypt(password, salt);
-               size = htonl(strlen(crypt_password) + 5);
-               pool_write(cp->con, "p", 1);
-               pool_write(cp->con, &size, sizeof(size));
-               pool_write_and_flush(cp->con, crypt_password, strlen(crypt_password) + 1);
-               status = pool_flush(cp->con);
-               if (status > 0)
-               {
-            ereport(ERROR,
-                               (errmsg("failed to authenticate"),
-                     errdetail("error while sending crypt password")));
-               }
-               s_do_auth(cp, password);
-               return;
-       }
-       else if (auth_kind == 5) /* md5 password? */
-       {
-               char salt[4];
-               char *buf, *buf1;
-               int size;
-
-               pool_read_with_error(cp->con, &salt, 4,"authentication md5 salt");
-
-               buf = palloc0(2 * (MD5_PASSWD_LEN + 4)); /* hash + "md5" + '\0' */
-
-               /* build md5 password */
-               buf1 = buf + MD5_PASSWD_LEN + 4;
-               pool_md5_encrypt(password, cp->sp->user, strlen(cp->sp->user), buf1);
-               pool_md5_encrypt(buf1, salt, 4, buf + 3);
-               memcpy(buf, "md5", 3);
-
-               size = htonl(strlen(buf) + 5);
-               pool_write(cp->con, "p", 1);
-               pool_write(cp->con, &size, sizeof(size));
-               pool_write_and_flush(cp->con, buf, strlen(buf) + 1);
-               status = pool_flush(cp->con);
-               s_do_auth(cp, password);
-               pfree(buf);
-               return;
-       }
-       else
-       {
-        ereport(ERROR,
-                       (errmsg("failed to authenticate"),
-                 errdetail("auth kind %d not supported yet", auth_kind)));
-       }
-
-       /*
-        * Read backend key data and wait until Ready for query arriving or
-        * error happens.
-        */
-
-       keydata_done = false;
-
-       for (;;)
-       {
-               pool_read_with_error(cp->con, &kind, sizeof(kind),
-                                      "authentication message kind");
-
-               switch (kind)
-               {
-                       case 'K':       /* backend key data */
-                               keydata_done = true;
-                               ereport(DEBUG1,
-                                       (errmsg("authenticate backend: key data received")));
-
-
-                               /* read message length */
-                               pool_read_with_error(cp->con, &length, sizeof(length),"message length for authentication kind 'K'");
-                               if (ntohl(length) != 12)
-                               {
-                    ereport(ERROR,
-                                               (errmsg("failed to authenticate"),
-                             errdetail("invalid backend key data length. received %d bytes when expecting 12 bytes"
-                                       , ntohl(length))));
-                               }
-
-                               /* read pid */
-                               pool_read_with_error(cp->con, &pid, sizeof(pid),"pid for authentication kind 'K'");
-                               cp->pid = pid;
-
-                               /* read key */
-                               pool_read_with_error(cp->con, &key, sizeof(key),
-                                     "key for authentication kind 'K'");
-                               cp->key = key;
-                               break;
-
-                       case 'Z':       /* Ready for query */
-                               /* read message length */
-                               pool_read_with_error(cp->con, &length, sizeof(length),
-                                   "message length for authentication kind 'Z'");
-                               length = ntohl(length);
-
-                               /* read transaction state */
-                               pool_read_with_error(cp->con, &state, sizeof(state),
-                                     "transaction state  for authentication kind 'Z'");
-                       
-                               ereport(DEBUG1,
-                                       (errmsg("authenticate backend: transaction state: %c", state)));
-
-                               cp->con->tstate = state;
-
-                               if (!keydata_done)
-                               {
-                    ereport(ERROR,
-                                               (errmsg("failed to authenticate"),
-                             errdetail("ready for query arrived before receiving keydata")));
-                               }
-                               return;
-                               break;
-
-                       case 'S':       /* parameter status */
-                       case 'N':       /* notice response */
-                       case 'E':       /* error response */
-                               /* Just throw away data */
-                               pool_read_with_error(cp->con, &length, sizeof(length),
-                                   "backend message length");
-                       
-                               length = ntohl(length);
-                               length -= 4;
-
-                               p = pool_read2(cp->con, length);
-                               if (p == NULL)
-                                       ereport(ERROR,
-                                               (errmsg("failed to authenticate"),
-                             errdetail("unable to read data from socket")));
-
-                               break;
-
-                       default:
-                ereport(ERROR,
-                                       (errmsg("failed to authenticate"),
-                         errdetail("unknown authentication message response received '%c'",kind)));
-                               break;
-               }
-       }
-       ereport(ERROR,
-               (errmsg("failed to authenticate")));
-}
-
 /*
  * Count up connection counter (from frontend to pgpool)
  * in shared memory
index e2acf06def629c4ef59b2d1875841d10ca509ba6..b0d4b3caaa07d9a0a30ea8e094c62e4734023215 100644 (file)
@@ -91,6 +91,12 @@ authentication_timeout = 60
                                    # Delay in seconds to complete client authentication
                                    # 0 means no timeout.
 
+allow_clear_text_frontend_auth = off
+                                                                  # Allow Pgpool-II to use clear text password authentication
+                                                                  # with clients, when pool_passwd does not
+                                                                  # contain the user password
+
+
 # - SSL Connections -
 
 ssl = off
@@ -354,8 +360,12 @@ sr_check_user = 'nobody'
                                    # This is necessary even if you disable
                                    # streaming replication delay check with
                                    # sr_check_period = 0
+
 sr_check_password = ''
-                                   # Password for streaming replication check user
+                                                                  # Password for streaming replication check user.
+                                                                  # Leaving it empty will make Pgpool-II to first look for the
+                                                                  # Password in pool_passwd file before using the empty password
+
 sr_check_database = 'postgres'
                                    # Database name for streaming replication check
 delay_threshold = 0
@@ -394,6 +404,9 @@ health_check_user = 'nobody'
                                    # Health check user
 health_check_password = ''
                                    # Password for health check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 health_check_database = ''
                                    # Database name for health check. If '', tries 'postgres' frist, then 'template1'
 
@@ -479,6 +492,9 @@ recovery_user = 'nobody'
                                    # Online recovery user
 recovery_password = ''
                                    # Online recovery password
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 recovery_1st_stage_command = ''
                                    # Executes a command in first stage
 recovery_2nd_stage_command = ''
@@ -661,6 +677,8 @@ wd_lifecheck_user = 'nobody'
                                     # (change requires restart)
 wd_lifecheck_password = ''
                                     # Password for watchdog user in lifecheck
+                                                                       # Leaving it empty will make Pgpool-II to first look for the
+                                                                       # Password in pool_passwd file before using the empty password
                                     # (change requires restart)
 
 # - Other pgpool Connection Settings -
index 064f6d1c8702cb964473307aa2c6aa364f896e7b..809ed6f7865774d2ac831b59e7a14c887d83dcbf 100644 (file)
@@ -92,6 +92,11 @@ authentication_timeout = 60
                                    # Delay in seconds to complete client authentication
                                    # 0 means no timeout.
 
+allow_clear_text_frontend_auth = off
+                                   # Allow Pgpool-II to use clear text password authentication
+                                   # with clients, when pool_passwd does not
+                                   # contain the user password
+
 # - SSL Connections -
 
 ssl = off
@@ -343,7 +348,10 @@ sr_check_user = 'nobody'
                                    # This is neccessary even if you disable streaming
                                    # replication delay check by sr_check_period = 0
 sr_check_password = ''
-                                   # Password for streaming replication check user
+                                   # Password for streaming replication check user.
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 sr_check_database = 'postgres'
                                    # Database name for streaming replication check
 delay_threshold = 10000000
@@ -382,6 +390,9 @@ health_check_user = 'nobody'
                                    # Health check user
 health_check_password = ''
                                    # Password for health check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 health_check_database = ''
                                    # Database name for health check. If '', tries 'postgres' frist, 
 health_check_max_retries = 0
@@ -466,6 +477,9 @@ recovery_user = 'nobody'
                                    # Online recovery user
 recovery_password = ''
                                    # Online recovery password
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 recovery_1st_stage_command = ''
                                    # Executes a command in first stage
 recovery_2nd_stage_command = ''
@@ -642,6 +656,8 @@ wd_lifecheck_user = 'nobody'
                                     # (change requires restart)
 wd_lifecheck_password = ''
                                     # Password for watchdog user in lifecheck
+                                    # Leaving it empty will make Pgpool-II to first look for the
+                                    # Password in pool_passwd file before using the empty password
                                     # (change requires restart)
 
 # - Other pgpool Connection Settings -
index d67512296de41611a115572e7f7145a7fb12dfc0..56fedcae31533b972849e6b1918bb104093d6bb7 100644 (file)
@@ -91,6 +91,11 @@ authentication_timeout = 60
                                    # Delay in seconds to complete client authentication
                                    # 0 means no timeout.
 
+allow_clear_text_frontend_auth = off
+                                                                  # Allow Pgpool-II to use clear text password authentication
+                                                                  # with clients, when pool_passwd does not
+                                                                  # contain the user password
+
 # - SSL Connections -
 
 ssl = off
@@ -354,8 +359,12 @@ sr_check_user = 'nobody'
                                    # Streaming replication check user
                                    # This is neccessary even if you disable streaming
                                    # replication delay check by sr_check_period = 0
+
 sr_check_password = ''
                                    # Password for streaming replication check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 sr_check_database = 'postgres'
                                    # Database name for streaming replication check
 delay_threshold = 0
@@ -394,6 +403,9 @@ health_check_user = 'nobody'
                                    # Health check user
 health_check_password = ''
                                    # Password for health check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 health_check_database = ''
                                    # Database name for health check. If '', tries 'postgres' frist, 
 health_check_max_retries = 0
@@ -479,6 +491,9 @@ recovery_user = 'nobody'
                                    # Online recovery user
 recovery_password = ''
                                    # Online recovery password
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 recovery_1st_stage_command = ''
                                    # Executes a command in first stage
 recovery_2nd_stage_command = ''
@@ -661,6 +676,8 @@ wd_lifecheck_user = 'nobody'
                                     # (change requires restart)
 wd_lifecheck_password = ''
                                     # Password for watchdog user in lifecheck
+                                                                       # Leaving it empty will make Pgpool-II to first look for the
+                                                                       # Password in pool_passwd file before using the empty password
                                     # (change requires restart)
 
 # - Other pgpool Connection Settings -
index 2fd2906bff050843833e1730619d31fe3787111c..fa6d5f306e52e13470ff6a95a2b5ad126dc016d6 100644 (file)
@@ -90,6 +90,11 @@ authentication_timeout = 60
                                    # Delay in seconds to complete client authentication
                                    # 0 means no timeout.
 
+allow_clear_text_frontend_auth = off
+                                   # Allow Pgpool-II to use clear text password authentication
+                                   # with clients, when pool_passwd does not
+                                   # contain the user password
+
 # - SSL Connections -
 
 ssl = off
@@ -353,8 +358,12 @@ sr_check_user = 'nobody'
                                    # Streaming replication check user
                                    # This is neccessary even if you disable streaming
                                    # replication delay check by sr_check_period = 0
+
 sr_check_password = ''
                                    # Password for streaming replication check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 sr_check_database = 'postgres'
                                    # Database name for streaming replication check
 delay_threshold = 0
@@ -393,6 +402,9 @@ health_check_user = 'nobody'
                                    # Health check user
 health_check_password = ''
                                    # Password for health check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 health_check_database = ''
                                    # Database name for health check. If '', tries 'postgres' frist, 
 health_check_max_retries = 0
@@ -477,6 +489,9 @@ recovery_user = 'nobody'
                                    # Online recovery user
 recovery_password = ''
                                    # Online recovery password
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 recovery_1st_stage_command = ''
                                    # Executes a command in first stage
 recovery_2nd_stage_command = ''
@@ -660,6 +675,8 @@ wd_lifecheck_user = 'nobody'
                                     # (change requires restart)
 wd_lifecheck_password = ''
                                     # Password for watchdog user in lifecheck
+                                                                       # Leaving it empty will make Pgpool-II to first look for the
+                                                                       # Password in pool_passwd file before using the empty password
                                     # (change requires restart)
 
 # - Other pgpool Connection Settings -
index b64432f6dc27409cb595ae091acbf4fcce68b7a8..9138862ab78416f71b450eb97463919aeef7dc7e 100644 (file)
@@ -92,6 +92,11 @@ authentication_timeout = 60
                                    # Delay in seconds to complete client authentication
                                    # 0 means no timeout.
 
+allow_clear_text_frontend_auth = off
+                                   # Allow Pgpool-II to use clear text password authentication
+                                   # with clients, when pool_passwd does not
+                                   # contain the user password
+
 # - SSL Connections -
 
 ssl = off
@@ -357,6 +362,9 @@ sr_check_user = 'nobody'
                                    # replication delay check by sr_check_period = 0
 sr_check_password = ''
                                    # Password for streaming replication check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 sr_check_database = 'postgres'
                                    # Database name for streaming replication check
 delay_threshold = 10000000
@@ -395,6 +403,9 @@ health_check_user = 'nobody'
                                    # Health check user
 health_check_password = ''
                                    # Password for health check user
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 health_check_database = ''
                                    # Database name for health check. If '', tries 'postgres' frist, 
 health_check_max_retries = 0
@@ -479,6 +490,9 @@ recovery_user = 'nobody'
                                    # Online recovery user
 recovery_password = ''
                                    # Online recovery password
+                                   # Leaving it empty will make Pgpool-II to first look for the
+                                   # Password in pool_passwd file before using the empty password
+
 recovery_1st_stage_command = ''
                                    # Executes a command in first stage
 recovery_2nd_stage_command = ''
@@ -661,6 +675,8 @@ wd_lifecheck_user = 'nobody'
                                     # (change requires restart)
 wd_lifecheck_password = ''
                                     # Password for watchdog user in lifecheck
+                                    # Leaving it empty will make Pgpool-II to first look for the
+                                    # Password in pool_passwd file before using the empty password
                                     # (change requires restart)
 
 # - Other pgpool Connection Settings -
index 6e37e27360c636b93eb3a7bbcbebb31f01902be2..b174f74b6bc52b66ff901fe569472d30bbf91719 100644 (file)
@@ -38,8 +38,8 @@
 # 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" or "pam".  Note that "pam" sends passwords
-# in clear text.
+# METHOD can be "trust", "reject", "md5" , "scram-sha-256" or "pam".
+# Note that "pam" sends passwords in clear text.
 #
 # OPTION is the name of the PAM service. Default service name is "pgpool"
 #
index 4189fee004f502717327aebcfa6792c47c9c15f3..58e95adad66a9810be2ddda0541147e66f4cdf57 100644 (file)
@@ -214,7 +214,10 @@ static void establish_persistent_connection(void)
 {
        int i;
        BackendInfo *bkinfo;
-       
+
+       char *password = get_pgpool_config_user_password(pool_config->sr_check_user,
+                                                                                                        pool_config->sr_check_password);
+
        for (i=0;i<NUM_BACKENDS;i++)
        {
                if (!VALID_BACKEND(i))
@@ -227,9 +230,12 @@ static void establish_persistent_connection(void)
                                                                                          bkinfo->backend_port,
                                                                                          pool_config->sr_check_database,
                                                                                          pool_config->sr_check_user,
-                                                                                         pool_config->sr_check_password, true);
+                                                                                         password?password:"", true);
                }
        }
+
+       if(password)
+               pfree(password);
 }
 
 /*
index 8bf2045cd1b6bceec9a0b1193a290483e66cc0d2..e4a314523118da42d2f446e4acfc890bdbe33084 100755 (executable)
@@ -277,6 +277,7 @@ chmod 755 $1/$PGPOOL_REMOTE_START_SCRIPT
 function set_postgresql_conf
 {
        PGCONF=$1/postgresql.conf
+       PGHBACONF=$1/pg_hba.conf
 
        echo "listen_addresses = '*'" >> $PGCONF
        echo "port = $PORT" >> $PGCONF
@@ -309,14 +310,24 @@ function set_postgresql_conf
                fi
        fi
 
-       ed $1/pg_hba.conf <<EOF
+    sed -i '/host.*all.*all.*trust$/s/^/#/g' $PGHBACONF
+    sed -i '/local.*all.*all.*trust$/s/^/#/g' $PGHBACONF
+
+    echo "host      all   scram_user     0/0    scram-sha-256" >> $PGHBACONF
+    echo "host      all   md5_user       0/0    md5" >> $PGHBACONF
+    echo "host      all   all       0/0    trust" >> $PGHBACONF
+
+    echo "local      all   scram_user      scram-sha-256" >> $PGHBACONF
+    echo "local      all   md5_user        md5" >> $PGHBACONF
+    echo "local      all   all      trust" >> $PGHBACONF
+
+    ed $1/pg_hba.conf <<EOF
 /^#local *replication/s/^#//p
 /^#host *replication/s/^#//p
 /^#host *replication/s/^#//p
 w
 q
 EOF
-
 }
 
 #-------------------------------------------
@@ -556,6 +567,23 @@ EOF
 fi
 }
 
+function set_pool_hba_conf {
+    POOL_HBACONF=$1
+    sed -i '/host.*all.*all.*trust$/s/^/#/g' $POOL_HBACONF
+    sed -i '/local.*all.*all.*trust$/s/^/#/g' $POOL_HBACONF
+
+    echo "host    all         scram_user  0/0                   scram-sha-256" >> $POOL_HBACONF
+    echo "host    all         md5_user    0/0                   md5" >> $POOL_HBACONF
+
+    echo "local   all         scram_user                        scram-sha-256" >> $POOL_HBACONF
+    echo "local   all         md5_user                          md5" >> $POOL_HBACONF
+
+    echo "local   all         all                               trust" >> $POOL_HBACONF
+
+    echo "host    all         all         127.0.0.1/32          trust" >> $POOL_HBACONF
+    echo "host    all         all         ::1/128               trust" >> $POOL_HBACONF
+}
+
 #-------------------------------------------
 # set pgpool.conf
 # argument: absolute path to pgpool.conf
@@ -746,6 +774,7 @@ BASEPORT=$PGBASEPORT
 #-------------------------------------------
 test ! -d etc && mkdir etc
 cp $SAMPLE_CONF $CONF
+cp $PGPOOLDIR/pool_hba.conf.sample $BASEDIR/etc/pool_hba.conf
 
 #-------------------------------------------
 # create startall, shutdownall and pgpool_reload
@@ -818,6 +847,7 @@ do
 done
 
 set_pgpool_conf $CONF
+set_pool_hba_conf $BASEDIR/etc/pool_hba.conf
 
 PGPOOL_PORT=$ORIG_BASEPORT
 echo "port = $PGPOOL_PORT" >> $CONF
diff --git a/src/test/regression/tests/020.allow_clear_text_frontend_auth/test.sh b/src/test/regression/tests/020.allow_clear_text_frontend_auth/test.sh
new file mode 100755 (executable)
index 0000000..65109f3
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+#-------------------------------------------------------------------
+# test script for scram and md5 authentication test
+# when allow_clear_text_frontend_auth is set
+#
+source $TESTLIBS
+TESTDIR=testdir
+PSQL=$PGBIN/psql
+CREATEUSER=$PGBIN/createuser
+
+
+rm -fr $TESTDIR
+mkdir $TESTDIR
+cd $TESTDIR
+
+# create test environment
+echo -n "creating test environment..."
+$PGPOOL_SETUP -m s -n 2 || exit 1
+echo "done."
+
+source ./bashrc.ports
+
+#echo "enable_pool_hba = on" >> etc/pgpool.conf
+echo "allow_clear_text_frontend_auth = on" >> etc/pgpool.conf
+#set max_init_children to 1 to make sure we reuse the
+#connection to test wrong password rejection
+echo "num_init_children = 1" >> etc/pgpool.conf
+
+./startall
+
+export PGPORT=$PGPOOL_PORT
+wait_for_pgpool_startup
+
+#set passwords
+$PSQL -c "SET password_encryption = 'scram-sha-256'; CREATE ROLE scram_user PASSWORD 'scram_password'; ALTER ROLE scram_user WITH LOGIN;" test
+$PSQL -c "SET password_encryption = 'md5'; CREATE ROLE md5_user PASSWORD 'md5_password'; ALTER ROLE md5_user WITH LOGIN" test
+$PSQL -c "CREATE ROLE trust_user PASSWORD 'trust_password';ALTER ROLE trust_user WITH LOGIN" test
+
+
+if $PSQL -c "SELECT rolname,substring(rolpassword, 1, 14) FROM pg_authid where rolname ='scram_user'" test|grep "SCRAM-SHA-256" >/dev/null 2>&1
+then
+    echo "scram_user authenticated setup successfuly"
+else
+    echo "scram authentication setup failed"
+    failed=1
+fi
+if $PSQL -c "SELECT rolname,substring(rolpassword, 1, 4) FROM pg_authid where rolname ='md5_user'" test|grep "md5" >/dev/null 2>&1
+then
+    echo "md5 authenticated setup successfuly"
+else
+    echo "md5 authentication setup failed"
+    failed=1
+fi
+#create pgpass file
+echo "127.0.0.1:$PGPORT:test:scram_user:scram_password" >> pgpass
+echo "127.0.0.1:$PGPORT:test:md5_user:md5_password" >> pgpass
+chmod 0600 pgpass
+echo "127.0.0.1:$PGPORT:test:scram_user:scram_wrong_password" >> pgpasswrong
+echo "127.0.0.1:$PGPORT:test:md5_user:md5_wrong_password" >> pgpasswrong
+chmod 0600 pgpasswrong
+
+export PGPASSFILE=$PWD/pgpass
+
+if $PSQL -h 127.0.0.1 -U scram_user -c "SELECT user, inet_client_addr();" test|grep "scram_user" >/dev/null 2>&1
+then
+    echo "scram_user authenticated successfuly"
+else
+    echo "scram authentication failed"
+    failed=1
+fi
+
+if $PSQL -h 127.0.0.1 -U md5_user -c "SELECT user, inet_client_addr();" test|grep "md5_user" >/dev/null 2>&1
+then
+    echo "md5_user authenticated successfuly"
+else
+    echo "md5 authentication failed"
+    failed=1
+fi
+#now try using the wrong passwords1:
+export PGPASSFILE=$PWD/pgpasswrong
+echo "Trying wrong passwords now"
+#try connect using scram_user and md5 user
+if $PSQL -h 127.0.0.1 -U scram_user -c "SELECT user, inet_client_addr();" test|grep "scram_user" >/dev/null 2>&1
+then
+    failed=1
+    echo "serious problem with scram authentication, password not verified by pgpool"
+else
+    echo "wrong password rejected properly for scram auth"
+fi
+
+if $PSQL -h 127.0.0.1 -U md5_user -c "SELECT user, inet_client_addr();" test|grep "md5_user" >/dev/null 2>&1
+then
+    failed=1
+    echo "serious problem with md5 authentication, password not verified by pgpool"
+else
+    echo "wrong password rejected properly for md5 auth"
+fi
+
+./shutdownall
+if [ -z "$failed" ]; then
+    exit 0
+fi
+
+exit 1
diff --git a/src/test/regression/tests/021.pool_passwd_auth/pool_hba.conf b/src/test/regression/tests/021.pool_passwd_auth/pool_hba.conf
new file mode 100644 (file)
index 0000000..1b2e47e
--- /dev/null
@@ -0,0 +1,71 @@
+# pgpool Client Authentication Configuration File
+# ===============================================
+#
+# The format rule in this file follows the rules in the PostgreSQL
+# Administrator's Guide. Refer to chapter "Client Authentication" for a
+# complete description.  A short synopsis follows.
+#
+# This file controls: which hosts are allowed to connect, how clients
+# are authenticated, which user names they can use, which databases they
+# can access.  Records take one of these forms:
+#
+# local      DATABASE  USER  METHOD  [OPTION]
+# host       DATABASE  USER  CIDR-ADDRESS  METHOD  [OPTION]
+#
+# (The uppercase items must be replaced by actual values.)
+#
+# The first field is the connection type: "local" is a Unix-domain
+# socket, "host" is either a plain or SSL-encrypted TCP/IP socket.
+#
+# DATABASE can be "all", "sameuser", a database name, or a comma-separated
+# list thereof. Note that "samegroup" like in PostgreSQL's pg_hba.conf
+# file is not supported, since pgpool does not know which group a user
+# belongs to. Also note that the database specified here may not exist in
+# the backend PostgreSQL. pgpool will authenticate based on the database's
+# name, not based on whether it exists or not.
+#
+# USER can be "all", a user name, or a comma-separated list thereof.  In
+# both the DATABASE and USER fields you can also write a file name prefixed
+# with "@" to include names from a separate file. Note that a group name
+# prefixed with "+" like in PostgreSQL's pg_hba.conf file is not supported
+# because of the same reason as "samegroup" token. Also note that a user
+# name specified here may not exist in the backend PostgreSQL. pgpool will
+# authenticate based on the user's name, not based on whether he/she exists.
+#
+# CIDR-ADDRESS specifies the set of hosts the record matches.
+# It is made up of an IP address and a CIDR mask that is an integer
+# (between 0 and 32 (IPv4) that specifies 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" , "scram-sha-256" or "pam".
+# Note that "pam" sends passwords in clear text.
+#
+# OPTION is the name of the PAM service. Default service name is "pgpool"
+#
+# Database and user names containing spaces, commas, quotes and other special
+# characters must be quoted. Quoting one of the keywords "all" or "sameuser"
+# makes the name lose its special character, and just match a database or
+# username with that name.
+#
+# This file is read on pgpool startup.  If you edit the file on a running
+# system, you have to restart the pgpool  for the changes to take effect.
+
+# Put your actual configuration here
+# ----------------------------------
+#
+# If you want to allow non-local connections, you need to add more
+# "host" records. In that case you will also need to make pgpool listen
+# on a non-local interface via the listen_addresses configuration parameter.
+#
+
+# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
+
+# "local" is for Unix domain socket connections only
+local   all         all                               trust
+# IPv4 local connections:
+host    all         scram_user         127.0.0.1/32        scram-sha-256
+host    all         scram_user         ::1/128             scram-sha-256
+
+host    all         md5_user         127.0.0.1/32          md5
+host    all         md5_user         ::1/128               md5
diff --git a/src/test/regression/tests/021.pool_passwd_auth/test.sh b/src/test/regression/tests/021.pool_passwd_auth/test.sh
new file mode 100755 (executable)
index 0000000..f786244
--- /dev/null
@@ -0,0 +1,111 @@
+#!/usr/bin/env bash
+#-------------------------------------------------------------------
+# test script for scram and md5 authentication
+# where password is stored in pool_passwd file
+#
+source $TESTLIBS
+TESTDIR=testdir
+PSQL=$PGBIN/psql
+CREATEUSER=$PGBIN/createuser
+
+
+rm -fr $TESTDIR
+mkdir $TESTDIR
+cd $TESTDIR
+
+# create test environment
+echo -n "creating test environment..."
+$PGPOOL_SETUP -m s -n 2 || exit 1
+echo "done."
+
+source ./bashrc.ports
+
+#echo "enable_pool_hba = on" >> etc/pgpool.conf
+echo "allow_clear_text_frontend_auth = off" >> etc/pgpool.conf
+#set max_init_children to 1 to make sure we reuse the
+#connection to test wrong password rejection
+echo "num_init_children = 1" >> etc/pgpool.conf
+echo "enable_pool_hba = on" >> etc/pgpool.conf
+
+#create pool_passwd file
+echo "scram_user:scram_password" >> etc/pool_passwd
+echo "md5_user:md5_password" >> etc/pool_passwd
+#copy the pool_hba to etc dir
+cp ../pool_hba.conf etc/
+./startall
+
+export PGPORT=$PGPOOL_PORT
+wait_for_pgpool_startup
+
+#set passwords
+$PSQL -c "SET password_encryption = 'scram-sha-256'; CREATE ROLE scram_user PASSWORD 'scram_password'; ALTER ROLE scram_user WITH LOGIN;" test
+$PSQL -c "SET password_encryption = 'md5'; CREATE ROLE md5_user PASSWORD 'md5_password'; ALTER ROLE md5_user WITH LOGIN" test
+$PSQL -c "CREATE ROLE trust_user PASSWORD 'trust_password';ALTER ROLE trust_user WITH LOGIN" test
+
+
+if $PSQL -c "SELECT rolname,substring(rolpassword, 1, 14) FROM pg_authid where rolname ='scram_user'" test|grep "SCRAM-SHA-256" >/dev/null 2>&1
+then
+    echo "scram_user authenticated setup successfuly"
+else
+    echo "scram authentication setup failed"
+    failed=1
+fi
+if $PSQL -c "SELECT rolname,substring(rolpassword, 1, 4) FROM pg_authid where rolname ='md5_user'" test|grep "md5" >/dev/null 2>&1
+then
+    echo "md5 authenticated setup successfuly"
+else
+    echo "md5 authentication setup failed"
+    failed=1
+fi
+#create pgpass file
+echo "127.0.0.1:$PGPORT:test:scram_user:scram_password" >> pgpass
+echo "127.0.0.1:$PGPORT:test:md5_user:md5_password" >> pgpass
+chmod 0600 pgpass
+echo "127.0.0.1:$PGPORT:test:scram_user:scram_wrong_password" >> pgpasswrong
+echo "127.0.0.1:$PGPORT:test:md5_user:md5_wrong_password" >> pgpasswrong
+chmod 0600 pgpasswrong
+
+
+export PGPASSFILE=$PWD/pgpass
+
+if $PSQL -h 127.0.0.1 -U scram_user -c "SELECT user, inet_client_addr();" test|grep "scram_user" >/dev/null 2>&1
+then
+    echo "scram_user authenticated successfuly"
+else
+    echo "scram authentication failed"
+    failed=1
+fi
+
+if $PSQL -h 127.0.0.1 -U md5_user -c "SELECT user, inet_client_addr();" test|grep "md5_user" >/dev/null 2>&1
+then
+    echo "md5_user authenticated successfuly"
+else
+    echo "md5 authentication failed"
+    failed=1
+fi
+#now try using the wrong passwords1:
+export PGPASSFILE=$PWD/pgpasswrong
+echo "Trying wrong passwords now"
+#try connect using scram_user and md5 user
+if $PSQL -h 127.0.0.1 -U scram_user -c "SELECT user, inet_client_addr();" test|grep "scram_user" >/dev/null 2>&1
+then
+    failed=1
+    echo "serious problem with scram authentication, password not verified by pgpool"
+else
+    echo "wrong password rejected properly for scram auth"
+fi
+
+if $PSQL -h 127.0.0.1 -U md5_user -c "SELECT user, inet_client_addr();" test|grep "md5_user" >/dev/null 2>&1
+then
+    failed=1
+    echo "serious problem with md5 authentication, password not verified by pgpool"
+else
+    echo "wrong password rejected properly for md5 auth"
+fi
+
+./shutdownall
+if [ -z "$failed" ]; then
+    exit 0
+fi
+
+exit 1
diff --git a/src/test/regression/tests/022.pool_passwd_alternative_auth/pool_hba.conf b/src/test/regression/tests/022.pool_passwd_alternative_auth/pool_hba.conf
new file mode 100644 (file)
index 0000000..9a3ab38
--- /dev/null
@@ -0,0 +1,71 @@
+# pgpool Client Authentication Configuration File
+# ===============================================
+#
+# The format rule in this file follows the rules in the PostgreSQL
+# Administrator's Guide. Refer to chapter "Client Authentication" for a
+# complete description.  A short synopsis follows.
+#
+# This file controls: which hosts are allowed to connect, how clients
+# are authenticated, which user names they can use, which databases they
+# can access.  Records take one of these forms:
+#
+# local      DATABASE  USER  METHOD  [OPTION]
+# host       DATABASE  USER  CIDR-ADDRESS  METHOD  [OPTION]
+#
+# (The uppercase items must be replaced by actual values.)
+#
+# The first field is the connection type: "local" is a Unix-domain
+# socket, "host" is either a plain or SSL-encrypted TCP/IP socket.
+#
+# DATABASE can be "all", "sameuser", a database name, or a comma-separated
+# list thereof. Note that "samegroup" like in PostgreSQL's pg_hba.conf
+# file is not supported, since pgpool does not know which group a user
+# belongs to. Also note that the database specified here may not exist in
+# the backend PostgreSQL. pgpool will authenticate based on the database's
+# name, not based on whether it exists or not.
+#
+# USER can be "all", a user name, or a comma-separated list thereof.  In
+# both the DATABASE and USER fields you can also write a file name prefixed
+# with "@" to include names from a separate file. Note that a group name
+# prefixed with "+" like in PostgreSQL's pg_hba.conf file is not supported
+# because of the same reason as "samegroup" token. Also note that a user
+# name specified here may not exist in the backend PostgreSQL. pgpool will
+# authenticate based on the user's name, not based on whether he/she exists.
+#
+# CIDR-ADDRESS specifies the set of hosts the record matches.
+# It is made up of an IP address and a CIDR mask that is an integer
+# (between 0 and 32 (IPv4) that specifies 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" , "scram-sha-256" or "pam".
+# Note that "pam" sends passwords in clear text.
+#
+# OPTION is the name of the PAM service. Default service name is "pgpool"
+#
+# Database and user names containing spaces, commas, quotes and other special
+# characters must be quoted. Quoting one of the keywords "all" or "sameuser"
+# makes the name lose its special character, and just match a database or
+# username with that name.
+#
+# This file is read on pgpool startup.  If you edit the file on a running
+# system, you have to restart the pgpool  for the changes to take effect.
+
+# Put your actual configuration here
+# ----------------------------------
+#
+# If you want to allow non-local connections, you need to add more
+# "host" records. In that case you will also need to make pgpool listen
+# on a non-local interface via the listen_addresses configuration parameter.
+#
+
+# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
+
+# "local" is for Unix domain socket connections only
+local   all         all                               trust
+# IPv4 local connections:
+host    all         md5_user         127.0.0.1/32        scram-sha-256
+host    all         md5_user         ::1/128             scram-sha-256
+
+host    all         scram_user         127.0.0.1/32          md5
+host    all         scram_user         ::1/128               md5
diff --git a/src/test/regression/tests/022.pool_passwd_alternative_auth/test.sh b/src/test/regression/tests/022.pool_passwd_alternative_auth/test.sh
new file mode 100755 (executable)
index 0000000..ed8bc22
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/env bash
+#-------------------------------------------------------------------
+# test script for scram and md5 authentication
+# where password is stored in pool_passwd file and
+# frontend uses different auth from backends
+#
+source $TESTLIBS
+TESTDIR=testdir
+PSQL=$PGBIN/psql
+CREATEUSER=$PGBIN/createuser
+
+
+rm -fr $TESTDIR
+mkdir $TESTDIR
+cd $TESTDIR
+
+# create test environment
+echo -n "creating test environment..."
+$PGPOOL_SETUP -m s -n 2 || exit 1
+echo "done."
+
+source ./bashrc.ports
+
+#echo "enable_pool_hba = on" >> etc/pgpool.conf
+echo "allow_clear_text_frontend_auth = off" >> etc/pgpool.conf
+#set max_init_children to 1 to make sure we reuse the
+#connection to test wrong password rejection
+echo "num_init_children = 1" >> etc/pgpool.conf
+echo "enable_pool_hba = on" >> etc/pgpool.conf
+
+#create pool_passwd file
+echo "scram_user:scram_password" >> etc/pool_passwd
+echo "md5_user:md5_password" >> etc/pool_passwd
+#copy the pool_hba to etc dir
+cp ../pool_hba.conf etc/
+./startall
+
+export PGPORT=$PGPOOL_PORT
+wait_for_pgpool_startup
+
+#set passwords
+$PSQL -c "SET password_encryption = 'scram-sha-256'; CREATE ROLE scram_user PASSWORD 'scram_password'; ALTER ROLE scram_user WITH LOGIN;" test
+$PSQL -c "SET password_encryption = 'md5'; CREATE ROLE md5_user PASSWORD 'md5_password'; ALTER ROLE md5_user WITH LOGIN" test
+$PSQL -c "CREATE ROLE trust_user PASSWORD 'trust_password';ALTER ROLE trust_user WITH LOGIN" test
+
+
+if $PSQL -c "SELECT rolname,substring(rolpassword, 1, 14) FROM pg_authid where rolname ='scram_user'" test|grep "SCRAM-SHA-256" >/dev/null 2>&1
+then
+    echo "scram_user authenticated setup successfuly"
+else
+    echo "scram authentication setup failed"
+    failed=1
+fi
+if $PSQL -c "SELECT rolname,substring(rolpassword, 1, 4) FROM pg_authid where rolname ='md5_user'" test|grep "md5" >/dev/null 2>&1
+then
+    echo "md5 authenticated setup successfuly"
+else
+    echo "md5 authentication setup failed"
+    failed=1
+fi
+#create pgpass file
+echo "127.0.0.1:$PGPORT:test:scram_user:scram_password" >> pgpass
+echo "127.0.0.1:$PGPORT:test:md5_user:md5_password" >> pgpass
+chmod 0600 pgpass
+echo "127.0.0.1:$PGPORT:test:scram_user:scram_wrong_password" >> pgpasswrong
+echo "127.0.0.1:$PGPORT:test:md5_user:md5_wrong_password" >> pgpasswrong
+chmod 0600 pgpasswrong
+
+
+export PGPASSFILE=$PWD/pgpass
+
+if $PSQL -h 127.0.0.1 -U scram_user -c "SELECT user, inet_client_addr();" test|grep "scram_user" >/dev/null 2>&1
+then
+    echo "scram_user authenticated successfuly"
+else
+    echo "scram authentication failed"
+    failed=1
+fi
+
+if $PSQL -h 127.0.0.1 -U md5_user -c "SELECT user, inet_client_addr();" test|grep "md5_user" >/dev/null 2>&1
+then
+    echo "md5_user authenticated successfuly"
+else
+    echo "md5 authentication failed"
+    failed=1
+fi
+#now try using the wrong passwords1:
+export PGPASSFILE=$PWD/pgpasswrong
+echo "Trying wrong passwords now"
+#try connect using scram_user and md5 user
+if $PSQL -h 127.0.0.1 -U scram_user -c "SELECT user, inet_client_addr();" test|grep "scram_user" >/dev/null 2>&1
+then
+    failed=1
+    echo "serious problem with scram authentication, password not verified by pgpool"
+else
+    echo "wrong password rejected properly for scram auth"
+fi
+
+if $PSQL -h 127.0.0.1 -U md5_user -c "SELECT user, inet_client_addr();" test|grep "md5_user" >/dev/null 2>&1
+then
+    failed=1
+    echo "serious problem with md5 authentication, password not verified by pgpool"
+else
+    echo "wrong password rejected properly for md5 auth"
+fi
+
+./shutdownall
+if [ -z "$failed" ]; then
+    exit 0
+fi
+
+exit 1
index cb31c42f7b23e278a5e8c0872ae9dd9e568b5e11..7f446c87e502fc5f3e6b76c813c19c2d50c55cff 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = pcp pgmd5
+SUBDIRS = pcp pgmd5 pgenc
 
 dist_bin_SCRIPTS =  pgpool_setup watchdog_setup
 
index bebf92ecbb36ffb0ab8a7c903b9944b89cb951ae..ea12591d607374e4674b65103c563788b2741445 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -89,6 +79,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/tools
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs $(dist_bin_SCRIPTS)
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -99,8 +91,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(dist_bin_SCRIPTS) \
-       $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -191,7 +181,6 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
   dir0=`pwd`; \
@@ -346,7 +335,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -355,7 +343,7 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-SUBDIRS = pcp pgmd5
+SUBDIRS = pcp pgmd5 pgenc
 dist_bin_SCRIPTS = pgpool_setup watchdog_setup
 all: all-recursive
 
@@ -372,6 +360,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/tools/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/tools/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -706,8 +695,6 @@ uninstall-am: uninstall-dist_binSCRIPTS
        ps ps-am tags tags-am uninstall uninstall-am \
        uninstall-dist_binSCRIPTS
 
-.PRECIOUS: Makefile
-
 
 pgpool_setup:
        ln -s ../test/pgpool_setup .
index 9dca9fafb461eabe5337a19b69a30e5e302aab28..3a5ce59b851ac5e4aefac7b44361a9633da2ea80 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -95,6 +85,8 @@ bin_PROGRAMS = pcp_stop_pgpool$(EXEEXT) pcp_node_count$(EXEEXT) \
        pcp_promote_node$(EXEEXT) pcp_pool_status$(EXEEXT) \
        pcp_watchdog_info$(EXEEXT)
 subdir = src/tools/pcp
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -105,7 +97,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -221,7 +212,6 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -351,7 +341,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -401,6 +390,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/tools/pcp/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/tools/pcp/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -749,8 +739,6 @@ uninstall-am: uninstall-binPROGRAMS
        mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
        tags tags-am uninstall uninstall-am uninstall-binPROGRAMS
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/tools/pgenc/Makefile.am b/src/tools/pgenc/Makefile.am
new file mode 100644 (file)
index 0000000..b04fb4d
--- /dev/null
@@ -0,0 +1,54 @@
+AM_CPPFLAGS = -D_GNU_SOURCE -DPOOL_PRIVATE -I @PGSQL_INCLUDE_DIR@
+bin_PROGRAMS = pg_enc
+
+dist_pg_enc_SOURCES = pg_enc.c \
+               ../fe_port.c
+nodist_pg_enc_SOURCES = ssl_utils.c \
+               md5.c \
+               base64.c \
+               pool_passwd.c \
+               pool_signal.c \
+               strlcpy.c \
+               regex_array.c \
+               pool_config_variables.c \
+               pool_config.c \
+               fe_memutils.c \
+               pool_path.c \
+               pool_globals.c
+
+DEFS = @DEFS@ \
+    -DDEFAULT_CONFIGDIR=\"$(sysconfdir)\" -DPOOL_TOOLS
+
+pool_passwd.c: ../../../src/auth/pool_passwd.c
+       rm -f $@ && ln -s $< .
+pool_path.c: ../../../src/utils/pool_path.c
+       rm -f $@ && ln -s $< .
+md5.c: ../../../src/auth/md5.c
+       rm -f $@ && ln -s $< .
+md5.h: ../../../src/include/auth/md5.h
+       rm -f $@ && ln -s $< .
+base64.c: ../../../src/utils/base64.c
+       rm -f $@ && ln -s $< .
+ssl_utils.c: ../../../src/utils/ssl_utils.c
+       rm -f $@ && ln -s $< .
+base64.h: ../../../src/include/utils/base64.h
+       rm -f $@ && ln -s $< .
+ssl_utils.h: ../../../src/include/utils/ssl_utils.h
+       rm -f $@ && ln -s $< .
+pool_signal.c: ../../../src/utils/pool_signal.c
+       rm -f $@ && ln -s $< .
+strlcpy.c: ../../../src/utils/strlcpy.c
+       rm -f $@ && ln -s $< .
+regex_array.c: ../../../src/utils/regex_array.c
+       rm -f $@ && ln -s $< .
+pool_config_variables.c: ../../../src/config/pool_config_variables.c
+       rm -f $@ && ln -s $< .
+pool_config.c: ../../../src/config/pool_config.c
+       rm -f $@ && ln -s $< .
+fe_memutils.c: ../../../src/tools/fe_memutils.c
+       rm -f $@ && ln -s $< .
+pool_globals.c: ../../../src/main/pool_globals.c
+       rm -f $@ && ln -s $< .
+
+clean-local:
+       -rm -f $(nodist_pg_enc_SOURCES)
diff --git a/src/tools/pgenc/Makefile.in b/src/tools/pgenc/Makefile.in
new file mode 100644 (file)
index 0000000..e71fdbb
--- /dev/null
@@ -0,0 +1,687 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \  ]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs  ]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = pg_enc$(EXEEXT)
+subdir = src/tools/pgenc
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
+       $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+       $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+       $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/c-compiler.m4 \
+       $(top_srcdir)/c-library.m4 $(top_srcdir)/general.m4 \
+       $(top_srcdir)/ac_func_accept_argtypes.m4 \
+       $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/src/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am__dirstamp = $(am__leading_dot)dirstamp
+dist_pg_enc_OBJECTS = pg_enc.$(OBJEXT) ../fe_port.$(OBJEXT)
+nodist_pg_enc_OBJECTS = ssl_utils.$(OBJEXT) md5.$(OBJEXT) \
+       base64.$(OBJEXT) pool_passwd.$(OBJEXT) pool_signal.$(OBJEXT) \
+       strlcpy.$(OBJEXT) regex_array.$(OBJEXT) \
+       pool_config_variables.$(OBJEXT) pool_config.$(OBJEXT) \
+       fe_memutils.$(OBJEXT) pool_path.$(OBJEXT) \
+       pool_globals.$(OBJEXT)
+pg_enc_OBJECTS = $(dist_pg_enc_OBJECTS) $(nodist_pg_enc_OBJECTS)
+pg_enc_LDADD = $(LDADD)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src/include
+depcomp =
+am__depfiles_maybe =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+       $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(dist_pg_enc_SOURCES) $(nodist_pg_enc_SOURCES)
+DIST_SOURCES = $(dist_pg_enc_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOG = @CATALOG@
+CC = @CC@
+CFLAGS = @CFLAGS@
+COLLATEINDEX = @COLLATEINDEX@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@ \
+    -DDEFAULT_CONFIGDIR=\"$(sysconfdir)\" -DPOOL_TOOLS
+
+DLLTOOL = @DLLTOOL@
+DOCBOOKSTYLE = @DOCBOOKSTYLE@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JADE = @JADE@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LYNX = @LYNX@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MEMCACHED_DIR = @MEMCACHED_DIR@
+MEMCACHED_INCLUDE_OPT = @MEMCACHED_INCLUDE_OPT@
+MEMCACHED_LINK_OPT = @MEMCACHED_LINK_OPT@
+MEMCACHED_RPATH_OPT = @MEMCACHED_RPATH_OPT@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSGMLS = @NSGMLS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OSX = @OSX@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PGCONFIG = @PGCONFIG@
+PGSQL_INCLUDE_DIR = @PGSQL_INCLUDE_DIR@
+PGSQL_LIB_DIR = @PGSQL_LIB_DIR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+STYLE = @STYLE@
+VERSION = @VERSION@
+XMLLINT = @XMLLINT@
+XSLTPROC = @XSLTPROC@
+XSLTPROC_HTML_FLAGS = @XSLTPROC_HTML_FLAGS@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__leading_dot = @am__leading_dot@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+have_docbook = @have_docbook@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -D_GNU_SOURCE -DPOOL_PRIVATE -I @PGSQL_INCLUDE_DIR@
+dist_pg_enc_SOURCES = pg_enc.c \
+               ../fe_port.c
+
+nodist_pg_enc_SOURCES = ssl_utils.c \
+               md5.c \
+               base64.c \
+               pool_passwd.c \
+               pool_signal.c \
+               strlcpy.c \
+               regex_array.c \
+               pool_config_variables.c \
+               pool_config.c \
+               fe_memutils.c \
+               pool_path.c \
+               pool_globals.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+               && { if test -f $@; then exit 0; else break; fi; }; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/tools/pgenc/Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --foreign --ignore-deps src/tools/pgenc/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+       if test -n "$$list"; then \
+         echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+         $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+       fi; \
+       for p in $$list; do echo "$$p $$p"; done | \
+       sed 's/$(EXEEXT)$$//' | \
+       while read p p1; do if test -f $$p \
+        || test -f $$p1 \
+         ; then echo "$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n;h' \
+           -e 's|.*|.|' \
+           -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+       sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) files[d] = files[d] " " $$1; \
+           else { print "f", $$3 "/" $$4, $$1; } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+           if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+           test -z "$$files" || { \
+           echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+           $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+           } \
+       ; done
+
+uninstall-binPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+       files=`for p in $$list; do echo "$$p"; done | \
+         sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+             -e 's/$$/$(EXEEXT)/' \
+       `; \
+       test -n "$$list" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+       @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+../$(am__dirstamp):
+       @$(MKDIR_P) ..
+       @: > ../$(am__dirstamp)
+../fe_port.$(OBJEXT): ../$(am__dirstamp)
+
+pg_enc$(EXEEXT): $(pg_enc_OBJECTS) $(pg_enc_DEPENDENCIES) $(EXTRA_pg_enc_DEPENDENCIES) 
+       @rm -f pg_enc$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(pg_enc_OBJECTS) $(pg_enc_LDADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+       -rm -f ../*.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+.c.o:
+       $(AM_V_CC)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+       $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+       $(AM_V_CC)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+       $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+       set x; \
+       here=`pwd`; \
+       $(am__define_uniq_tagged_files); \
+       shift; \
+       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         if test $$# -gt 0; then \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             "$$@" $$unique; \
+         else \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             $$unique; \
+         fi; \
+       fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+       $(am__define_uniq_tagged_files); \
+       test -z "$(CTAGS_ARGS)$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && $(am__cd) $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+       list='$(am__tagged_files)'; \
+       case "$(srcdir)" in \
+         [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+         *) sdir=$(subdir)/$(srcdir) ;; \
+       esac; \
+       for i in $$list; do \
+         if test -f "$$i"; then \
+           echo "$(subdir)/$$i"; \
+         else \
+           echo "$$sdir/$$i"; \
+         fi; \
+       done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d "$(distdir)/$$file"; then \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+         else \
+           test -f "$(distdir)/$$file" \
+           || cp -p $$d/$$file "$(distdir)/$$file" \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+       for dir in "$(DESTDIR)$(bindir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       if test -z '$(STRIP)'; then \
+         $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+           install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+             install; \
+       else \
+         $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+           install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+           "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+       fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+       -rm -f ../$(am__dirstamp)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool clean-local \
+       mostlyclean-am
+
+distclean: distclean-am
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+       clean-binPROGRAMS clean-generic clean-libtool clean-local \
+       cscopelist-am ctags ctags-am distclean distclean-compile \
+       distclean-generic distclean-libtool distclean-tags distdir dvi \
+       dvi-am html html-am info info-am install install-am \
+       install-binPROGRAMS install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am install-man \
+       install-pdf install-pdf-am install-ps install-ps-am \
+       install-strip installcheck installcheck-am installdirs \
+       maintainer-clean maintainer-clean-generic mostlyclean \
+       mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+       pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+       uninstall-binPROGRAMS
+
+
+pool_passwd.c: ../../../src/auth/pool_passwd.c
+       rm -f $@ && ln -s $< .
+pool_path.c: ../../../src/utils/pool_path.c
+       rm -f $@ && ln -s $< .
+md5.c: ../../../src/auth/md5.c
+       rm -f $@ && ln -s $< .
+md5.h: ../../../src/include/auth/md5.h
+       rm -f $@ && ln -s $< .
+base64.c: ../../../src/utils/base64.c
+       rm -f $@ && ln -s $< .
+ssl_utils.c: ../../../src/utils/ssl_utils.c
+       rm -f $@ && ln -s $< .
+base64.h: ../../../src/include/utils/base64.h
+       rm -f $@ && ln -s $< .
+ssl_utils.h: ../../../src/include/utils/ssl_utils.h
+       rm -f $@ && ln -s $< .
+pool_signal.c: ../../../src/utils/pool_signal.c
+       rm -f $@ && ln -s $< .
+strlcpy.c: ../../../src/utils/strlcpy.c
+       rm -f $@ && ln -s $< .
+regex_array.c: ../../../src/utils/regex_array.c
+       rm -f $@ && ln -s $< .
+pool_config_variables.c: ../../../src/config/pool_config_variables.c
+       rm -f $@ && ln -s $< .
+pool_config.c: ../../../src/config/pool_config.c
+       rm -f $@ && ln -s $< .
+fe_memutils.c: ../../../src/tools/fe_memutils.c
+       rm -f $@ && ln -s $< .
+pool_globals.c: ../../../src/main/pool_globals.c
+       rm -f $@ && ln -s $< .
+
+clean-local:
+       -rm -f $(nodist_pg_enc_SOURCES)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/tools/pgenc/pg_enc.c b/src/tools/pgenc/pg_enc.c
new file mode 100644 (file)
index 0000000..a9c4cd9
--- /dev/null
@@ -0,0 +1,449 @@
+/* -*-pgsql-c-*- */
+/*
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2018     PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * pg_enc command main
+ *
+ */
+#include "pool.h"
+#include "pool_config.h"
+#include "auth/pool_passwd.h"
+#include "utils/ssl_utils.h"
+#include "utils/pool_path.h"
+#include "utils/base64.h"
+#include "version.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <termios.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+#include "utils/getopt_long.h"
+#endif
+#include <pwd.h>
+#include <libgen.h>
+
+#define MAX_ENCODED_PASSWD_LEN (MAX_POOL_KEY_LEN * 2)
+
+static void    print_usage(const char prog[], int exit_code);
+static void    set_tio_attr(int enable);
+static void update_pool_passwd(char *conf_file, char *username, char *password, char *key);
+static bool get_pool_key_filename(char *poolKeyFile);
+
+int
+main(int argc, char *argv[])
+{
+#define PRINT_USAGE(exit_code) print_usage(argv[0], exit_code)
+
+       char conf_file[POOLMAXPATHLEN+1];
+       char enc_key[MAX_POOL_KEY_LEN+1];
+       char pg_pass[MAX_PGPASS_LEN+1];
+       char username[MAX_USER_NAME_LEN+1];
+       char key_file_path[POOLMAXPATHLEN];
+       int opt;
+       int optindex;
+       bool updatepasswd = false;
+       bool prompt = false;
+       bool prompt_for_key = false;
+       char *pool_key = NULL;
+
+       static struct option long_options[] = {
+               {"help", no_argument, NULL, 'h'},
+               {"prompt", no_argument, NULL, 'p'},
+               {"prompt-for-key", no_argument, NULL, 'P'},
+               {"update-pass", no_argument, NULL, 'm'},
+               {"username", required_argument, NULL, 'u'},
+               {"enc-key", required_argument, NULL, 'K'},
+               {"key-file", required_argument, NULL, 'k'},
+               {"config-file", required_argument, NULL, 'f'},
+               {NULL, 0, NULL, 0}
+       };
+
+       snprintf(conf_file, sizeof(conf_file), "%s/%s", DEFAULT_CONFIGDIR, POOL_CONF_FILE_NAME);
+
+       /* initialize username buffer with zeros so that we can use strlen on it later
+          to check if a username was given on the command line
+        */
+       memset(username, 0, sizeof(username));
+       memset(enc_key, 0, sizeof(enc_key));
+       memset(key_file_path, 0, sizeof(key_file_path));
+
+       while ((opt = getopt_long(argc, argv, "hPpmf:u:k:K:", long_options, &optindex)) != -1)
+       {
+               switch (opt)
+               {
+                       case 'p':    /* prompt for postgres password */
+                               prompt = true;
+                               break;
+
+                       case 'P':    /* prompt for encryption key */
+                               prompt_for_key = true;
+                               break;
+
+                       case 'm':       /* update password file */
+                               updatepasswd = true;
+                               break;
+
+                       case 'f':       /* specify configuration file */
+                               if (!optarg)
+                               {
+                                       PRINT_USAGE(EXIT_SUCCESS);
+                               }
+                               strlcpy(conf_file, optarg, sizeof(conf_file));
+                               break;
+
+                       case 'k':       /* specify key file for encrypting pool_password entries */
+                               if (!optarg)
+                               {
+                                       PRINT_USAGE(EXIT_SUCCESS);
+                               }
+                               strlcpy(key_file_path, optarg, sizeof(key_file_path));
+                               break;
+
+                       case 'K':       /* specify configuration file */
+                               if (!optarg)
+                               {
+                                       PRINT_USAGE(EXIT_SUCCESS);
+                               }
+                               strlcpy(enc_key, optarg, sizeof(enc_key));
+                               break;
+
+                       case 'u':
+                               if (!optarg)
+                               {
+                                       PRINT_USAGE(EXIT_SUCCESS);
+                               }
+                               /* check the input limit early */
+                               if (strlen(optarg) > sizeof(username))
+                               {
+                                       fprintf(stderr, "Error: input exceeds maximum username length!\n\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                               strlcpy(username, optarg, sizeof(username));
+                               break;
+
+                       default:
+                               PRINT_USAGE(EXIT_SUCCESS);
+                               break;
+               }
+       }
+
+       /* Prompt for password. */
+       if (prompt || optind >= argc)
+       {
+               char     buf[MAX_PGPASS_LEN];
+               int              len;
+
+               set_tio_attr(1);
+               printf("db password: ");
+               if (!fgets(buf, sizeof(buf), stdin))
+               {
+                       int eno = errno;
+
+                       fprintf(stderr, "Couldn't read input from stdin. (fgets(): %s)",
+                                       strerror(eno));
+
+                       exit(EXIT_FAILURE);
+               }
+               printf("\n");
+               set_tio_attr(0);
+
+               /* Remove LF at the end of line, if there is any. */
+               len = strlen(buf);
+               if (len > 0 && buf[len-1] == '\n')
+               {
+                       buf[len-1] = '\0';
+                       len--;
+               }
+               stpncpy(pg_pass,buf,sizeof(pg_pass));
+       }
+
+       /* Read password from argv. */
+       else
+       {
+               int             len;
+
+               len = strlen(argv[optind]);
+
+               if (len > MAX_PGPASS_LEN)
+               {
+                       fprintf(stderr, "Error: Input exceeds maximum password length given:%d max allowed:%d!\n\n",len,MAX_PGPASS_LEN);
+                       PRINT_USAGE(EXIT_FAILURE);
+               }
+
+               stpncpy(pg_pass,argv[optind],sizeof(pg_pass));
+       }
+       /* prompt for key, overrides all key related arguments */
+       if (prompt_for_key)
+       {
+               char    buf[MAX_POOL_KEY_LEN];
+               int     len;
+               /* we need to read the encryption key from stdin */
+               set_tio_attr(1);
+               printf("encryption key: ");
+               if (!fgets(buf, sizeof(buf), stdin))
+               {
+                       int eno = errno;
+
+                       fprintf(stderr, "Couldn't read input from stdin. (fgets(): %s)",
+                                       strerror(eno));
+
+                       exit(EXIT_FAILURE);
+               }
+               printf("\n");
+               set_tio_attr(0);
+               /* Remove LF at the end of line, if there is any. */
+               len = strlen(buf);
+               if (len > 0 && buf[len-1] == '\n')
+               {
+                       buf[len-1] = '\0';
+                       len--;
+               }
+               if (len == 0)
+               {
+                       fprintf(stderr, "encryption key not provided\n");
+                       exit(EXIT_FAILURE);
+               }
+               stpncpy(enc_key,buf,sizeof(enc_key));
+       }
+       else
+       {
+               /* check if we already have not got the key from command line argument */
+               if (strlen(enc_key) == 0)
+               {
+                       /* read from file */
+                       if (strlen(key_file_path) == 0)
+                       {
+                               get_pool_key_filename(key_file_path);
+                       }
+
+                       fprintf(stdout, "trying to read key from file %s\n",key_file_path);
+
+                       pool_key = read_pool_key(key_file_path);
+               }
+               else
+               {
+                       pool_key = enc_key;
+               }
+       }
+
+       if (pool_key == NULL)
+       {
+               fprintf(stderr, "encryption key not provided\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (updatepasswd)
+       {
+               update_pool_passwd(conf_file, username, pg_pass, pool_key);
+       }
+       else
+       {
+               unsigned char ciphertext[MAX_ENCODED_PASSWD_LEN];
+               unsigned char b64_enc[MAX_ENCODED_PASSWD_LEN];
+               int len;
+               int cypher_len;
+
+               cypher_len = aes_encrypt_with_password((unsigned char*)pg_pass,
+                                                                               strlen(pg_pass), pool_key, ciphertext);
+
+               /* generate the hash for the given username */
+               len = pg_b64_encode((const char*)ciphertext, cypher_len, (char*)b64_enc);
+               b64_enc[len] = 0;
+               fprintf(stdout,"\n%s\n",b64_enc);
+               fprintf(stdout,"pool_passwd string: AES%s\n",b64_enc);
+
+#ifdef DEBUG_ENCODING
+               unsigned char b64_dec[MAX_ENCODED_PASSWD_LEN];
+               unsigned char plaintext[MAX_PGPASS_LEN];
+               len = pg_b64_decode(b64_enc, len, b64_dec);
+               len = aes_decrypt_with_password(b64_dec, len,
+                                                                               pool_key, plaintext);
+               plaintext[len] = 0;
+#endif
+       }
+
+       if (pool_key != enc_key)
+               free(pool_key);
+
+       return EXIT_SUCCESS;
+}
+
+static void update_pool_passwd(char *conf_file, char *username, char *password, char *key)
+{
+       struct passwd *pw;
+       char pool_passwd[MAX_PGPASS_LEN+1];
+       char dirnamebuf[POOLMAXPATHLEN+1];
+       char *dirp;
+       char *user = username;
+
+       unsigned char ciphertext[MAX_ENCODED_PASSWD_LEN];
+       unsigned char b64_enc[MAX_ENCODED_PASSWD_LEN];
+       int len;
+
+       if (pool_init_config())
+       {
+               fprintf(stderr, "pool_init_config() failed\n\n");
+               exit(EXIT_FAILURE);
+       }
+       if (pool_get_config(conf_file, CFGCXT_RELOAD) == false)
+       {
+               fprintf(stderr, "Unable to get configuration. Exiting...\n\n");
+               exit(EXIT_FAILURE);
+       }
+
+       strlcpy(dirnamebuf, conf_file, sizeof(dirnamebuf));
+       dirp = dirname(dirnamebuf);
+       snprintf(pool_passwd, sizeof(pool_passwd), "%s/%s",
+                        dirp, pool_config->pool_passwd);
+       pool_init_pool_passwd(pool_passwd, POOL_PASSWD_RW);
+
+       if(username == NULL || strlen(username) == 0)
+       {
+               /* get the user information from the current uid */
+               pw = getpwuid(getuid());
+               if (!pw)
+               {
+                       fprintf(stderr, "getpwuid() failed\n\n");
+                       exit(EXIT_FAILURE);
+               }
+               user = pw->pw_name;
+       }
+
+       /* generate the hash for the given username */
+       int cypher_len = aes_encrypt_with_password((unsigned char*)password, strlen(password), key, ciphertext);
+       if (cypher_len <= 0 )
+       {
+               fprintf(stderr, "password encryption failed\n\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* copy the prefix at the start of string */
+       strcpy((char*)b64_enc, (char*)PASSWORD_AES_PREFIX);
+       len = pg_b64_encode((const char*)ciphertext, cypher_len, (char*)b64_enc + strlen(PASSWORD_AES_PREFIX));
+       if (cypher_len <= 0 )
+       {
+               fprintf(stderr, "base64 encoding failed\n\n");
+               exit(EXIT_FAILURE);
+       }
+       len+= strlen(PASSWORD_AES_PREFIX);
+       b64_enc[len] = 0;
+
+       pool_create_passwdent(user, (char*)b64_enc);
+       pool_finish_pool_passwd();
+}
+
+static void
+print_usage(const char prog[], int exit_code)
+{
+       char    homedir[POOLMAXPATHLEN];
+       FILE *stream = (exit_code == EXIT_SUCCESS) ? stdout : stderr;
+       if (!get_home_directory(homedir, sizeof(homedir)))
+               strncpy(homedir,"USER-HOME-DIR",POOLMAXPATHLEN);
+
+       fprintf(stream, "%s version %s (%s),\n",        PACKAGE, VERSION, PGPOOLVERSION);
+       fprintf(stream, "  password encryption utility for Pgpool\n\n");
+       fprintf(stream, "Usage:\n");
+       fprintf(stream, "  %s [OPTIONS] <PASSWORD>\n",prog);
+       fprintf(stream, "  -k, --key-file=KEY_FILE\n");
+       fprintf(stream, "                       Set the path to the encryption key file.\n");
+       fprintf(stream, "                       Default: %s/%s\n",homedir, POOLKEYFILE);
+       fprintf(stream, "                       Can be overridden by the %s environment variable.\n",POOLKEYFILEENV);
+       fprintf(stream, "  -K, --enc-key=ENCRYPTION_KEY\n");
+       fprintf(stream, "                       Encryption key to be used for encrypting database passwords.\n");
+       fprintf(stream, "  -f, --config-file=CONFIG_FILE\n");
+       fprintf(stream, "                       Specifies the pgpool.conf file.\n");
+       fprintf(stream, "  -p, --prompt         Prompt for database password using standard input.\n");
+       fprintf(stream, "  -P, --prompt-for-key Prompt for encryption key using standard input.\n");
+       fprintf(stream, "  -m, --update-pass    Create encrypted password entry in the pool_passwd file.\n");
+       fprintf(stream, "  -u, --username       The username for the pool_password entry.\n");
+       fprintf(stream, "  -h, --help           Print this help.\n\n");
+
+       exit(exit_code);
+}
+
+
+static void
+set_tio_attr(int set)
+{
+       struct termios tio;
+       static struct termios tio_save;
+
+
+       if (!isatty(0))
+       {
+               fprintf(stderr, "stdin is not tty\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (set)
+       {
+               if (tcgetattr(0, &tio) < 0)
+               {
+                       fprintf(stderr, "set_tio_attr(set): tcgetattr failed\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               tio_save = tio;
+
+               tio.c_iflag &= ~(BRKINT|ISTRIP|IXON);
+               tio.c_lflag &= ~(ICANON|IEXTEN|ECHO|ECHOE|ECHOK|ECHONL);
+               tio.c_cc[VMIN] = 1;
+               tio.c_cc[VTIME] = 0;
+
+               if (tcsetattr(0, TCSANOW, &tio) < 0)
+               {
+                       fprintf(stderr, "(set_tio_attr(set): tcsetattr failed\n");
+                       exit(EXIT_FAILURE);
+               }
+       }
+       else
+       {
+               if (tcsetattr(0, TCSANOW, &tio_save) < 0)
+               {
+                       fprintf(stderr, "set_tio_attr(reset): tcsetattr failed\n");
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+static bool
+get_pool_key_filename(char *poolKeyFile)
+{
+       char    *passfile_env;
+
+       if ((passfile_env = getenv(POOLKEYFILEENV)) != NULL)
+       {
+               /* use the literal path from the environment, if set */
+               strlcpy(poolKeyFile, passfile_env, POOLMAXPATHLEN);
+       }
+       else
+       {
+               char    homedir[POOLMAXPATHLEN];
+               if (!get_home_directory(homedir, sizeof(homedir)))
+                       return false;
+               snprintf(poolKeyFile, POOLMAXPATHLEN, "%s/%s", homedir, POOLKEYFILE);
+       }
+       return true;
+}
+
index 7b0363b9efb5a6c6d32628e7cc2f6ccbaaf1a72d..db6bd8a7b84fe687a81dd62f6abdc2cff4f27b40 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -90,6 +80,8 @@ build_triplet = @build@
 host_triplet = @host@
 bin_PROGRAMS = pg_md5$(EXEEXT)
 subdir = src/tools/pgmd5
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -100,7 +92,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -178,7 +169,6 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -310,7 +300,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -349,6 +338,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/tools/pgmd5/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/tools/pgmd5/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -652,8 +642,6 @@ uninstall-am: uninstall-binPROGRAMS
        pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
        uninstall-binPROGRAMS
 
-.PRECIOUS: Makefile
-
 
 md5.c: ../../../src/auth/md5.c
        rm -f $@ && ln -s $< .
deleted file mode 100644 (file)
index 278de7003e330113f2c98d81060993bb71968d9c..0000000000000000000000000000000000000000
+++ /dev/null
-
-#line 3 "pool_config.c"
-
-#define  YY_INT_ALIGNED short int
-
-/* A lexical scanner generated by flex */
-
-#define FLEX_SCANNER
-#define YY_FLEX_MAJOR_VERSION 2
-#define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 35
-#if YY_FLEX_SUBMINOR_VERSION > 0
-#define FLEX_BETA
-#endif
-
-/* First, we deal with  platform-specific or compiler-specific issues. */
-
-/* begin standard C headers. */
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-/* end standard C headers. */
-
-/* flex integer type definitions */
-
-#ifndef FLEXINT_H
-#define FLEXINT_H
-
-/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
-
-#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-
-/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
- * if you want the limit (max/min) macros for int types. 
- */
-#ifndef __STDC_LIMIT_MACROS
-#define __STDC_LIMIT_MACROS 1
-#endif
-
-#include <inttypes.h>
-typedef int8_t flex_int8_t;
-typedef uint8_t flex_uint8_t;
-typedef int16_t flex_int16_t;
-typedef uint16_t flex_uint16_t;
-typedef int32_t flex_int32_t;
-typedef uint32_t flex_uint32_t;
-typedef uint64_t flex_uint64_t;
-#else
-typedef signed char flex_int8_t;
-typedef short int flex_int16_t;
-typedef int flex_int32_t;
-typedef unsigned char flex_uint8_t; 
-typedef unsigned short int flex_uint16_t;
-typedef unsigned int flex_uint32_t;
-#endif /* ! C99 */
-
-/* Limits of integral types. */
-#ifndef INT8_MIN
-#define INT8_MIN               (-128)
-#endif
-#ifndef INT16_MIN
-#define INT16_MIN              (-32767-1)
-#endif
-#ifndef INT32_MIN
-#define INT32_MIN              (-2147483647-1)
-#endif
-#ifndef INT8_MAX
-#define INT8_MAX               (127)
-#endif
-#ifndef INT16_MAX
-#define INT16_MAX              (32767)
-#endif
-#ifndef INT32_MAX
-#define INT32_MAX              (2147483647)
-#endif
-#ifndef UINT8_MAX
-#define UINT8_MAX              (255U)
-#endif
-#ifndef UINT16_MAX
-#define UINT16_MAX             (65535U)
-#endif
-#ifndef UINT32_MAX
-#define UINT32_MAX             (4294967295U)
-#endif
-
-#endif /* ! FLEXINT_H */
-
-#ifdef __cplusplus
-
-/* The "const" storage-class-modifier is valid. */
-#define YY_USE_CONST
-
-#else  /* ! __cplusplus */
-
-/* C99 requires __STDC__ to be defined as 1. */
-#if defined (__STDC__)
-
-#define YY_USE_CONST
-
-#endif /* defined (__STDC__) */
-#endif /* ! __cplusplus */
-
-#ifdef YY_USE_CONST
-#define yyconst const
-#else
-#define yyconst
-#endif
-
-/* Returned upon end-of-file. */
-#define YY_NULL 0
-
-/* Promotes a possibly negative, possibly signed char to an unsigned
- * integer for use as an array index.  If the signed char is negative,
- * we want to instead treat it as an 8-bit unsigned char, hence the
- * double cast.
- */
-#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
-
-/* Enter a start condition.  This macro really ought to take a parameter,
- * but we do it the disgusting crufty way forced on us by the ()-less
- * definition of BEGIN.
- */
-#define BEGIN (yy_start) = 1 + 2 *
-
-/* Translate the current start state into a value that can be later handed
- * to BEGIN to return to the state.  The YYSTATE alias is for lex
- * compatibility.
- */
-#define YY_START (((yy_start) - 1) / 2)
-#define YYSTATE YY_START
-
-/* Action number for EOF rule of a given start state. */
-#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
-
-/* Special action meaning "start processing a new file". */
-#define YY_NEW_FILE yyrestart(yyin  )
-
-#define YY_END_OF_BUFFER_CHAR 0
-
-/* Size of default input buffer. */
-#ifndef YY_BUF_SIZE
-#define YY_BUF_SIZE 16384
-#endif
-
-/* The state buf must be large enough to hold one state per character in the main buffer.
- */
-#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
-
-#ifndef YY_TYPEDEF_YY_BUFFER_STATE
-#define YY_TYPEDEF_YY_BUFFER_STATE
-typedef struct yy_buffer_state *YY_BUFFER_STATE;
-#endif
-
-#ifndef YY_TYPEDEF_YY_SIZE_T
-#define YY_TYPEDEF_YY_SIZE_T
-typedef size_t yy_size_t;
-#endif
-
-extern yy_size_t yyleng;
-
-extern FILE *yyin, *yyout;
-
-#define EOB_ACT_CONTINUE_SCAN 0
-#define EOB_ACT_END_OF_FILE 1
-#define EOB_ACT_LAST_MATCH 2
-
-    #define YY_LESS_LINENO(n)
-    
-/* Return all but the first "n" matched characters back to the input stream. */
-#define yyless(n) \
-       do \
-               { \
-               /* Undo effects of setting up yytext. */ \
-        int yyless_macro_arg = (n); \
-        YY_LESS_LINENO(yyless_macro_arg);\
-               *yy_cp = (yy_hold_char); \
-               YY_RESTORE_YY_MORE_OFFSET \
-               (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
-               YY_DO_BEFORE_ACTION; /* set up yytext again */ \
-               } \
-       while ( 0 )
-
-#define unput(c) yyunput( c, (yytext_ptr)  )
-
-#ifndef YY_STRUCT_YY_BUFFER_STATE
-#define YY_STRUCT_YY_BUFFER_STATE
-struct yy_buffer_state
-       {
-       FILE *yy_input_file;
-
-       char *yy_ch_buf;                /* input buffer */
-       char *yy_buf_pos;               /* current position in input buffer */
-
-       /* Size of input buffer in bytes, not including room for EOB
-        * characters.
-        */
-       yy_size_t yy_buf_size;
-
-       /* Number of characters read into yy_ch_buf, not including EOB
-        * characters.
-        */
-       yy_size_t yy_n_chars;
-
-       /* Whether we "own" the buffer - i.e., we know we created it,
-        * and can realloc() it to grow it, and should free() it to
-        * delete it.
-        */
-       int yy_is_our_buffer;
-
-       /* Whether this is an "interactive" input source; if so, and
-        * if we're using stdio for input, then we want to use getc()
-        * instead of fread(), to make sure we stop fetching input after
-        * each newline.
-        */
-       int yy_is_interactive;
-
-       /* Whether we're considered to be at the beginning of a line.
-        * If so, '^' rules will be active on the next match, otherwise
-        * not.
-        */
-       int yy_at_bol;
-
-    int yy_bs_lineno; /**< The line count. */
-    int yy_bs_column; /**< The column count. */
-    
-       /* Whether to try to fill the input buffer when we reach the
-        * end of it.
-        */
-       int yy_fill_buffer;
-
-       int yy_buffer_status;
-
-#define YY_BUFFER_NEW 0
-#define YY_BUFFER_NORMAL 1
-       /* When an EOF's been seen but there's still some text to process
-        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
-        * shouldn't try reading from the input source any more.  We might
-        * still have a bunch of tokens to match, though, because of
-        * possible backing-up.
-        *
-        * When we actually see the EOF, we change the status to "new"
-        * (via yyrestart()), so that the user can continue scanning by
-        * just pointing yyin at a new input file.
-        */
-#define YY_BUFFER_EOF_PENDING 2
-
-       };
-#endif /* !YY_STRUCT_YY_BUFFER_STATE */
-
-/* Stack of input buffers. */
-static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
-static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
-static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
-
-/* We provide macros for accessing buffer states in case in the
- * future we want to put the buffer states in a more general
- * "scanner state".
- *
- * Returns the top of the stack, or NULL.
- */
-#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
-                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
-                          : NULL)
-
-/* Same as previous macro, but useful when we know that the buffer stack is not
- * NULL or when we need an lvalue. For internal use only.
- */
-#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
-
-/* yy_hold_char holds the character lost when yytext is formed. */
-static char yy_hold_char;
-static yy_size_t yy_n_chars;           /* number of characters read into yy_ch_buf */
-yy_size_t yyleng;
-
-/* Points to current character in buffer. */
-static char *yy_c_buf_p = (char *) 0;
-static int yy_init = 0;                /* whether we need to initialize */
-static int yy_start = 0;       /* start state number */
-
-/* Flag which is used to allow yywrap()'s to do buffer switches
- * instead of setting up a fresh yyin.  A bit of a hack ...
- */
-static int yy_did_buffer_switch_on_eof;
-
-void yyrestart (FILE *input_file  );
-void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
-YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
-void yy_delete_buffer (YY_BUFFER_STATE b  );
-void yy_flush_buffer (YY_BUFFER_STATE b  );
-void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
-void yypop_buffer_state (void );
-
-static void yyensure_buffer_stack (void );
-static void yy_load_buffer_state (void );
-static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
-
-#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
-
-YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
-YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
-YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len  );
-
-void *yyalloc (yy_size_t  );
-void *yyrealloc (void *,yy_size_t  );
-void yyfree (void *  );
-
-#define yy_new_buffer yy_create_buffer
-
-#define yy_set_interactive(is_interactive) \
-       { \
-       if ( ! YY_CURRENT_BUFFER ){ \
-        yyensure_buffer_stack (); \
-               YY_CURRENT_BUFFER_LVALUE =    \
-            yy_create_buffer(yyin,YY_BUF_SIZE ); \
-       } \
-       YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
-       }
-
-#define yy_set_bol(at_bol) \
-       { \
-       if ( ! YY_CURRENT_BUFFER ){\
-        yyensure_buffer_stack (); \
-               YY_CURRENT_BUFFER_LVALUE =    \
-            yy_create_buffer(yyin,YY_BUF_SIZE ); \
-       } \
-       YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
-       }
-
-#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
-
-/* Begin user sect3 */
-
-#define yywrap(n) 1
-#define YY_SKIP_YYWRAP
-
-typedef unsigned char YY_CHAR;
-
-FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
-
-typedef int yy_state_type;
-
-extern int yylineno;
-
-int yylineno = 1;
-
-extern char *yytext;
-#define yytext_ptr yytext
-
-static yy_state_type yy_get_previous_state (void );
-static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
-static int yy_get_next_buffer (void );
-static void yy_fatal_error (yyconst char msg[]  );
-
-/* Done after the current pattern has been matched and before the
- * corresponding action - sets up yytext.
- */
-#define YY_DO_BEFORE_ACTION \
-       (yytext_ptr) = yy_bp; \
-       yyleng = (yy_size_t) (yy_cp - yy_bp); \
-       (yy_hold_char) = *yy_cp; \
-       *yy_cp = '\0'; \
-       (yy_c_buf_p) = yy_cp;
-
-#define YY_NUM_RULES 11
-#define YY_END_OF_BUFFER 12
-/* This struct is not used in this scanner,
-   but its presence is necessary. */
-struct yy_trans_info
-       {
-       flex_int32_t yy_verify;
-       flex_int32_t yy_nxt;
-       };
-static yyconst flex_int16_t yy_accept[38] =
-    {   0,
-        0,    0,   12,   10,    2,    1,   10,   10,   10,    8,
-        7,    7,    9,    4,    2,    0,    3,    0,    5,    0,
-        8,    7,    7,    8,    0,    0,    6,    4,    4,    0,
-        5,    0,    0,    8,    7,    6,    0
-    } ;
-
-static yyconst flex_int32_t yy_ec[256] =
-    {   0,
-        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
-        1,    1,    2,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    2,    1,    1,    4,    1,    1,    1,    5,    1,
-        1,    1,    6,    1,    7,    8,    9,   10,   11,   11,
-       11,   11,   11,   11,   11,   11,   11,    9,    1,    1,
-       12,    1,    1,    1,   13,   13,   13,   13,   14,   13,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-        1,   16,    1,    1,   17,    1,   13,   13,   13,   13,
-
-       14,   13,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   18,
-       15,   15,    1,    1,    1,    1,    1,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
-       15,   15,   15,   15,   15
-    } ;
-
-static yyconst flex_int32_t yy_meta[19] =
-    {   0,
-        1,    1,    2,    1,    1,    1,    3,    3,    3,    4,
-        4,    1,    5,    4,    3,    1,    3,    3
-    } ;
-
-static yyconst flex_int16_t yy_base[45] =
-    {   0,
-        0,    0,   61,   86,   58,   86,   55,   14,   23,   43,
-       10,   46,   86,   28,   47,   40,   86,   16,   86,   22,
-       28,    0,    0,    0,   40,    0,   24,   45,    0,   24,
-       39,   43,   12,   14,    0,   22,   86,   62,   67,   22,
-       70,   75,   77,   80
-    } ;
-
-static yyconst flex_int16_t yy_def[45] =
-    {   0,
-       37,    1,   37,   37,   37,   37,   38,   39,   37,   40,
-        9,    9,   37,   41,   37,   38,   37,   39,   37,   42,
-       40,   11,   12,   21,   37,   43,   44,   41,   28,   39,
-       39,   42,   37,   37,   43,   44,    0,   37,   37,   37,
-       37,   37,   37,   37
-    } ;
-
-static yyconst flex_int16_t yy_nxt[105] =
-    {   0,
-        4,    5,    6,    7,    8,    9,    9,   10,    4,   11,
-       12,   13,   14,   14,   14,    4,   14,   14,   19,   23,
-       19,   34,   34,   34,   34,   24,   31,   26,   19,   20,
-       21,   20,   22,   23,   27,   27,   27,   32,   36,   20,
-       36,   25,   17,   19,   29,   33,   33,   31,   15,   34,
-       34,   27,   27,   27,   20,   23,   25,   17,   32,   15,
-       37,   29,   16,   16,   16,   16,   16,   18,   37,   18,
-       18,   18,   28,   28,   28,   30,   37,   30,   30,   30,
-       35,   35,   27,   27,   27,    3,   37,   37,   37,   37,
-       37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
-
-       37,   37,   37,   37
-    } ;
-
-static yyconst flex_int16_t yy_chk[105] =
-    {   0,
-        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    1,    1,    8,   11,
-       18,   33,   33,   34,   34,   40,   20,   11,   30,    8,
-        9,   18,    9,    9,   14,   14,   14,   20,   36,   30,
-       27,   21,   16,   31,   14,   25,   25,   32,   15,   25,
-       25,   28,   28,   28,   31,   12,   10,    7,   32,    5,
-        3,   28,   38,   38,   38,   38,   38,   39,    0,   39,
-       39,   39,   41,   41,   41,   42,    0,   42,   42,   42,
-       43,   43,   44,   44,   44,   37,   37,   37,   37,   37,
-       37,   37,   37,   37,   37,   37,   37,   37,   37,   37,
-
-       37,   37,   37,   37
-    } ;
-
-static yy_state_type yy_last_accepting_state;
-static char *yy_last_accepting_cpos;
-
-extern int yy_flex_debug;
-int yy_flex_debug = 0;
-
-/* The intent behind this definition is that it'll catch
- * any uses of REJECT which flex missed.
- */
-#define REJECT reject_used_but_not_detected
-#define yymore() yymore_used_but_not_detected
-#define YY_MORE_ADJ 0
-#define YY_RESTORE_YY_MORE_OFFSET
-char *yytext;
-#line 1 "pool_config.l"
-/* -*-pgsql-c-*- */
-/*
- *
- * $Header$
- *
- * pgpool: a language independent connection pool server for PostgreSQL
- * written by Tatsuo Ishii
- *
- * Copyright (c) 2003-2016     PgPool Global Development Group
- *
- * Permission to use, copy, modify, and distribute this software and
- * its documentation for any purpose and without fee is hereby
- * granted, provided that the above copyright notice appear in all
- * copies and that both that copyright notice and this permission
- * notice appear in supporting documentation, and that the name of the
- * author not be used in advertising or publicity pertaining to
- * distribution of the software without specific, written prior
- * permission. The author makes no representations about the
- * suitability of this software for any purpose.  It is provided "as
- * is" without express or implied warranty.
- *
- * pool_config.l: read configuration file
- *
- */
-#line 27 "pool_config.l"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include "pool.h"
-#include "pool_config.h"
-#include "pool_config_variables.h"
-#include "utils/regex_array.h"
-#ifndef POOL_PRIVATE
-#include "utils/elog.h"
-#else
-#include "utils/fe_ports.h"
-#endif
-
-/* to shut off compiler warnings */
-int yylex(void);
-
-POOL_CONFIG g_pool_config;     /* configuration values */
-POOL_CONFIG *pool_config = &g_pool_config;     /* for lagacy reason pointer to the above struct */
-static unsigned Lineno;
-
-typedef enum {
-  POOL_KEY = 1,
-  POOL_INTEGER,
-  POOL_REAL,
-  POOL_STRING,
-  POOL_UNQUOTED_STRING,
-  POOL_EQUALS,
-  POOL_EOL,
-  POOL_PARSE_ERROR
-} POOL_TOKEN;
-
-static char *extract_string(char *value, POOL_TOKEN token);
-static void FreeConfigVariable(ConfigVariable *item);
-static bool ParseConfigFile( const char *config_file, int elevel,
-                       ConfigVariable **head_p, ConfigVariable **tail_p);
-
-#define YY_NO_INPUT 1
-#line 550 "pool_config.c"
-
-#define INITIAL 0
-
-#ifndef YY_NO_UNISTD_H
-/* Special case for "unistd.h", since it is non-ANSI. We include it way
- * down here because we want the user's section 1 to have been scanned first.
- * The user has a chance to override it with an option.
- */
-#include <unistd.h>
-#endif
-
-#ifndef YY_EXTRA_TYPE
-#define YY_EXTRA_TYPE void *
-#endif
-
-static int yy_init_globals (void );
-
-/* Accessor methods to globals.
-   These are made visible to non-reentrant scanners for convenience. */
-
-int yylex_destroy (void );
-
-int yyget_debug (void );
-
-void yyset_debug (int debug_flag  );
-
-YY_EXTRA_TYPE yyget_extra (void );
-
-void yyset_extra (YY_EXTRA_TYPE user_defined  );
-
-FILE *yyget_in (void );
-
-void yyset_in  (FILE * in_str  );
-
-FILE *yyget_out (void );
-
-void yyset_out  (FILE * out_str  );
-
-yy_size_t yyget_leng (void );
-
-char *yyget_text (void );
-
-int yyget_lineno (void );
-
-void yyset_lineno (int line_number  );
-
-/* Macros after this point can all be overridden by user definitions in
- * section 1.
- */
-
-#ifndef YY_SKIP_YYWRAP
-#ifdef __cplusplus
-extern "C" int yywrap (void );
-#else
-extern int yywrap (void );
-#endif
-#endif
-
-#ifndef yytext_ptr
-static void yy_flex_strncpy (char *,yyconst char *,int );
-#endif
-
-#ifdef YY_NEED_STRLEN
-static int yy_flex_strlen (yyconst char * );
-#endif
-
-#ifndef YY_NO_INPUT
-
-#ifdef __cplusplus
-static int yyinput (void );
-#else
-static int input (void );
-#endif
-
-#endif
-
-/* Amount of stuff to slurp up with each read. */
-#ifndef YY_READ_BUF_SIZE
-#define YY_READ_BUF_SIZE 8192
-#endif
-
-/* Copy whatever the last rule matched to the standard output. */
-#ifndef ECHO
-/* This used to be an fputs(), but since the string might contain NUL's,
- * we now use fwrite().
- */
-#define ECHO fwrite( yytext, yyleng, 1, yyout )
-#endif
-
-/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
- * is returned in "result".
- */
-#ifndef YY_INPUT
-#define YY_INPUT(buf,result,max_size) \
-       if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
-               { \
-               int c = '*'; \
-               yy_size_t n; \
-               for ( n = 0; n < max_size && \
-                            (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
-                       buf[n] = (char) c; \
-               if ( c == '\n' ) \
-                       buf[n++] = (char) c; \
-               if ( c == EOF && ferror( yyin ) ) \
-                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
-               result = n; \
-               } \
-       else \
-               { \
-               errno=0; \
-               while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
-                       { \
-                       if( errno != EINTR) \
-                               { \
-                               YY_FATAL_ERROR( "input in flex scanner failed" ); \
-                               break; \
-                               } \
-                       errno=0; \
-                       clearerr(yyin); \
-                       } \
-               }\
-\
-
-#endif
-
-/* No semi-colon after return; correct usage is to write "yyterminate();" -
- * we don't want an extra ';' after the "return" because that will cause
- * some compilers to complain about unreachable statements.
- */
-#ifndef yyterminate
-#define yyterminate() return YY_NULL
-#endif
-
-/* Number of entries by which start-condition stack grows. */
-#ifndef YY_START_STACK_INCR
-#define YY_START_STACK_INCR 25
-#endif
-
-/* Report a fatal error. */
-#ifndef YY_FATAL_ERROR
-#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
-#endif
-
-/* end tables serialization structures and prototypes */
-
-/* Default declaration of generated scanner - a define so the user can
- * easily add parameters.
- */
-#ifndef YY_DECL
-#define YY_DECL_IS_OURS 1
-
-extern int yylex (void);
-
-#define YY_DECL int yylex (void)
-#endif /* !YY_DECL */
-
-/* Code executed at the beginning of each rule, after yytext and yyleng
- * have been set up.
- */
-#ifndef YY_USER_ACTION
-#define YY_USER_ACTION
-#endif
-
-/* Code executed at the end of each rule. */
-#ifndef YY_BREAK
-#define YY_BREAK break;
-#endif
-
-#define YY_RULE_SETUP \
-       YY_USER_ACTION
-
-/** The main scanner function which does all the work.
- */
-YY_DECL
-{
-       register yy_state_type yy_current_state;
-       register char *yy_cp, *yy_bp;
-       register int yy_act;
-    
-#line 89 "pool_config.l"
-
-
-#line 733 "pool_config.c"
-
-       if ( !(yy_init) )
-               {
-               (yy_init) = 1;
-
-#ifdef YY_USER_INIT
-               YY_USER_INIT;
-#endif
-
-               if ( ! (yy_start) )
-                       (yy_start) = 1; /* first start state */
-
-               if ( ! yyin )
-                       yyin = stdin;
-
-               if ( ! yyout )
-                       yyout = stdout;
-
-               if ( ! YY_CURRENT_BUFFER ) {
-                       yyensure_buffer_stack ();
-                       YY_CURRENT_BUFFER_LVALUE =
-                               yy_create_buffer(yyin,YY_BUF_SIZE );
-               }
-
-               yy_load_buffer_state( );
-               }
-
-       while ( 1 )             /* loops until end-of-file is reached */
-               {
-               yy_cp = (yy_c_buf_p);
-
-               /* Support of yytext. */
-               *yy_cp = (yy_hold_char);
-
-               /* yy_bp points to the position in yy_ch_buf of the start of
-                * the current run.
-                */
-               yy_bp = yy_cp;
-
-               yy_current_state = (yy_start);
-yy_match:
-               do
-                       {
-                       register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
-                       if ( yy_accept[yy_current_state] )
-                               {
-                               (yy_last_accepting_state) = yy_current_state;
-                               (yy_last_accepting_cpos) = yy_cp;
-                               }
-                       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
-                               {
-                               yy_current_state = (int) yy_def[yy_current_state];
-                               if ( yy_current_state >= 38 )
-                                       yy_c = yy_meta[(unsigned int) yy_c];
-                               }
-                       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-                       ++yy_cp;
-                       }
-               while ( yy_current_state != 37 );
-               yy_cp = (yy_last_accepting_cpos);
-               yy_current_state = (yy_last_accepting_state);
-
-yy_find_action:
-               yy_act = yy_accept[yy_current_state];
-
-               YY_DO_BEFORE_ACTION;
-
-do_action:     /* This label is used only to access EOF actions. */
-
-               switch ( yy_act )
-       { /* beginning of action switch */
-                       case 0: /* must back up */
-                       /* undo the effects of YY_DO_BEFORE_ACTION */
-                       *yy_cp = (yy_hold_char);
-                       yy_cp = (yy_last_accepting_cpos);
-                       yy_current_state = (yy_last_accepting_state);
-                       goto yy_find_action;
-
-case 1:
-/* rule 1 can match eol */
-YY_RULE_SETUP
-#line 91 "pool_config.l"
-Lineno++; return POOL_EOL;
-       YY_BREAK
-case 2:
-YY_RULE_SETUP
-#line 92 "pool_config.l"
-/* eat whitespace */
-       YY_BREAK
-case 3:
-*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */
-(yy_c_buf_p) = yy_cp -= 1;
-YY_DO_BEFORE_ACTION; /* set up yytext again */
-YY_RULE_SETUP
-#line 93 "pool_config.l"
-/* eat comment */
-       YY_BREAK
-case 4:
-YY_RULE_SETUP
-#line 95 "pool_config.l"
-return POOL_KEY;
-       YY_BREAK
-case 5:
-YY_RULE_SETUP
-#line 96 "pool_config.l"
-return POOL_STRING;
-       YY_BREAK
-case 6:
-YY_RULE_SETUP
-#line 97 "pool_config.l"
-return POOL_UNQUOTED_STRING;
-       YY_BREAK
-case 7:
-YY_RULE_SETUP
-#line 98 "pool_config.l"
-return POOL_INTEGER;
-       YY_BREAK
-case 8:
-YY_RULE_SETUP
-#line 99 "pool_config.l"
-return POOL_REAL;
-       YY_BREAK
-case 9:
-YY_RULE_SETUP
-#line 100 "pool_config.l"
-return POOL_EQUALS;
-       YY_BREAK
-case 10:
-YY_RULE_SETUP
-#line 102 "pool_config.l"
-return POOL_PARSE_ERROR;
-       YY_BREAK
-case 11:
-YY_RULE_SETUP
-#line 104 "pool_config.l"
-ECHO;
-       YY_BREAK
-#line 871 "pool_config.c"
-case YY_STATE_EOF(INITIAL):
-       yyterminate();
-
-       case YY_END_OF_BUFFER:
-               {
-               /* Amount of text matched not including the EOB char. */
-               int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
-
-               /* Undo the effects of YY_DO_BEFORE_ACTION. */
-               *yy_cp = (yy_hold_char);
-               YY_RESTORE_YY_MORE_OFFSET
-
-               if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
-                       {
-                       /* We're scanning a new file or input source.  It's
-                        * possible that this happened because the user
-                        * just pointed yyin at a new source and called
-                        * yylex().  If so, then we have to assure
-                        * consistency between YY_CURRENT_BUFFER and our
-                        * globals.  Here is the right place to do so, because
-                        * this is the first action (other than possibly a
-                        * back-up) that will match for the new input source.
-                        */
-                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
-                       YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
-                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
-                       }
-
-               /* Note that here we test for yy_c_buf_p "<=" to the position
-                * of the first EOB in the buffer, since yy_c_buf_p will
-                * already have been incremented past the NUL character
-                * (since all states make transitions on EOB to the
-                * end-of-buffer state).  Contrast this with the test
-                * in input().
-                */
-               if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
-                       { /* This was really a NUL. */
-                       yy_state_type yy_next_state;
-
-                       (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
-
-                       yy_current_state = yy_get_previous_state(  );
-
-                       /* Okay, we're now positioned to make the NUL
-                        * transition.  We couldn't have
-                        * yy_get_previous_state() go ahead and do it
-                        * for us because it doesn't know how to deal
-                        * with the possibility of jamming (and we don't
-                        * want to build jamming into it because then it
-                        * will run more slowly).
-                        */
-
-                       yy_next_state = yy_try_NUL_trans( yy_current_state );
-
-                       yy_bp = (yytext_ptr) + YY_MORE_ADJ;
-
-                       if ( yy_next_state )
-                               {
-                               /* Consume the NUL. */
-                               yy_cp = ++(yy_c_buf_p);
-                               yy_current_state = yy_next_state;
-                               goto yy_match;
-                               }
-
-                       else
-                               {
-                               yy_cp = (yy_last_accepting_cpos);
-                               yy_current_state = (yy_last_accepting_state);
-                               goto yy_find_action;
-                               }
-                       }
-
-               else switch ( yy_get_next_buffer(  ) )
-                       {
-                       case EOB_ACT_END_OF_FILE:
-                               {
-                               (yy_did_buffer_switch_on_eof) = 0;
-
-                               if ( yywrap( ) )
-                                       {
-                                       /* Note: because we've taken care in
-                                        * yy_get_next_buffer() to have set up
-                                        * yytext, we can now set up
-                                        * yy_c_buf_p so that if some total
-                                        * hoser (like flex itself) wants to
-                                        * call the scanner after we return the
-                                        * YY_NULL, it'll still work - another
-                                        * YY_NULL will get returned.
-                                        */
-                                       (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
-
-                                       yy_act = YY_STATE_EOF(YY_START);
-                                       goto do_action;
-                                       }
-
-                               else
-                                       {
-                                       if ( ! (yy_did_buffer_switch_on_eof) )
-                                               YY_NEW_FILE;
-                                       }
-                               break;
-                               }
-
-                       case EOB_ACT_CONTINUE_SCAN:
-                               (yy_c_buf_p) =
-                                       (yytext_ptr) + yy_amount_of_matched_text;
-
-                               yy_current_state = yy_get_previous_state(  );
-
-                               yy_cp = (yy_c_buf_p);
-                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
-                               goto yy_match;
-
-                       case EOB_ACT_LAST_MATCH:
-                               (yy_c_buf_p) =
-                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
-
-                               yy_current_state = yy_get_previous_state(  );
-
-                               yy_cp = (yy_c_buf_p);
-                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
-                               goto yy_find_action;
-                       }
-               break;
-               }
-
-       default:
-               YY_FATAL_ERROR(
-                       "fatal flex scanner internal error--no action found" );
-       } /* end of action switch */
-               } /* end of scanning one token */
-} /* end of yylex */
-
-/* yy_get_next_buffer - try to read in a new buffer
- *
- * Returns a code representing an action:
- *     EOB_ACT_LAST_MATCH -
- *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
- *     EOB_ACT_END_OF_FILE - end of file
- */
-static int yy_get_next_buffer (void)
-{
-       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
-       register char *source = (yytext_ptr);
-       register int number_to_move, i;
-       int ret_val;
-
-       if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
-               YY_FATAL_ERROR(
-               "fatal flex scanner internal error--end of buffer missed" );
-
-       if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
-               { /* Don't try to fill the buffer, so this is an EOF. */
-               if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
-                       {
-                       /* We matched a single character, the EOB, so
-                        * treat this as a final EOF.
-                        */
-                       return EOB_ACT_END_OF_FILE;
-                       }
-
-               else
-                       {
-                       /* We matched some text prior to the EOB, first
-                        * process it.
-                        */
-                       return EOB_ACT_LAST_MATCH;
-                       }
-               }
-
-       /* Try to read more data. */
-
-       /* First move last chars to start of buffer. */
-       number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
-
-       for ( i = 0; i < number_to_move; ++i )
-               *(dest++) = *(source++);
-
-       if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
-               /* don't do the read, it's not guaranteed to return an EOF,
-                * just force an EOF
-                */
-               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
-
-       else
-               {
-                       yy_size_t num_to_read =
-                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
-
-               while ( num_to_read <= 0 )
-                       { /* Not enough room in the buffer - grow it. */
-
-                       /* just a shorter name for the current buffer */
-                       YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
-
-                       int yy_c_buf_p_offset =
-                               (int) ((yy_c_buf_p) - b->yy_ch_buf);
-
-                       if ( b->yy_is_our_buffer )
-                               {
-                               yy_size_t new_size = b->yy_buf_size * 2;
-
-                               if ( new_size <= 0 )
-                                       b->yy_buf_size += b->yy_buf_size / 8;
-                               else
-                                       b->yy_buf_size *= 2;
-
-                               b->yy_ch_buf = (char *)
-                                       /* Include room in for 2 EOB chars. */
-                                       yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
-                               }
-                       else
-                               /* Can't grow it, we don't own it. */
-                               b->yy_ch_buf = 0;
-
-                       if ( ! b->yy_ch_buf )
-                               YY_FATAL_ERROR(
-                               "fatal error - scanner input buffer overflow" );
-
-                       (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
-
-                       num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
-                                               number_to_move - 1;
-
-                       }
-
-               if ( num_to_read > YY_READ_BUF_SIZE )
-                       num_to_read = YY_READ_BUF_SIZE;
-
-               /* Read in more data. */
-               YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
-                       (yy_n_chars), num_to_read );
-
-               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
-               }
-
-       if ( (yy_n_chars) == 0 )
-               {
-               if ( number_to_move == YY_MORE_ADJ )
-                       {
-                       ret_val = EOB_ACT_END_OF_FILE;
-                       yyrestart(yyin  );
-                       }
-
-               else
-                       {
-                       ret_val = EOB_ACT_LAST_MATCH;
-                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
-                               YY_BUFFER_EOF_PENDING;
-                       }
-               }
-
-       else
-               ret_val = EOB_ACT_CONTINUE_SCAN;
-
-       if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
-               /* Extend the array by 50%, plus the number we really need. */
-               yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
-               YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
-               if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
-                       YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
-       }
-
-       (yy_n_chars) += number_to_move;
-       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
-       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
-
-       (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
-
-       return ret_val;
-}
-
-/* yy_get_previous_state - get the state just before the EOB char was reached */
-
-    static yy_state_type yy_get_previous_state (void)
-{
-       register yy_state_type yy_current_state;
-       register char *yy_cp;
-    
-       yy_current_state = (yy_start);
-
-       for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
-               {
-               register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
-               if ( yy_accept[yy_current_state] )
-                       {
-                       (yy_last_accepting_state) = yy_current_state;
-                       (yy_last_accepting_cpos) = yy_cp;
-                       }
-               while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
-                       {
-                       yy_current_state = (int) yy_def[yy_current_state];
-                       if ( yy_current_state >= 38 )
-                               yy_c = yy_meta[(unsigned int) yy_c];
-                       }
-               yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-               }
-
-       return yy_current_state;
-}
-
-/* yy_try_NUL_trans - try to make a transition on the NUL character
- *
- * synopsis
- *     next_state = yy_try_NUL_trans( current_state );
- */
-    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
-{
-       register int yy_is_jam;
-       register char *yy_cp = (yy_c_buf_p);
-
-       register YY_CHAR yy_c = 1;
-       if ( yy_accept[yy_current_state] )
-               {
-               (yy_last_accepting_state) = yy_current_state;
-               (yy_last_accepting_cpos) = yy_cp;
-               }
-       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
-               {
-               yy_current_state = (int) yy_def[yy_current_state];
-               if ( yy_current_state >= 38 )
-                       yy_c = yy_meta[(unsigned int) yy_c];
-               }
-       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-       yy_is_jam = (yy_current_state == 37);
-
-       return yy_is_jam ? 0 : yy_current_state;
-}
-
-#ifndef YY_NO_INPUT
-#ifdef __cplusplus
-    static int yyinput (void)
-#else
-    static int input  (void)
-#endif
-
-{
-       int c;
-    
-       *(yy_c_buf_p) = (yy_hold_char);
-
-       if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
-               {
-               /* yy_c_buf_p now points to the character we want to return.
-                * If this occurs *before* the EOB characters, then it's a
-                * valid NUL; if not, then we've hit the end of the buffer.
-                */
-               if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
-                       /* This was really a NUL. */
-                       *(yy_c_buf_p) = '\0';
-
-               else
-                       { /* need more input */
-                       yy_size_t offset = (yy_c_buf_p) - (yytext_ptr);
-                       ++(yy_c_buf_p);
-
-                       switch ( yy_get_next_buffer(  ) )
-                               {
-                               case EOB_ACT_LAST_MATCH:
-                                       /* This happens because yy_g_n_b()
-                                        * sees that we've accumulated a
-                                        * token and flags that we need to
-                                        * try matching the token before
-                                        * proceeding.  But for input(),
-                                        * there's no matching to consider.
-                                        * So convert the EOB_ACT_LAST_MATCH
-                                        * to EOB_ACT_END_OF_FILE.
-                                        */
-
-                                       /* Reset buffer status. */
-                                       yyrestart(yyin );
-
-                                       /*FALLTHROUGH*/
-
-                               case EOB_ACT_END_OF_FILE:
-                                       {
-                                       if ( yywrap( ) )
-                                               return 0;
-
-                                       if ( ! (yy_did_buffer_switch_on_eof) )
-                                               YY_NEW_FILE;
-#ifdef __cplusplus
-                                       return yyinput();
-#else
-                                       return input();
-#endif
-                                       }
-
-                               case EOB_ACT_CONTINUE_SCAN:
-                                       (yy_c_buf_p) = (yytext_ptr) + offset;
-                                       break;
-                               }
-                       }
-               }
-
-       c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
-       *(yy_c_buf_p) = '\0';   /* preserve yytext */
-       (yy_hold_char) = *++(yy_c_buf_p);
-
-       return c;
-}
-#endif /* ifndef YY_NO_INPUT */
-
-/** Immediately switch to a different input stream.
- * @param input_file A readable stream.
- * 
- * @note This function does not reset the start condition to @c INITIAL .
- */
-    void yyrestart  (FILE * input_file )
-{
-    
-       if ( ! YY_CURRENT_BUFFER ){
-        yyensure_buffer_stack ();
-               YY_CURRENT_BUFFER_LVALUE =
-            yy_create_buffer(yyin,YY_BUF_SIZE );
-       }
-
-       yy_init_buffer(YY_CURRENT_BUFFER,input_file );
-       yy_load_buffer_state( );
-}
-
-/** Switch to a different input buffer.
- * @param new_buffer The new input buffer.
- * 
- */
-    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
-{
-    
-       /* TODO. We should be able to replace this entire function body
-        * with
-        *              yypop_buffer_state();
-        *              yypush_buffer_state(new_buffer);
-     */
-       yyensure_buffer_stack ();
-       if ( YY_CURRENT_BUFFER == new_buffer )
-               return;
-
-       if ( YY_CURRENT_BUFFER )
-               {
-               /* Flush out information for old buffer. */
-               *(yy_c_buf_p) = (yy_hold_char);
-               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
-               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
-               }
-
-       YY_CURRENT_BUFFER_LVALUE = new_buffer;
-       yy_load_buffer_state( );
-
-       /* We don't actually know whether we did this switch during
-        * EOF (yywrap()) processing, but the only time this flag
-        * is looked at is after yywrap() is called, so it's safe
-        * to go ahead and always set it.
-        */
-       (yy_did_buffer_switch_on_eof) = 1;
-}
-
-static void yy_load_buffer_state  (void)
-{
-       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
-       (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
-       yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
-       (yy_hold_char) = *(yy_c_buf_p);
-}
-
-/** Allocate and initialize an input buffer state.
- * @param file A readable stream.
- * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
- * 
- * @return the allocated buffer state.
- */
-    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
-{
-       YY_BUFFER_STATE b;
-    
-       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
-       if ( ! b )
-               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
-
-       b->yy_buf_size = size;
-
-       /* yy_ch_buf has to be 2 characters longer than the size given because
-        * we need to put in 2 end-of-buffer characters.
-        */
-       b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
-       if ( ! b->yy_ch_buf )
-               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
-
-       b->yy_is_our_buffer = 1;
-
-       yy_init_buffer(b,file );
-
-       return b;
-}
-
-/** Destroy the buffer.
- * @param b a buffer created with yy_create_buffer()
- * 
- */
-    void yy_delete_buffer (YY_BUFFER_STATE  b )
-{
-    
-       if ( ! b )
-               return;
-
-       if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
-               YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
-
-       if ( b->yy_is_our_buffer )
-               yyfree((void *) b->yy_ch_buf  );
-
-       yyfree((void *) b  );
-}
-
-/* Initializes or reinitializes a buffer.
- * This function is sometimes called more than once on the same buffer,
- * such as during a yyrestart() or at EOF.
- */
-    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
-
-{
-       int oerrno = errno;
-    
-       yy_flush_buffer(b );
-
-       b->yy_input_file = file;
-       b->yy_fill_buffer = 1;
-
-    /* If b is the current buffer, then yy_init_buffer was _probably_
-     * called from yyrestart() or through yy_get_next_buffer.
-     * In that case, we don't want to reset the lineno or column.
-     */
-    if (b != YY_CURRENT_BUFFER){
-        b->yy_bs_lineno = 1;
-        b->yy_bs_column = 0;
-    }
-
-        b->yy_is_interactive = 0;
-    
-       errno = oerrno;
-}
-
-/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
- * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
- * 
- */
-    void yy_flush_buffer (YY_BUFFER_STATE  b )
-{
-       if ( ! b )
-               return;
-
-       b->yy_n_chars = 0;
-
-       /* We always need two end-of-buffer characters.  The first causes
-        * a transition to the end-of-buffer state.  The second causes
-        * a jam in that state.
-        */
-       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
-       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
-
-       b->yy_buf_pos = &b->yy_ch_buf[0];
-
-       b->yy_at_bol = 1;
-       b->yy_buffer_status = YY_BUFFER_NEW;
-
-       if ( b == YY_CURRENT_BUFFER )
-               yy_load_buffer_state( );
-}
-
-/** Pushes the new state onto the stack. The new state becomes
- *  the current state. This function will allocate the stack
- *  if necessary.
- *  @param new_buffer The new state.
- *  
- */
-void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
-{
-       if (new_buffer == NULL)
-               return;
-
-       yyensure_buffer_stack();
-
-       /* This block is copied from yy_switch_to_buffer. */
-       if ( YY_CURRENT_BUFFER )
-               {
-               /* Flush out information for old buffer. */
-               *(yy_c_buf_p) = (yy_hold_char);
-               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
-               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
-               }
-
-       /* Only push if top exists. Otherwise, replace top. */
-       if (YY_CURRENT_BUFFER)
-               (yy_buffer_stack_top)++;
-       YY_CURRENT_BUFFER_LVALUE = new_buffer;
-
-       /* copied from yy_switch_to_buffer. */
-       yy_load_buffer_state( );
-       (yy_did_buffer_switch_on_eof) = 1;
-}
-
-/** Removes and deletes the top of the stack, if present.
- *  The next element becomes the new top.
- *  
- */
-void yypop_buffer_state (void)
-{
-       if (!YY_CURRENT_BUFFER)
-               return;
-
-       yy_delete_buffer(YY_CURRENT_BUFFER );
-       YY_CURRENT_BUFFER_LVALUE = NULL;
-       if ((yy_buffer_stack_top) > 0)
-               --(yy_buffer_stack_top);
-
-       if (YY_CURRENT_BUFFER) {
-               yy_load_buffer_state( );
-               (yy_did_buffer_switch_on_eof) = 1;
-       }
-}
-
-/* Allocates the stack if it does not exist.
- *  Guarantees space for at least one push.
- */
-static void yyensure_buffer_stack (void)
-{
-       yy_size_t num_to_alloc;
-    
-       if (!(yy_buffer_stack)) {
-
-               /* First allocation is just for 2 elements, since we don't know if this
-                * scanner will even need a stack. We use 2 instead of 1 to avoid an
-                * immediate realloc on the next call.
-         */
-               num_to_alloc = 1;
-               (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
-                                                               (num_to_alloc * sizeof(struct yy_buffer_state*)
-                                                               );
-               if ( ! (yy_buffer_stack) )
-                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
-                                                                 
-               memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
-                               
-               (yy_buffer_stack_max) = num_to_alloc;
-               (yy_buffer_stack_top) = 0;
-               return;
-       }
-
-       if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
-
-               /* Increase the buffer to prepare for a possible push. */
-               int grow_size = 8 /* arbitrary grow size */;
-
-               num_to_alloc = (yy_buffer_stack_max) + grow_size;
-               (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
-                                                               ((yy_buffer_stack),
-                                                               num_to_alloc * sizeof(struct yy_buffer_state*)
-                                                               );
-               if ( ! (yy_buffer_stack) )
-                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
-
-               /* zero only the new slots.*/
-               memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
-               (yy_buffer_stack_max) = num_to_alloc;
-       }
-}
-
-/** Setup the input buffer state to scan directly from a user-specified character buffer.
- * @param base the character buffer
- * @param size the size in bytes of the character buffer
- * 
- * @return the newly allocated buffer state object. 
- */
-YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
-{
-       YY_BUFFER_STATE b;
-    
-       if ( size < 2 ||
-            base[size-2] != YY_END_OF_BUFFER_CHAR ||
-            base[size-1] != YY_END_OF_BUFFER_CHAR )
-               /* They forgot to leave room for the EOB's. */
-               return 0;
-
-       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
-       if ( ! b )
-               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
-
-       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
-       b->yy_buf_pos = b->yy_ch_buf = base;
-       b->yy_is_our_buffer = 0;
-       b->yy_input_file = 0;
-       b->yy_n_chars = b->yy_buf_size;
-       b->yy_is_interactive = 0;
-       b->yy_at_bol = 1;
-       b->yy_fill_buffer = 0;
-       b->yy_buffer_status = YY_BUFFER_NEW;
-
-       yy_switch_to_buffer(b  );
-
-       return b;
-}
-
-/** Setup the input buffer state to scan a string. The next call to yylex() will
- * scan from a @e copy of @a str.
- * @param yystr a NUL-terminated string to scan
- * 
- * @return the newly allocated buffer state object.
- * @note If you want to scan bytes that may contain NUL values, then use
- *       yy_scan_bytes() instead.
- */
-YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
-{
-    
-       return yy_scan_bytes(yystr,strlen(yystr) );
-}
-
-/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
- * scan from a @e copy of @a bytes.
- * @param bytes the byte buffer to scan
- * @param len the number of bytes in the buffer pointed to by @a bytes.
- * 
- * @return the newly allocated buffer state object.
- */
-YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, yy_size_t  _yybytes_len )
-{
-       YY_BUFFER_STATE b;
-       char *buf;
-       yy_size_t n, i;
-    
-       /* Get memory for full buffer, including space for trailing EOB's. */
-       n = _yybytes_len + 2;
-       buf = (char *) yyalloc(n  );
-       if ( ! buf )
-               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
-
-       for ( i = 0; i < _yybytes_len; ++i )
-               buf[i] = yybytes[i];
-
-       buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
-
-       b = yy_scan_buffer(buf,n );
-       if ( ! b )
-               YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
-
-       /* It's okay to grow etc. this buffer, and we should throw it
-        * away when we're done.
-        */
-       b->yy_is_our_buffer = 1;
-
-       return b;
-}
-
-#ifndef YY_EXIT_FAILURE
-#define YY_EXIT_FAILURE 2
-#endif
-
-static void yy_fatal_error (yyconst char* msg )
-{
-       (void) fprintf( stderr, "%s\n", msg );
-       exit( YY_EXIT_FAILURE );
-}
-
-/* Redefine yyless() so it works in section 3 code. */
-
-#undef yyless
-#define yyless(n) \
-       do \
-               { \
-               /* Undo effects of setting up yytext. */ \
-        int yyless_macro_arg = (n); \
-        YY_LESS_LINENO(yyless_macro_arg);\
-               yytext[yyleng] = (yy_hold_char); \
-               (yy_c_buf_p) = yytext + yyless_macro_arg; \
-               (yy_hold_char) = *(yy_c_buf_p); \
-               *(yy_c_buf_p) = '\0'; \
-               yyleng = yyless_macro_arg; \
-               } \
-       while ( 0 )
-
-/* Accessor  methods (get/set functions) to struct members. */
-
-/** Get the current line number.
- * 
- */
-int yyget_lineno  (void)
-{
-        
-    return yylineno;
-}
-
-/** Get the input stream.
- * 
- */
-FILE *yyget_in  (void)
-{
-        return yyin;
-}
-
-/** Get the output stream.
- * 
- */
-FILE *yyget_out  (void)
-{
-        return yyout;
-}
-
-/** Get the length of the current token.
- * 
- */
-yy_size_t yyget_leng  (void)
-{
-        return yyleng;
-}
-
-/** Get the current token.
- * 
- */
-
-char *yyget_text  (void)
-{
-        return yytext;
-}
-
-/** Set the current line number.
- * @param line_number
- * 
- */
-void yyset_lineno (int  line_number )
-{
-    
-    yylineno = line_number;
-}
-
-/** Set the input stream. This does not discard the current
- * input buffer.
- * @param in_str A readable stream.
- * 
- * @see yy_switch_to_buffer
- */
-void yyset_in (FILE *  in_str )
-{
-        yyin = in_str ;
-}
-
-void yyset_out (FILE *  out_str )
-{
-        yyout = out_str ;
-}
-
-int yyget_debug  (void)
-{
-        return yy_flex_debug;
-}
-
-void yyset_debug (int  bdebug )
-{
-        yy_flex_debug = bdebug ;
-}
-
-static int yy_init_globals (void)
-{
-        /* Initialization is the same as for the non-reentrant scanner.
-     * This function is called from yylex_destroy(), so don't allocate here.
-     */
-
-    (yy_buffer_stack) = 0;
-    (yy_buffer_stack_top) = 0;
-    (yy_buffer_stack_max) = 0;
-    (yy_c_buf_p) = (char *) 0;
-    (yy_init) = 0;
-    (yy_start) = 0;
-
-/* Defined in main.c */
-#ifdef YY_STDINIT
-    yyin = stdin;
-    yyout = stdout;
-#else
-    yyin = (FILE *) 0;
-    yyout = (FILE *) 0;
-#endif
-
-    /* For future reference: Set errno on error, since we are called by
-     * yylex_init()
-     */
-    return 0;
-}
-
-/* yylex_destroy is for both reentrant and non-reentrant scanners. */
-int yylex_destroy  (void)
-{
-    
-    /* Pop the buffer stack, destroying each element. */
-       while(YY_CURRENT_BUFFER){
-               yy_delete_buffer(YY_CURRENT_BUFFER  );
-               YY_CURRENT_BUFFER_LVALUE = NULL;
-               yypop_buffer_state();
-       }
-
-       /* Destroy the stack itself. */
-       yyfree((yy_buffer_stack) );
-       (yy_buffer_stack) = NULL;
-
-    /* Reset the globals. This is important in a non-reentrant scanner so the next time
-     * yylex() is called, initialization will occur. */
-    yy_init_globals( );
-
-    return 0;
-}
-
-/*
- * Internal utility routines.
- */
-
-#ifndef yytext_ptr
-static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
-{
-       register int i;
-       for ( i = 0; i < n; ++i )
-               s1[i] = s2[i];
-}
-#endif
-
-#ifdef YY_NEED_STRLEN
-static int yy_flex_strlen (yyconst char * s )
-{
-       register int n;
-       for ( n = 0; s[n]; ++n )
-               ;
-
-       return n;
-}
-#endif
-
-void *yyalloc (yy_size_t  size )
-{
-       return (void *) malloc( size );
-}
-
-void *yyrealloc  (void * ptr, yy_size_t  size )
-{
-       /* The cast to (char *) in the following accommodates both
-        * implementations that use char* generic pointers, and those
-        * that use void* generic pointers.  It works with the latter
-        * because both ANSI C and C++ allow castless assignment from
-        * any pointer type to void*, and deal with argument conversions
-        * as though doing an assignment.
-        */
-       return (void *) realloc( (char *) ptr, size );
-}
-
-void yyfree (void * ptr )
-{
-       free( (char *) ptr );   /* see yyrealloc() for (char *) cast */
-}
-
-#define YYTABLES_NAME "yytables"
-
-#line 104 "pool_config.l"
-
-
-
-int pool_init_config(void)
-{
-       memset(pool_config, 0, sizeof(POOL_CONFIG));
-
-#ifndef POOL_PRIVATE
-       g_pool_config.backend_desc = pool_shared_memory_create(sizeof(BackendDesc));
-       memset(g_pool_config.backend_desc, 0, sizeof(BackendDesc));
-#else
-       g_pool_config.backend_desc = palloc0(sizeof(BackendDesc));
-#endif
-       g_pool_config.health_check_params = palloc0(MAX_NUM_BACKENDS*sizeof(HealthCheckParams));
-
-       InitializeConfigOptions();
-
-       return 0;
-}
-
-/*
- * Add regex expression to patterns array
- * The supported type are: black_function_list and white_function_list
- * Return 0 on error, 1 on success
- */
-int add_regex_pattern(const char *type, char *s)
-{
-       int regex_flags = REG_NOSUB;
-       RegPattern currItem;
-       /* force case insensitive pattern matching */
-       regex_flags |= REG_ICASE;
-       /* Add extended regex search */
-       regex_flags |= REG_EXTENDED;
-       /* Fill the pattern type */
-       if (strcmp(type, "black_function_list") == 0 ||
-        strcmp(type, "black_memqcache_table_list") == 0)
-       {
-               currItem.type = BLACKLIST;
-       }
-       else if (strcmp(type, "white_function_list") == 0 ||
-                strcmp(type, "white_memqcache_table_list") == 0)
-       {
-               currItem.type = WHITELIST;
-       }
-       else
-       {
-               ereport(WARNING,
-                       (errmsg("unable to add regex pattern, bad pattern type %s", type)));
-               return 0;
-       }
-       /* Fill the pattern flag */
-       currItem.flag = regex_flags;
-
-       /* Fill pattern array */
-       currItem.pattern = palloc(sizeof(char)*(strlen(s)+3));
-       /* Force exact matching of function name with ^ and $ on the regex
-          if required to prevent partial matching. It also allow backward
-          compatibility.
-        */
-       if (strncmp(s, "^", 1) != 0) {
-               strncpy(currItem.pattern, "^", 2);
-               strncat(currItem.pattern, s, strlen(s) + 1);
-       } else {
-               strncpy(currItem.pattern, s, strlen(s) + 1);
-       }
-       if (s[strlen(s)-1] != '$') {
-               strncat(currItem.pattern, "$", 2);
-       }
-       ereport(DEBUG1,
-               (errmsg("initializing pool configuration"),
-                       errdetail("adding regex pattern for \"%s\" pattern: %s",type, currItem.pattern)));
-
-       /* compile our regex */
-       if (regcomp(&currItem.regexv, currItem.pattern, currItem.flag) != 0)
-       {
-               ereport(WARNING,
-                       (errmsg("unable to add regex pattern for \"%s\", invalid pattern: \"%s\"", type,currItem.pattern)));
-       }
-    else if ((strcmp(type, "white_function_list") == 0 ||
-                 strcmp(type, "black_function_list") == 0) &&
-             growFunctionPatternArray(currItem) < 0)
-    {
-               ereport(WARNING,
-                       (errmsg("unable to add regex pattern for \"%s\", unable to allocate new pattern", type)));
-        return 0;
-    }
-    else if ((strcmp(type, "white_memqcache_table_list") == 0 ||
-                 strcmp(type, "black_memqcache_table_list") == 0) &&
-             growMemqcacheTablePatternArray(currItem) < 0)
-    {
-               ereport(WARNING,
-                       (errmsg("unable to add regex pattern for \"%s\", unable to allocate new pattern", type)));
-        return 0;
-    }
-
-       return 1;
-}
-
-/*
- * Dynamically grow the regex pattern array
- * The array start with PATTERN_ARR_SIZE storage place, if required
- * it will grow of PATTERN_ARR_SIZE more each time.
- */
-int growFunctionPatternArray(RegPattern item)
-{
-       void *_tmp = NULL;
-       if (pool_config->pattc == pool_config->current_pattern_size)
-       {
-               pool_config->current_pattern_size += PATTERN_ARR_SIZE;
-               _tmp = repalloc(pool_config->lists_patterns,
-                              (pool_config->current_pattern_size * sizeof(RegPattern)));
-               if (!_tmp)
-               {
-                       return(-1);
-               }
-
-               pool_config->lists_patterns = (RegPattern*)_tmp;
-       }
-       pool_config->lists_patterns[pool_config->pattc] = item;
-       pool_config->pattc++;
-
-       return(pool_config->pattc);
-}
-
-int growMemqcacheTablePatternArray(RegPattern item)
-{
-       void *_tmp = NULL;
-       if (pool_config->memqcache_table_pattc == pool_config->current_memqcache_table_pattern_size)
-       {
-               pool_config->current_memqcache_table_pattern_size += PATTERN_ARR_SIZE;
-               _tmp = repalloc(pool_config->lists_memqcache_table_patterns,
-                              (pool_config->current_memqcache_table_pattern_size * sizeof(RegPattern)));
-               if (!_tmp)
-               {
-                       return(-1);
-               }
-
-               pool_config->lists_memqcache_table_patterns = (RegPattern*)_tmp;
-       }
-       pool_config->lists_memqcache_table_patterns[pool_config->memqcache_table_pattc] = item;
-       pool_config->memqcache_table_pattc++;
-
-       return(pool_config->memqcache_table_pattc);
-}
-
-/*
- * Free a single ConfigVariable
- */
-static void
-FreeConfigVariable(ConfigVariable *item)
-{
-       if (item->name)
-               pfree(item->name);
-       if (item->value)
-               pfree(item->value);
-       pfree(item);
-}
-
-/*
- * Free a list of ConfigVariables, including the names and the values
- */
-static void
-FreeConfigVariables(ConfigVariable *list)
-{
-       ConfigVariable *item;
-       
-       item = list;
-       while (item)
-       {
-               ConfigVariable *next = item->next;
-               FreeConfigVariable(item);
-               item = next;
-       }
-}
-
-/*
- * Read and parse a single configuration file.
- *
- * Input parameters:
- *  config_file: absolute or relative path name of the configuration file
- *  elevel: error logging level to use
- * Input/Output parameters:
- *  head_p, tail_p: head and tail of linked list of name/value pairs
- *
- * *head_p and *tail_p must be initialized, either to NULL or valid pointers
- * to a ConfigVariable list, before calling the outer recursion level.  Any
- * name-value pairs read from the input file(s) will be appended to the list.
- *
- * Note: if elevel >= ERROR then an error will not return control to the
- * caller, so there is no need to check the return value in that case.
- *
- */
-static bool
-ParseConfigFile(const char *config_file, int elevel,
-                               ConfigVariable **head_p, ConfigVariable **tail_p)
-{
-
-       FILE *fd;
-       int token;
-       char *key;
-       char *val;
-       ConfigVariable *item;
-       
-       *head_p = NULL;
-
-       /* open config file */
-       fd = fopen(config_file, "r");
-       if (!fd)
-       {
-               ereport(WARNING,
-                       (errmsg("could not open configuration file: \"%s\"",config_file),
-                               errdetail("using default configuration parameter values")));
-               return false;
-       }
-
-       yyin = fd;
-       Lineno = 1;
-
-       for(;;)
-       {
-               token = yylex();
-
-               if (token == 0)
-                       break;
-
-               if (token == POOL_EOL)
-                       continue;
-
-               if (token == POOL_PARSE_ERROR)
-                       goto parse_error;
-
-               if (token != POOL_KEY)
-                       goto parse_error;
-
-               key = pstrdup(yytext);
-
-               token = yylex();
-
-               /* next we have an optional equal sign; discard if present */
-               if (token == POOL_EQUALS)
-                       token = yylex();
-
-               val = extract_string(yytext,token);
-
-               ereport(DEBUG5,
-                       (errmsg("key: \"%s\" value: \"%s\" kind: %d",key, val, token)));
-
-               /* Add this to the list */
-               item = palloc(sizeof(ConfigVariable));
-               item->name = key;
-               item->value = val;
-               item->sourceline = Lineno;
-               item->next = NULL;
-               if (*head_p == NULL)
-                       *head_p = item;
-               else
-                       (*tail_p)->next = item;
-               *tail_p = item;
-       }
-
-       fclose(fd);
-       return true;
-
-parse_error:
-
-       fclose(fd);
-       FreeConfigVariables(*head_p);
-       *head_p = NULL;
-       *tail_p = NULL;
-       ereport(elevel,
-               (errmsg("syntex error in configuration file \"%s\"",config_file),
-                       errdetail("parse error at line %d '%s' token = %d", Lineno, yytext,token)));
-
-       return false;
-}
-
-/*
- * Read the configuration file and load the values of all parameters
- */
-bool pool_get_config(const char *config_file, ConfigContext context)
-{
-       ConfigVariable *head_p = NULL;
-       ConfigVariable *tail_p = NULL;
-       bool res;
-       int elevel = (context == CFGCXT_INIT)?FATAL:WARNING;
-
-       res = ParseConfigFile(config_file, elevel, &head_p, &tail_p);
-       if (res == false || head_p == NULL)
-               return false;
-
-       res = set_config_options(head_p, context, PGC_S_FILE, elevel);
-       FreeConfigVariables(head_p);
-       return res;
-}
-
-
-static char *extract_string(char *value, POOL_TOKEN token)
-{
-       char *ret = NULL;
-
-       if (token == POOL_STRING)
-       {
-               ret = pstrdup(value+1);
-               ret[strlen(ret)-1] = '\0';
-       }
-       else
-               ret = pstrdup(value);
-
-       return ret;
-}
-
-/*
- * Try to interpret value as boolean value.  Valid values are: true,
- * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof.
- * If the string parses okay, return true, else false.
- * If okay and result is not NULL, return the value in *result.
- * This function copied from PostgreSQL source code.
- */
-static bool parse_bool_with_len(const char *value, size_t len, bool *result)
-{
-       switch (*value)
-       {
-               case 't':
-               case 'T':
-                       if (strncasecmp(value, "true", len) == 0)
-                       {
-                               if (result)
-                                       *result = true;
-                               return true;
-                       }
-                       break;
-               case 'f':
-               case 'F':
-                       if (strncasecmp(value, "false", len) == 0)
-                       {
-                               if (result)
-                                       *result = false;
-                               return true;
-                       }
-                       break;
-               case 'y':
-               case 'Y':
-                       if (strncasecmp(value, "yes", len) == 0)
-                       {
-                               if (result)
-                                       *result = true;
-                               return true;
-                       }
-                       break;
-               case 'n':
-               case 'N':
-                       if (strncasecmp(value, "no", len) == 0)
-                       {
-                               if (result)
-                                       *result = false;
-                               return true;
-                       }
-                       break;
-               case 'o':
-               case 'O':
-                       /* 'o' is not unique enough */
-                       if (strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
-                       {
-                               if (result)
-                                       *result = true;
-                               return true;
-                       }
-                       else if (strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
-                       {
-                               if (result)
-                                       *result = false;
-                               return true;
-                       }
-                       break;
-               case '1':
-                       if (len == 1)
-                       {
-                               if (result)
-                                       *result = true;
-                               return true;
-                       }
-                       break;
-               case '0':
-                       if (len == 1)
-                       {
-                               if (result)
-                                       *result = false;
-                               return true;
-                       }
-                       break;
-               default:
-                       break;
-       }
-
-       if (result)
-               *result = false;                /* suppress compiler warning */
-       return false;
-}
-
-int eval_logical(const char *str)
-{
-       bool result;
-
-       if (!parse_bool_with_len(str, strlen(str), &result))
-               return -1;
-
-       return (result ? 1 : 0);
-}
-
-
-#ifdef DEBUG
-static void print_host_entry(int slot)
-{
-       ereport(DEBUG1,
-               (errmsg("initializing pool configuration"),
-                       errdetail("slot: %d host: %s port: %d status: %d weight: %f",
-                                       slot,
-                                       pool_config->server_hostnames[slot],
-                                       pool_config->server_ports[slot],
-                                       pool_config->server_status[slot],
-                                       pool_config->server_weights[slot])));
-}
-#endif
-
-
-/*
- * Translate binary form of backend flag to string.
- * The returned data is in static buffer, and it will be destroyed
- * at the next call to this function.
- */
-char *pool_flag_to_str(unsigned short flag)
-{
-       static char buf[1024];          /* should be large enough */
-
-       *buf = '\0';
-
-       if (POOL_ALLOW_TO_FAILOVER(flag))
-               snprintf(buf, sizeof(buf), "ALLOW_TO_FAILOVER");
-       else if (POOL_DISALLOW_TO_FAILOVER(flag))
-               snprintf(buf, sizeof(buf), "DISALLOW_TO_FAILOVER");
-
-       if (POOL_ALWAYS_MASTER & flag)
-       {
-               if (*buf == '\0')
-                       snprintf(buf, sizeof(buf), "ALWAYS_MASTER");
-               else
-                       snprintf(buf+strlen(buf), sizeof(buf), "|ALWAYS_MASTER");
-       }
-
-       return buf;
-}
-
-/*
- * Translate the BACKEND_STATUS enum value to string.
- * the function returns the constant string so should not be freed
- */
-char* backend_status_to_str(BackendInfo *bi)
-{
-       char *statusName;
-
-       switch (bi->backend_status) {
-
-               case CON_UNUSED:
-               statusName = BACKEND_STATUS_CON_UNUSED;
-               break;
-
-               case CON_CONNECT_WAIT:
-               statusName = BACKEND_STATUS_CON_CONNECT_WAIT;
-               break;
-
-               case CON_UP:
-               statusName = BACKEND_STATUS_CON_UP;
-               break;
-
-               case CON_DOWN:
-               {
-                       if (bi->quarantine)
-                               statusName = BACKEND_STATUS_QUARANTINE;
-                       else
-                               statusName = BACKEND_STATUS_CON_DOWN;
-               }
-               break;
-
-               default:
-               statusName = "unknown";
-               break;
-       }
-       return statusName;
-}
-
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..2636b7861a0a42aaf910a8f6abee8ecf05d64936
--- /dev/null
@@ -0,0 +1 @@
+../../../src/config/pool_config.c
\ No newline at end of file
diff --git a/src/utils/base64.c b/src/utils/base64.c
new file mode 100644 (file)
index 0000000..0c6a446
--- /dev/null
@@ -0,0 +1,196 @@
+/*-------------------------------------------------------------------------
+ *
+ * base64.c
+ *       Encoding and decoding routines for base64 without whitespace.
+ *
+ * Copyright (c) 2001-2017, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *       src/common/base64.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include "pool_type.h"
+#include "utils/base64.h"
+
+
+/*
+ * BASE64
+ */
+
+static const char _base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const int8 b64lookup[128] = {
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+       52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+       -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+       15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+       -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+       41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+};
+
+/*
+ * pg_b64_encode
+ *
+ * Encode into base64 the given string.  Returns the length of the encoded
+ * string.
+ */
+int
+pg_b64_encode(const char *src, int len, char *dst)
+{
+       char       *p;
+       const char *s,
+                          *end = src + len;
+       int                     pos = 2;
+       uint32          buf = 0;
+
+       s = src;
+       p = dst;
+
+       while (s < end)
+       {
+               buf |= (unsigned char) *s << (pos << 3);
+               pos--;
+               s++;
+
+               /* write it out */
+               if (pos < 0)
+               {
+                       *p++ = _base64[(buf >> 18) & 0x3f];
+                       *p++ = _base64[(buf >> 12) & 0x3f];
+                       *p++ = _base64[(buf >> 6) & 0x3f];
+                       *p++ = _base64[buf & 0x3f];
+
+                       pos = 2;
+                       buf = 0;
+               }
+       }
+       if (pos != 2)
+       {
+               *p++ = _base64[(buf >> 18) & 0x3f];
+               *p++ = _base64[(buf >> 12) & 0x3f];
+               *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
+               *p++ = '=';
+       }
+
+       return p - dst;
+}
+
+/*
+ * pg_b64_decode
+ *
+ * Decode the given base64 string.  Returns the length of the decoded
+ * string on success, and -1 in the event of an error.
+ */
+int
+pg_b64_decode(const char *src, int len, char *dst)
+{
+       const char *srcend = src + len,
+                          *s = src;
+       char       *p = dst;
+       char            c;
+       int                     b = 0;
+       uint32          buf = 0;
+       int                     pos = 0,
+                               end = 0;
+
+       while (s < srcend)
+       {
+               c = *s++;
+
+               /* Leave if a whitespace is found */
+               if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+                       return -1;
+
+               if (c == '=')
+               {
+                       /* end sequence */
+                       if (!end)
+                       {
+                               if (pos == 2)
+                                       end = 1;
+                               else if (pos == 3)
+                                       end = 2;
+                               else
+                               {
+                                       /*
+                                        * Unexpected "=" character found while decoding base64
+                                        * sequence.
+                                        */
+                                       return -1;
+                               }
+                       }
+                       b = 0;
+               }
+               else
+               {
+                       b = -1;
+                       if (c > 0 && c < 127)
+                               b = b64lookup[(unsigned char) c];
+                       if (b < 0)
+                       {
+                               /* invalid symbol found */
+                               return -1;
+                       }
+               }
+               /* add it to buffer */
+               buf = (buf << 6) + b;
+               pos++;
+               if (pos == 4)
+               {
+                       *p++ = (buf >> 16) & 255;
+                       if (end == 0 || end > 1)
+                               *p++ = (buf >> 8) & 255;
+                       if (end == 0 || end > 2)
+                               *p++ = buf & 255;
+                       buf = 0;
+                       pos = 0;
+               }
+       }
+
+       if (pos != 0)
+       {
+               /*
+                * base64 end sequence is invalid.  Input data is missing padding, is
+                * truncated or is otherwise corrupted.
+                */
+               return -1;
+       }
+
+       return p - dst;
+}
+
+/*
+ * pg_b64_enc_len
+ *
+ * Returns to caller the length of the string if it were encoded with
+ * base64 based on the length provided by caller.  This is useful to
+ * estimate how large a buffer allocation needs to be done before doing
+ * the actual encoding.
+ */
+int
+pg_b64_enc_len(int srclen)
+{
+       /* 3 bytes will be converted to 4 */
+       return (srclen + 2) * 4 / 3;
+}
+
+/*
+ * pg_b64_dec_len
+ *
+ * Returns to caller the length of the string if it were to be decoded
+ * with base64, based on the length given by caller.  This is useful to
+ * estimate how large a buffer allocation needs to be done before doing
+ * the actual decoding.
+ */
+int
+pg_b64_dec_len(int srclen)
+{
+       return (srclen * 3) >> 2;
+}
index 1a04cd94a1d437d39a483e1490ca3ae447d7e4ec..2158c9f1773af985f2dda1c1a294e324e5ded598 100644 (file)
@@ -213,6 +213,11 @@ POOL_REPORT_CONFIG* get_config(int *nrows)
        StrNCpy(status[i].desc, "maximum time in seconds to complete client authentication", POOLCONFIG_MAXNAMELEN);
        i++;
 
+       StrNCpy(status[i].name, "allow_clear_text_frontend_auth", POOLCONFIG_MAXNAMELEN);
+       snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->allow_clear_text_frontend_auth);
+       StrNCpy(status[i].desc, "allow to use clear text password auth when pool_passwd does not contain password", POOLCONFIG_MAXDESCLEN);
+       i++;
+
        /* - SSL Connections - */
        StrNCpy(status[i].name, "ssl", POOLCONFIG_MAXNAMELEN);
        snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->ssl);
index 1b2de402e197fa2c0dce592e9ff790cc39328384..2105d3ea709433eb3b0fc1a34c9bf3bf912c08fd 100644 (file)
 #include "config.h"
 #include "pool.h"
 #include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
 #include "utils/pool_stream.h"
 #include "pool_config.h"
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
 
 #ifdef USE_SSL
 
+static SSL_CTX *SSL_frontend_context = NULL;
+static bool SSL_initialized = false;
+static bool ssl_passwd_cb_called = false;
+static int     ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+static int  verify_cb(int ok, X509_STORE_CTX *ctx);
+static const char * SSLerrmessage(unsigned long ecode);
+static void fetch_pool_ssl_cert(POOL_CONNECTION *cp);
+
 #define SSL_RETURN_VOID_IF(cond, msg) \
        do { \
                if ( (cond) ) { \
@@ -62,7 +76,9 @@ static int init_ssl_ctx(POOL_CONNECTION *cp, enum ssl_conn_type conntype);
 /* OpenSSL error message */
 static void perror_ssl(const char *context);
 
-/* attempt to negotiate a secure connection */
+/* Attempt to negotiate a secure connection
+ * between pgpool-II and PostgreSQL backends
+ */
 void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp) {
        int ssl_packet[2] = { htonl(sizeof(int)*2), htonl(NEGOTIATE_SSL_CODE) };
        char server_response;
@@ -112,21 +128,24 @@ void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp) {
 }
 
 
-/* attempt to negotiate a secure connection */
+/* attempt to negotiate a secure connection
+ * between frontend and Pgpool-II
+ */
 void pool_ssl_negotiate_serverclient(POOL_CONNECTION *cp) {
 
        cp->ssl_active = -1;
-
-       if ( (!pool_config->ssl) || init_ssl_ctx(cp, ssl_conn_serverclient)) {
-               /* write back an "SSL reject" response before returning */
+       if ( (!pool_config->ssl) || !SSL_frontend_context) {
                pool_write_and_flush(cp, "N", 1);
        } else {
+               cp->ssl = SSL_new(SSL_frontend_context);
+
                /* write back an "SSL accept" response */
                pool_write_and_flush(cp, "S", 1);
 
                SSL_set_fd(cp->ssl, cp->fd);
                SSL_RETURN_VOID_IF( (SSL_accept(cp->ssl) < 0), "SSL_accept");
                cp->ssl_active = 1;
+               fetch_pool_ssl_cert(cp);
        }
 }
 
@@ -267,6 +286,7 @@ static int init_ssl_ctx(POOL_CONNECTION *cp, enum ssl_conn_type conntype) {
        SSL_CTX_set_mode(cp->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 
        if ( conntype == ssl_conn_serverclient) {
+               /* between frontend and pgpool */
                error = SSL_CTX_use_certificate_chain_file(cp->ssl_ctx,
                                                     pool_config->ssl_cert);
                SSL_RETURN_ERROR_IF( (error != 1), "Loading SSL certificate");
@@ -276,6 +296,7 @@ static int init_ssl_ctx(POOL_CONNECTION *cp, enum ssl_conn_type conntype) {
                                                    SSL_FILETYPE_PEM);
                SSL_RETURN_ERROR_IF( (error != 1), "Loading SSL private key");
        } else {
+               /* between pgpool and backend */
                /* set extra verification if ssl_ca_cert or ssl_ca_cert_dir are set */
                if (strlen(pool_config->ssl_ca_cert))
                        cacert = pool_config->ssl_ca_cert;
@@ -318,6 +339,31 @@ static void perror_ssl(const char *context) {
        }
 }
 
+/*
+ * Obtain reason string for passed SSL errcode
+ *
+ * ERR_get_error() is used by caller to get errcode to pass here.
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static const char *
+SSLerrmessage(unsigned long ecode)
+{
+       const char *errreason;
+       static char errbuf[32];
+
+       if (ecode == 0)
+               return _("no SSL error reported");
+       errreason = ERR_reason_error_string(ecode);
+       if (errreason != NULL)
+               return errreason;
+       snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), ecode);
+       return errbuf;
+}
+
+
 /*
  * Return true if SSL layer has any pending data in buffer
  */
@@ -328,6 +374,295 @@ bool pool_ssl_pending(POOL_CONNECTION *cp)
        return false;
 }
 
+static void fetch_pool_ssl_cert(POOL_CONNECTION *cp)
+{
+       int         len;
+       X509 *peer = SSL_get_peer_certificate(cp->ssl);
+       cp->peer = peer;
+       if (peer)
+       {
+               ereport(DEBUG1,
+                               (errmsg("got the SSL certificate")));
+               len = X509_NAME_get_text_by_NID(X509_get_subject_name(peer),NID_commonName, NULL, 0);
+               if (len != -1)
+               {
+                       char       *peer_cn;
+                       peer_cn = palloc(len + 1);
+                       int r = X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_cn, len + 1);
+                       peer_cn[len] = '\0';
+                       if (r != len)
+                       {
+                               /* shouldn't happen */
+                               pfree(peer_cn);
+                               return;
+                       }
+                       cp->client_cert_loaded = true;
+                       cp->cert_cn = MemoryContextStrdup(TopMemoryContext,peer_cn);
+                       pfree(peer_cn);
+               }
+               else
+               {
+                       cp->client_cert_loaded = false;
+               }
+       }
+       else
+       {
+               cp->client_cert_loaded = false;
+       }
+}
+
+/*
+ *     Passphrase collection callback
+ *
+ * If OpenSSL is told to use a passphrase-protected server key, by default
+ * it will issue a prompt on /dev/tty and try to read a key from there.
+ * That's no good during a postmaster SIGHUP cycle, not to mention SSL context
+ * reload in an EXEC_BACKEND postmaster child.  So override it with this dummy
+ * function that just returns an empty passphrase, guaranteeing failure.
+ */
+static int
+ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+{
+       /* Set flag to change the error message we'll report */
+       ssl_passwd_cb_called = true;
+       /* And return empty string */
+       Assert(size > 0);
+       buf[0] = '\0';
+       return 0;
+}
+/*
+ *     Certificate verification callback
+ *
+ *     This callback allows us to log intermediate problems during
+ *     verification, but for now we'll see if the final error message
+ *     contains enough information.
+ *
+ *     This callback also allows us to override the default acceptance
+ *     criteria (e.g., accepting self-signed or expired certs), but
+ *     for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+       return ok;
+}
+
+/*
+ *     Initialize global SSL context.
+ *
+ * If isServerStart is true, report any errors as FATAL (so we don't return).
+ * Otherwise, log errors at LOG level and return -1 to indicate trouble,
+ * preserving the old SSL state if any.  Returns 0 if OK.
+ */
+int
+SSL_ServerSide_init(void)
+{
+       STACK_OF(X509_NAME) *root_cert_list = NULL;
+       SSL_CTX    *context;
+       struct stat buf;
+
+       /* This stuff need be done only once. */
+       if (!SSL_initialized)
+       {
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+               OPENSSL_init_ssl(0, NULL);
+#else
+               SSL_library_init();
+#endif
+               SSL_load_error_strings();
+
+               SSL_initialized = true;
+       }
+
+       /*
+        * We use SSLv23_method() because it can negotiate use of the highest
+        * mutually supported protocol version, while alternatives like
+        * TLSv1_2_method() permit only one specific version.  Note that we don't
+        * actually allow SSL v2 or v3, only TLS protocols (see below).
+        */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+       context = SSL_CTX_new(TLS_method());
+#else
+       context = SSL_CTX_new(SSLv23_method());
+#endif
+
+       if (!context)
+       {
+               ereport(WARNING,
+                               (errmsg("could not create SSL context: %s",
+                                               SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+
+       /*
+        * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+        * unnecessary failures in nonblocking send cases.
+        */
+       SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+       /*
+        * prompt for password for passphrase-protected files
+        */
+       SSL_CTX_set_default_passwd_cb(context, ssl_passwd_cb);
+
+       /*
+        * Load and verify server's certificate and private key
+        */
+       if (SSL_CTX_use_certificate_chain_file(context, pool_config->ssl_cert) != 1)
+       {
+               ereport(WARNING,
+                               (errmsg("could not load server certificate file \"%s\": %s",
+                                               pool_config->ssl_cert, SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+
+       if (stat(pool_config->ssl_key, &buf) != 0)
+       {
+               ereport(WARNING,
+                               (errmsg("could not access private key file \"%s\": %m",
+                                               pool_config->ssl_key)));
+               goto error;
+       }
+
+       if (!S_ISREG(buf.st_mode))
+       {
+               ereport(WARNING,
+                               (errmsg("private key file \"%s\" is not a regular file",
+                                               pool_config->ssl_key)));
+               goto error;
+       }
+
+       /*
+        * Refuse to load key files owned by users other than us or root.
+        *
+        * XXX surely we can check this on Windows somehow, too.
+        */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+       if (buf.st_uid != geteuid() && buf.st_uid != 0)
+       {
+               ereport(WARNING,
+                               (errmsg("private key file \"%s\" must be owned by the Pgpool-II user or root",
+                                               pool_config->ssl_key)));
+               goto error;
+       }
+#endif
+
+       /*
+        * Require no public access to key file. If the file is owned by us,
+        * require mode 0600 or less. If owned by root, require 0640 or less to
+        * allow read access through our gid, or a supplementary gid that allows
+        * to read system-wide certificates.
+        *
+        * XXX temporarily suppress check when on Windows, because there may not
+        * be proper support for Unix-y file permissions.  Need to think of a
+        * reasonable check to apply on Windows.  (See also the data directory
+        * permission check in postmaster.c)
+        */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+       if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+               (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+       {
+               ereport(WARNING,
+                               (errmsg("private key file \"%s\" has group or world access",
+                                               pool_config->ssl_key),
+                                errdetail("File must have permissions u=rw (0600) or less if owned by the Pgpool-II user, or permissions u=rw,g=r (0640) or less if owned by root.")));
+               goto error;
+       }
+#endif
+
+       /*
+        * OK, try to load the private key file.
+        */
+       ssl_passwd_cb_called = false;
+
+       if (SSL_CTX_use_PrivateKey_file(context,
+                                                                       pool_config->ssl_key,
+                                                                       SSL_FILETYPE_PEM) != 1)
+       {
+               if (ssl_passwd_cb_called)
+                       ereport(WARNING,
+                                       (errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
+                                                       pool_config->ssl_key)));
+               else
+                       ereport(WARNING,
+                                       (errmsg("could not load private key file \"%s\": %s",
+                                                       pool_config->ssl_key, SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+
+       if (SSL_CTX_check_private_key(context) != 1)
+       {
+               ereport(WARNING,
+                               (errmsg("check of private key failed: %s",
+                                               SSLerrmessage(ERR_get_error()))));
+               goto error;
+       }
+
+       /* disallow SSL v2/v3 */
+       SSL_CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+       /* disallow SSL session tickets */
+#ifdef SSL_OP_NO_TICKET                        /* added in openssl 0.9.8f */
+       SSL_CTX_set_options(context, SSL_OP_NO_TICKET);
+#endif
+
+       /* disallow SSL session caching, too */
+       SSL_CTX_set_session_cache_mode(context, SSL_SESS_CACHE_OFF);
+
+       /*
+        * Load CA store, so we can verify client certificates if needed.
+        */
+       if (pool_config->ssl_ca_cert)
+       {
+               char *cacert = NULL, *cacert_dir = NULL;
+
+               if (strlen(pool_config->ssl_ca_cert))
+                       cacert = pool_config->ssl_ca_cert;
+               if (strlen(pool_config->ssl_ca_cert_dir))
+                       cacert_dir = pool_config->ssl_ca_cert_dir;
+
+               if (SSL_CTX_load_verify_locations(context, cacert, cacert_dir) != 1 ||
+                       (root_cert_list = SSL_load_client_CA_file(cacert)) == NULL)
+               {
+                       ereport(WARNING,
+                                       (errmsg("could not load root certificate file \"%s\": %s",
+                                                       pool_config->ssl_ca_cert, SSLerrmessage(ERR_get_error()))));
+                       goto error;
+               }
+               /*
+                * Always ask for SSL client cert, but don't fail if it's not
+                * presented.  We might fail such connections later, depending on what
+                * we find in pg_hba.conf.
+                */
+               SSL_CTX_set_verify(context,
+                                                  (SSL_VERIFY_PEER |
+                                                       SSL_VERIFY_CLIENT_ONCE),
+                                                  verify_cb);
+
+               /*
+                * Tell OpenSSL to send the list of root certs we trust to clients in
+                * CertificateRequests.  This lets a client with a keystore select the
+                * appropriate client certificate to send to us.
+                */
+               SSL_CTX_set_client_CA_list(context, root_cert_list);
+       }
+
+       /*
+        * Success!  Replace any existing SSL_context.
+        */
+       if (SSL_frontend_context)
+               SSL_CTX_free(SSL_frontend_context);
+
+       SSL_frontend_context = context;
+
+       return 0;
+
+error:
+       if (context)
+               SSL_CTX_free(context);
+       return -1;
+}
+
 #else /* USE_SSL: wrap / no-op ssl functionality if it's not available */
 
 void pool_ssl_negotiate_serverclient(POOL_CONNECTION *cp) {
@@ -363,6 +698,11 @@ int pool_ssl_write(POOL_CONNECTION *cp, const void *buf, int size) {
        return -1; /* never reached */
 }
 
+int SSL_ServerSide_init(void)
+{
+       return 0;
+}
+
 bool pool_ssl_pending(POOL_CONNECTION *cp)
 {
        return false;
diff --git a/src/utils/scram-common.c b/src/utils/scram-common.c
new file mode 100644 (file)
index 0000000..0b59e11
--- /dev/null
@@ -0,0 +1,238 @@
+/*-------------------------------------------------------------------------
+ * scram-common.c
+ *             Shared frontend/backend code for SCRAM authentication
+ *
+ * This contains the common low-level functions needed in both frontend and
+ * backend, for implement the Salted Challenge Response Authentication
+ * Mechanism (SCRAM), per IETF's RFC 5802.
+ *
+ * Portions Copyright (c) 2017, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *       src/common/scram-common.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* for htonl */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/base64.h"
+#include "auth/scram-common.h"
+
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5C
+
+/*
+ * Calculate HMAC per RFC2104.
+ *
+ * The hash function used is SHA-256.
+ */
+void
+scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
+{
+       uint8           k_ipad[SHA256_HMAC_B];
+       int                     i;
+       uint8           keybuf[SCRAM_KEY_LEN];
+
+       /*
+        * If the key is longer than the block size (64 bytes for SHA-256), pass
+        * it through SHA-256 once to shrink it down.
+        */
+       if (keylen > SHA256_HMAC_B)
+       {
+               pg_sha256_ctx sha256_ctx;
+
+               pg_sha256_init(&sha256_ctx);
+               pg_sha256_update(&sha256_ctx, key, keylen);
+               pg_sha256_final(&sha256_ctx, keybuf);
+               key = keybuf;
+               keylen = SCRAM_KEY_LEN;
+       }
+
+       memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B);
+       memset(ctx->k_opad, HMAC_OPAD, SHA256_HMAC_B);
+
+       for (i = 0; i < keylen; i++)
+       {
+               k_ipad[i] ^= key[i];
+               ctx->k_opad[i] ^= key[i];
+       }
+
+       /* tmp = H(K XOR ipad, text) */
+       pg_sha256_init(&ctx->sha256ctx);
+       pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B);
+}
+
+/*
+ * Update HMAC calculation
+ * The hash function used is SHA-256.
+ */
+void
+scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen)
+{
+       pg_sha256_update(&ctx->sha256ctx, (const uint8 *) str, slen);
+}
+
+/*
+ * Finalize HMAC calculation.
+ * The hash function used is SHA-256.
+ */
+void
+scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx)
+{
+       uint8           h[SCRAM_KEY_LEN];
+
+       pg_sha256_final(&ctx->sha256ctx, h);
+
+       /* H(K XOR opad, tmp) */
+       pg_sha256_init(&ctx->sha256ctx);
+       pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B);
+       pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN);
+       pg_sha256_final(&ctx->sha256ctx, result);
+}
+
+/*
+ * Calculate SaltedPassword.
+ *
+ * The password should already be normalized by SASLprep.
+ */
+void
+scram_SaltedPassword(const char *password,
+                                        const char *salt, int saltlen, int iterations,
+                                        uint8 *result)
+{
+       int                     password_len = strlen(password);
+       uint32          one = htonl(1);
+       int                     i,
+                               j;
+       uint8           Ui[SCRAM_KEY_LEN];
+       uint8           Ui_prev[SCRAM_KEY_LEN];
+       scram_HMAC_ctx hmac_ctx;
+
+       /*
+        * Iterate hash calculation of HMAC entry using given salt.  This is
+        * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom
+        * function.
+        */
+
+       /* First iteration */
+       scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
+       scram_HMAC_update(&hmac_ctx, salt, saltlen);
+       scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32));
+       scram_HMAC_final(Ui_prev, &hmac_ctx);
+       memcpy(result, Ui_prev, SCRAM_KEY_LEN);
+
+       /* Subsequent iterations */
+       for (i = 2; i <= iterations; i++)
+       {
+               scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
+               scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN);
+               scram_HMAC_final(Ui, &hmac_ctx);
+               for (j = 0; j < SCRAM_KEY_LEN; j++)
+                       result[j] ^= Ui[j];
+               memcpy(Ui_prev, Ui, SCRAM_KEY_LEN);
+       }
+}
+
+
+/*
+ * Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is
+ * not included in the hash).
+ */
+void
+scram_H(const uint8 *input, int len, uint8 *result)
+{
+       pg_sha256_ctx ctx;
+
+       pg_sha256_init(&ctx);
+       pg_sha256_update(&ctx, input, len);
+       pg_sha256_final(&ctx, result);
+}
+
+/*
+ * Calculate ClientKey.
+ */
+void
+scram_ClientKey(const uint8 *salted_password, uint8 *result)
+{
+       scram_HMAC_ctx ctx;
+
+       scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
+       scram_HMAC_update(&ctx, "Client Key", strlen("Client Key"));
+       scram_HMAC_final(result, &ctx);
+}
+
+/*
+ * Calculate ServerKey.
+ */
+void
+scram_ServerKey(const uint8 *salted_password, uint8 *result)
+{
+       scram_HMAC_ctx ctx;
+
+       scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
+       scram_HMAC_update(&ctx, "Server Key", strlen("Server Key"));
+       scram_HMAC_final(result, &ctx);
+}
+
+
+/*
+ * Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
+ *
+ * The password should already have been processed with SASLprep, if necessary!
+ *
+ * If iterations is 0, default number of iterations is used.  The result is
+ * palloc'd or malloc'd, so caller is responsible for freeing it.
+ */
+char *
+scram_build_verifier(const char *salt, int saltlen, int iterations,
+                                        const char *password)
+{
+       uint8           salted_password[SCRAM_KEY_LEN];
+       uint8           stored_key[SCRAM_KEY_LEN];
+       uint8           server_key[SCRAM_KEY_LEN];
+       char       *result;
+       char       *p;
+       int                     maxlen;
+
+       if (iterations <= 0)
+               iterations = SCRAM_DEFAULT_ITERATIONS;
+
+       /* Calculate StoredKey and ServerKey */
+       scram_SaltedPassword(password, salt, saltlen, iterations,
+                                                salted_password);
+       scram_ClientKey(salted_password, stored_key);
+       scram_H(stored_key, SCRAM_KEY_LEN, stored_key);
+
+       scram_ServerKey(salted_password, server_key);
+
+       /*----------
+        * The format is:
+        * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
+        *----------
+        */
+       maxlen = strlen("SCRAM-SHA-256") + 1
+               + 10 + 1                                /* iteration count */
+               + pg_b64_enc_len(saltlen) + 1   /* Base64-encoded salt */
+               + pg_b64_enc_len(SCRAM_KEY_LEN) + 1 /* Base64-encoded StoredKey */
+               + pg_b64_enc_len(SCRAM_KEY_LEN) + 1;    /* Base64-encoded ServerKey */
+
+       result = palloc(maxlen);
+
+       p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
+
+       p += pg_b64_encode(salt, saltlen, p);
+       *(p++) = '$';
+       p += pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p);
+       *(p++) = ':';
+       p += pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p);
+       *(p++) = '\0';
+
+       Assert(p - result <= maxlen);
+
+       return result;
+}
diff --git a/src/utils/sha2.c b/src/utils/sha2.c
new file mode 100644 (file)
index 0000000..e2f7296
--- /dev/null
@@ -0,0 +1,999 @@
+/*-------------------------------------------------------------------------
+ *
+ * sha2.c
+ *       Set of SHA functions for SHA-224, SHA-256, SHA-384 and SHA-512.
+ *
+ * This is the set of in-core functions used when there are no other
+ * alternative options like OpenSSL.
+ *
+ * Portions Copyright (c) 2016-2017, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *       src/common/sha2.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*     $OpenBSD: sha2.c,v 1.6 2004/05/03 02:57:36 millert Exp $        */
+/*
+ * FILE:       sha2.c
+ * AUTHOR:     Aaron D. Gifford <me@aarongifford.com>
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ *       may be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
+ */
+
+#include <string.h>
+#include <sys/param.h>
+#include "utils/sha2.h"
+
+/*
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file).  Either define on the command line, for example:
+ *
+ *      cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ *      #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define PG_SHA256_SHORT_BLOCK_LENGTH   (PG_SHA256_BLOCK_LENGTH - 8)
+#define PG_SHA384_SHORT_BLOCK_LENGTH   (PG_SHA384_BLOCK_LENGTH - 16)
+#define PG_SHA512_SHORT_BLOCK_LENGTH   (PG_SHA512_BLOCK_LENGTH - 16)
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#ifndef WORDS_BIGENDIAN
+#define REVERSE32(w,x) { \
+       uint32 tmp = (w); \
+       tmp = (tmp >> 16) | (tmp << 16); \
+       (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#define REVERSE64(w,x) { \
+       uint64 tmp = (w); \
+       tmp = (tmp >> 32) | (tmp << 32); \
+       tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+                 ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+       (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+                 ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#endif                                                 /* not bigendian */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n) { \
+       (w)[0] += (uint64)(n); \
+       if ((w)[0] < (n)) { \
+               (w)[1]++; \
+       } \
+}
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ *      NOTE:  The naming of R and S appears backwards here (R is a SHIFT and
+ *      S is a ROTATION) because the SHA-256/384/512 description document
+ *      (see http://www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf)
+ *      uses this same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x)         ((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x)       (((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x)       (((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z)      (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z)     (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x)  (S32(2,  (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x)  (S32(6,  (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x)  (S32(7,  (x)) ^ S32(18, (x)) ^ R(3 ,   (x)))
+#define sigma1_256(x)  (S32(17, (x)) ^ S32(19, (x)) ^ R(10,   (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x)  (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x)  (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x)  (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7,   (x)))
+#define sigma1_512(x)  (S64(19, (x)) ^ S64(61, (x)) ^ R( 6,   (x)))
+
+/*** INTERNAL FUNCTION PROTOTYPES *************************************/
+/* NOTE: These should not be accessed directly from outside this
+ * library -- they are intended for private internal visibility/use
+ * only.
+ */
+static void SHA512_Last(pg_sha512_ctx *context);
+static void SHA256_Transform(pg_sha256_ctx *context, const uint8 *data);
+static void SHA512_Transform(pg_sha512_ctx *context, const uint8 *data);
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+static const uint32 K256[64] = {
+       0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+       0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+       0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+       0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+       0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+       0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+       0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+       0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+       0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+       0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+       0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+       0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+       0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+       0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+       0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+       0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Initial hash value H for SHA-224: */
+static const uint32 sha224_initial_hash_value[8] = {
+       0xc1059ed8UL,
+       0x367cd507UL,
+       0x3070dd17UL,
+       0xf70e5939UL,
+       0xffc00b31UL,
+       0x68581511UL,
+       0x64f98fa7UL,
+       0xbefa4fa4UL
+};
+
+/* Initial hash value H for SHA-256: */
+static const uint32 sha256_initial_hash_value[8] = {
+       0x6a09e667UL,
+       0xbb67ae85UL,
+       0x3c6ef372UL,
+       0xa54ff53aUL,
+       0x510e527fUL,
+       0x9b05688cUL,
+       0x1f83d9abUL,
+       0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+static const uint64 K512[80] = {
+       0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+       0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+       0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+       0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+       0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+       0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+       0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+       0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+       0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+       0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+       0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+       0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+       0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+       0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+       0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+       0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+       0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+       0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+       0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+       0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+       0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+       0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+       0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+       0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+       0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+       0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+       0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+       0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+       0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+       0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+       0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+       0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+       0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+       0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+       0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+       0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+       0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+       0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+       0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+       0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* Initial hash value H for SHA-384 */
+static const uint64 sha384_initial_hash_value[8] = {
+       0xcbbb9d5dc1059ed8ULL,
+       0x629a292a367cd507ULL,
+       0x9159015a3070dd17ULL,
+       0x152fecd8f70e5939ULL,
+       0x67332667ffc00b31ULL,
+       0x8eb44a8768581511ULL,
+       0xdb0c2e0d64f98fa7ULL,
+       0x47b5481dbefa4fa4ULL
+};
+
+/* Initial hash value H for SHA-512 */
+static const uint64 sha512_initial_hash_value[8] = {
+       0x6a09e667f3bcc908ULL,
+       0xbb67ae8584caa73bULL,
+       0x3c6ef372fe94f82bULL,
+       0xa54ff53a5f1d36f1ULL,
+       0x510e527fade682d1ULL,
+       0x9b05688c2b3e6c1fULL,
+       0x1f83d9abfb41bd6bULL,
+       0x5be0cd19137e2179ULL
+};
+
+
+/*** SHA-256: *********************************************************/
+void
+pg_sha256_init(pg_sha256_ctx *context)
+{
+       if (context == NULL)
+               return;
+       memcpy(context->state, sha256_initial_hash_value, PG_SHA256_DIGEST_LENGTH);
+       memset(context->buffer, 0, PG_SHA256_BLOCK_LENGTH);
+       context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do {                                 \
+       W256[j] = (uint32)data[3] | ((uint32)data[2] << 8) |            \
+               ((uint32)data[1] << 16) | ((uint32)data[0] << 24);              \
+       data += 4;                                                              \
+       T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \
+       (d) += T1;                                                              \
+       (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c));                        \
+       j++;                                                                    \
+} while(0)
+
+#define ROUND256(a,b,c,d,e,f,g,h) do {                                         \
+       s0 = W256[(j+1)&0x0f];                                                  \
+       s0 = sigma0_256(s0);                                                    \
+       s1 = W256[(j+14)&0x0f];                                                 \
+       s1 = sigma1_256(s1);                                                    \
+       T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] +              \
+                (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);                  \
+       (d) += T1;                                                              \
+       (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c));                        \
+       j++;                                                                    \
+} while(0)
+
+static void
+SHA256_Transform(pg_sha256_ctx *context, const uint8 *data)
+{
+       uint32          a,
+                               b,
+                               c,
+                               d,
+                               e,
+                               f,
+                               g,
+                               h,
+                               s0,
+                               s1;
+       uint32          T1,
+                          *W256;
+       int                     j;
+
+       W256 = (uint32 *) context->buffer;
+
+       /* Initialize registers with the prev. intermediate value */
+       a = context->state[0];
+       b = context->state[1];
+       c = context->state[2];
+       d = context->state[3];
+       e = context->state[4];
+       f = context->state[5];
+       g = context->state[6];
+       h = context->state[7];
+
+       j = 0;
+       do
+       {
+               /* Rounds 0 to 15 (unrolled): */
+               ROUND256_0_TO_15(a, b, c, d, e, f, g, h);
+               ROUND256_0_TO_15(h, a, b, c, d, e, f, g);
+               ROUND256_0_TO_15(g, h, a, b, c, d, e, f);
+               ROUND256_0_TO_15(f, g, h, a, b, c, d, e);
+               ROUND256_0_TO_15(e, f, g, h, a, b, c, d);
+               ROUND256_0_TO_15(d, e, f, g, h, a, b, c);
+               ROUND256_0_TO_15(c, d, e, f, g, h, a, b);
+               ROUND256_0_TO_15(b, c, d, e, f, g, h, a);
+       } while (j < 16);
+
+       /* Now for the remaining rounds to 64: */
+       do
+       {
+               ROUND256(a, b, c, d, e, f, g, h);
+               ROUND256(h, a, b, c, d, e, f, g);
+               ROUND256(g, h, a, b, c, d, e, f);
+               ROUND256(f, g, h, a, b, c, d, e);
+               ROUND256(e, f, g, h, a, b, c, d);
+               ROUND256(d, e, f, g, h, a, b, c);
+               ROUND256(c, d, e, f, g, h, a, b);
+               ROUND256(b, c, d, e, f, g, h, a);
+       } while (j < 64);
+
+       /* Compute the current intermediate hash value */
+       context->state[0] += a;
+       context->state[1] += b;
+       context->state[2] += c;
+       context->state[3] += d;
+       context->state[4] += e;
+       context->state[5] += f;
+       context->state[6] += g;
+       context->state[7] += h;
+
+       /* Clean up */
+       a = b = c = d = e = f = g = h = T1 = 0;
+}
+#else                                                  /* SHA2_UNROLL_TRANSFORM */
+
+static void
+SHA256_Transform(pg_sha256_ctx *context, const uint8 *data)
+{
+       uint32          a,
+                               b,
+                               c,
+                               d,
+                               e,
+                               f,
+                               g,
+                               h,
+                               s0,
+                               s1;
+       uint32          T1,
+                               T2,
+                          *W256;
+       int                     j;
+
+       W256 = (uint32 *) context->buffer;
+
+       /* Initialize registers with the prev. intermediate value */
+       a = context->state[0];
+       b = context->state[1];
+       c = context->state[2];
+       d = context->state[3];
+       e = context->state[4];
+       f = context->state[5];
+       g = context->state[6];
+       h = context->state[7];
+
+       j = 0;
+       do
+       {
+               W256[j] = (uint32) data[3] | ((uint32) data[2] << 8) |
+                       ((uint32) data[1] << 16) | ((uint32) data[0] << 24);
+               data += 4;
+               /* Apply the SHA-256 compression function to update a..h */
+               T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+               T2 = Sigma0_256(a) + Maj(a, b, c);
+               h = g;
+               g = f;
+               f = e;
+               e = d + T1;
+               d = c;
+               c = b;
+               b = a;
+               a = T1 + T2;
+
+               j++;
+       } while (j < 16);
+
+       do
+       {
+               /* Part of the message block expansion: */
+               s0 = W256[(j + 1) & 0x0f];
+               s0 = sigma0_256(s0);
+               s1 = W256[(j + 14) & 0x0f];
+               s1 = sigma1_256(s1);
+
+               /* Apply the SHA-256 compression function to update a..h */
+               T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
+                       (W256[j & 0x0f] += s1 + W256[(j + 9) & 0x0f] + s0);
+               T2 = Sigma0_256(a) + Maj(a, b, c);
+               h = g;
+               g = f;
+               f = e;
+               e = d + T1;
+               d = c;
+               c = b;
+               b = a;
+               a = T1 + T2;
+
+               j++;
+       } while (j < 64);
+
+       /* Compute the current intermediate hash value */
+       context->state[0] += a;
+       context->state[1] += b;
+       context->state[2] += c;
+       context->state[3] += d;
+       context->state[4] += e;
+       context->state[5] += f;
+       context->state[6] += g;
+       context->state[7] += h;
+
+       /* Clean up */
+       a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+#endif                                                 /* SHA2_UNROLL_TRANSFORM */
+
+void
+pg_sha256_update(pg_sha256_ctx *context, const uint8 *data, size_t len)
+{
+       size_t          freespace,
+                               usedspace;
+
+       /* Calling with no data is valid (we do nothing) */
+       if (len == 0)
+               return;
+
+       usedspace = (context->bitcount >> 3) % PG_SHA256_BLOCK_LENGTH;
+       if (usedspace > 0)
+       {
+               /* Calculate how much free space is available in the buffer */
+               freespace = PG_SHA256_BLOCK_LENGTH - usedspace;
+
+               if (len >= freespace)
+               {
+                       /* Fill the buffer completely and process it */
+                       memcpy(&context->buffer[usedspace], data, freespace);
+                       context->bitcount += freespace << 3;
+                       len -= freespace;
+                       data += freespace;
+                       SHA256_Transform(context, context->buffer);
+               }
+               else
+               {
+                       /* The buffer is not yet full */
+                       memcpy(&context->buffer[usedspace], data, len);
+                       context->bitcount += len << 3;
+                       /* Clean up: */
+                       usedspace = freespace = 0;
+                       return;
+               }
+       }
+       while (len >= PG_SHA256_BLOCK_LENGTH)
+       {
+               /* Process as many complete blocks as we can */
+               SHA256_Transform(context, data);
+               context->bitcount += PG_SHA256_BLOCK_LENGTH << 3;
+               len -= PG_SHA256_BLOCK_LENGTH;
+               data += PG_SHA256_BLOCK_LENGTH;
+       }
+       if (len > 0)
+       {
+               /* There's left-overs, so save 'em */
+               memcpy(context->buffer, data, len);
+               context->bitcount += len << 3;
+       }
+       /* Clean up: */
+       usedspace = freespace = 0;
+}
+
+static void
+SHA256_Last(pg_sha256_ctx *context)
+{
+       unsigned int usedspace;
+
+       usedspace = (context->bitcount >> 3) % PG_SHA256_BLOCK_LENGTH;
+#ifndef WORDS_BIGENDIAN
+       /* Convert FROM host byte order */
+       REVERSE64(context->bitcount, context->bitcount);
+#endif
+       if (usedspace > 0)
+       {
+               /* Begin padding with a 1 bit: */
+               context->buffer[usedspace++] = 0x80;
+
+               if (usedspace <= PG_SHA256_SHORT_BLOCK_LENGTH)
+               {
+                       /* Set-up for the last transform: */
+                       memset(&context->buffer[usedspace], 0, PG_SHA256_SHORT_BLOCK_LENGTH - usedspace);
+               }
+               else
+               {
+                       if (usedspace < PG_SHA256_BLOCK_LENGTH)
+                       {
+                               memset(&context->buffer[usedspace], 0, PG_SHA256_BLOCK_LENGTH - usedspace);
+                       }
+                       /* Do second-to-last transform: */
+                       SHA256_Transform(context, context->buffer);
+
+                       /* And set-up for the last transform: */
+                       memset(context->buffer, 0, PG_SHA256_SHORT_BLOCK_LENGTH);
+               }
+       }
+       else
+       {
+               /* Set-up for the last transform: */
+               memset(context->buffer, 0, PG_SHA256_SHORT_BLOCK_LENGTH);
+
+               /* Begin padding with a 1 bit: */
+               *context->buffer = 0x80;
+       }
+       /* Set the bit count: */
+       *(uint64 *) &context->buffer[PG_SHA256_SHORT_BLOCK_LENGTH] = context->bitcount;
+
+       /* Final transform: */
+       SHA256_Transform(context, context->buffer);
+}
+
+void
+pg_sha256_final(pg_sha256_ctx *context, uint8 *digest)
+{
+       /* If no digest buffer is passed, we don't bother doing this: */
+       if (digest != NULL)
+       {
+               SHA256_Last(context);
+
+#ifndef WORDS_BIGENDIAN
+               {
+                       /* Convert TO host byte order */
+                       int                     j;
+
+                       for (j = 0; j < 8; j++)
+                       {
+                               REVERSE32(context->state[j], context->state[j]);
+                       }
+               }
+#endif
+               memcpy(digest, context->state, PG_SHA256_DIGEST_LENGTH);
+       }
+
+       /* Clean up state data: */
+       memset(context, 0, sizeof(pg_sha256_ctx));
+}
+
+
+/*** SHA-512: *********************************************************/
+void
+pg_sha512_init(pg_sha512_ctx *context)
+{
+       if (context == NULL)
+               return;
+       memcpy(context->state, sha512_initial_hash_value, PG_SHA512_DIGEST_LENGTH);
+       memset(context->buffer, 0, PG_SHA512_BLOCK_LENGTH);
+       context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do {                                 \
+       W512[j] = (uint64)data[7] | ((uint64)data[6] << 8) |            \
+               ((uint64)data[5] << 16) | ((uint64)data[4] << 24) |             \
+               ((uint64)data[3] << 32) | ((uint64)data[2] << 40) |             \
+               ((uint64)data[1] << 48) | ((uint64)data[0] << 56);              \
+       data += 8;                                                              \
+       T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \
+       (d) += T1;                                                              \
+       (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c));                        \
+       j++;                                                                    \
+} while(0)
+
+
+#define ROUND512(a,b,c,d,e,f,g,h) do {                                         \
+       s0 = W512[(j+1)&0x0f];                                                  \
+       s0 = sigma0_512(s0);                                                    \
+       s1 = W512[(j+14)&0x0f];                                                 \
+       s1 = sigma1_512(s1);                                                    \
+       T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] +              \
+                        (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);                  \
+       (d) += T1;                                                              \
+       (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c));                        \
+       j++;                                                                    \
+} while(0)
+
+static void
+SHA512_Transform(pg_sha512_ctx *context, const uint8 *data)
+{
+       uint64          a,
+                               b,
+                               c,
+                               d,
+                               e,
+                               f,
+                               g,
+                               h,
+                               s0,
+                               s1;
+       uint64          T1,
+                          *W512 = (uint64 *) context->buffer;
+       int                     j;
+
+       /* Initialize registers with the prev. intermediate value */
+       a = context->state[0];
+       b = context->state[1];
+       c = context->state[2];
+       d = context->state[3];
+       e = context->state[4];
+       f = context->state[5];
+       g = context->state[6];
+       h = context->state[7];
+
+       j = 0;
+       do
+       {
+               ROUND512_0_TO_15(a, b, c, d, e, f, g, h);
+               ROUND512_0_TO_15(h, a, b, c, d, e, f, g);
+               ROUND512_0_TO_15(g, h, a, b, c, d, e, f);
+               ROUND512_0_TO_15(f, g, h, a, b, c, d, e);
+               ROUND512_0_TO_15(e, f, g, h, a, b, c, d);
+               ROUND512_0_TO_15(d, e, f, g, h, a, b, c);
+               ROUND512_0_TO_15(c, d, e, f, g, h, a, b);
+               ROUND512_0_TO_15(b, c, d, e, f, g, h, a);
+       } while (j < 16);
+
+       /* Now for the remaining rounds up to 79: */
+       do
+       {
+               ROUND512(a, b, c, d, e, f, g, h);
+               ROUND512(h, a, b, c, d, e, f, g);
+               ROUND512(g, h, a, b, c, d, e, f);
+               ROUND512(f, g, h, a, b, c, d, e);
+               ROUND512(e, f, g, h, a, b, c, d);
+               ROUND512(d, e, f, g, h, a, b, c);
+               ROUND512(c, d, e, f, g, h, a, b);
+               ROUND512(b, c, d, e, f, g, h, a);
+       } while (j < 80);
+
+       /* Compute the current intermediate hash value */
+       context->state[0] += a;
+       context->state[1] += b;
+       context->state[2] += c;
+       context->state[3] += d;
+       context->state[4] += e;
+       context->state[5] += f;
+       context->state[6] += g;
+       context->state[7] += h;
+
+       /* Clean up */
+       a = b = c = d = e = f = g = h = T1 = 0;
+}
+#else                                                  /* SHA2_UNROLL_TRANSFORM */
+
+static void
+SHA512_Transform(pg_sha512_ctx *context, const uint8 *data)
+{
+       uint64          a,
+                               b,
+                               c,
+                               d,
+                               e,
+                               f,
+                               g,
+                               h,
+                               s0,
+                               s1;
+       uint64          T1,
+                               T2,
+                          *W512 = (uint64 *) context->buffer;
+       int                     j;
+
+       /* Initialize registers with the prev. intermediate value */
+       a = context->state[0];
+       b = context->state[1];
+       c = context->state[2];
+       d = context->state[3];
+       e = context->state[4];
+       f = context->state[5];
+       g = context->state[6];
+       h = context->state[7];
+
+       j = 0;
+       do
+       {
+               W512[j] = (uint64) data[7] | ((uint64) data[6] << 8) |
+                       ((uint64) data[5] << 16) | ((uint64) data[4] << 24) |
+                       ((uint64) data[3] << 32) | ((uint64) data[2] << 40) |
+                       ((uint64) data[1] << 48) | ((uint64) data[0] << 56);
+               data += 8;
+               /* Apply the SHA-512 compression function to update a..h */
+               T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+               T2 = Sigma0_512(a) + Maj(a, b, c);
+               h = g;
+               g = f;
+               f = e;
+               e = d + T1;
+               d = c;
+               c = b;
+               b = a;
+               a = T1 + T2;
+
+               j++;
+       } while (j < 16);
+
+       do
+       {
+               /* Part of the message block expansion: */
+               s0 = W512[(j + 1) & 0x0f];
+               s0 = sigma0_512(s0);
+               s1 = W512[(j + 14) & 0x0f];
+               s1 = sigma1_512(s1);
+
+               /* Apply the SHA-512 compression function to update a..h */
+               T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+                       (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0);
+               T2 = Sigma0_512(a) + Maj(a, b, c);
+               h = g;
+               g = f;
+               f = e;
+               e = d + T1;
+               d = c;
+               c = b;
+               b = a;
+               a = T1 + T2;
+
+               j++;
+       } while (j < 80);
+
+       /* Compute the current intermediate hash value */
+       context->state[0] += a;
+       context->state[1] += b;
+       context->state[2] += c;
+       context->state[3] += d;
+       context->state[4] += e;
+       context->state[5] += f;
+       context->state[6] += g;
+       context->state[7] += h;
+
+       /* Clean up */
+       a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+#endif                                                 /* SHA2_UNROLL_TRANSFORM */
+
+void
+pg_sha512_update(pg_sha512_ctx *context, const uint8 *data, size_t len)
+{
+       size_t          freespace,
+                               usedspace;
+
+       /* Calling with no data is valid (we do nothing) */
+       if (len == 0)
+               return;
+
+       usedspace = (context->bitcount[0] >> 3) % PG_SHA512_BLOCK_LENGTH;
+       if (usedspace > 0)
+       {
+               /* Calculate how much free space is available in the buffer */
+               freespace = PG_SHA512_BLOCK_LENGTH - usedspace;
+
+               if (len >= freespace)
+               {
+                       /* Fill the buffer completely and process it */
+                       memcpy(&context->buffer[usedspace], data, freespace);
+                       ADDINC128(context->bitcount, freespace << 3);
+                       len -= freespace;
+                       data += freespace;
+                       SHA512_Transform(context, context->buffer);
+               }
+               else
+               {
+                       /* The buffer is not yet full */
+                       memcpy(&context->buffer[usedspace], data, len);
+                       ADDINC128(context->bitcount, len << 3);
+                       /* Clean up: */
+                       usedspace = freespace = 0;
+                       return;
+               }
+       }
+       while (len >= PG_SHA512_BLOCK_LENGTH)
+       {
+               /* Process as many complete blocks as we can */
+               SHA512_Transform(context, data);
+               ADDINC128(context->bitcount, PG_SHA512_BLOCK_LENGTH << 3);
+               len -= PG_SHA512_BLOCK_LENGTH;
+               data += PG_SHA512_BLOCK_LENGTH;
+       }
+       if (len > 0)
+       {
+               /* There's left-overs, so save 'em */
+               memcpy(context->buffer, data, len);
+               ADDINC128(context->bitcount, len << 3);
+       }
+       /* Clean up: */
+       usedspace = freespace = 0;
+}
+
+static void
+SHA512_Last(pg_sha512_ctx *context)
+{
+       unsigned int usedspace;
+
+       usedspace = (context->bitcount[0] >> 3) % PG_SHA512_BLOCK_LENGTH;
+#ifndef WORDS_BIGENDIAN
+       /* Convert FROM host byte order */
+       REVERSE64(context->bitcount[0], context->bitcount[0]);
+       REVERSE64(context->bitcount[1], context->bitcount[1]);
+#endif
+       if (usedspace > 0)
+       {
+               /* Begin padding with a 1 bit: */
+               context->buffer[usedspace++] = 0x80;
+
+               if (usedspace <= PG_SHA512_SHORT_BLOCK_LENGTH)
+               {
+                       /* Set-up for the last transform: */
+                       memset(&context->buffer[usedspace], 0, PG_SHA512_SHORT_BLOCK_LENGTH - usedspace);
+               }
+               else
+               {
+                       if (usedspace < PG_SHA512_BLOCK_LENGTH)
+                       {
+                               memset(&context->buffer[usedspace], 0, PG_SHA512_BLOCK_LENGTH - usedspace);
+                       }
+                       /* Do second-to-last transform: */
+                       SHA512_Transform(context, context->buffer);
+
+                       /* And set-up for the last transform: */
+                       memset(context->buffer, 0, PG_SHA512_BLOCK_LENGTH - 2);
+               }
+       }
+       else
+       {
+               /* Prepare for final transform: */
+               memset(context->buffer, 0, PG_SHA512_SHORT_BLOCK_LENGTH);
+
+               /* Begin padding with a 1 bit: */
+               *context->buffer = 0x80;
+       }
+       /* Store the length of input data (in bits): */
+       *(uint64 *) &context->buffer[PG_SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1];
+       *(uint64 *) &context->buffer[PG_SHA512_SHORT_BLOCK_LENGTH + 8] = context->bitcount[0];
+
+       /* Final transform: */
+       SHA512_Transform(context, context->buffer);
+}
+
+void
+pg_sha512_final(pg_sha512_ctx *context, uint8 *digest)
+{
+       /* If no digest buffer is passed, we don't bother doing this: */
+       if (digest != NULL)
+       {
+               SHA512_Last(context);
+
+               /* Save the hash data for output: */
+#ifndef WORDS_BIGENDIAN
+               {
+                       /* Convert TO host byte order */
+                       int                     j;
+
+                       for (j = 0; j < 8; j++)
+                       {
+                               REVERSE64(context->state[j], context->state[j]);
+                       }
+               }
+#endif
+               memcpy(digest, context->state, PG_SHA512_DIGEST_LENGTH);
+       }
+
+       /* Zero out state data */
+       memset(context, 0, sizeof(pg_sha512_ctx));
+}
+
+
+/*** SHA-384: *********************************************************/
+void
+pg_sha384_init(pg_sha384_ctx *context)
+{
+       if (context == NULL)
+               return;
+       memcpy(context->state, sha384_initial_hash_value, PG_SHA512_DIGEST_LENGTH);
+       memset(context->buffer, 0, PG_SHA384_BLOCK_LENGTH);
+       context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void
+pg_sha384_update(pg_sha384_ctx *context, const uint8 *data, size_t len)
+{
+       pg_sha512_update((pg_sha512_ctx *) context, data, len);
+}
+
+void
+pg_sha384_final(pg_sha384_ctx *context, uint8 *digest)
+{
+       /* If no digest buffer is passed, we don't bother doing this: */
+       if (digest != NULL)
+       {
+               SHA512_Last((pg_sha512_ctx *) context);
+
+               /* Save the hash data for output: */
+#ifndef WORDS_BIGENDIAN
+               {
+                       /* Convert TO host byte order */
+                       int                     j;
+
+                       for (j = 0; j < 6; j++)
+                       {
+                               REVERSE64(context->state[j], context->state[j]);
+                       }
+               }
+#endif
+               memcpy(digest, context->state, PG_SHA384_DIGEST_LENGTH);
+       }
+
+       /* Zero out state data */
+       memset(context, 0, sizeof(pg_sha384_ctx));
+}
+
+/*** SHA-224: *********************************************************/
+void
+pg_sha224_init(pg_sha224_ctx *context)
+{
+       if (context == NULL)
+               return;
+       memcpy(context->state, sha224_initial_hash_value, PG_SHA256_DIGEST_LENGTH);
+       memset(context->buffer, 0, PG_SHA256_BLOCK_LENGTH);
+       context->bitcount = 0;
+}
+
+void
+pg_sha224_update(pg_sha224_ctx *context, const uint8 *data, size_t len)
+{
+       pg_sha256_update((pg_sha256_ctx *) context, data, len);
+}
+
+void
+pg_sha224_final(pg_sha224_ctx *context, uint8 *digest)
+{
+       /* If no digest buffer is passed, we don't bother doing this: */
+       if (digest != NULL)
+       {
+               SHA256_Last(context);
+
+#ifndef WORDS_BIGENDIAN
+               {
+                       /* Convert TO host byte order */
+                       int                     j;
+
+                       for (j = 0; j < 8; j++)
+                       {
+                               REVERSE32(context->state[j], context->state[j]);
+                       }
+               }
+#endif
+               memcpy(digest, context->state, PG_SHA224_DIGEST_LENGTH);
+       }
+
+       /* Clean up state data: */
+       memset(context, 0, sizeof(pg_sha224_ctx));
+}
diff --git a/src/utils/ssl_utils.c b/src/utils/ssl_utils.c
new file mode 100644 (file)
index 0000000..df35655
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * $Header$
+ *
+ * Handles watchdog connection, and protocol communication with pgpool-II
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2018     PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "pool.h"
+#include "auth/md5.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
+#include "pool_config.h"
+#include "utils/ssl_utils.h"
+
+#ifdef USE_SSL
+static int aes_get_key(const char *password, unsigned char     *key, unsigned char     *iv);
+static int aes_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
+                               unsigned char *iv, unsigned char *ciphertext);
+
+static int aes_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
+                               unsigned char *iv, unsigned char *plaintext);
+#endif
+
+#ifdef USE_SSL
+
+int aes_encrypt_with_password(unsigned char *plaintext, int plaintext_len,
+                                                 const char *password, unsigned char *ciphertext)
+{
+       unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
+
+       /* First get the key and iv using the password */
+       if (aes_get_key(password, key, iv) != 0)
+               return -1;
+       return aes_encrypt(plaintext, plaintext_len, key, iv, ciphertext);
+}
+
+int aes_decrypt_with_password(unsigned char *ciphertext, int ciphertext_len,
+                                                         const char *password, unsigned char *plaintext)
+{
+       unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
+
+       /* First get the key and iv using the password */
+       if (aes_get_key(password, key, iv) != 0)
+               return -1;
+       return aes_decrypt(ciphertext, ciphertext_len, key,
+                                          iv, plaintext);
+}
+
+/*
+ * key must be EVP_MAX_KEY_LENGTH length
+ * iv must be EVP_MAX_IV_LENGTH length
+ */
+static int aes_get_key(const char *password, unsigned char     *key, unsigned char     *iv)
+{
+       const unsigned char *salt = NULL;
+
+       OpenSSL_add_all_algorithms();
+
+       if(!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt,
+                                          (unsigned char *) password,
+                                          strlen(password), 1, key, iv))
+       {
+               ereport(ERROR,
+                               (errmsg("unable to generate AES key from password"),
+                                errdetail("EVP_BytesToKey failed")));
+               return 1;
+       }
+
+#ifdef DEBUG_ENCRYPT
+       printf("Key: "); for(i=0; i<EVP_aes_256_cbc()->key_len; ++i) { printf("%02x", key[i]); } printf("\n");
+       printf("IV: "); for(i=0; i<EVP_aes_256_cbc()->iv_len; ++i) { printf("%02x", iv[i]); } printf("\n");
+#endif
+       return 0;
+}
+
+/*
+ * from: https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption
+ */
+static int aes_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
+                       unsigned char *iv, unsigned char *ciphertext)
+{
+       EVP_CIPHER_CTX *ctx = NULL;
+       int len;
+       int ciphertext_len;
+
+       /* Create and initialise the context */
+       if(!(ctx = EVP_CIPHER_CTX_new()))
+               goto encrypt_error;
+       /* Initialise the encryption operation. IMPORTANT - ensure you use a key
+        * and IV size appropriate for your cipher
+        * In this example we are using 256 bit AES (i.e. a 256 bit key). The
+        * IV size for *most* modes is the same as the block size. For AES this
+        * is 128 bits */
+       if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1)
+               goto encrypt_error;
+
+       /* Provide the message to be encrypted, and obtain the encrypted output.
+        * EVP_EncryptUpdate can be called multiple times if necessary
+        */
+       if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len) != 1)
+               goto encrypt_error;
+       ciphertext_len = len;
+
+       /* Finalise the encryption. Further ciphertext bytes may be written at
+        * this stage.
+        */
+       if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1)
+               goto encrypt_error;
+       ciphertext_len += len;
+
+       /* Clean up */
+       EVP_CIPHER_CTX_free(ctx);
+
+       return ciphertext_len;
+
+encrypt_error:
+       if (ctx)
+               EVP_CIPHER_CTX_free(ctx);
+       return -1;
+}
+
+static int aes_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
+                       unsigned char *iv, unsigned char *plaintext)
+{
+       EVP_CIPHER_CTX *ctx = NULL;
+       int len;
+       int plaintext_len;
+
+       /* Create and initialise the context */
+       if(!(ctx = EVP_CIPHER_CTX_new()))
+               goto decrypt_error;
+
+       /* Initialise the decryption operation. IMPORTANT - ensure you use a key
+        * and IV size appropriate for your cipher
+        * In this example we are using 256 bit AES (i.e. a 256 bit key). The
+        * IV size for *most* modes is the same as the block size. For AES this
+        * is 128 bits */
+       if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1)
+               goto decrypt_error;
+
+       /* Provide the message to be decrypted, and obtain the plaintext output.
+        * EVP_DecryptUpdate can be called multiple times if necessary
+        */
+       if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) != 1)
+               goto decrypt_error;
+       plaintext_len = len;
+
+       /* Finalise the decryption. Further plaintext bytes may be written at
+        * this stage.
+        */
+       if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1)
+               goto decrypt_error;
+       plaintext_len += len;
+
+       /* Clean up */
+       EVP_CIPHER_CTX_free(ctx);
+
+       return plaintext_len;
+
+decrypt_error:
+       if (ctx)
+               EVP_CIPHER_CTX_free(ctx);
+       return -1;
+}
+
+/* HMAC SHA-256*/
+void calculate_hmac_sha256(const char *data, int len, char *buf)
+{
+       char* key = pool_config->wd_authkey;
+       char str[WD_AUTH_HASH_LEN/2];
+       unsigned int res_len = WD_AUTH_HASH_LEN;
+       HMAC_CTX *ctx = NULL;
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+       ctx = HMAC_CTX_new();
+       HMAC_CTX_reset(ctx);
+#else
+       HMAC_CTX ctx_obj;
+       ctx = &ctx_obj;
+       HMAC_CTX_init(ctx);
+#endif
+       HMAC_Init_ex(ctx, key, strlen(key), EVP_sha256(), NULL);
+       HMAC_Update(ctx, (unsigned char*)data, len);
+       HMAC_Final(ctx, (unsigned char*)str, &res_len);
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
+       HMAC_CTX_reset(ctx);
+       HMAC_CTX_free(ctx);
+#else
+       HMAC_CTX_cleanup(ctx);
+#endif
+       bytesToHex(str,32,buf);
+       buf[WD_AUTH_HASH_LEN] = '\0';
+}
+
+#else
+
+int aes_decrypt_with_password(unsigned char *ciphertext, int ciphertext_len,
+                                                         const char *password, unsigned char *plaintext)
+{
+       ereport(ERROR,
+               (errmsg("AES decryption is not supported by this build"),
+                        errhint("Compile with --with-openssl to use AES.")));
+       return -1;
+}
+int aes_encrypt_with_password(unsigned char *plaintext, int plaintext_len,
+                                                         const char *password, unsigned char *ciphertext)
+{
+       ereport(ERROR,
+               (errmsg("AES encryption is not supported by this build"),
+                        errhint("Compile with --with-openssl to use AES.")));
+       return -1;
+}
+void calculate_hmac_sha256(const char *data, int len, char *buf)
+{
+       ereport(ERROR,
+                       (errmsg("HMAC SHA256 encryption is not supported by this build"),
+                        errhint("Compile with --with-openssl to use AES.")));
+}
+#endif
+
+
index e8ba027348f221e03ac35e54e8dc3ec05c795c83..45e9c1d4a3d17eeb58585cb2b2d564c58310eef7 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -89,6 +79,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 subdir = src/watchdog
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+       $(top_srcdir)/mkinstalldirs
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
@@ -99,7 +91,6 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/docbook.m4 \
        $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
        $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/src/include/config.h
 CONFIG_CLEAN_FILES =
@@ -181,7 +172,6 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
-am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/mkinstalldirs
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 AMTAR = @AMTAR@
@@ -311,7 +301,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -349,6 +338,7 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
        echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps src/watchdog/Makefile'; \
        $(am__cd) $(top_srcdir) && \
          $(AUTOMAKE) --foreign --ignore-deps src/watchdog/Makefile
+.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
        @case '$?' in \
          *config.status*) \
@@ -597,8 +587,6 @@ uninstall-am:
        mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
        tags tags-am uninstall uninstall-am
 
-.PRECIOUS: Makefile
-
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index 08f5a09a750458ecca0986ff6a72029a7f894926..4ee6ee51abde9f65a146ff6196a4ad136473aad7 100644 (file)
@@ -6372,6 +6372,7 @@ static void verify_pool_configurations(WatchdogNode* wdNode, POOL_CONFIG* config
        WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_BOOL(config, wdNode, enable_pool_hba);
        WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_BOOL(config, wdNode, load_balance_mode);
        WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_BOOL(config, wdNode, replication_stop_on_mismatch);
+       WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_BOOL(config, wdNode, allow_clear_text_frontend_auth);
        WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_BOOL(config, wdNode, failover_if_affected_tuples_mismatch);
        WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_BOOL(config, wdNode, fail_over_on_backend_error);
        WD_VERIFY_RECEIVED_CONFIG_PARAMETER_VAL_BOOL(config, wdNode, replicate_select);
index 58465c49614f352508c73ecc055bcfd485b390b5..328191d7d3d8cf0c85c9acb0f2e9b60f7971aa1b 100644 (file)
@@ -68,6 +68,8 @@ POOL_CONFIG* get_pool_config_from_json(char* json_data, int data_len)
                goto ERROR_EXIT;
        if (json_get_bool_value_for_key(root, "replication_stop_on_mismatch", &config->replication_stop_on_mismatch))
                goto ERROR_EXIT;
+       if (json_get_bool_value_for_key(root, "allow_clear_text_frontend_auth", &config->allow_clear_text_frontend_auth))
+               goto ERROR_EXIT;
        if (json_get_bool_value_for_key(root, "failover_if_affected_tuples_mismatch", &config->failover_if_affected_tuples_mismatch))
                goto ERROR_EXIT;
        if (json_get_bool_value_for_key(root, "replicate_select", &config->replicate_select))
@@ -181,6 +183,7 @@ char* get_pool_config_json(void)
        jw_put_bool(jNode, "replication_mode", pool_config->replication_mode);
        jw_put_bool(jNode, "enable_pool_hba", pool_config->enable_pool_hba);
        jw_put_int(jNode, "load_balance_mode", pool_config->load_balance_mode);
+       jw_put_bool(jNode, "allow_clear_text_frontend_auth", pool_config->allow_clear_text_frontend_auth);
        jw_put_bool(jNode, "replication_stop_on_mismatch", pool_config->replication_stop_on_mismatch);
        jw_put_bool(jNode, "failover_if_affected_tuples_mismatch", pool_config->failover_if_affected_tuples_mismatch);
        jw_put_bool(jNode, "replicate_select", pool_config->replicate_select);
index b34f08b9a3a941ec1648f09ab9ecd31d3a8161ac..4312787b6bb77626d240b4c1a7e499ae04a209ae 100644 (file)
@@ -870,6 +870,9 @@ create_conn(char * hostname, int port)
 {
        static char conninfo[1024];
        PGconn *conn;
+       char *password = get_pgpool_config_user_password(pool_config->wd_lifecheck_user,
+                                                                                                        pool_config->wd_lifecheck_password);
+
 
        if (strlen(pool_config->wd_lifecheck_dbname) == 0)
        {
@@ -891,10 +894,13 @@ create_conn(char * hostname, int port)
                port,
                pool_config->wd_lifecheck_dbname,
                pool_config->wd_lifecheck_user,
-               pool_config->wd_lifecheck_password,
+               password?password:"",
                pool_config->wd_interval / 2 + 1);
        conn = PQconnectdb(conninfo);
 
+       if (password)
+               pfree(password);
+
        if (PQstatus(conn) != CONNECTION_OK)
        {
                ereport(DEBUG1,
index b16b6974ff136bd2c8fa5d3636b162facbb6c436..24e27b6ea86f33f496de3dcf037b1eef019003b6 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2015     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -39,6 +39,7 @@
 #include "utils/memutils.h"
 #include "pool_config.h"
 #include "watchdog/wd_utils.h"
+#include "utils/ssl_utils.h"
 
 static int has_setuid_bit(char * path);
 static void *exec_func(void *arg);
@@ -146,34 +147,6 @@ has_setuid_bit(char * path)
 
 
 #ifdef USE_SSL
-/* HMAC SHA-256*/
-static void calculate_hmac_sha256(const char *data, int len, char *buf)
-{
-       char* key = pool_config->wd_authkey;
-       char str[WD_AUTH_HASH_LEN/2];
-       unsigned int res_len = WD_AUTH_HASH_LEN;
-       HMAC_CTX *ctx = NULL;
-
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
-       ctx = HMAC_CTX_new();
-       HMAC_CTX_reset(ctx);
-#else
-       HMAC_CTX ctx_obj;
-       ctx = &ctx_obj;
-       HMAC_CTX_init(ctx);
-#endif
-       HMAC_Init_ex(ctx, key, strlen(key), EVP_sha256(), NULL);
-       HMAC_Update(ctx, (unsigned char*)data, len);
-       HMAC_Final(ctx, (unsigned char*)str, &res_len);
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
-       HMAC_CTX_reset(ctx);
-       HMAC_CTX_free(ctx);
-#else
-       HMAC_CTX_cleanup(ctx);
-#endif
-       bytesToHex(str,32,buf);
-       buf[WD_AUTH_HASH_LEN] = '\0';
-}
 
 void
 wd_calc_hash(const char *str, int len, char *buf)
@@ -181,6 +154,7 @@ wd_calc_hash(const char *str, int len, char *buf)
        calculate_hmac_sha256(str, len, buf);
 }
 #else
+
 /* calculate hash for authentication using packet contents */
 void
 wd_calc_hash(const char *str, int len, char *buf)