Skip to content

Commit f6dcc39

Browse files
authored
Add application_name parameter to clientauth (#291)
1 parent 1fad531 commit f6dcc39

File tree

5 files changed

+147
-55
lines changed

5 files changed

+147
-55
lines changed

docs/04_hooks.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -209,14 +209,15 @@ A `clientauth` hook function takes the following arguments and returns either `t
209209

210210
clientauth_hook(port pgtle.clientauth_port_subset, status integer)
211211

212-
* `port` (`pgtle.clientauth_port_subset`) - an object containing the following fields. These are a subset of the Port object that client authentication hook passes to internal C functions.
212+
* `port` (`pgtle.clientauth_port_subset`) - an object containing the following fields. These are a subset of the [Port](https://doxygen.postgresql.org/structPort.html) object that client authentication hook passes to internal C functions.
213213
* `noblock` (`bool`)
214214
* `remote_host` (`text`)
215215
* `remote_hostname` (`text`)
216216
* `remote_hostname_resolv` (`integer`)
217217
* `remote_hostname_errcode` (`integer`)
218218
* `database_name` (`text`)
219219
* `user_name` (`text`)
220+
* `application_name` (`text`)
220221
* `status` (`integer`) - connection status code. This can be one of the following:
221222
* 0, representing successful connection
222223
* -1, representing a connection error

include/constants.h

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#define TLE_INPUT_FUNC_STR "input"
3737
#define TLE_OUTPUT_FUNC_STR "output"
3838

39+
#define TLE_CLIENTAUTH_PORT_SUBSET_TYPE "clientauth_port_subset"
40+
3941
/*
4042
* TLE_BASE_TYPE_SIZE_LIMIT is the maximum allowed size of pg_tle type.
4143
*

pg_tle--1.4.1--1.5.0.sql

+3
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,6 @@ CREATE FUNCTION pgtle.available_extensions
8181
RETURNS SETOF RECORD
8282
AS 'MODULE_PATHNAME', 'pg_tle_available_extensions'
8383
LANGUAGE C STABLE STRICT;
84+
85+
ALTER TYPE pgtle.clientauth_port_subset
86+
ADD ATTRIBUTE application_name text;

src/clientauth.c

+100-53
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "commands/extension.h"
5151
#include "commands/user.h"
5252
#include "executor/spi.h"
53+
#include "funcapi.h"
5354
#include "libpq/auth.h"
5455
#include "nodes/pg_list.h"
5556
#include "postmaster/bgworker_internals.h"
@@ -96,51 +97,6 @@
9697
*/
9798
#define CLIENT_AUTH_USER_ERROR_MAX_STRLEN 256
9899

99-
static const char *clientauth_shmem_name = "pgtle_clientauth";
100-
static const char *clientauth_feature = "clientauth";
101-
static const char *clientauth_worker_name = "pg_tle_clientauth worker";
102-
103-
/* Background worker main entry function */
104-
PGDLLEXPORT void clientauth_launcher_main(Datum arg);
105-
106-
/* Set up our hooks */
107-
static ClientAuthentication_hook_type prev_clientauth_hook = NULL;
108-
static void clientauth_hook(Port *port, int status);
109-
110-
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
111-
static void clientauth_shmem_startup(void);
112-
113-
#if (PG_VERSION_NUM >= 150000)
114-
static shmem_request_hook_type prev_shmem_request_hook = NULL;
115-
static void clientauth_shmem_request(void);
116-
#endif
117-
118-
/* Helper functions */
119-
static Size clientauth_shared_memsize(void);
120-
static void clientauth_sighup(SIGNAL_ARGS);
121-
122-
void clientauth_init(void);
123-
static bool can_allow_without_executing(void);
124-
static bool can_reject_without_executing(void);
125-
126-
/* GUC that determines whether clientauth is enabled */
127-
static int enable_clientauth_feature = FEATURE_OFF;
128-
129-
/* GUC that determines which database SPI_exec runs against */
130-
static char *clientauth_database_name = "postgres";
131-
132-
/* GUC that determines the number of background workers */
133-
static int clientauth_num_parallel_workers = 1;
134-
135-
/* GUC that determines users that clientauth feature skips */
136-
static char *clientauth_users_to_skip = "";
137-
138-
/* GUC that determines databases that clientauth feature skips */
139-
static char *clientauth_databases_to_skip = "";
140-
141-
/* Global flags */
142-
static bool clientauth_reload_config = false;
143-
144100
/*
145101
* Fixed-length subset of Port, passed to user function. A corresponding SQL
146102
* base type is defined. Shared memory structs are required to be fixed-size,
@@ -163,6 +119,7 @@ typedef struct PortSubset
163119

164120
char database_name[CLIENT_AUTH_PORT_SUBSET_MAX_STRLEN];
165121
char user_name[CLIENT_AUTH_PORT_SUBSET_MAX_STRLEN];
122+
char application_name[CLIENT_AUTH_PORT_SUBSET_MAX_STRLEN];
166123
} PortSubset;
167124

168125
/* Represents a pending connection */
@@ -233,6 +190,53 @@ typedef struct ClientAuthBgwShmemSharedState
233190
ClientAuthStatusEntry requests[CLIENT_AUTH_MAX_PENDING_ENTRIES];
234191
} ClientAuthBgwShmemSharedState;
235192

193+
static const char *clientauth_shmem_name = "pgtle_clientauth";
194+
static const char *clientauth_feature = "clientauth";
195+
static const char *clientauth_worker_name = "pg_tle_clientauth worker";
196+
197+
/* Background worker main entry function */
198+
PGDLLEXPORT void clientauth_launcher_main(Datum arg);
199+
200+
/* Set up our hooks */
201+
static ClientAuthentication_hook_type prev_clientauth_hook = NULL;
202+
static void clientauth_hook(Port *port, int status);
203+
204+
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
205+
static void clientauth_shmem_startup(void);
206+
207+
#if (PG_VERSION_NUM >= 150000)
208+
static shmem_request_hook_type prev_shmem_request_hook = NULL;
209+
static void clientauth_shmem_request(void);
210+
#endif
211+
212+
/* Helper functions */
213+
static Size clientauth_shared_memsize(void);
214+
static void clientauth_sighup(SIGNAL_ARGS);
215+
216+
void clientauth_init(void);
217+
static bool can_allow_without_executing(void);
218+
static bool can_reject_without_executing(void);
219+
220+
static char *create_port_subset_str(PortSubset * port);
221+
222+
/* GUC that determines whether clientauth is enabled */
223+
static int enable_clientauth_feature = FEATURE_OFF;
224+
225+
/* GUC that determines which database SPI_exec runs against */
226+
static char *clientauth_database_name = "postgres";
227+
228+
/* GUC that determines the number of background workers */
229+
static int clientauth_num_parallel_workers = 1;
230+
231+
/* GUC that determines users that clientauth feature skips */
232+
static char *clientauth_users_to_skip = "";
233+
234+
/* GUC that determines databases that clientauth feature skips */
235+
static char *clientauth_databases_to_skip = "";
236+
237+
/* Global flags */
238+
static bool clientauth_reload_config = false;
239+
236240
static ClientAuthBgwShmemSharedState * clientauth_ss = NULL;
237241

238242
void clientauth_launcher_run_user_functions(bool *error, char (*error_msg)[CLIENT_AUTH_USER_ERROR_MAX_STRLEN], PortSubset * port, int *status);
@@ -587,14 +591,7 @@ clientauth_launcher_run_user_functions(bool *error, char (*error_msg)[CLIENT_AUT
587591
func_name,
588592
quote_identifier(PG_TLE_NSPNAME));
589593

590-
port_subset_str = psprintf("(%d,%s,%s,%d,%d,%s,%s)",
591-
port->noblock,
592-
quote_identifier(port->remote_host),
593-
quote_identifier(port->remote_hostname),
594-
port->remote_hostname_resolv,
595-
port->remote_hostname_errcode,
596-
quote_identifier(port->database_name),
597-
quote_identifier(port->user_name));
594+
port_subset_str = create_port_subset_str(port);
598595

599596
hookargs[0] = CStringGetTextDatum(port_subset_str);
600597
hookargs[1] = Int32GetDatum(*status);
@@ -718,6 +715,10 @@ clientauth_hook(Port *port, int status)
718715
CLIENT_AUTH_PORT_SUBSET_MAX_STRLEN,
719716
"%s",
720717
port->user_name == NULL ? "" : port->user_name);
718+
snprintf(clientauth_ss->requests[idx].port_info.application_name,
719+
CLIENT_AUTH_PORT_SUBSET_MAX_STRLEN,
720+
"%s",
721+
port->application_name == NULL ? "" : port->application_name);
721722
clientauth_ss->requests[idx].port_info.noblock = port->noblock;
722723
clientauth_ss->requests[idx].port_info.remote_hostname_resolv = port->remote_hostname_resolv;
723724
clientauth_ss->requests[idx].port_info.remote_hostname_errcode = port->remote_hostname_errcode;
@@ -894,3 +895,49 @@ can_reject_without_executing()
894895

895896
return false;
896897
}
898+
899+
/*
900+
* Constructs a string representation of the PortSubset composite type and
901+
* returns it in an allocated string. The signature of the PortSubset type
902+
* varies based on the pg_tle version, so check the signature first.
903+
*/
904+
static char *
905+
create_port_subset_str(PortSubset * port)
906+
{
907+
TupleDesc tupdesc =
908+
RelationNameGetTupleDesc(PG_TLE_NSPNAME "."
909+
TLE_CLIENTAUTH_PORT_SUBSET_TYPE);
910+
char *port_subset_str;
911+
912+
if (tupdesc->natts == 7)
913+
port_subset_str = psprintf("(%d,%s,%s,%d,%d,%s,%s)",
914+
port->noblock,
915+
quote_identifier(port->remote_host),
916+
quote_identifier(port->remote_hostname),
917+
port->remote_hostname_resolv,
918+
port->remote_hostname_errcode,
919+
quote_identifier(port->database_name),
920+
quote_identifier(port->user_name));
921+
else if (tupdesc->natts == 8)
922+
port_subset_str = psprintf("(%d,%s,%s,%d,%d,%s,%s,%s)",
923+
port->noblock,
924+
quote_identifier(port->remote_host),
925+
quote_identifier(port->remote_hostname),
926+
port->remote_hostname_resolv,
927+
port->remote_hostname_errcode,
928+
quote_identifier(port->database_name),
929+
quote_identifier(port->user_name),
930+
quote_identifier(port->application_name));
931+
else
932+
933+
/*
934+
* Should be unreachable. If we add more fields in the future, we
935+
* need to modify the logic above.
936+
*/
937+
ereport(ERROR,
938+
errmsg("\"%s.clientauth\" feature encountered an unexpected number of fields in the \"%s.%s\" composite type: %d",
939+
PG_TLE_NSPNAME, PG_TLE_NSPNAME,
940+
TLE_CLIENTAUTH_PORT_SUBSET_TYPE, tupdesc->natts));
941+
942+
return port_subset_str;
943+
}

test/t/004_pg_tle_clientauth.pl

+40-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
### 15. Rejects connections when no schema qualified function is found
3030
### 16. Database does not come up if clientauth workers fail to start
3131
### 17. Malformed strings cannot be used for SQL injection
32+
### 18. pg_tle can be updated from 1.4.1 to 1.5.0 without affecting clientauth functions
33+
### 19. application_name field works
3234

3335
use strict;
3436
use warnings;
@@ -322,5 +324,42 @@
322324
like($psql_out, qr/^"\) \/\*$/,
323325
"role with injection payload in name can connect");
324326

327+
### 18. pg_tle can be updated from 1.4.1 to 1.5.0 without affecting clientauth functions
328+
$node->safe_psql('postgres', 'DROP EXTENSION pg_tle CASCADE');
329+
$node->safe_psql('postgres', "CREATE EXTENSION pg_tle VERSION '1.4.1'");
330+
$node->safe_psql('postgres', q[
331+
CREATE FUNCTION reject_testuser(port pgtle.clientauth_port_subset, status integer) RETURNS void AS $$
332+
BEGIN
333+
IF port.user_name = 'testuser' THEN
334+
RAISE EXCEPTION 'testuser is not allowed to connect';
335+
END IF;
336+
END
337+
$$ LANGUAGE plpgsql]);
338+
$node->safe_psql('postgres', qq[SELECT pgtle.register_feature('reject_testuser', 'clientauth')]);
339+
340+
$node->safe_psql('postgres', 'SELECT 1');
341+
$node->psql('not_excluded', 'select', extra_params => ['-U', 'testuser'], stderr => \$psql_err);
342+
like($psql_err, qr/FATAL: testuser is not allowed to connect/,
343+
"clientauth function works on pg_tle 1.4.1");
344+
345+
$node->safe_psql('postgres', "ALTER EXTENSION pg_tle UPDATE TO '1.5.0'");
346+
$node->psql('not_excluded', 'select', extra_params => ['-U', 'testuser'], stderr => \$psql_err);
347+
like($psql_err, qr/FATAL: testuser is not allowed to connect/,
348+
"clientauth function works on pg_tle 1.5.0");
349+
350+
### 19. application_name field works
351+
$node->safe_psql('postgres', q[
352+
CREATE OR REPLACE FUNCTION reject_testuser(port pgtle.clientauth_port_subset, status integer) RETURNS void AS $$
353+
BEGIN
354+
IF port.user_name = 'testuser' THEN
355+
RAISE EXCEPTION '%', port.application_name;
356+
END IF;
357+
END
358+
$$ LANGUAGE plpgsql]);
359+
360+
$node->psql('not_excluded', 'select', extra_params => ['-U', 'testuser'], stderr => \$psql_err);
361+
like($psql_err, qr/FATAL: 004_pg_tle_clientauth.pl/,
362+
"application_name field works on pg_tle 1.5.0");
363+
325364
$node->stop;
326-
done_testing();
365+
done_testing();

0 commit comments

Comments
 (0)