pgpool2.git / summary / log / commit / refs

commit    c00f8be7f03fc894f2cb0f09f386c01bd22a8638
Author:   Tatsuo Ishii <ishii at sraoss.co.jp>
Date:     Tue Jan 26 14:49:58 2010 +0000

    Add SSL support. Patch contributed by Sean Finney.
    -------------------------------------------------------------------
    The new pool_ssl.c provides the necessary functions for negotiating
    SSL based connections for both frontend and backend connections.  child.c
    has been updated to request the negotiation in both cases, and pool_stream.c
    has been modified to use SSL based i/o functions if a connection has
    successfully negotiated a connection.
    
    SSL related failures should be handled as gracefully as possible.  For
    frontend connections, the client is allowed to continue in cleartext
    in the case that SSL negotiation was not successful.  For backend
    connections, plaintext connections should continue as well, modulo
    any pg_hba restrictions on the remote server.  It is also possible to
    have connections where only one of the frontend/backend uses SSL.
    
    By default this functionality is off (further testing should be done
    and perhaps better controls on certificate verification, etc).
    
    New fields are added to the global pool configuration to allow specifying
    SSL related settings:
    
     * "ssl", global on/off switch (default: off)
     * "ssl_cert", path to SSL public certificate (default: "")
     * "ssl_key", path to SSL private key (default: "")
    
    The sample pgpool.conf files have been updated with comments and sample
    values for these new settings.
    
    The autoconf build system has been updated with a new "--with-openssl" option
    to allow enabling/disabling SSL support at build time.  For simplicity and
    clarity no effort is made to provide extra flexibility similar to the existing
    --with-pgsql{,-include-dir,lib-dir} ./configure flags.  In the meantime it's
    expected that the necessary flags are provided via CFLAGS/LDFLAGS etc when
    calling ./configure.


