From f694b67401d93051c3442f60c06f8327391239cf Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 5 Mar 2018 14:42:11 -0500 Subject: [PATCH v2] Tests for Kerberos/GSSAPI authentication --- configure | 4 + configure.in | 2 + doc/src/sgml/regress.sgml | 12 ++- src/Makefile.global.in | 2 + src/test/Makefile | 7 +- src/test/kerberos/.gitignore | 2 + src/test/kerberos/Makefile | 25 ++++++ src/test/kerberos/README | 35 ++++++++ src/test/kerberos/t/001_auth.pl | 177 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 src/test/kerberos/.gitignore create mode 100644 src/test/kerberos/Makefile create mode 100644 src/test/kerberos/README create mode 100644 src/test/kerberos/t/001_auth.pl diff --git a/configure b/configure index 1242e310b4..3943711283 100755 --- a/configure +++ b/configure @@ -709,7 +709,9 @@ with_systemd with_selinux with_openssl with_ldap +with_krb_srvnam krb_srvtab +with_gssapi with_python with_perl with_tcl @@ -5788,6 +5790,7 @@ $as_echo "$with_gssapi" >&6; } + # # Kerberos configuration parameters # @@ -5815,6 +5818,7 @@ fi + cat >>confdefs.h <<_ACEOF #define PG_KRB_SRVNAM "$with_krb_srvnam" _ACEOF diff --git a/configure.in b/configure.in index aee3ab0867..1babdbb755 100644 --- a/configure.in +++ b/configure.in @@ -638,6 +638,7 @@ PGAC_ARG_BOOL(with, gssapi, no, [build with GSSAPI support], krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab" ]) AC_MSG_RESULT([$with_gssapi]) +AC_SUBST(with_gssapi) AC_SUBST(krb_srvtab) @@ -650,6 +651,7 @@ PGAC_ARG_REQ(with, krb-srvnam, [NAME], [default service principal name in Kerberos (GSSAPI) [postgres]], [], [with_krb_srvnam="postgres"]) +AC_SUBST(with_krb_srvnam) AC_DEFINE_UNQUOTED([PG_KRB_SRVNAM], ["$with_krb_srvnam"], [Define to the name of the default PostgreSQL service principal in Kerberos (GSSAPI). (--with-krb-srvnam=NAME)]) diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index 3c448dc5bc..673a8c2164 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -220,10 +220,20 @@ Additional Test Suites PG_TEST_EXTRA to a whitespace-separated list, for example: -make check-world PG_TEST_EXTRA='ldap ssl' +make check-world PG_TEST_EXTRA='kerberos ldap ssl' The following values are currently supported: + + kerberos + + + Runs the test suite under src/test/kerberos. This + requires an MIT Kerberos installation and opens TCP/IP listen sockets. + + + + ldap diff --git a/src/Makefile.global.in b/src/Makefile.global.in index dcb8dc5d90..15c14951e8 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -186,6 +186,8 @@ with_tcl = @with_tcl@ with_openssl = @with_openssl@ with_selinux = @with_selinux@ with_systemd = @with_systemd@ +with_gssapi = @with_gssapi@ +with_krb_srvnam = @with_krb_srvnam@ with_ldap = @with_ldap@ with_libxml = @with_libxml@ with_libxslt = @with_libxslt@ diff --git a/src/test/Makefile b/src/test/Makefile index 3de9428299..efb206aa75 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -17,6 +17,11 @@ SUBDIRS = perl regress isolation modules authentication recovery subscription # Test suites that are not safe by default but can be run if selected # by the user via the whitespace-separated list in variable # PG_TEST_EXTRA: +ifeq ($(with_gssapi),yes) +ifneq (,$(filter kerberos,$(PG_TEST_EXTRA))) +SUBDIRS += kerberos +endif +endif ifeq ($(with_ldap),yes) ifneq (,$(filter ldap,$(PG_TEST_EXTRA))) SUBDIRS += ldap @@ -32,7 +37,7 @@ endif # clean" etc to recurse into them. (We must filter out those that we # have conditionally included into SUBDIRS above, else there will be # make confusion.) -ALWAYS_SUBDIRS = $(filter-out $(SUBDIRS),examples ldap locale thread ssl) +ALWAYS_SUBDIRS = $(filter-out $(SUBDIRS),examples kerberos ldap locale thread ssl) # We want to recurse to all subdirs for all standard targets, except that # installcheck and install should not recurse into the subdirectory "modules". diff --git a/src/test/kerberos/.gitignore b/src/test/kerberos/.gitignore new file mode 100644 index 0000000000..871e943d50 --- /dev/null +++ b/src/test/kerberos/.gitignore @@ -0,0 +1,2 @@ +# Generated by test suite +/tmp_check/ diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile new file mode 100644 index 0000000000..4df4989470 --- /dev/null +++ b/src/test/kerberos/Makefile @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/test/kerberos +# +# Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/test/kerberos/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/test/kerberos +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +export with_gssapi with_krb_srvnam + +check: + $(prove_check) + +installcheck: + $(prove_installcheck) + +clean distclean maintainer-clean: + rm -rf tmp_check diff --git a/src/test/kerberos/README b/src/test/kerberos/README new file mode 100644 index 0000000000..cdfaeb89d3 --- /dev/null +++ b/src/test/kerberos/README @@ -0,0 +1,35 @@ +src/test/kerberos/README + +Tests for Kerberos/GSSAPI functionality +======================================= + +This directory contains a test suite for Kerberos/GSSAPI +functionality. This requires a full MIT Kerberos installation, +including server and client tools, and is therefore kept separate and +not run by default. + +Also, this test suite creates a KDC server that listens for TCP/IP +connections on localhost without any real access control, so it is not +safe to run this on a system where there might be untrusted local +users. + +Running the tests +================= + + make check + +or + + make installcheck + +Requirements +============ + +MIT Kerberos server and client tools are required. Heimdal is not +supported. + +Debian/Ubuntu packages: krb5-admin-server krb5-kdc krb5-user + +RHEL/CentOS packages: krb5-server krb5-workstation + +FreeBSD port: krb5 (base system has Heimdal) diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl new file mode 100644 index 0000000000..f26460e627 --- /dev/null +++ b/src/test/kerberos/t/001_auth.pl @@ -0,0 +1,177 @@ +use strict; +use warnings; +use TestLib; +use PostgresNode; +use Test::More; + +if ($ENV{with_gssapi} eq 'yes') +{ + plan tests => 4; +} +else +{ + plan skip_all => 'GSSAPI/Kerberos not supported by this build'; +} + +my ($krb5_bin_dir, $krb5_sbin_dir); + +if ($^O eq 'darwin') +{ + $krb5_bin_dir = '/usr/local/opt/krb5/bin'; + $krb5_sbin_dir = '/usr/local/opt/krb5/sbin'; +} +elsif ($^O eq 'freebsd') +{ + $krb5_bin_dir = '/usr/local/bin'; + $krb5_sbin_dir = '/usr/local/sbin'; +} +elsif ($^O eq 'linux') +{ + $krb5_sbin_dir = '/usr/sbin'; +} + +my $krb5_config = 'krb5-config'; +my $kinit = 'kinit'; +my $kdb5_util = 'kdb5_util'; +my $kadmin_local = 'kadmin.local'; +my $krb5kdc = 'krb5kdc'; + +if ($krb5_bin_dir && -d $krb5_bin_dir) +{ + $krb5_config = $krb5_bin_dir . '/' . $krb5_config; + $kinit = $krb5_bin_dir . '/' . $kinit; +} +if ($krb5_sbin_dir && -d $krb5_sbin_dir) +{ + $kdb5_util = $krb5_sbin_dir . '/' . $kdb5_util; + $kadmin_local = $krb5_sbin_dir . '/' . $kadmin_local; + $krb5kdc = $krb5_sbin_dir . '/' . $krb5kdc; +} + +my $realm = 'EXAMPLE.COM'; + +my $krb5_conf = "${TestLib::tmp_check}/krb5.conf"; +my $kdc_conf = "${TestLib::tmp_check}/kdc.conf"; +my $krb5_log = "${TestLib::tmp_check}/krb5libs.log"; +my $kdc_log = "${TestLib::tmp_check}/krb5kdc.log"; +my $kdc_port = int(rand() * 16384) + 49152; +my $kdc_datadir = "${TestLib::tmp_check}/krb5kdc"; +my $kdc_pidfile = "${TestLib::tmp_check}/krb5kdc.pid"; +my $keytab = "${TestLib::tmp_check}/krb5.keytab"; + +note "setting up Kerberos"; + +my ($stdout, $krb5_version); +run_log [ $krb5_config, '--version' ], '>', \$stdout or BAIL_OUT("could not execute krb5-config"); +BAIL_OUT("Heimdal is not supported") if $stdout =~ m/heimdal/; +$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/ or BAIL_OUT("could not get Kerberos version"); +$krb5_version = $1; + +append_to_file($krb5_conf, +qq![logging] +default = FILE:$krb5_log +kdc = FILE:$kdc_log + +[libdefaults] +default_realm = $realm + +[realms] +$realm = { + kdc = localhost:$kdc_port +}!); + +append_to_file($kdc_conf, +qq![kdcdefaults] +!); +# For new-enough versions of krb5, use the _listen settings rather +# than the _ports settings so that we can bind to localhost only. +if ($krb5_version >= 1.15) +{ + append_to_file($kdc_conf, +qq!kdc_listen = localhost:$kdc_port +kdc_tcp_listen = localhost:$kdc_port +!); +} +else +{ + append_to_file($kdc_conf, +qq!kdc_ports = $kdc_port +kdc_tcp_ports = $kdc_port +!); +} +append_to_file($kdc_conf, +qq! +[realms] +$realm = { + database_name = $kdc_datadir/principal + admin_keytab = FILE:$kdc_datadir/kadm5.keytab + acl_file = $kdc_datadir/kadm5.acl + key_stash_file = $kdc_datadir/_k5.$realm +}!); + +mkdir $kdc_datadir or die; + +$ENV{'KRB5_CONFIG'} = $krb5_conf; +$ENV{'KRB5_KDC_PROFILE'} = $kdc_conf; + +my $service_principal = "$ENV{with_krb_srvnam}/localhost"; + +system_or_bail $kdb5_util, 'create', '-s', '-P', 'secret0'; + +my $test1_password = 'secret1'; +system_or_bail $kadmin_local, '-q', "addprinc -pw $test1_password test1"; + +system_or_bail $kadmin_local, '-q', "addprinc -randkey $service_principal"; +system_or_bail $kadmin_local, '-q', "ktadd -k $keytab $service_principal"; + +system_or_bail $krb5kdc, '-P', $kdc_pidfile; + +END +{ + kill 'INT', `cat $kdc_pidfile` if -f $kdc_pidfile; +} + +note "setting up PostgreSQL instance"; + +my $node = get_new_node('node'); +$node->init; +$node->append_conf('postgresql.conf', "listen_addresses = 'localhost'"); +$node->append_conf('postgresql.conf', "krb_server_keyfile = '$keytab'"); +$node->start; + +$node->safe_psql('postgres', 'CREATE USER test1;'); + +note "running tests"; + +sub test_access +{ + my ($node, $role, $expected_res, $test_name) = @_; + + # need to connect over TCP/IP for Kerberos + my $res = $node->psql('postgres', 'SELECT 1', + extra_params => [ '-d', $node->connstr('postgres').' host=localhost', + '-U', $role ]); + is($res, $expected_res, $test_name); +} + +unlink($node->data_dir . '/pg_hba.conf'); +$node->append_conf('pg_hba.conf', qq{host all all localhost gss map=mymap}); +$node->restart; + +test_access($node, 'test1', 2, 'fails without ticket'); + +run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?); + +test_access($node, 'test1', 2, 'fails without mapping'); + +$node->append_conf('pg_ident.conf', qq{mymap /^(.*)\@$realm\$ \\1}); +$node->restart; + +test_access($node, 'test1', 0, 'succeeds with mapping'); + +truncate($node->data_dir . '/pg_ident.conf', 0); +unlink($node->data_dir . '/pg_hba.conf'); +$node->append_conf('pg_hba.conf', qq{host all all localhost gss include_realm=0}); +$node->restart; + +test_access($node, 'test1', 0, 'succeeds with include_realm=0'); base-commit: 2f3e2340cd1b67e91cefdf45e4c915297d1034e2 -- 2.16.2