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