mirror of https://github.com/postgres/postgres
The hook can be installed by a shared_preload library. A similar mechanism could be used for radius paswords, for example, and the type name auth_password_hook_typ has been shosen with that in mind. John Naylor and Andrew Dunstan Discussion: https://postgr.es/m/469b06ed-69de-ba59-c13a-91d2372e52a9@dunslane.netpull/136/head
parent
e3ac85014e
commit
419a8dd814
@ -0,0 +1,25 @@ |
||||
# Copyright (c) 2022, PostgreSQL Global Development Group
|
||||
|
||||
# ldap_password_func Makefile
|
||||
|
||||
export with_ldap |
||||
|
||||
MODULE_big = ldap_password_func
|
||||
OBJS = ldap_password_func.o $(WIN32RES)
|
||||
PGFILEDESC = "set hook to mutate ldapbindpasswd"
|
||||
|
||||
TAP_TESTS = 1
|
||||
|
||||
ifdef USE_PGXS |
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS) |
||||
else |
||||
subdir = src/test/modules/ldap_password_func
|
||||
top_builddir = ../../../..
|
||||
include $(top_builddir)/src/Makefile.global |
||||
include $(top_srcdir)/contrib/contrib-global.mk |
||||
endif |
||||
|
||||
|
||||
|
@ -0,0 +1,65 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* Copyright (c) 2022, PostgreSQL Global Development Group |
||||
* |
||||
* ldap_password_func.c |
||||
* |
||||
* Loadable PostgreSQL module to mutate the ldapbindpasswd. This |
||||
* implementation just hands back the configured password rot13'd. |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include "postgres.h" |
||||
|
||||
#include <float.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "libpq/libpq.h" |
||||
#include "libpq/libpq-be.h" |
||||
#include "libpq/auth.h" |
||||
#include "utils/guc.h" |
||||
|
||||
PG_MODULE_MAGIC; |
||||
|
||||
void _PG_init(void); |
||||
void _PG_fini(void); |
||||
|
||||
/* hook function */ |
||||
static char *rot13_passphrase(char *password); |
||||
|
||||
/*
|
||||
* Module load callback |
||||
*/ |
||||
void |
||||
_PG_init(void) |
||||
{ |
||||
ldap_password_hook = rot13_passphrase; |
||||
} |
||||
|
||||
void |
||||
_PG_fini(void) |
||||
{ |
||||
/* do nothing yet */ |
||||
} |
||||
|
||||
static char * |
||||
rot13_passphrase(char *pw) |
||||
{ |
||||
size_t size = strlen(pw) + 1; |
||||
|
||||
char *new_pw = (char *) palloc(size); |
||||
|
||||
strlcpy(new_pw, pw, size); |
||||
for (char *p = new_pw; *p; p++) |
||||
{ |
||||
char c = *p; |
||||
|
||||
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) |
||||
*p = c + 13; |
||||
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) |
||||
*p = c - 13; |
||||
} |
||||
|
||||
return new_pw; |
||||
} |
@ -0,0 +1,33 @@ |
||||
if not ldap.found() |
||||
subdir_done() |
||||
endif |
||||
|
||||
ldap_password_func_sources = files( |
||||
'ldap_password_func.c', |
||||
) |
||||
|
||||
if host_system == 'windows' |
||||
ldap_password_func_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ |
||||
'--NAME', 'ldap_password_func', |
||||
'--FILEDESC', 'set hook to mutate ldapbindpassw',]) |
||||
endif |
||||
|
||||
ldap_password_func = shared_module('ldap_password_func', |
||||
ldap_password_func_sources, |
||||
kwargs: pg_mod_args + { |
||||
'dependencies': [ldap, pg_mod_args['dependencies']], |
||||
}, |
||||
) |
||||
test_install_libs += ldap_password_func |
||||
|
||||
tests += { |
||||
'name': 'ldap_password_func', |
||||
'sd': meson.current_source_dir(), |
||||
'bd': meson.current_build_dir(), |
||||
'tap': { |
||||
'tests': [ |
||||
't/001_mutated_bindpasswd.pl', |
||||
], |
||||
'env': {'with_ldap': 'yes'} |
||||
}, |
||||
} |
@ -0,0 +1,103 @@ |
||||
|
||||
# Copyright (c) 2022, PostgreSQL Global Development Group |
||||
|
||||
use strict; |
||||
use warnings; |
||||
use File::Copy; |
||||
use FindBin; |
||||
use PostgreSQL::Test::Utils; |
||||
use PostgreSQL::Test::Cluster; |
||||
use Test::More; |
||||
|
||||
use lib "$FindBin::RealBin/../../../ldap"; |
||||
use LdapServer; |
||||
|
||||
my ($slapd, $ldap_bin_dir, $ldap_schema_dir); |
||||
|
||||
$ldap_bin_dir = undef; # usually in PATH |
||||
|
||||
if ($ENV{with_ldap} ne 'yes') |
||||
{ |
||||
plan skip_all => 'LDAP not supported by this build'; |
||||
} |
||||
elsif ($ENV{PG_TEST_EXTRA} !~ /\bldap\b/) |
||||
{ |
||||
plan skip_all => |
||||
'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA'; |
||||
} |
||||
elsif (!$LdapServer::setup) |
||||
{ |
||||
plan skip_all => |
||||
"ldap tests not supported on $^O or dependencies not installed"; |
||||
} |
||||
|
||||
my $clear_ldap_rootpw = "FooBaR1"; |
||||
my $rot13_ldap_rootpw = "SbbOnE1"; |
||||
|
||||
my $ldap = LdapServer->new($clear_ldap_rootpw, 'users'); # no anonymous auth |
||||
$ldap->ldapadd_file("$FindBin::RealBin/../../../ldap/authdata.ldif"); |
||||
$ldap->ldapsetpw('uid=test1,dc=example,dc=net', 'secret1'); |
||||
|
||||
my ($ldap_server, $ldap_port, $ldap_basedn, $ldap_rootdn) = |
||||
$ldap->prop(qw(server port basedn rootdn)); |
||||
|
||||
|
||||
note "setting up PostgreSQL instance"; |
||||
|
||||
my $node = PostgreSQL::Test::Cluster->new('node'); |
||||
$node->init; |
||||
$node->append_conf('postgresql.conf', "log_connections = on\n"); |
||||
$node->append_conf('postgresql.conf', "shared_preload_libraries = 'ldap_password_func'"); |
||||
$node->start; |
||||
|
||||
$node->safe_psql('postgres', 'CREATE USER test1;'); |
||||
|
||||
note "running tests"; |
||||
|
||||
sub test_access |
||||
{ |
||||
local $Test::Builder::Level = $Test::Builder::Level + 1; |
||||
|
||||
my ($node, $role, $expected_res, $test_name, %params) = @_; |
||||
my $connstr = "user=$role"; |
||||
|
||||
if ($expected_res eq 0) |
||||
{ |
||||
$node->connect_ok($connstr, $test_name, %params); |
||||
} |
||||
else |
||||
{ |
||||
# No checks of the error message, only the status code. |
||||
$node->connect_fails($connstr, $test_name, %params); |
||||
} |
||||
} |
||||
|
||||
note "use ldapbindpasswd"; |
||||
|
||||
$ENV{"PGPASSWORD"} = 'secret1'; |
||||
|
||||
unlink($node->data_dir . '/pg_hba.conf'); |
||||
$node->append_conf('pg_hba.conf', |
||||
qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd=wrong} |
||||
); |
||||
$node->restart; |
||||
|
||||
test_access($node, 'test1', 2, 'search+bind authentication fails with wrong ldapbindpasswd'); |
||||
|
||||
unlink($node->data_dir . '/pg_hba.conf'); |
||||
$node->append_conf('pg_hba.conf', |
||||
qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$clear_ldap_rootpw"} |
||||
); |
||||
$node->restart; |
||||
|
||||
test_access($node, 'test1', 2, 'search+bind authentication fails with clear password'); |
||||
|
||||
unlink($node->data_dir . '/pg_hba.conf'); |
||||
$node->append_conf('pg_hba.conf', |
||||
qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$rot13_ldap_rootpw"} |
||||
); |
||||
$node->restart; |
||||
|
||||
test_access($node, 'test1', 0, 'search+bind authentication succeeds with rot13ed password'); |
||||
|
||||
done_testing(); |
Loading…
Reference in new issue