Makefile.am | 2 +- Makefile.in | 8 +- child.c | 24 +--- config.h.in | 6 + configure | 235 +++++++++++++++++++++++++++++++++++----- configure.in | 19 ++++ main.c | 8 +- pgpool.conf.sample | 9 ++ pgpool.conf.sample-master-slave | 14 +++ pgpool.conf.sample-replication | 14 +++ pool.h | 50 ++++++--- pool_config.c | 50 +++++++++ pool_config.l | 50 +++++++++ pool_ssl.c | 180 ++++++++++++++++++++++++++++++ pool_stream.c | 32 +++++- 15 files changed, 630 insertions(+), 71 deletions(-) diff --git a/Makefile.am b/Makefile.am index f51a4d2e7..06ae98544 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,7 +11,7 @@ pgpool_SOURCES = pool.h pool_type.h version.h pgpool.conf.sample \ pool_query_cache.c pool_hba.conf.sample sample/pgpool.pam\ pool_hba.c pool_path.h pool_path.c pool_ip.h pool_ip.c pool_type.h \ ps_status.c strlcpy.c recovery.c pool_relcache.c pool_process_reporting.c \ - pool_timestamp.c pool_timestamp.h \ + pool_ssl.c pool_timestamp.c pool_timestamp.h \ pool_proto_modules.c pool_proto_modules.h \ pool_lobj.c diff --git a/Makefile.in b/Makefile.in index e06a6e225..355df25b2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -73,8 +73,9 @@ am_pgpool_OBJECTS = main.$(OBJEXT) child.$(OBJEXT) pool_auth.$(OBJEXT) \ pool_query_cache.$(OBJEXT) pool_hba.$(OBJEXT) \ pool_path.$(OBJEXT) pool_ip.$(OBJEXT) ps_status.$(OBJEXT) \ strlcpy.$(OBJEXT) recovery.$(OBJEXT) pool_relcache.$(OBJEXT) \ - pool_process_reporting.$(OBJEXT) pool_timestamp.$(OBJEXT) \ - pool_proto_modules.$(OBJEXT) pool_lobj.$(OBJEXT) + pool_process_reporting.$(OBJEXT) pool_ssl.$(OBJEXT) \ + pool_timestamp.$(OBJEXT) pool_proto_modules.$(OBJEXT) \ + pool_lobj.$(OBJEXT) pgpool_OBJECTS = $(am_pgpool_OBJECTS) pgpool_DEPENDENCIES = parser/libsql-parser.a pcp/libpcp.la \ parser/nodes.o @@ -243,7 +244,7 @@ pgpool_SOURCES = pool.h pool_type.h version.h pgpool.conf.sample \ pool_query_cache.c pool_hba.conf.sample sample/pgpool.pam\ pool_hba.c pool_path.h pool_path.c pool_ip.h pool_ip.c pool_type.h \ ps_status.c strlcpy.c recovery.c pool_relcache.c pool_process_reporting.c \ - pool_timestamp.c pool_timestamp.h \ + pool_ssl.c pool_timestamp.c pool_timestamp.h \ pool_proto_modules.c pool_proto_modules.h \ pool_lobj.c @@ -417,6 +418,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_sema.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_shmem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_signal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_ssl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_stream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_system.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_timestamp.Po@am__quote@ diff --git a/child.c b/child.c index 43d9da8a0..78023d8a0 100644 --- a/child.c +++ b/child.c @@ -169,7 +169,6 @@ void do_child(int unix_fd, int inet_fd) for (;;) { int connection_reuse = 1; - int ssl_request = 0; StartupPacket *sp; idle = 1; @@ -235,25 +234,10 @@ void do_child(int unix_fd, int inet_fd) } /* SSL? */ - if (sp->major == 1234 && sp->minor == 5679) + if (sp->major == 1234 && sp->minor == 5679 && !frontend->ssl_active) { - /* SSL not supported */ - pool_debug("SSLRequest: sent N; retry startup"); - if (ssl_request) - { - pool_close(frontend); - pool_free_startup_packet(sp); - connection_count_down(); - continue; - } - - /* - * say to the frontend "we do not suppport SSL" - * note that this is not a NOTICE response despite it's an 'N'! - */ - pool_write_and_flush(frontend, "N", 1); - ssl_request = 1; - pool_free_startup_packet(sp); + pool_debug("SSLRequest from client"); + pool_ssl_negotiate_serverclient(frontend); goto retry_startup; } @@ -1167,6 +1151,7 @@ static POOL_CONNECTION_POOL *connect_backend(StartupPacket *sp, POOL_CONNECTION /* mark this is a backend connection */ CONNECTION(backend, i)->isbackend = 1; + pool_ssl_negotiate_clientserver(CONNECTION(backend, i)); /* * save startup packet info @@ -1434,6 +1419,7 @@ POOL_CONNECTION_POOL_SLOT *make_persistent_db_connection( cp->con = pool_open(fd); cp->closetime = 0; cp->con->isbackend = 1; + pool_ssl_negotiate_clientserver(cp->con); /* * build V3 startup packet diff --git a/config.h.in b/config.h.in index 3942330ff..d0c104393 100644 --- a/config.h.in +++ b/config.h.in @@ -107,6 +107,9 @@ /* Define to 1 if you have the <netinet/tcp.h> header file. */ #undef HAVE_NETINET_TCP_H +/* Define to 1 if you have the <openssl/ssl.h> header file. */ +#undef HAVE_OPENSSL_SSL_H + /* Define to 1 if you have the <pam/pam_appl.h> header file. */ #undef HAVE_PAM_PAM_APPL_H @@ -279,6 +282,9 @@ /* Use replacement snprintf() functions. */ #undef USE_REPL_SNPRINTF +/* Define to 1 to build with SSL support. (--with-openssl) */ +#undef USE_SSL + /* Version number of package */ #undef VERSION diff --git a/configure b/configure index 7196a45af..3e1761096 100755 --- a/configure +++ b/configure @@ -1046,6 +1046,7 @@ Optional Packages: --with-pgsql=DIR site header files for PostgreSQL in DIR --with-pgsql-includedir=DIR site header files for PostgreSQL in DIR --with-pgsql-libdir=DIR site library files for PostgreSQL in DIR + --with-openssl build with OpenSSL support --with-pam build with PAM support Some influential environment variables: @@ -3743,7 +3744,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 3746 "configure"' > conftest.$ac_ext + echo '#line 3747 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -5342,7 +5343,7 @@ fi # Provide some information about the compiler. -echo "$as_me:5345:" \ +echo "$as_me:5346:" \ "checking for Fortran 77 compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 @@ -6405,11 +6406,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6408: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6409: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6412: \$? = $ac_status" >&5 + echo "$as_me:6413: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -6673,11 +6674,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6676: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6677: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6680: \$? = $ac_status" >&5 + echo "$as_me:6681: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -6777,11 +6778,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6780: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6781: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:6784: \$? = $ac_status" >&5 + echo "$as_me:6785: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -8246,7 +8247,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 8249 "configure"' > conftest.$ac_ext + echo '#line 8250 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -9143,7 +9144,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<EOF -#line 9146 "configure" +#line 9147 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -9243,7 +9244,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<EOF -#line 9246 "configure" +#line 9247 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11583,11 +11584,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11586: $lt_compile\"" >&5) + (eval echo "\"\$as_me:11587: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:11590: \$? = $ac_status" >&5 + echo "$as_me:11591: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -11687,11 +11688,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11690: $lt_compile\"" >&5) + (eval echo "\"\$as_me:11691: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:11694: \$? = $ac_status" >&5 + echo "$as_me:11695: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -12223,7 +12224,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 12226 "configure"' > conftest.$ac_ext + echo '#line 12227 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -13278,11 +13279,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13281: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13282: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:13285: \$? = $ac_status" >&5 + echo "$as_me:13286: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -13382,11 +13383,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13385: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13386: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:13389: \$? = $ac_status" >&5 + echo "$as_me:13390: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -14831,7 +14832,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 14834 "configure"' > conftest.$ac_ext + echo '#line 14835 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -15606,11 +15607,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15609: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15610: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:15613: \$? = $ac_status" >&5 + echo "$as_me:15614: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -15874,11 +15875,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15877: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15878: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:15881: \$? = $ac_status" >&5 + echo "$as_me:15882: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -15978,11 +15979,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15981: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15982: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:15985: \$? = $ac_status" >&5 + echo "$as_me:15986: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -17447,7 +17448,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 17450 "configure"' > conftest.$ac_ext + echo '#line 17451 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -24129,6 +24130,184 @@ echo "$as_me: error: *** You must supply an argument to the --with-pgsql-libdir fi; + +# Check whether --with-openssl or --without-openssl was given. +if test "${with_openssl+set}" = set; then + withval="$with_openssl" + +else + openssl=no +fi; + +if test "$with_openssl" = yes || test "$with_openssl" = auto; then + +for ac_header in openssl/ssl.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +cat >>confdefs.h <<\_ACEOF +#define USE_SSL 1 +_ACEOF + +else + + if test "$with_openssl" = yes; then + { { echo "$as_me:$LINENO: error: header file <openssl/ssl.h> is required for SSL" >&5 +echo "$as_me: error: header file <openssl/ssl.h> is required for SSL" >&2;} + { (exit 1); exit 1; }; } + else + { echo "$as_me:$LINENO: WARNING: header file <openssl/ssl.h> is required for SSL" >&5 +echo "$as_me: WARNING: header file <openssl/ssl.h> is required for SSL" >&2;} + fi + +fi + +done + +fi + + # Check whether --with-pam or --without-pam was given. if test "${with_pam+set}" = set; then withval="$with_pam" diff --git a/configure.in b/configure.in index 90f3513da..8cb616717 100644 --- a/configure.in +++ b/configure.in @@ -275,6 +275,25 @@ AC_ARG_WITH(pgsql-libdir, PGSQL_LIB_DIR="$withval" ]) + +AC_ARG_WITH(openssl, + [ --with-openssl build with OpenSSL support], + [], + [openssl=no]) + +if test "$with_openssl" = yes || test "$with_openssl" = auto; then + AC_CHECK_HEADERS(openssl/ssl.h, + [AC_DEFINE([USE_SSL], 1, + [Define to 1 to build with SSL support. (--with-openssl)])], + [ + if test "$with_openssl" = yes; then + AC_MSG_ERROR([header file <openssl/ssl.h> is required for SSL]) + else + AC_MSG_WARN([header file <openssl/ssl.h> is required for SSL]) + fi + ]) +fi + AC_ARG_WITH(pam, [ --with-pam build with PAM support], [AC_DEFINE([USE_PAM], 1, [Define to 1 to build with PAM support. (--with-pam)])]) diff --git a/main.c b/main.c index 827982feb..222d308f4 100644 --- a/main.c +++ b/main.c @@ -5,7 +5,7 @@ * pgpool: a language independent connection pool server for PostgreSQL * written by Tatsuo Ishii * - * Copyright (c) 2003-2009 PgPool Global Development Group + * Copyright (c) 2003-2010 PgPool Global Development Group * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -266,6 +266,12 @@ int main(int argc, char **argv) } } +#ifdef USE_SSL + /* global ssl init */ + SSL_library_init(); + SSL_load_error_strings(); +#endif /* USE_SSL */ + mypid = getpid(); if (pool_init_config()) diff --git a/pgpool.conf.sample b/pgpool.conf.sample index f20b6fc0a..f49434795 100644 --- a/pgpool.conf.sample +++ b/pgpool.conf.sample @@ -219,3 +219,12 @@ client_idle_limit_in_recovery = 0 # command in replication mode. The table must exist and has writable # permission to public. If the table name is '', no rewriting occurs. lobj_lock_table = '' + +# If true, enable SSL support for both frontend and backend connections. +# note that you must also set ssl_key and ssl_cert for SSL to work in +# the frontend connections. +ssl = false +# path to the SSL private key file +#ssl_key = './server.key' +# path to the SSL public certificate file +#ssl_key = './server.cert' diff --git a/pgpool.conf.sample-master-slave b/pgpool.conf.sample-master-slave index 028544feb..350d07604 100644 --- a/pgpool.conf.sample-master-slave +++ b/pgpool.conf.sample-master-slave @@ -214,3 +214,17 @@ recovery_timeout = 90 # explicit transactions!) 0 means no disconnect. This parameter only # takes effect in recovery 2nd stage. client_idle_limit_in_recovery = 0 + +# Specify table name to lock. This is used when rewriting lo_creat +# command in replication mode. The table must exist and has writable +# permission to public. If the table name is '', no rewriting occurs. +lobj_lock_table = '' + +# If true, enable SSL support for both frontend and backend connections. +# note that you must also set ssl_key and ssl_cert for SSL to work in +# the frontend connections. +ssl = false +# path to the SSL private key file +#ssl_key = './server.key' +# path to the SSL public certificate file +#ssl_key = './server.cert' diff --git a/pgpool.conf.sample-replication b/pgpool.conf.sample-replication index a66ff334a..5279bc919 100644 --- a/pgpool.conf.sample-replication +++ b/pgpool.conf.sample-replication @@ -214,3 +214,17 @@ recovery_timeout = 90 # explicit transactions!) 0 means no disconnect. This parameter only # takes effect in recovery 2nd stage. client_idle_limit_in_recovery = 0 + +# Specify table name to lock. This is used when rewriting lo_creat +# command in replication mode. The table must exist and has writable +# permission to public. If the table name is '', no rewriting occurs. +lobj_lock_table = '' + +# If true, enable SSL support for both frontend and backend connections. +# note that you must also set ssl_key and ssl_cert for SSL to work in +# the frontend connections. +ssl = false +# path to the SSL private key file +#ssl_key = './server.key' +# path to the SSL public certificate file +#ssl_key = './server.cert' diff --git a/pool.h b/pool.h index 39ba503fe..777de2f3a 100644 --- a/pool.h +++ b/pool.h @@ -35,6 +35,12 @@ #include <sys/types.h> #include <limits.h> +#ifdef USE_SSL +#include <openssl/crypto.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#endif + /* undef this if you have problems with non blocking accept() */ #define NONE_BLOCK @@ -210,6 +216,11 @@ typedef struct { int replication_enabled; /* replication mode enabled */ int master_slave_enabled; /* master/slave mode enabled */ int num_reset_queries; /* number of queries in reset_query_list */ + + /* ssl configuration */ + int ssl; /* if non 0, activate ssl support (frontend+backend) */ + char *ssl_cert; /* path to ssl certificate (frontend only) */ + char *ssl_key; /* path to ssl key (frontend only) */ } POOL_CONFIG; #define MAX_PASSWORD_SIZE 1024 @@ -230,6 +241,12 @@ typedef struct { int wbufsz; /* write buffer size */ int wbufpo; /* buffer offset */ +#ifdef USE_SSL + SSL_CTX *ssl_ctx; /* SSL connection context */ + SSL *ssl; /* SSL connection */ +#endif + int ssl_active; /* SSL is failed if < 0, off if 0, on if > 0 */ + char *hp; /* pending data buffer head address */ int po; /* pending data offset */ int bufsz; /* pending data buffer size */ @@ -532,6 +549,13 @@ extern POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int protoMa extern void pool_discard_cp(char *user, char *database, int protoMajor); extern void pool_backend_timer(void); +/* SSL functionality */ +extern void pool_ssl_negotiate_serverclient(POOL_CONNECTION *cp); +extern void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp); +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 POOL_STATUS ErrorResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend); @@ -620,8 +644,6 @@ extern POOL_STATUS OneNode_do_command(POOL_CONNECTION *frontend, POOL_CONNECTION extern POOL_CONNECTION_POOL_SLOT *make_persistent_db_connection( char *hostname, int port, char *dbname, char *user, char *password); -extern POOL_STATUS do_query(POOL_CONNECTION *backend, char *query, POOL_SELECT_RESULT **result, int major); - /* define pool_system.c */ extern POOL_CONNECTION_POOL_SLOT *pool_system_db_connection(void); extern DistDefInfo *pool_get_dist_def_info (char * dbname, char * schema_name, char * table_name); @@ -666,24 +688,24 @@ extern void cancel_request(CancelPacket *sp); extern void check_stop_request(void); /* pool_process_query.c */ -void reset_variables(void); -void per_node_statement_log(POOL_CONNECTION_POOL *backend, int node_id, char *query); -POOL_STATUS pool_extract_error_message(POOL_CONNECTION *backend, int major, bool unread, char **message); -POOL_STATUS do_command(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, +extern void reset_variables(void); +extern void per_node_statement_log(POOL_CONNECTION_POOL *backend, int node_id, char *query); +extern POOL_STATUS pool_extract_error_message(POOL_CONNECTION *backend, int major, bool unread, char **message); +extern POOL_STATUS do_command(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *query, int protoMajor, int pid, int key, int no_ready_for_query); -POOL_STATUS do_query(POOL_CONNECTION *backend, char *query, POOL_SELECT_RESULT **result, int major); -void free_select_result(POOL_SELECT_RESULT *result); +extern POOL_STATUS do_query(POOL_CONNECTION *backend, char *query, POOL_SELECT_RESULT **result, int major); +extern void free_select_result(POOL_SELECT_RESULT *result); /* pool_relcache.c */ -POOL_RELCACHE *pool_create_relcache(int cachesize, char *sql, +extern POOL_RELCACHE *pool_create_relcache(int cachesize, char *sql, func_ptr register_func, func_ptr unregister_func, bool issessionlocal); -void pool_discard_relcache(POOL_RELCACHE *relcache); -void *pool_search_relcache(POOL_RELCACHE *relcache, POOL_CONNECTION_POOL *backend, char *table); -void *int_register_func(POOL_SELECT_RESULT *res); -void *int_unregister_func(void *data); +extern void pool_discard_relcache(POOL_RELCACHE *relcache); +extern void *pool_search_relcache(POOL_RELCACHE *relcache, POOL_CONNECTION_POOL *backend, char *table); +extern void *int_register_func(POOL_SELECT_RESULT *res); +extern void *int_unregister_func(void *data); /* pool_lobj.c */ -char *pool_rewrite_lo_creat(char kind, char *packet, int packet_len, POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, int* len); +extern char *pool_rewrite_lo_creat(char kind, char *packet, int packet_len, POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, int* len); #endif /* POOL_H */ diff --git a/pool_config.c b/pool_config.c index 8eb815f5a..9670a630a 100644 --- a/pool_config.c +++ b/pool_config.c @@ -1902,6 +1902,9 @@ int pool_init_config(void) pool_config->recovery_timeout = 90; pool_config->client_idle_limit_in_recovery = 0; pool_config->lobj_lock_table = ""; + pool_config->ssl = 0; + pool_config->ssl_cert = ""; + pool_config->ssl_key = ""; res = gethostname(localhostname,sizeof(localhostname)); if(res !=0 ) @@ -2958,6 +2961,53 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) pool_config->lobj_lock_table = str; } + else if (!strcmp(key, "ssl") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + int v = eval_logical(yytext); + + if (v < 0) + { + pool_error("pool_config: invalid value %s for %s", yytext, key); + return(-1); + } + pool_config->ssl = v; + } + else if (!strcmp(key, "ssl_cert") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->ssl_cert = str; + } + else if (!strcmp(key, "ssl_key") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->ssl_key = str; + } } fclose(fd); diff --git a/pool_config.l b/pool_config.l index 214ad7e83..adb0c71fc 100644 --- a/pool_config.l +++ b/pool_config.l @@ -177,6 +177,9 @@ int pool_init_config(void) pool_config->recovery_timeout = 90; pool_config->client_idle_limit_in_recovery = 0; pool_config->lobj_lock_table = ""; + pool_config->ssl = 0; + pool_config->ssl_cert = ""; + pool_config->ssl_key = ""; res = gethostname(localhostname,sizeof(localhostname)); if(res !=0 ) @@ -1233,6 +1236,53 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) pool_config->lobj_lock_table = str; } + else if (!strcmp(key, "ssl") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + int v = eval_logical(yytext); + + if (v < 0) + { + pool_error("pool_config: invalid value %s for %s", yytext, key); + return(-1); + } + pool_config->ssl = v; + } + else if (!strcmp(key, "ssl_cert") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->ssl_cert = str; + } + else if (!strcmp(key, "ssl_key") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->ssl_key = str; + } } fclose(fd); diff --git a/pool_ssl.c b/pool_ssl.c new file mode 100644 index 000000000..fcae52a8b --- /dev/null +++ b/pool_ssl.c @@ -0,0 +1,180 @@ +/* -*-pgsql-c-*- */ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2010 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_ssl.c: ssl negotiation functions + * + */ + +#include "config.h" +#include "pool.h" + +#ifdef USE_SSL + +#include <arpa/inet.h> /* for htonl() */ + +/* Major/minor codes to negotiate SSL prior to startup packet */ +#define NEGOTIATE_SSL_CODE ( 1234<<16 | 5679 ) + +/* enum flag for differentiating server->client vs client->server SSL */ +enum ssl_conn_type { ssl_conn_clientserver, ssl_conn_serverclient }; + +/* perform per-connection ssl initialization. returns nonzero on error */ +static int init_ssl_ctx(POOL_CONNECTION *cp, enum ssl_conn_type conntype); + +/* attempt to negotiate a secure connection */ +void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp) { + int ssl_packet[2] = { htonl(sizeof(int)*2), htonl(NEGOTIATE_SSL_CODE) }; + char server_response; + + cp->ssl_active = -1; + + if ( (!pool_config->ssl) || init_ssl_ctx(cp, ssl_conn_clientserver)) + return; + + pool_debug("pool_ssl: sending client->server SSL request"); + pool_write_and_flush(cp, ssl_packet, sizeof(int)*2); + pool_read(cp, &server_response, 1); + pool_debug("pool_ssl: client->server SSL response: %c", server_response); + + switch (server_response) { + case 'S': + SSL_set_fd(cp->ssl, cp->fd); + if (SSL_connect(cp->ssl) < 0) { + pool_error("pool_ssl: SSL_connect failed: %ld", ERR_get_error()); + } else { + cp->ssl_active = 1; + } + break; + case 'N': + pool_error("pool_ssl: server doesn't want to talk SSL"); + break; + default: + pool_error("pool_ssl: unhandled response: %c", server_response); + break; + } +} + + +/* attempt to negotiate a secure connection */ +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 */ + pool_write_and_flush(cp, "N", 1); + } else { + /* write back an "SSL accept" response */ + pool_write_and_flush(cp, "S", 1); + + SSL_set_fd(cp->ssl, cp->fd); + if (SSL_accept(cp->ssl) < 0) { + pool_error("pool_ssl: SSL_accept failed: %ld", ERR_get_error()); + } else { + cp->ssl_active = 1; + } + } +} + +void pool_ssl_close(POOL_CONNECTION *cp) { + if (cp->ssl) { + SSL_shutdown(cp->ssl); + SSL_free(cp->ssl); + } + + if (cp->ssl_ctx) + SSL_CTX_free(cp->ssl_ctx); +} + +int pool_ssl_read(POOL_CONNECTION *cp, void *buf, int size) { + return SSL_read(cp->ssl, buf, size); +} + +int pool_ssl_write(POOL_CONNECTION *cp, const void *buf, int size) { + return SSL_write(cp->ssl, buf, size); +} + +static int init_ssl_ctx(POOL_CONNECTION *cp, enum ssl_conn_type conntype) { + int error = 0; + + /* initialize SSL members */ + cp->ssl_ctx = SSL_CTX_new(TLSv1_method()); + if (! cp->ssl_ctx) { + pool_error("pool_ssl: SSL_CTX_new failed: %ld", ERR_get_error()); + error = -1; + } + + if ( conntype == ssl_conn_serverclient) { + if ( (!error) && SSL_CTX_use_certificate_file(cp->ssl_ctx, + pool_config->ssl_cert, + SSL_FILETYPE_PEM) <= 0) { + pool_error("pool_ssl: SSL cert failure: %ld", ERR_get_error()); + error = -1; + } + + if ( (!error) && SSL_CTX_use_PrivateKey_file(cp->ssl_ctx, + pool_config->ssl_key, + SSL_FILETYPE_PEM) <= 0) { + pool_error("pool_ssl: SSL key failure: %ld", ERR_get_error()); + error = -1; + } + } + + if (! error) { + cp->ssl = SSL_new(cp->ssl_ctx); + if (! cp->ssl) { + pool_error("pool_ssl: SSL_new failed: %ld", ERR_get_error()); + error = -1; + } + } + + return error; +} + +#else /* USE_SSL: wrap / no-op ssl functionality if it's not available */ + +void pool_ssl_negotiate_serverclient(POOL_CONNECTION *cp) { + pool_debug("pool_ssl: SSL requested but SSL support is not available"); + pool_write_and_flush(cp, "N", 1); + cp->ssl_active = -1; +} + +void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp) { + pool_debug("pool_ssl: SSL requested but SSL support is not available"); + cp->ssl_active = -1; +} + +void pool_ssl_close(POOL_CONNECTION *cp) { return; } + +int pool_ssl_read(POOL_CONNECTION *cp, void *buf, int size) { + pool_error("pool_ssl: SSL i/o called but SSL support is not available"); + notice_backend_error(cp->db_node_id); + child_exit(1); + return -1; /* never reached */ +} + +int pool_ssl_write(POOL_CONNECTION *cp, const void *buf, int size) { + pool_error("pool_ssl: SSL i/o called but SSL support is not available"); + notice_backend_error(cp->db_node_id); + child_exit(1); + return -1; /* never reached */ +} + +#endif /* USE_SSL */ diff --git a/pool_stream.c b/pool_stream.c index fe5ff9845..ecdfe3193 100644 --- a/pool_stream.c +++ b/pool_stream.c @@ -5,7 +5,7 @@ * pgpool: a language independent connection pool server for PostgreSQL * written by Tatsuo Ishii * -* Copyright (c) 2003-2009 PgPool Global Development Group +* Copyright (c) 2003-2010 PgPool Global Development Group * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -109,6 +109,9 @@ void pool_close(POOL_CONNECTION *cp) if (cp->buf2) free(cp->buf2); pool_discard_params(&cp->params); + + pool_ssl_close(cp); + free(cp); } @@ -144,7 +147,12 @@ int pool_read(POOL_CONNECTION *cp, void *buf, int len) } } - readlen = read(cp->fd, readbuf, READBUFSZ); + if (cp->ssl_active > 0) { + readlen = pool_ssl_read(cp, readbuf, READBUFSZ); + } else { + readlen = read(cp->fd, readbuf, READBUFSZ); + } + if (readlen == -1) { if (errno == EINTR || errno == EAGAIN) @@ -254,7 +262,12 @@ char *pool_read2(POOL_CONNECTION *cp, int len) } } - readlen = read(cp->fd, buf, len); + if (cp->ssl_active > 0) { + readlen = pool_ssl_read(cp, buf, len); + } else { + readlen = read(cp->fd, buf, len); + } + if (readlen == -1) { if (errno == EINTR || errno == EAGAIN) @@ -404,7 +417,11 @@ int pool_flush_it(POOL_CONNECTION *cp) } } #endif - sts = write(cp->fd, cp->wbuf + offset, wlen); + if (cp->ssl_active > 0) { + sts = pool_ssl_write(cp, cp->wbuf + offset, wlen); + } else { + sts = write(cp->fd, cp->wbuf + offset, wlen); + } if (sts > 0) { @@ -593,7 +610,12 @@ char *pool_read_string(POOL_CONNECTION *cp, int *len, int line) } } - readlen = read(cp->fd, cp->sbuf+readp, readsize); + if (cp->ssl_active > 0) { + readlen = pool_ssl_read(cp, cp->sbuf+readp, readsize); + } else { + readlen = read(cp->fd, cp->sbuf+readp, readsize); + } + if (readlen == -1) { pool_error("pool_read_string: read() failed. reason:%s", strerror(errno)); [parent: ac1d371a6c70]