diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index c0b94bc..4583400 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -16751,6 +16751,19 @@ SELECT collation for ('foo' COLLATE "de_DE"); text set parameter and return new value + + + + pg_hba_lookup + + pg_hba_lookup(database text, + user_name text + [, address text] + [, ssl_inuse text) + + record + scan pg_hba.conf and return examined entries up to a matching one + @@ -16808,6 +16821,32 @@ SELECT set_config('log_statement_stats', 'off', false); + + pg_hba_lookup returns a set of records containing the + line number, mode, type, database, user_name, address, netmask, hostname, + method, options and skip reason. For example, to debug problems with user + kommih trying to connect to a database postgres + from IPv6-address ::1, one can issue a following query: + +postgres=# SELECT * FROM pg_hba_lookup('postgres', 'kommih', '::1'); + line_number | mode | type | database | user_name | address | netmask | hostname | method | options | reason +-------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+-------------------------- + 84 | skipped | local | {all} | {all} | | | | trust | {} | connection type mismatch + 86 | skipped | host | {all} | {all} | 127.0.0.1 | 255.255.255.255 | | trust | {} | IP address mismatch + 88 | matched | host | {all} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | | trust | {} | +(3 rows) + + + This function actually loads the contents of pg_hba.conf file + into memory to perform matching, thus a database administrator can use it + to test the effects of changes made to the file in isolation prior to + actually applying the configuration cluster-wide with a signal to the + postmaster process. + + Only superusers can access this function to look the + pg_hba.conf entries up. + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index abf9a70..615a4d5 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -971,3 +971,12 @@ RETURNS jsonb LANGUAGE INTERNAL STRICT IMMUTABLE AS 'jsonb_set'; + +CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text, + IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false, + OUT line_number int, OUT mode text, OUT type text, OUT database text[], + OUT user_name text[], OUT address inet, OUT netmask inet, + OUT hostname text, OUT method text, OUT options jsonb, OUT reason text) +RETURNS SETOF RECORD +LANGUAGE INTERNAL +AS 'pg_hba_lookup'; diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 28f9fb5..d0099dc 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -25,15 +25,22 @@ #include #include +#include "access/htup_details.h" +#include "catalog/objectaddress.h" #include "catalog/pg_collation.h" +#include "catalog/pg_type.h" +#include "funcapi.h" #include "libpq/ip.h" #include "libpq/libpq.h" +#include "miscadmin.h" #include "postmaster/postmaster.h" #include "regex/regex.h" #include "replication/walsender.h" #include "storage/fd.h" #include "utils/acl.h" +#include "utils/builtins.h" #include "utils/guc.h" +#include "utils/jsonb.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -52,6 +59,8 @@ #define MAX_TOKEN 256 #define MAX_LINE 8192 +#define NUM_PG_HBA_LOOKUP_ATTS 11 + /* callback data for check_network_callback */ typedef struct check_network_data { @@ -75,6 +84,24 @@ typedef struct HbaToken } HbaToken; /* + * Optional callback function type for check_hba() function. + * Currently, valid callback function is passed to check_hba() + * by pg_hba_lookup system function to frame the hba tuple row + * based on inputs provided by the user. + */ +typedef void (*hba_line_callback) (void *context, HbaLine *hba_line, + const char *reason); + +/* Context to use with hba_line_callback function. */ +typedef struct +{ + MemoryContext memcxt; + TupleDesc tupdesc; + Tuplestorestate *tuple_store; +} HbalineContext; + + +/* * pre-parsed content of HBA config file: list of HbaLine structs. * parsed_hba_context is the memory context where it lives. */ @@ -99,6 +126,9 @@ static List *tokenize_inc_file(List *tokens, const char *outer_filename, const char *inc_filename); static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num); +static Datum getauthmethod(UserAuth auth_method); +static Jsonb *gethba_options(HbaLine *hba); +static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason); /* * isblank() exists in the ISO C99 spec, but it's not very portable yet, @@ -1640,7 +1670,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) * request. */ static void -check_hba(hbaPort *port) +check_hba(hbaPort *port, hba_line_callback callback, + void *callback_context) { Oid roleid; ListCell *line; @@ -1657,25 +1688,41 @@ check_hba(hbaPort *port) if (hba->conntype == ctLocal) { if (!IS_AF_UNIX(port->raddr.addr.ss_family)) + { + if (callback) + callback(callback_context, hba, _("connection type mismatch")); continue; + } } else { if (IS_AF_UNIX(port->raddr.addr.ss_family)) + { + if (callback) + callback(callback_context, hba, _("connection type mismatch")); continue; + } /* Check SSL state */ if (port->ssl_in_use) { /* Connection is SSL, match both "host" and "hostssl" */ if (hba->conntype == ctHostNoSSL) + { + if (callback) + callback(callback_context, hba, _("connection type mismatch")); continue; + } } else { /* Connection is not SSL, match both "host" and "hostnossl" */ if (hba->conntype == ctHostSSL) + { + if (callback) + callback(callback_context, hba, _("connection type mismatch")); continue; + } } /* Check IP address */ @@ -1686,14 +1733,22 @@ check_hba(hbaPort *port) { if (!check_hostname(port, hba->hostname)) + { + if (callback) + callback(callback_context, hba, _("hostname mismatch")); continue; + } } else { if (!check_ip(&port->raddr, (struct sockaddr *) & hba->addr, (struct sockaddr *) & hba->mask)) + { + if (callback) + callback(callback_context, hba, _("IP address mismatch")); continue; + } } break; case ipCmpAll: @@ -1702,7 +1757,11 @@ check_hba(hbaPort *port) case ipCmpSameNet: if (!check_same_host_or_net(&port->raddr, hba->ip_cmp_method)) + { + if (callback) + callback(callback_context, hba, _("samehost/samenet mismatch")); continue; + } break; default: /* shouldn't get here, but deem it no-match if so */ @@ -1713,10 +1772,21 @@ check_hba(hbaPort *port) /* Check database and role */ if (!check_db(port->database_name, port->user_name, roleid, hba->databases)) + { + if (callback) + callback(callback_context, hba, _("database name mismatch")); continue; + } if (!check_role(port->user_name, roleid, hba->roles)) + { + if (callback) + callback(callback_context, hba, _("user name mismatch")); continue; + } + + if (callback) + callback(callback_context, hba, NULL); /* Found a record that matched! */ port->hba = hba; @@ -1770,8 +1840,7 @@ load_hba(void) FreeFile(file); /* Now parse all the lines */ - Assert(PostmasterContext); - hbacxt = AllocSetContextCreate(PostmasterContext, + hbacxt = AllocSetContextCreate(CurrentMemoryContext, "hba parser context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_MINSIZE, @@ -1829,14 +1898,26 @@ load_hba(void) } /* Loaded new file successfully, replace the one we use */ - if (parsed_hba_context != NULL) - MemoryContextDelete(parsed_hba_context); + discard_hba(); parsed_hba_context = hbacxt; parsed_hba_lines = new_parsed_lines; return true; } + +void +discard_hba(void) +{ + if (parsed_hba_context != NULL) + { + MemoryContextDelete(parsed_hba_context); + parsed_hba_context = NULL; + parsed_hba_lines = NIL; + } +} + + /* * Parse one tokenised line from the ident config file and store the result in * an IdentLine structure, or NULL if parsing fails. @@ -2148,8 +2229,7 @@ load_ident(void) FreeFile(file); /* Now parse all the lines */ - Assert(PostmasterContext); - ident_context = AllocSetContextCreate(PostmasterContext, + ident_context = AllocSetContextCreate(CurrentMemoryContext, "ident parser context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_MINSIZE, @@ -2202,6 +2282,20 @@ load_ident(void) } /* Loaded new file successfully, replace the one we use */ + discard_ident(); + parsed_ident_context = ident_context; + parsed_ident_lines = new_parsed_lines; + + return true; +} + + +void +discard_ident(void) +{ + ListCell *parsed_line_cell; + IdentLine *newline; + if (parsed_ident_lines != NIL) { foreach(parsed_line_cell, parsed_ident_lines) @@ -2210,18 +2304,17 @@ load_ident(void) if (newline->ident_user[0] == '/') pg_regfree(&newline->re); } + parsed_ident_lines = NIL; } + if (parsed_ident_context != NULL) + { MemoryContextDelete(parsed_ident_context); - - parsed_ident_context = ident_context; - parsed_ident_lines = new_parsed_lines; - - return true; + parsed_ident_context = NULL; + } } - /* * Determine what authentication method should be used when accessing database * "database" from frontend "raddr", user "user". Return the method and @@ -2233,5 +2326,505 @@ load_ident(void) void hba_getauthmethod(hbaPort *port) { - check_hba(port); + check_hba(port, NULL, NULL); +} + + +/* + * Returns the Text Datum representation of authentication method + */ +static Datum +getauthmethod(UserAuth auth_method) +{ + Datum result; + + switch (auth_method) + { + case uaReject: + result = CStringGetTextDatum("reject"); + break; + case uaTrust: + result = CStringGetTextDatum("trust"); + break; + case uaIdent: + result = CStringGetTextDatum("ident"); + break; + case uaPassword: + result = CStringGetTextDatum("password"); + break; + case uaMD5: + result = CStringGetTextDatum("md5"); + break; + case uaGSS: + result = CStringGetTextDatum("gss"); + break; + case uaSSPI: + result = CStringGetTextDatum("sspi"); + break; + case uaPAM: + result = CStringGetTextDatum("pam"); + break; + case uaLDAP: + result = CStringGetTextDatum("ldap"); + break; + case uaCert: + result = CStringGetTextDatum("cert"); + break; + case uaRADIUS: + result = CStringGetTextDatum("radius"); + break; + case uaPeer: + result = CStringGetTextDatum("peer"); + break; + default: + elog(ERROR, "unexpected authentication method in parsed HBA entry"); + break; + } + + return result; +} + +static Jsonb * +gethba_options(HbaLine *hba) +{ + JsonbParseState *parseState = NULL; + JsonbValue *result; + + result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL); + + if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI) + { + if (hba->include_realm) + { + push_jsonb_string_key(&parseState, "include_realm"); + push_jsonb_bool_value(&parseState, true); + } + + if (hba->krb_realm) + { + push_jsonb_string_key(&parseState, "krb_realm"); + push_jsonb_string_value(&parseState, hba->krb_realm); + } + } + + if (hba->usermap) + { + push_jsonb_string_key(&parseState, "map"); + push_jsonb_string_value(&parseState, hba->usermap); + } + + if (hba->clientcert) + { + push_jsonb_string_key(&parseState, "clientcert"); + push_jsonb_bool_value(&parseState, true); + } + + if (hba->pamservice) + { + push_jsonb_string_key(&parseState, "pamservice"); + push_jsonb_string_value(&parseState, hba->pamservice); + } + + if (hba->auth_method == uaLDAP) + { + if (hba->ldapserver) + { + push_jsonb_string_key(&parseState, "ldapserver"); + push_jsonb_string_value(&parseState, hba->ldapserver); + } + + if (hba->ldapport) + { + push_jsonb_string_key(&parseState, "ldapport"); + push_jsonb_int32_value(&parseState, hba->ldapport); + } + + if (hba->ldaptls) + { + push_jsonb_string_key(&parseState, "ldaptls"); + push_jsonb_bool_value(&parseState, true); + } + + if (hba->ldapprefix) + { + push_jsonb_string_key(&parseState, "ldapprefix"); + push_jsonb_string_value(&parseState, hba->ldapprefix); + } + + if (hba->ldapsuffix) + { + push_jsonb_string_key(&parseState, "ldapsuffix"); + push_jsonb_string_value(&parseState, hba->ldapsuffix); + } + + if (hba->ldapbasedn) + { + push_jsonb_string_key(&parseState, "ldapbasedn"); + push_jsonb_string_value(&parseState, hba->ldapbasedn); + } + + if (hba->ldapbinddn) + { + push_jsonb_string_key(&parseState, "ldapbinddn"); + push_jsonb_string_value(&parseState, hba->ldapbinddn); + } + + if (hba->ldapbindpasswd) + { + push_jsonb_string_key(&parseState, "ldapbindpasswd"); + push_jsonb_string_value(&parseState, hba->ldapbindpasswd); + } + + if (hba->ldapsearchattribute) + { + push_jsonb_string_key(&parseState, "ldapsearchattribute"); + push_jsonb_string_value(&parseState, hba->ldapsearchattribute); + } + + if (hba->ldapscope) + { + push_jsonb_string_key(&parseState, "ldapscope"); + push_jsonb_int32_value(&parseState, hba->ldapscope); + } + } + + if (hba->auth_method == uaRADIUS) + { + if (hba->radiusserver) + { + push_jsonb_string_key(&parseState, "radiusserver"); + push_jsonb_string_value(&parseState, hba->radiusserver); + } + + if (hba->radiussecret) + { + push_jsonb_string_key(&parseState, "radiussecret"); + push_jsonb_string_value(&parseState, hba->radiussecret); + } + + if (hba->radiusidentifier) + { + push_jsonb_string_key(&parseState, "radiusidentifier"); + push_jsonb_string_value(&parseState, hba->radiusidentifier); + } + + if (hba->radiusport) + { + push_jsonb_string_key(&parseState, "radiusport"); + push_jsonb_int32_value(&parseState, hba->radiusport); + } + } + + result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL); + return JsonbValueToJsonb(result); +} + + +static void +lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason) +{ + Datum values[NUM_PG_HBA_LOOKUP_ATTS]; + bool nulls[NUM_PG_HBA_LOOKUP_ATTS]; + ListCell *dbcell; + char buffer[NI_MAXHOST]; + HeapTuple tuple; + int index; + MemoryContext old_cxt; + HbalineContext *mycxt; + + mycxt = (HbalineContext *) context; + + index = 0; + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + old_cxt = MemoryContextSwitchTo(mycxt->memcxt); + + /* line_number */ + values[index] = Int32GetDatum(hba->linenumber); + + /* mode */ + index++; + if (reason == NULL) + values[index] = CStringGetTextDatum(_("matched")); + else + values[index] = CStringGetTextDatum(_("skipped")); + + /* type */ + index++; + switch (hba->conntype) + { + case ctLocal: + values[index] = CStringGetTextDatum("local"); + break; + case ctHost: + values[index] = CStringGetTextDatum("host"); + break; + case ctHostSSL: + values[index] = CStringGetTextDatum("hostssl"); + break; + case ctHostNoSSL: + values[index] = CStringGetTextDatum("hostnossl"); + break; + default: + elog(ERROR, "unexpected connection type in parsed HBA entry"); + break; + } + + /* database */ + index++; + if (list_length(hba->databases) != 0) + { + List *names = NULL; + HbaToken *tok; + + foreach(dbcell, hba->databases) + { + tok = lfirst(dbcell); + names = lappend(names, tok->string); + } + + values[index] = PointerGetDatum(strlist_to_textarray(names)); + } + else + nulls[index] = true; + + /* user */ + index++; + if (list_length(hba->roles) != 0) + { + List *roles = NULL; + HbaToken *tok; + + foreach(dbcell, hba->roles) + { + tok = lfirst(dbcell); + roles = lappend(roles, tok->string); + } + + values[index] = PointerGetDatum(strlist_to_textarray(roles)); + } + else + nulls[index] = true; + + /* address */ + index++; + if (pg_getnameinfo_all(&hba->addr, sizeof(struct sockaddr_storage), + buffer, sizeof(buffer), + NULL, 0, + NI_NUMERICHOST) == 0) + { + clean_ipv6_addr(hba->addr.ss_family, buffer); + values[index] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer)); + } + else + nulls[index] = true; + + /* netmask */ + index++; + if (pg_getnameinfo_all(&hba->mask, sizeof(struct sockaddr_storage), + buffer, sizeof(buffer), + NULL, 0, + NI_NUMERICHOST) == 0) + { + clean_ipv6_addr(hba->mask.ss_family, buffer); + values[index] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer)); + } + else + nulls[index] = true; + + /* hostname */ + index++; + if (hba->hostname) + values[index] = CStringGetTextDatum(hba->hostname); + else + nulls[index] = true; + + /* method */ + index++; + values[index] = getauthmethod(hba->auth_method); + + /* options */ + index++; + values[index] = PointerGetDatum(gethba_options(hba)); + + /* reason */ + index++; + if (reason) + values[index] = CStringGetTextDatum(reason); + else + nulls[index] = true; + + tuple = heap_form_tuple(mycxt->tupdesc, values, nulls); + tuplestore_puttuple(mycxt->tuple_store, tuple); + + MemoryContextSwitchTo(old_cxt); + return; +} + +/* + * SQL-accessible SRF to return all the settings from the pg_hba.conf + * file. + */ +Datum +pg_hba_lookup(PG_FUNCTION_ARGS) +{ + hbaPort *port; + Tuplestorestate *tuple_store; + TupleDesc tupdesc; + MemoryContext old_cxt; + HbalineContext *mycxt; + ReturnSetInfo *rsi; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to view pg_hba.conf settings")))); + + /* + * We must use the Materialize mode to be safe against HBA file reloads + * while the cursor is open. It's also more efficient than having to look + * up our current position in the parsed list every time. + */ + rsi = (ReturnSetInfo *) fcinfo->resultinfo; + + if (!rsi || !IsA(rsi, ReturnSetInfo) || + (rsi->allowedModes & SFRM_Materialize) == 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + + port = palloc0(sizeof(hbaPort)); + + if (PG_ARGISNULL(0)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + (errmsg("database name is required to match pg_hba configuration entry")))); + else + port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0)); + + if (PG_ARGISNULL(1)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + (errmsg("user name is required to match pg_hba configuration entry")))); + else + port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1)); + + + if (!PG_ARGISNULL(2)) + { + char *address = NULL; + struct addrinfo *gai_result = NULL; + struct addrinfo hints; + int ret; + + address = TextDatumGetCString(PG_GETARG_DATUM(2)); + + /* Get the IP address either way */ + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + hints.ai_addrlen = 0; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + ret = getaddrinfo(address, NULL, &hints, &gai_result); + if (ret == 0 && gai_result) + memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen); + else if (ret == EAI_NONAME) + { + struct addrinfo *gai_result2 = NULL; + + port->remote_hostname = pstrdup(address); + + ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2); + if (ret == 0 && gai_result2) + memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen); + else + ereport(ERROR, + (errmsg("getaddrinfo failed to look into hostname \"%s\": %s", + port->remote_hostname, gai_strerror(ret)))); + + if (gai_result2) + freeaddrinfo(gai_result2); + } + else + ereport(ERROR, + (errmsg("invalid IP address \"%s\": %s", + address, gai_strerror(ret)))); + if (gai_result) + freeaddrinfo(gai_result); + } + else + port->raddr.addr.ss_family = AF_UNIX; + + port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3)); + + rsi->returnMode = SFRM_Materialize; + + if (!load_hba()) + ereport(ERROR, + (errmsg("failed to load pg_hba.conf file"), + errhint("more details may be available in the server log."))); + + /* + * Create the tupledesc and tuplestore in the per_query context as + * required for SFRM_Materialize. + */ + old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); + + tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database", + TEXTARRAYOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name", + TEXTARRAYOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address", + INETOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "netmask", + INETOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, "hostname", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 9, "method", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 10, "options", + JSONBOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 11, "reason", + TEXTOID, -1, 0); + BlessTupleDesc(tupdesc); + + tuple_store = + tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, + false, work_mem); + + MemoryContextSwitchTo(old_cxt); + + mycxt = (HbalineContext *) palloc(sizeof(HbalineContext)); + mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext, + "pg_hba_lookup tuple cxt", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + mycxt->tupdesc = tupdesc; + mycxt->tuple_store = tuple_store; + + check_hba(port, lookup_hba_line_callback, mycxt); + + MemoryContextDelete(mycxt->memcxt); + pfree(mycxt); + + /* Clean up the memory allocated by load_hba() explicitly. */ + discard_hba(); + + rsi->setDesc = tupdesc; + rsi->setResult = tuple_store; + + PG_RETURN_NULL(); } diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c index e1ceea6..171e532 100644 --- a/src/backend/utils/adt/jsonb_util.c +++ b/src/backend/utils/adt/jsonb_util.c @@ -1802,3 +1802,49 @@ uniqueifyJsonbObject(JsonbValue *object) object->val.object.nPairs = res + 1 - object->val.object.pairs; } } + +void +push_jsonb_string_key(JsonbParseState **pstate, char *string_key) +{ + JsonbValue jb; + + jb.type = jbvString; + jb.val.string.len = strlen(string_key); + jb.val.string.val = pstrdup(string_key); + pushJsonbValue(pstate, WJB_KEY, &jb); +} + +void +push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val) +{ + JsonbValue jb; + + jb.type = jbvBool; + jb.val.boolean = bool_val; + + pushJsonbValue(pstate, WJB_VALUE, &jb); +} + +void +push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val) +{ + JsonbValue jb; + char outputstr[64]; + + snprintf(outputstr, 64, "%d", int32_val); + jb.type = jbvNumeric; + jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1)); + + pushJsonbValue(pstate, WJB_VALUE, &jb); +} + +void +push_jsonb_string_value(JsonbParseState **pstate, char *string_value) +{ + JsonbValue jb; + + jb.type = jbvString; + jb.val.string.len = strlen(string_value); + jb.val.string.val = pstrdup(string_value); + pushJsonbValue(pstate, WJB_VALUE, &jb); +} diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 6b760d4..d92cd9a 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -191,18 +191,6 @@ PerformAuthentication(Port *port) * FIXME: [fork/exec] Ugh. Is there a way around this overhead? */ #ifdef EXEC_BACKEND - /* - * load_hba() and load_ident() want to work within the PostmasterContext, - * so create that if it doesn't exist (which it won't). We'll delete it - * again later, in PostgresMain. - */ - if (PostmasterContext == NULL) - PostmasterContext = AllocSetContextCreate(TopMemoryContext, - "Postmaster", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - if (!load_hba()) { /* @@ -736,6 +724,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, } /* + * We don't need the HBA and ident data going forward, but we can't rely + * on release of PostmasterContext to clean that up, so discard them + * explicitly here. + */ + discard_hba(); + discard_ident(); + + /* * If we're trying to shut down, only superusers can connect, and new * replication connections are not allowed. */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index aec6c4c..3253382 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3010,6 +3010,8 @@ DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 1 1000 0 0 f f f f DESCR("SHOW ALL as a function"); DATA(insert OID = 3329 ( pg_show_all_file_settings PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,23,23,25,25,16,25}" "{o,o,o,o,o,o,o}" "{sourcefile,sourceline,seqno,name,setting,applied,error}" _null_ _null_ show_all_file_settings _null_ _null_ _null_ )); DESCR("show config file settings"); +DATA(insert OID = 3997 ( pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_)); +DESCR("view client authentication settings"); DATA(insert OID = 1371 ( pg_lock_status PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,26,26,23,21,25,28,26,26,21,25,23,25,16,16}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{locktype,database,relation,page,tuple,virtualxid,transactionid,classid,objid,objsubid,virtualtransaction,pid,mode,granted,fastpath}" _null_ _null_ pg_lock_status _null_ _null_ _null_ )); DESCR("view system lock information"); DATA(insert OID = 2561 ( pg_blocking_pids PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 1007 "23" _null_ _null_ _null_ _null_ _null_ pg_blocking_pids _null_ _null_ _null_ )); diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 68a953a..291300b 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -98,6 +98,10 @@ typedef struct Port hbaPort; extern bool load_hba(void); extern bool load_ident(void); + +extern void discard_hba(void); +extern void discard_ident(void); + extern void hba_getauthmethod(hbaPort *port); extern int check_usermap(const char *usermap_name, const char *pg_role, const char *auth_user, diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 7ec93c9..24625e5 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1151,6 +1151,9 @@ extern Datum show_all_file_settings(PG_FUNCTION_ARGS); /* pg_config.c */ extern Datum pg_config(PG_FUNCTION_ARGS); +/* hba.c */ +extern Datum pg_hba_lookup(PG_FUNCTION_ARGS); + /* rls.c */ extern Datum row_security_active(PG_FUNCTION_ARGS); extern Datum row_security_active_name(PG_FUNCTION_ARGS); diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h index 5f49d8d..33e00d2 100644 --- a/src/include/utils/jsonb.h +++ b/src/include/utils/jsonb.h @@ -427,6 +427,12 @@ extern bool JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained); extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash); +/* jsonb_util.c support functions */ +void push_jsonb_string_key(JsonbParseState **pstate, char *string_key); +void push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val); +void push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val); +void push_jsonb_string_value(JsonbParseState **pstate, char *string_value); + /* jsonb.c support functions */ extern char *JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len);