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

Lines changed: 11 additions & 1 deletion
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 11 additions & 0 deletions
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)
Lines changed: 25 additions & 0 deletions
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+
Lines changed: 65 additions & 0 deletions
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+
}
Lines changed: 33 additions & 0 deletions
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+
}
Lines changed: 103 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy