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