From 108530921d7a3780cd5d1a31aebadd29621de429 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Wed, 14 Feb 2018 09:10:50 -0500 Subject: [PATCH] Tests for Kerberos/GSSAPI authentication --- configure | 2 + configure.in | 1 + src/Makefile.global.in | 1 + src/test/kerberos/.gitignore | 2 + src/test/kerberos/Makefile | 25 +++++++ src/test/kerberos/README | 25 +++++++ src/test/kerberos/t/001_auth.pl | 143 ++++++++++++++++++++++++++++++++++++++++ src/test/perl/PostgresNode.pm | 8 ++- 8 files changed, 205 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 7dcca506f8..84b547be79 100755 --- a/configure +++ b/configure @@ -708,6 +708,7 @@ with_uuid with_systemd with_selinux with_openssl +with_krb_srvnam krb_srvtab with_python with_perl @@ -5814,6 +5815,7 @@ fi + cat >>confdefs.h <<_ACEOF #define PG_KRB_SRVNAM "$with_krb_srvnam" _ACEOF diff --git a/configure.in b/configure.in index 4d26034579..dbd7fa2c48 100644 --- a/configure.in +++ b/configure.in @@ -650,6 +650,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/src/Makefile.global.in b/src/Makefile.global.in index d980f81046..1c6c0d3eea 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -188,6 +188,7 @@ with_selinux = @with_selinux@ with_systemd = @with_systemd@ with_libxml = @with_libxml@ with_libxslt = @with_libxslt@ +with_krb_srvnam = @with_krb_srvnam@ with_system_tzdata = @with_system_tzdata@ with_uuid = @with_uuid@ with_zlib = @with_zlib@ 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..1363529498 --- /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_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..94cf7ff42e --- /dev/null +++ b/src/test/kerberos/README @@ -0,0 +1,25 @@ +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. You might need to adjust some paths in the test +file to have it find MIT Kerberos in a place that hadn't been thought +of yet. + +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 diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl new file mode 100644 index 0000000000..5d6f3df8bf --- /dev/null +++ b/src/test/kerberos/t/001_auth.pl @@ -0,0 +1,143 @@ +use strict; +use warnings; +use TestLib; +use PostgresNode; +use Test::More tests => 4; + +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 'linux') +{ + $krb5_sbin_dir = '/usr/sbin'; +} + +$ENV{PATH} = "$krb5_sbin_dir:$ENV{PATH}" if $krb5_sbin_dir; +$ENV{PATH} = "$krb5_bin_dir:$ENV{PATH}" if $krb5_bin_dir; + +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); +IPC::Run::run [ 'krb5-config', '--version' ], '>', \$stdout or die "could not execute krb5-config"; +$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/ or die "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'; + +system_or_bail 'kadmin.local', '-q', "addprinc -pw secret1 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) = @_; + + my $res = $node->psql('postgres', 'SELECT 1', tcpip => 1, extra_params => [ '-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'); + +system_or_bail 'echo secret1 | kinit test1'; + +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'); diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm index 1d5ac4ee35..fcc6de3671 100644 --- a/src/test/perl/PostgresNode.pm +++ b/src/test/perl/PostgresNode.pm @@ -1129,8 +1129,7 @@ sub psql my $stderr = $params{stderr}; my $timeout = undef; my $timeout_exception = 'psql timed out'; - my @psql_params = - ('psql', '-XAtq', '-d', $self->connstr($dbname), '-f', '-'); + my $connstr = $self->connstr($dbname); # If the caller wants an array and hasn't passed stdout/stderr # references, allocate temporary ones to capture them so we @@ -1152,6 +1151,11 @@ sub psql $params{on_error_stop} = 1 unless defined $params{on_error_stop}; $params{on_error_die} = 0 unless defined $params{on_error_die}; + $connstr .= ' host=localhost' if defined $params{tcpip}; + + my @psql_params = + ('psql', '-XAtq', '-d', $connstr, '-f', '-'); + push @psql_params, '-v', 'ON_ERROR_STOP=1' if $params{on_error_stop}; push @psql_params, @{ $params{extra_params} } if defined $params{extra_params}; -- 2.16.1