Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * hbafuncs.c
4 : : * Support functions for SQL views of authentication files.
5 : : *
6 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/utils/adt/hbafuncs.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/htup_details.h"
18 : : #include "catalog/objectaddress.h"
19 : : #include "common/ip.h"
20 : : #include "funcapi.h"
21 : : #include "libpq/hba.h"
22 : : #include "utils/array.h"
23 : : #include "utils/builtins.h"
24 : : #include "utils/guc.h"
25 : :
26 : :
27 : : static ArrayType *get_hba_options(HbaLine *hba);
28 : : static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
29 : : int rule_number, char *filename, int lineno,
30 : : HbaLine *hba, const char *err_msg);
31 : : static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
32 : : static void fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
33 : : int map_number, char *filename, int lineno,
34 : : IdentLine *ident, const char *err_msg);
35 : : static void fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
36 : :
37 : :
38 : : /*
39 : : * This macro specifies the maximum number of authentication options
40 : : * that are possible with any given authentication method that is supported.
41 : : * Currently LDAP supports 12, and there are 3 that are not dependent on
42 : : * the auth method here. It may not actually be possible to set all of them
43 : : * at the same time, but we'll set the macro value high enough to be
44 : : * conservative and avoid warnings from static analysis tools.
45 : : */
46 : : #define MAX_HBA_OPTIONS 15
47 : :
48 : : /*
49 : : * Create a text array listing the options specified in the HBA line.
50 : : * Return NULL if no options are specified.
51 : : */
52 : : static ArrayType *
1314 michael@paquier.xyz 53 :CBC 31 : get_hba_options(HbaLine *hba)
54 : : {
55 : : int noptions;
56 : : Datum options[MAX_HBA_OPTIONS];
57 : :
58 : 31 : noptions = 0;
59 : :
60 [ + - - + ]: 31 : if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
61 : : {
1314 michael@paquier.xyz 62 [ # # ]:UBC 0 : if (hba->include_realm)
63 : 0 : options[noptions++] =
64 : 0 : CStringGetTextDatum("include_realm=true");
65 : :
66 [ # # ]: 0 : if (hba->krb_realm)
67 : 0 : options[noptions++] =
68 : 0 : CStringGetTextDatum(psprintf("krb_realm=%s", hba->krb_realm));
69 : : }
70 : :
1314 michael@paquier.xyz 71 [ - + ]:CBC 31 : if (hba->usermap)
1314 michael@paquier.xyz 72 :UBC 0 : options[noptions++] =
73 : 0 : CStringGetTextDatum(psprintf("map=%s", hba->usermap));
74 : :
1314 michael@paquier.xyz 75 [ - + ]:CBC 31 : if (hba->clientcert != clientCertOff)
1314 michael@paquier.xyz 76 :UBC 0 : options[noptions++] =
77 [ # # ]: 0 : CStringGetTextDatum(psprintf("clientcert=%s", (hba->clientcert == clientCertCA) ? "verify-ca" : "verify-full"));
78 : :
1314 michael@paquier.xyz 79 [ - + ]:CBC 31 : if (hba->pamservice)
1314 michael@paquier.xyz 80 :UBC 0 : options[noptions++] =
81 : 0 : CStringGetTextDatum(psprintf("pamservice=%s", hba->pamservice));
82 : :
1314 michael@paquier.xyz 83 [ - + ]:CBC 31 : if (hba->auth_method == uaLDAP)
84 : : {
1314 michael@paquier.xyz 85 [ # # ]:UBC 0 : if (hba->ldapserver)
86 : 0 : options[noptions++] =
87 : 0 : CStringGetTextDatum(psprintf("ldapserver=%s", hba->ldapserver));
88 : :
89 [ # # ]: 0 : if (hba->ldapport)
90 : 0 : options[noptions++] =
91 : 0 : CStringGetTextDatum(psprintf("ldapport=%d", hba->ldapport));
92 : :
291 dgustafsson@postgres 93 [ # # ]: 0 : if (hba->ldapscheme)
94 : 0 : options[noptions++] =
95 : 0 : CStringGetTextDatum(psprintf("ldapscheme=%s", hba->ldapscheme));
96 : :
1314 michael@paquier.xyz 97 [ # # ]: 0 : if (hba->ldaptls)
98 : 0 : options[noptions++] =
99 : 0 : CStringGetTextDatum("ldaptls=true");
100 : :
101 [ # # ]: 0 : if (hba->ldapprefix)
102 : 0 : options[noptions++] =
103 : 0 : CStringGetTextDatum(psprintf("ldapprefix=%s", hba->ldapprefix));
104 : :
105 [ # # ]: 0 : if (hba->ldapsuffix)
106 : 0 : options[noptions++] =
107 : 0 : CStringGetTextDatum(psprintf("ldapsuffix=%s", hba->ldapsuffix));
108 : :
109 [ # # ]: 0 : if (hba->ldapbasedn)
110 : 0 : options[noptions++] =
111 : 0 : CStringGetTextDatum(psprintf("ldapbasedn=%s", hba->ldapbasedn));
112 : :
113 [ # # ]: 0 : if (hba->ldapbinddn)
114 : 0 : options[noptions++] =
115 : 0 : CStringGetTextDatum(psprintf("ldapbinddn=%s", hba->ldapbinddn));
116 : :
117 [ # # ]: 0 : if (hba->ldapbindpasswd)
118 : 0 : options[noptions++] =
119 : 0 : CStringGetTextDatum(psprintf("ldapbindpasswd=%s",
120 : : hba->ldapbindpasswd));
121 : :
122 [ # # ]: 0 : if (hba->ldapsearchattribute)
123 : 0 : options[noptions++] =
124 : 0 : CStringGetTextDatum(psprintf("ldapsearchattribute=%s",
125 : : hba->ldapsearchattribute));
126 : :
127 [ # # ]: 0 : if (hba->ldapsearchfilter)
128 : 0 : options[noptions++] =
129 : 0 : CStringGetTextDatum(psprintf("ldapsearchfilter=%s",
130 : : hba->ldapsearchfilter));
131 : :
132 [ # # ]: 0 : if (hba->ldapscope)
133 : 0 : options[noptions++] =
134 : 0 : CStringGetTextDatum(psprintf("ldapscope=%d", hba->ldapscope));
135 : : }
136 : :
1314 michael@paquier.xyz 137 [ - + ]:CBC 31 : if (hba->auth_method == uaRADIUS)
138 : : {
1314 michael@paquier.xyz 139 [ # # ]:UBC 0 : if (hba->radiusservers_s)
140 : 0 : options[noptions++] =
141 : 0 : CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
142 : :
143 [ # # ]: 0 : if (hba->radiussecrets_s)
144 : 0 : options[noptions++] =
145 : 0 : CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
146 : :
147 [ # # ]: 0 : if (hba->radiusidentifiers_s)
148 : 0 : options[noptions++] =
149 : 0 : CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
150 : :
151 [ # # ]: 0 : if (hba->radiusports_s)
152 : 0 : options[noptions++] =
153 : 0 : CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
154 : : }
155 : :
250 dgustafsson@postgres 156 [ + + ]:CBC 31 : if (hba->auth_method == uaOAuth)
157 : : {
158 [ + - ]: 3 : if (hba->oauth_issuer)
159 : 3 : options[noptions++] =
160 : 3 : CStringGetTextDatum(psprintf("issuer=%s", hba->oauth_issuer));
161 : :
162 [ + - ]: 3 : if (hba->oauth_scope)
163 : 3 : options[noptions++] =
164 : 3 : CStringGetTextDatum(psprintf("scope=%s", hba->oauth_scope));
165 : :
166 [ + - ]: 3 : if (hba->oauth_validator)
167 : 3 : options[noptions++] =
168 : 3 : CStringGetTextDatum(psprintf("validator=%s", hba->oauth_validator));
169 : :
170 [ - + ]: 3 : if (hba->oauth_skip_usermap)
250 dgustafsson@postgres 171 :UBC 0 : options[noptions++] =
172 : 0 : CStringGetTextDatum(psprintf("delegate_ident_mapping=true"));
173 : : }
174 : :
175 : : /* If you add more options, consider increasing MAX_HBA_OPTIONS. */
1314 michael@paquier.xyz 176 [ - + ]:CBC 31 : Assert(noptions <= MAX_HBA_OPTIONS);
177 : :
178 [ + + ]: 31 : if (noptions > 0)
1215 peter@eisentraut.org 179 : 3 : return construct_array_builtin(options, noptions, TEXTOID);
180 : : else
1314 michael@paquier.xyz 181 : 28 : return NULL;
182 : : }
183 : :
184 : : /* Number of columns in pg_hba_file_rules view */
185 : : #define NUM_PG_HBA_FILE_RULES_ATTS 11
186 : :
187 : : /*
188 : : * fill_hba_line
189 : : * Build one row of pg_hba_file_rules view, add it to tuplestore.
190 : : *
191 : : * tuple_store: where to store data
192 : : * tupdesc: tuple descriptor for the view
193 : : * rule_number: unique identifier among all valid rules
194 : : * filename: configuration file name (must always be valid)
195 : : * lineno: line number of configuration file (must always be valid)
196 : : * hba: parsed line data (can be NULL, in which case err_msg should be set)
197 : : * err_msg: error message (NULL if none)
198 : : *
199 : : * Note: leaks memory, but we don't care since this is run in a short-lived
200 : : * memory context.
201 : : */
202 : : static void
203 : 31 : fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
204 : : int rule_number, char *filename, int lineno, HbaLine *hba,
205 : : const char *err_msg)
206 : : {
207 : : Datum values[NUM_PG_HBA_FILE_RULES_ATTS];
208 : : bool nulls[NUM_PG_HBA_FILE_RULES_ATTS];
209 : : char buffer[NI_MAXHOST];
210 : : HeapTuple tuple;
211 : : int index;
212 : : ListCell *lc;
213 : : const char *typestr;
214 : : const char *addrstr;
215 : : const char *maskstr;
216 : : ArrayType *options;
217 : :
218 [ - + ]: 31 : Assert(tupdesc->natts == NUM_PG_HBA_FILE_RULES_ATTS);
219 : :
220 : 31 : memset(values, 0, sizeof(values));
221 : 31 : memset(nulls, 0, sizeof(nulls));
222 : 31 : index = 0;
223 : :
224 : : /* rule_number, nothing on error */
1098 225 [ - + ]: 31 : if (err_msg)
1098 michael@paquier.xyz 226 :UBC 0 : nulls[index++] = true;
227 : : else
1098 michael@paquier.xyz 228 :CBC 31 : values[index++] = Int32GetDatum(rule_number);
229 : :
230 : : /* file_name */
1069 231 : 31 : values[index++] = CStringGetTextDatum(filename);
232 : :
233 : : /* line_number */
1314 234 : 31 : values[index++] = Int32GetDatum(lineno);
235 : :
236 [ + - ]: 31 : if (hba != NULL)
237 : : {
238 : : /* type */
239 : : /* Avoid a default: case so compiler will warn about missing cases */
240 : 31 : typestr = NULL;
241 [ + + - - : 31 : switch (hba->conntype)
- - - ]
242 : : {
243 : 19 : case ctLocal:
244 : 19 : typestr = "local";
245 : 19 : break;
246 : 12 : case ctHost:
247 : 12 : typestr = "host";
248 : 12 : break;
1314 michael@paquier.xyz 249 :UBC 0 : case ctHostSSL:
250 : 0 : typestr = "hostssl";
251 : 0 : break;
252 : 0 : case ctHostNoSSL:
253 : 0 : typestr = "hostnossl";
254 : 0 : break;
255 : 0 : case ctHostGSS:
256 : 0 : typestr = "hostgssenc";
257 : 0 : break;
258 : 0 : case ctHostNoGSS:
259 : 0 : typestr = "hostnogssenc";
260 : 0 : break;
261 : : }
1314 michael@paquier.xyz 262 [ + - ]:CBC 31 : if (typestr)
263 : 31 : values[index++] = CStringGetTextDatum(typestr);
264 : : else
1314 michael@paquier.xyz 265 :UBC 0 : nulls[index++] = true;
266 : :
267 : : /* database */
1314 michael@paquier.xyz 268 [ + - ]:CBC 31 : if (hba->databases)
269 : : {
270 : : /*
271 : : * Flatten AuthToken list to string list. It might seem that we
272 : : * should re-quote any quoted tokens, but that has been rejected
273 : : * on the grounds that it makes it harder to compare the array
274 : : * elements to other system catalogs. That makes entries like
275 : : * "all" or "samerole" formally ambiguous ... but users who name
276 : : * databases/roles that way are inflicting their own pain.
277 : : */
278 : 31 : List *names = NIL;
279 : :
280 [ + - + + : 63 : foreach(lc, hba->databases)
+ + ]
281 : : {
282 : 32 : AuthToken *tok = lfirst(lc);
283 : :
284 : 32 : names = lappend(names, tok->string);
285 : : }
286 : 31 : values[index++] = PointerGetDatum(strlist_to_textarray(names));
287 : : }
288 : : else
1314 michael@paquier.xyz 289 :UBC 0 : nulls[index++] = true;
290 : :
291 : : /* user */
1314 michael@paquier.xyz 292 [ + - ]:CBC 31 : if (hba->roles)
293 : : {
294 : : /* Flatten AuthToken list to string list; see comment above */
295 : 31 : List *roles = NIL;
296 : :
297 [ + - + + : 62 : foreach(lc, hba->roles)
+ + ]
298 : : {
299 : 31 : AuthToken *tok = lfirst(lc);
300 : :
301 : 31 : roles = lappend(roles, tok->string);
302 : : }
303 : 31 : values[index++] = PointerGetDatum(strlist_to_textarray(roles));
304 : : }
305 : : else
1314 michael@paquier.xyz 306 :UBC 0 : nulls[index++] = true;
307 : :
308 : : /* address and netmask */
309 : : /* Avoid a default: case so compiler will warn about missing cases */
1314 michael@paquier.xyz 310 :CBC 31 : addrstr = maskstr = NULL;
311 [ + - - - : 31 : switch (hba->ip_cmp_method)
- ]
312 : : {
313 : 31 : case ipCmpMask:
314 [ - + ]: 31 : if (hba->hostname)
315 : : {
1314 michael@paquier.xyz 316 :UBC 0 : addrstr = hba->hostname;
317 : : }
318 : : else
319 : : {
320 : : /*
321 : : * Note: if pg_getnameinfo_all fails, it'll set buffer to
322 : : * "???", which we want to return.
323 : : */
1314 michael@paquier.xyz 324 [ + + ]:CBC 31 : if (hba->addrlen > 0)
325 : : {
326 [ + - ]: 12 : if (pg_getnameinfo_all(&hba->addr, hba->addrlen,
327 : : buffer, sizeof(buffer),
328 : : NULL, 0,
329 : : NI_NUMERICHOST) == 0)
330 : 12 : clean_ipv6_addr(hba->addr.ss_family, buffer);
331 : 12 : addrstr = pstrdup(buffer);
332 : : }
333 [ + + ]: 31 : if (hba->masklen > 0)
334 : : {
335 [ + - ]: 12 : if (pg_getnameinfo_all(&hba->mask, hba->masklen,
336 : : buffer, sizeof(buffer),
337 : : NULL, 0,
338 : : NI_NUMERICHOST) == 0)
339 : 12 : clean_ipv6_addr(hba->mask.ss_family, buffer);
340 : 12 : maskstr = pstrdup(buffer);
341 : : }
342 : : }
343 : 31 : break;
1314 michael@paquier.xyz 344 :UBC 0 : case ipCmpAll:
345 : 0 : addrstr = "all";
346 : 0 : break;
347 : 0 : case ipCmpSameHost:
348 : 0 : addrstr = "samehost";
349 : 0 : break;
350 : 0 : case ipCmpSameNet:
351 : 0 : addrstr = "samenet";
352 : 0 : break;
353 : : }
1314 michael@paquier.xyz 354 [ + + ]:CBC 31 : if (addrstr)
355 : 12 : values[index++] = CStringGetTextDatum(addrstr);
356 : : else
357 : 19 : nulls[index++] = true;
358 [ + + ]: 31 : if (maskstr)
359 : 12 : values[index++] = CStringGetTextDatum(maskstr);
360 : : else
361 : 19 : nulls[index++] = true;
362 : :
363 : : /* auth_method */
364 : 31 : values[index++] = CStringGetTextDatum(hba_authname(hba->auth_method));
365 : :
366 : : /* options */
367 : 31 : options = get_hba_options(hba);
368 [ + + ]: 31 : if (options)
369 : 3 : values[index++] = PointerGetDatum(options);
370 : : else
371 : 28 : nulls[index++] = true;
372 : : }
373 : : else
374 : : {
375 : : /* no parsing result, so set relevant fields to nulls */
1069 michael@paquier.xyz 376 :UBC 0 : memset(&nulls[3], true, (NUM_PG_HBA_FILE_RULES_ATTS - 4) * sizeof(bool));
377 : : }
378 : :
379 : : /* error */
1314 michael@paquier.xyz 380 [ - + ]:CBC 31 : if (err_msg)
1314 michael@paquier.xyz 381 :UBC 0 : values[NUM_PG_HBA_FILE_RULES_ATTS - 1] = CStringGetTextDatum(err_msg);
382 : : else
1314 michael@paquier.xyz 383 :CBC 31 : nulls[NUM_PG_HBA_FILE_RULES_ATTS - 1] = true;
384 : :
385 : 31 : tuple = heap_form_tuple(tupdesc, values, nulls);
386 : 31 : tuplestore_puttuple(tuple_store, tuple);
387 : 31 : }
388 : :
389 : : /*
390 : : * fill_hba_view
391 : : * Read the pg_hba.conf file and fill the tuplestore with view records.
392 : : */
393 : : static void
394 : 5 : fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
395 : : {
396 : : FILE *file;
397 : 5 : List *hba_lines = NIL;
398 : : ListCell *line;
1098 399 : 5 : int rule_number = 0;
400 : : MemoryContext hbacxt;
401 : : MemoryContext oldcxt;
402 : :
403 : : /*
404 : : * In the unlikely event that we can't open pg_hba.conf, we throw an
405 : : * error, rather than trying to report it via some sort of view entry.
406 : : * (Most other error conditions should result in a message in a view
407 : : * entry.)
408 : : */
1079 409 : 5 : file = open_auth_file(HbaFileName, ERROR, 0, NULL);
410 : :
1069 411 : 5 : tokenize_auth_file(HbaFileName, file, &hba_lines, DEBUG3, 0);
412 : :
413 : : /* Now parse all the lines */
1314 414 : 5 : hbacxt = AllocSetContextCreate(CurrentMemoryContext,
415 : : "hba parser context",
416 : : ALLOCSET_SMALL_SIZES);
417 : 5 : oldcxt = MemoryContextSwitchTo(hbacxt);
418 [ + - + + : 36 : foreach(line, hba_lines)
+ + ]
419 : : {
420 : 31 : TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
421 : 31 : HbaLine *hbaline = NULL;
422 : :
423 : : /* don't parse lines that already have errors */
424 [ + - ]: 31 : if (tok_line->err_msg == NULL)
425 : 31 : hbaline = parse_hba_line(tok_line, DEBUG3);
426 : :
427 : : /* No error, set a new rule number */
1098 428 [ + - ]: 31 : if (tok_line->err_msg == NULL)
429 : 31 : rule_number++;
430 : :
431 : 31 : fill_hba_line(tuple_store, tupdesc, rule_number,
432 : : tok_line->file_name, tok_line->line_num, hbaline,
1069 433 : 31 : tok_line->err_msg);
434 : : }
435 : :
436 : : /* Free tokenizer memory */
437 : 5 : free_auth_file(file, 0);
438 : : /* Free parse_hba_line memory */
1314 439 : 5 : MemoryContextSwitchTo(oldcxt);
440 : 5 : MemoryContextDelete(hbacxt);
441 : 5 : }
442 : :
443 : : /*
444 : : * pg_hba_file_rules
445 : : *
446 : : * SQL-accessible set-returning function to return all the entries in the
447 : : * pg_hba.conf file.
448 : : */
449 : : Datum
450 : 5 : pg_hba_file_rules(PG_FUNCTION_ARGS)
451 : : {
452 : : ReturnSetInfo *rsi;
453 : :
454 : : /*
455 : : * Build tuplestore to hold the result rows. We must use the Materialize
456 : : * mode to be safe against HBA file changes while the cursor is open. It's
457 : : * also more efficient than having to look up our current position in the
458 : : * parsed list every time.
459 : : */
1106 460 : 5 : InitMaterializedSRF(fcinfo, 0);
461 : :
462 : : /* Fill the tuplestore */
1314 463 : 5 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
464 : 5 : fill_hba_view(rsi->setResult, rsi->setDesc);
465 : :
466 : 5 : PG_RETURN_NULL();
467 : : }
468 : :
469 : : /* Number of columns in pg_ident_file_mappings view */
470 : : #define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 7
471 : :
472 : : /*
473 : : * fill_ident_line: build one row of pg_ident_file_mappings view, add it to
474 : : * tuplestore
475 : : *
476 : : * tuple_store: where to store data
477 : : * tupdesc: tuple descriptor for the view
478 : : * map_number: unique identifier among all valid maps
479 : : * filename: configuration file name (must always be valid)
480 : : * lineno: line number of configuration file (must always be valid)
481 : : * ident: parsed line data (can be NULL, in which case err_msg should be set)
482 : : * err_msg: error message (NULL if none)
483 : : *
484 : : * Note: leaks memory, but we don't care since this is run in a short-lived
485 : : * memory context.
486 : : */
487 : : static void
1309 michael@paquier.xyz 488 :LBC (8) : fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
489 : : int map_number, char *filename, int lineno, IdentLine *ident,
490 : : const char *err_msg)
491 : : {
492 : : Datum values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
493 : : bool nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
494 : : HeapTuple tuple;
495 : : int index;
496 : :
497 [ # # ]: (8) : Assert(tupdesc->natts == NUM_PG_IDENT_FILE_MAPPINGS_ATTS);
498 : :
499 : (8) : memset(values, 0, sizeof(values));
500 : (8) : memset(nulls, 0, sizeof(nulls));
501 : (8) : index = 0;
502 : :
503 : : /* map_number, nothing on error */
1098 504 [ # # ]: (8) : if (err_msg)
1098 michael@paquier.xyz 505 :UBC 0 : nulls[index++] = true;
506 : : else
1098 michael@paquier.xyz 507 :LBC (8) : values[index++] = Int32GetDatum(map_number);
508 : :
509 : : /* file_name */
1069 510 : (8) : values[index++] = CStringGetTextDatum(filename);
511 : :
512 : : /* line_number */
1309 513 : (8) : values[index++] = Int32GetDatum(lineno);
514 : :
515 [ # # ]: (8) : if (ident != NULL)
516 : : {
517 : (8) : values[index++] = CStringGetTextDatum(ident->usermap);
1020 518 : (8) : values[index++] = CStringGetTextDatum(ident->system_user->string);
1016 519 : (8) : values[index++] = CStringGetTextDatum(ident->pg_user->string);
520 : : }
521 : : else
522 : : {
523 : : /* no parsing result, so set relevant fields to nulls */
1069 michael@paquier.xyz 524 :UBC 0 : memset(&nulls[3], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 4) * sizeof(bool));
525 : : }
526 : :
527 : : /* error */
1309 michael@paquier.xyz 528 [ # # ]:LBC (8) : if (err_msg)
1309 michael@paquier.xyz 529 :UBC 0 : values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = CStringGetTextDatum(err_msg);
530 : : else
1309 michael@paquier.xyz 531 :LBC (8) : nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = true;
532 : :
533 : (8) : tuple = heap_form_tuple(tupdesc, values, nulls);
534 : (8) : tuplestore_puttuple(tuple_store, tuple);
535 : (8) : }
536 : :
537 : : /*
538 : : * Read the pg_ident.conf file and fill the tuplestore with view records.
539 : : */
540 : : static void
1309 michael@paquier.xyz 541 :CBC 3 : fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
542 : : {
543 : : FILE *file;
544 : 3 : List *ident_lines = NIL;
545 : : ListCell *line;
1098 546 : 3 : int map_number = 0;
547 : : MemoryContext identcxt;
548 : : MemoryContext oldcxt;
549 : :
550 : : /*
551 : : * In the unlikely event that we can't open pg_ident.conf, we throw an
552 : : * error, rather than trying to report it via some sort of view entry.
553 : : * (Most other error conditions should result in a message in a view
554 : : * entry.)
555 : : */
1079 556 : 3 : file = open_auth_file(IdentFileName, ERROR, 0, NULL);
557 : :
1069 558 : 3 : tokenize_auth_file(IdentFileName, file, &ident_lines, DEBUG3, 0);
559 : :
560 : : /* Now parse all the lines */
1309 561 : 3 : identcxt = AllocSetContextCreate(CurrentMemoryContext,
562 : : "ident parser context",
563 : : ALLOCSET_SMALL_SIZES);
564 : 3 : oldcxt = MemoryContextSwitchTo(identcxt);
565 [ - + - - : 3 : foreach(line, ident_lines)
- + ]
566 : : {
1309 michael@paquier.xyz 567 :LBC (8) : TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
568 : (8) : IdentLine *identline = NULL;
569 : :
570 : : /* don't parse lines that already have errors */
571 [ # # ]: (8) : if (tok_line->err_msg == NULL)
572 : (8) : identline = parse_ident_line(tok_line, DEBUG3);
573 : :
574 : : /* no error, set a new mapping number */
1098 575 [ # # ]: (8) : if (tok_line->err_msg == NULL)
576 : (8) : map_number++;
577 : :
578 : (8) : fill_ident_line(tuple_store, tupdesc, map_number,
579 : : tok_line->file_name, tok_line->line_num,
1069 580 : (8) : identline, tok_line->err_msg);
581 : : }
582 : :
583 : : /* Free tokenizer memory */
1069 michael@paquier.xyz 584 :CBC 3 : free_auth_file(file, 0);
585 : : /* Free parse_ident_line memory */
1309 586 : 3 : MemoryContextSwitchTo(oldcxt);
587 : 3 : MemoryContextDelete(identcxt);
588 : 3 : }
589 : :
590 : : /*
591 : : * SQL-accessible SRF to return all the entries in the pg_ident.conf file.
592 : : */
593 : : Datum
594 : 3 : pg_ident_file_mappings(PG_FUNCTION_ARGS)
595 : : {
596 : : ReturnSetInfo *rsi;
597 : :
598 : : /*
599 : : * Build tuplestore to hold the result rows. We must use the Materialize
600 : : * mode to be safe against HBA file changes while the cursor is open. It's
601 : : * also more efficient than having to look up our current position in the
602 : : * parsed list every time.
603 : : */
1106 604 : 3 : InitMaterializedSRF(fcinfo, 0);
605 : :
606 : : /* Fill the tuplestore */
1309 607 : 3 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
608 : 3 : fill_ident_view(rsi->setResult, rsi->setDesc);
609 : :
610 : 3 : PG_RETURN_NULL();
611 : : }
|