Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 419a8dd

Browse files
committed
Add a hook for modifying the ldapbind password
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.net
1 parent e3ac850 commit 419a8dd

File tree

8 files changed

+255
-1
lines changed

8 files changed

+255
-1
lines changed

src/backend/libpq/auth.c

+11-1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ static int CheckLDAPAuth(Port *port);
144144
#define LDAP_OPT_DIAGNOSTIC_MESSAGE LDAP_OPT_ERROR_STRING
145145
#endif
146146

147+
/* Default LDAP password mutator hook, can be overridden by a shared library */
148+
static char *dummy_ldap_password_mutator(char *input);
149+
auth_password_hook_typ ldap_password_hook = dummy_ldap_password_mutator;
150+
147151
#endif /* USE_LDAP */
148152

149153
/*----------------------------------------------------------------
@@ -2370,6 +2374,12 @@ InitializeLDAPConnection(Port *port, LDAP **ldap)
23702374
#define LDAPS_PORT 636
23712375
#endif
23722376

2377+
static char *
2378+
dummy_ldap_password_mutator(char *input)
2379+
{
2380+
return input;
2381+
}
2382+
23732383
/*
23742384
* Return a newly allocated C string copied from "pattern" with all
23752385
* occurrences of the placeholder "$username" replaced with "user_name".
@@ -2498,7 +2508,7 @@ CheckLDAPAuth(Port *port)
24982508
*/
24992509
r = ldap_simple_bind_s(ldap,
25002510
port->hba->ldapbinddn ? port->hba->ldapbinddn : "",
2501-
port->hba->ldapbindpasswd ? port->hba->ldapbindpasswd : "");
2511+
port->hba->ldapbindpasswd ? ldap_password_hook(port->hba->ldapbindpasswd) : "");
25022512
if (r != LDAP_SUCCESS)
25032513
{
25042514
ereport(LOG,

src/include/libpq/auth.h

+6
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,10 @@ extern void sendAuthRequest(Port *port, AuthRequest areq, const char *extradata,
2828
typedef void (*ClientAuthentication_hook_type) (Port *, int);
2929
extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook;
3030

31+
/* hook type for password manglers */
32+
typedef char *(*auth_password_hook_typ) (char *input);
33+
34+
/* Default LDAP password mutator hook, can be overridden by a shared library */
35+
extern PGDLLIMPORT auth_password_hook_typ ldap_password_hook;
36+
3137
#endif /* AUTH_H */

src/test/modules/Makefile

+11
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,16 @@ else
4242
ALWAYS_SUBDIRS += ssl_passphrase_callback
4343
endif
4444

45+
# Test runs an LDAP server, so only run if ldap is in PG_TEST_EXTRA
46+
ifeq ($(with_ldap),yes)
47+
ifneq (,$(filter ldap,$(PG_TEST_EXTRA)))
48+
SUBDIRS += ldap_password_func
49+
else
50+
ALWAYS_SUBDIRS += ldap_password_func
51+
endif
52+
else
53+
ALWAYS_SUBDIRS += ldap_password_func
54+
endif
55+
4556
$(recurse)
4657
$(recurse_always)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright (c) 2022, PostgreSQL Global Development Group
2+
3+
# ldap_password_func Makefile
4+
5+
export with_ldap
6+
7+
MODULE_big = ldap_password_func
8+
OBJS = ldap_password_func.o $(WIN32RES)
9+
PGFILEDESC = "set hook to mutate ldapbindpasswd"
10+
11+
TAP_TESTS = 1
12+
13+
ifdef USE_PGXS
14+
PG_CONFIG = pg_config
15+
PGXS := $(shell $(PG_CONFIG) --pgxs)
16+
include $(PGXS)
17+
else
18+
subdir = src/test/modules/ldap_password_func
19+
top_builddir = ../../../..
20+
include $(top_builddir)/src/Makefile.global
21+
include $(top_srcdir)/contrib/contrib-global.mk
22+
endif
23+
24+
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* Copyright (c) 2022, PostgreSQL Global Development Group
4+
*
5+
* ldap_password_func.c
6+
*
7+
* Loadable PostgreSQL module to mutate the ldapbindpasswd. This
8+
* implementation just hands back the configured password rot13'd.
9+
*
10+
*-------------------------------------------------------------------------
11+
*/
12+
13+
#include "postgres.h"
14+
15+
#include <float.h>
16+
#include <stdio.h>
17+
18+
#include "libpq/libpq.h"
19+
#include "libpq/libpq-be.h"
20+
#include "libpq/auth.h"
21+
#include "utils/guc.h"
22+
23+
PG_MODULE_MAGIC;
24+
25+
void _PG_init(void);
26+
void _PG_fini(void);
27+
28+
/* hook function */
29+
static char *rot13_passphrase(char *password);
30+
31+
/*
32+
* Module load callback
33+
*/
34+
void
35+
_PG_init(void)
36+
{
37+
ldap_password_hook = rot13_passphrase;
38+
}
39+
40+
void
41+
_PG_fini(void)
42+
{
43+
/* do nothing yet */
44+
}
45+
46+
static char *
47+
rot13_passphrase(char *pw)
48+
{
49+
size_t size = strlen(pw) + 1;
50+
51+
char *new_pw = (char *) palloc(size);
52+
53+
strlcpy(new_pw, pw, size);
54+
for (char *p = new_pw; *p; p++)
55+
{
56+
char c = *p;
57+
58+
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
59+
*p = c + 13;
60+
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
61+
*p = c - 13;
62+
}
63+
64+
return new_pw;
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
if not ldap.found()
2+
subdir_done()
3+
endif
4+
5+
ldap_password_func_sources = files(
6+
'ldap_password_func.c',
7+
)
8+
9+
if host_system == 'windows'
10+
ldap_password_func_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
11+
'--NAME', 'ldap_password_func',
12+
'--FILEDESC', 'set hook to mutate ldapbindpassw',])
13+
endif
14+
15+
ldap_password_func = shared_module('ldap_password_func',
16+
ldap_password_func_sources,
17+
kwargs: pg_mod_args + {
18+
'dependencies': [ldap, pg_mod_args['dependencies']],
19+
},
20+
)
21+
test_install_libs += ldap_password_func
22+
23+
tests += {
24+
'name': 'ldap_password_func',
25+
'sd': meson.current_source_dir(),
26+
'bd': meson.current_build_dir(),
27+
'tap': {
28+
'tests': [
29+
't/001_mutated_bindpasswd.pl',
30+
],
31+
'env': {'with_ldap': 'yes'}
32+
},
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
2+
# Copyright (c) 2022, PostgreSQL Global Development Group
3+
4+
use strict;
5+
use warnings;
6+
use File::Copy;
7+
use FindBin;
8+
use PostgreSQL::Test::Utils;
9+
use PostgreSQL::Test::Cluster;
10+
use Test::More;
11+
12+
use lib "$FindBin::RealBin/../../../ldap";
13+
use LdapServer;
14+
15+
my ($slapd, $ldap_bin_dir, $ldap_schema_dir);
16+
17+
$ldap_bin_dir = undef; # usually in PATH
18+
19+
if ($ENV{with_ldap} ne 'yes')
20+
{
21+
plan skip_all => 'LDAP not supported by this build';
22+
}
23+
elsif ($ENV{PG_TEST_EXTRA} !~ /\bldap\b/)
24+
{
25+
plan skip_all =>
26+
'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA';
27+
}
28+
elsif (!$LdapServer::setup)
29+
{
30+
plan skip_all =>
31+
"ldap tests not supported on $^O or dependencies not installed";
32+
}
33+
34+
my $clear_ldap_rootpw = "FooBaR1";
35+
my $rot13_ldap_rootpw = "SbbOnE1";
36+
37+
my $ldap = LdapServer->new($clear_ldap_rootpw, 'users'); # no anonymous auth
38+
$ldap->ldapadd_file("$FindBin::RealBin/../../../ldap/authdata.ldif");
39+
$ldap->ldapsetpw('uid=test1,dc=example,dc=net', 'secret1');
40+
41+
my ($ldap_server, $ldap_port, $ldap_basedn, $ldap_rootdn) =
42+
$ldap->prop(qw(server port basedn rootdn));
43+
44+
45+
note "setting up PostgreSQL instance";
46+
47+
my $node = PostgreSQL::Test::Cluster->new('node');
48+
$node->init;
49+
$node->append_conf('postgresql.conf', "log_connections = on\n");
50+
$node->append_conf('postgresql.conf', "shared_preload_libraries = 'ldap_password_func'");
51+
$node->start;
52+
53+
$node->safe_psql('postgres', 'CREATE USER test1;');
54+
55+
note "running tests";
56+
57+
sub test_access
58+
{
59+
local $Test::Builder::Level = $Test::Builder::Level + 1;
60+
61+
my ($node, $role, $expected_res, $test_name, %params) = @_;
62+
my $connstr = "user=$role";
63+
64+
if ($expected_res eq 0)
65+
{
66+
$node->connect_ok($connstr, $test_name, %params);
67+
}
68+
else
69+
{
70+
# No checks of the error message, only the status code.
71+
$node->connect_fails($connstr, $test_name, %params);
72+
}
73+
}
74+
75+
note "use ldapbindpasswd";
76+
77+
$ENV{"PGPASSWORD"} = 'secret1';
78+
79+
unlink($node->data_dir . '/pg_hba.conf');
80+
$node->append_conf('pg_hba.conf',
81+
qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd=wrong}
82+
);
83+
$node->restart;
84+
85+
test_access($node, 'test1', 2, 'search+bind authentication fails with wrong ldapbindpasswd');
86+
87+
unlink($node->data_dir . '/pg_hba.conf');
88+
$node->append_conf('pg_hba.conf',
89+
qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$clear_ldap_rootpw"}
90+
);
91+
$node->restart;
92+
93+
test_access($node, 'test1', 2, 'search+bind authentication fails with clear password');
94+
95+
unlink($node->data_dir . '/pg_hba.conf');
96+
$node->append_conf('pg_hba.conf',
97+
qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$rot13_ldap_rootpw"}
98+
);
99+
$node->restart;
100+
101+
test_access($node, 'test1', 0, 'search+bind authentication succeeds with rot13ed password');
102+
103+
done_testing();

src/test/modules/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ subdir('commit_ts')
55
subdir('delay_execution')
66
subdir('dummy_index_am')
77
subdir('dummy_seclabel')
8+
subdir('ldap_password_func')
89
subdir('libpq_pipeline')
910
subdir('plsample')
1011
subdir('snapshot_too_old')

0 commit comments

Comments
 (0)