1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * hba.c |
4 | * Routines to handle host based authentication (that's the scheme |
5 | * wherein you authenticate a user by seeing what IP address the system |
6 | * says he comes from and choosing authentication method based on it). |
7 | * |
8 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
9 | * Portions Copyright (c) 1994, Regents of the University of California |
10 | * |
11 | * |
12 | * IDENTIFICATION |
13 | * src/backend/libpq/hba.c |
14 | * |
15 | *------------------------------------------------------------------------- |
16 | */ |
17 | #include "postgres.h" |
18 | |
19 | #include <ctype.h> |
20 | #include <pwd.h> |
21 | #include <fcntl.h> |
22 | #include <sys/param.h> |
23 | #include <sys/socket.h> |
24 | #include <netinet/in.h> |
25 | #include <arpa/inet.h> |
26 | #include <unistd.h> |
27 | |
28 | #include "access/htup_details.h" |
29 | #include "catalog/pg_collation.h" |
30 | #include "catalog/pg_type.h" |
31 | #include "common/ip.h" |
32 | #include "funcapi.h" |
33 | #include "libpq/ifaddr.h" |
34 | #include "libpq/libpq.h" |
35 | #include "miscadmin.h" |
36 | #include "postmaster/postmaster.h" |
37 | #include "regex/regex.h" |
38 | #include "replication/walsender.h" |
39 | #include "storage/fd.h" |
40 | #include "utils/acl.h" |
41 | #include "utils/builtins.h" |
42 | #include "utils/varlena.h" |
43 | #include "utils/guc.h" |
44 | #include "utils/lsyscache.h" |
45 | #include "utils/memutils.h" |
46 | |
47 | #ifdef USE_LDAP |
48 | #ifdef WIN32 |
49 | #include <winldap.h> |
50 | #else |
51 | #include <ldap.h> |
52 | #endif |
53 | #endif |
54 | |
55 | |
56 | #define MAX_TOKEN 256 |
57 | #define MAX_LINE 8192 |
58 | |
59 | /* callback data for check_network_callback */ |
60 | typedef struct check_network_data |
61 | { |
62 | IPCompareMethod method; /* test method */ |
63 | SockAddr *raddr; /* client's actual address */ |
64 | bool result; /* set to true if match */ |
65 | } check_network_data; |
66 | |
67 | |
68 | #define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0) |
69 | #define token_matches(t, k) (strcmp(t->string, k) == 0) |
70 | |
71 | /* |
72 | * A single string token lexed from a config file, together with whether |
73 | * the token had been quoted. |
74 | */ |
75 | typedef struct HbaToken |
76 | { |
77 | char *string; |
78 | bool quoted; |
79 | } HbaToken; |
80 | |
81 | /* |
82 | * TokenizedLine represents one line lexed from a config file. |
83 | * Each item in the "fields" list is a sub-list of HbaTokens. |
84 | * We don't emit a TokenizedLine for empty or all-comment lines, |
85 | * so "fields" is never NIL (nor are any of its sub-lists). |
86 | * Exception: if an error occurs during tokenization, we might |
87 | * have fields == NIL, in which case err_msg != NULL. |
88 | */ |
89 | typedef struct TokenizedLine |
90 | { |
91 | List *fields; /* List of lists of HbaTokens */ |
92 | int line_num; /* Line number */ |
93 | char *raw_line; /* Raw line text */ |
94 | char *err_msg; /* Error message if any */ |
95 | } TokenizedLine; |
96 | |
97 | /* |
98 | * pre-parsed content of HBA config file: list of HbaLine structs. |
99 | * parsed_hba_context is the memory context where it lives. |
100 | */ |
101 | static List *parsed_hba_lines = NIL; |
102 | static MemoryContext parsed_hba_context = NULL; |
103 | |
104 | /* |
105 | * pre-parsed content of ident mapping file: list of IdentLine structs. |
106 | * parsed_ident_context is the memory context where it lives. |
107 | * |
108 | * NOTE: the IdentLine structs can contain pre-compiled regular expressions |
109 | * that live outside the memory context. Before destroying or resetting the |
110 | * memory context, they need to be explicitly free'd. |
111 | */ |
112 | static List *parsed_ident_lines = NIL; |
113 | static MemoryContext parsed_ident_context = NULL; |
114 | |
115 | /* |
116 | * The following character array represents the names of the authentication |
117 | * methods that are supported by PostgreSQL. |
118 | * |
119 | * Note: keep this in sync with the UserAuth enum in hba.h. |
120 | */ |
121 | static const char *const UserAuthName[] = |
122 | { |
123 | "reject" , |
124 | "implicit reject" , /* Not a user-visible option */ |
125 | "trust" , |
126 | "ident" , |
127 | "password" , |
128 | "md5" , |
129 | "scram-sha-256" , |
130 | "gss" , |
131 | "sspi" , |
132 | "pam" , |
133 | "bsd" , |
134 | "ldap" , |
135 | "cert" , |
136 | "radius" , |
137 | "peer" |
138 | }; |
139 | |
140 | |
141 | static MemoryContext tokenize_file(const char *filename, FILE *file, |
142 | List **tok_lines, int elevel); |
143 | static List *tokenize_inc_file(List *tokens, const char *outer_filename, |
144 | const char *inc_filename, int elevel, char **err_msg); |
145 | static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, |
146 | int elevel, char **err_msg); |
147 | static bool verify_option_list_length(List *options, const char *optionname, |
148 | List *masters, const char *mastername, int line_num); |
149 | static ArrayType *gethba_options(HbaLine *hba); |
150 | static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, |
151 | int lineno, HbaLine *hba, const char *err_msg); |
152 | static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc); |
153 | |
154 | |
155 | /* |
156 | * isblank() exists in the ISO C99 spec, but it's not very portable yet, |
157 | * so provide our own version. |
158 | */ |
159 | bool |
160 | pg_isblank(const char c) |
161 | { |
162 | return c == ' ' || c == '\t' || c == '\r'; |
163 | } |
164 | |
165 | |
166 | /* |
167 | * Grab one token out of the string pointed to by *lineptr. |
168 | * |
169 | * Tokens are strings of non-blank |
170 | * characters bounded by blank characters, commas, beginning of line, and |
171 | * end of line. Blank means space or tab. Tokens can be delimited by |
172 | * double quotes (this allows the inclusion of blanks, but not newlines). |
173 | * Comments (started by an unquoted '#') are skipped. |
174 | * |
175 | * The token, if any, is returned at *buf (a buffer of size bufsz), and |
176 | * *lineptr is advanced past the token. |
177 | * |
178 | * Also, we set *initial_quote to indicate whether there was quoting before |
179 | * the first character. (We use that to prevent "@x" from being treated |
180 | * as a file inclusion request. Note that @"x" should be so treated; |
181 | * we want to allow that to support embedded spaces in file paths.) |
182 | * |
183 | * We set *terminating_comma to indicate whether the token is terminated by a |
184 | * comma (which is not returned). |
185 | * |
186 | * In event of an error, log a message at ereport level elevel, and also |
187 | * set *err_msg to a string describing the error. Currently the only |
188 | * possible error is token too long for buf. |
189 | * |
190 | * If successful: store null-terminated token at *buf and return true. |
191 | * If no more tokens on line: set *buf = '\0' and return false. |
192 | * If error: fill buf with truncated or misformatted token and return false. |
193 | */ |
194 | static bool |
195 | next_token(char **lineptr, char *buf, int bufsz, |
196 | bool *initial_quote, bool *terminating_comma, |
197 | int elevel, char **err_msg) |
198 | { |
199 | int c; |
200 | char *start_buf = buf; |
201 | char *end_buf = buf + (bufsz - 1); |
202 | bool in_quote = false; |
203 | bool was_quote = false; |
204 | bool saw_quote = false; |
205 | |
206 | Assert(end_buf > start_buf); |
207 | |
208 | *initial_quote = false; |
209 | *terminating_comma = false; |
210 | |
211 | /* Move over any whitespace and commas preceding the next token */ |
212 | while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ',')) |
213 | ; |
214 | |
215 | /* |
216 | * Build a token in buf of next characters up to EOL, unquoted comma, or |
217 | * unquoted whitespace. |
218 | */ |
219 | while (c != '\0' && |
220 | (!pg_isblank(c) || in_quote)) |
221 | { |
222 | /* skip comments to EOL */ |
223 | if (c == '#' && !in_quote) |
224 | { |
225 | while ((c = (*(*lineptr)++)) != '\0') |
226 | ; |
227 | break; |
228 | } |
229 | |
230 | if (buf >= end_buf) |
231 | { |
232 | *buf = '\0'; |
233 | ereport(elevel, |
234 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
235 | errmsg("authentication file token too long, skipping: \"%s\"" , |
236 | start_buf))); |
237 | *err_msg = "authentication file token too long" ; |
238 | /* Discard remainder of line */ |
239 | while ((c = (*(*lineptr)++)) != '\0') |
240 | ; |
241 | /* Un-eat the '\0', in case we're called again */ |
242 | (*lineptr)--; |
243 | return false; |
244 | } |
245 | |
246 | /* we do not pass back a terminating comma in the token */ |
247 | if (c == ',' && !in_quote) |
248 | { |
249 | *terminating_comma = true; |
250 | break; |
251 | } |
252 | |
253 | if (c != '"' || was_quote) |
254 | *buf++ = c; |
255 | |
256 | /* Literal double-quote is two double-quotes */ |
257 | if (in_quote && c == '"') |
258 | was_quote = !was_quote; |
259 | else |
260 | was_quote = false; |
261 | |
262 | if (c == '"') |
263 | { |
264 | in_quote = !in_quote; |
265 | saw_quote = true; |
266 | if (buf == start_buf) |
267 | *initial_quote = true; |
268 | } |
269 | |
270 | c = *(*lineptr)++; |
271 | } |
272 | |
273 | /* |
274 | * Un-eat the char right after the token (critical in case it is '\0', |
275 | * else next call will read past end of string). |
276 | */ |
277 | (*lineptr)--; |
278 | |
279 | *buf = '\0'; |
280 | |
281 | return (saw_quote || buf > start_buf); |
282 | } |
283 | |
284 | /* |
285 | * Construct a palloc'd HbaToken struct, copying the given string. |
286 | */ |
287 | static HbaToken * |
288 | make_hba_token(const char *token, bool quoted) |
289 | { |
290 | HbaToken *hbatoken; |
291 | int toklen; |
292 | |
293 | toklen = strlen(token); |
294 | /* we copy string into same palloc block as the struct */ |
295 | hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1); |
296 | hbatoken->string = (char *) hbatoken + sizeof(HbaToken); |
297 | hbatoken->quoted = quoted; |
298 | memcpy(hbatoken->string, token, toklen + 1); |
299 | |
300 | return hbatoken; |
301 | } |
302 | |
303 | /* |
304 | * Copy a HbaToken struct into freshly palloc'd memory. |
305 | */ |
306 | static HbaToken * |
307 | copy_hba_token(HbaToken *in) |
308 | { |
309 | HbaToken *out = make_hba_token(in->string, in->quoted); |
310 | |
311 | return out; |
312 | } |
313 | |
314 | |
315 | /* |
316 | * Tokenize one HBA field from a line, handling file inclusion and comma lists. |
317 | * |
318 | * filename: current file's pathname (needed to resolve relative pathnames) |
319 | * *lineptr: current line pointer, which will be advanced past field |
320 | * |
321 | * In event of an error, log a message at ereport level elevel, and also |
322 | * set *err_msg to a string describing the error. Note that the result |
323 | * may be non-NIL anyway, so *err_msg must be tested to determine whether |
324 | * there was an error. |
325 | * |
326 | * The result is a List of HbaToken structs, one for each token in the field, |
327 | * or NIL if we reached EOL. |
328 | */ |
329 | static List * |
330 | next_field_expand(const char *filename, char **lineptr, |
331 | int elevel, char **err_msg) |
332 | { |
333 | char buf[MAX_TOKEN]; |
334 | bool trailing_comma; |
335 | bool initial_quote; |
336 | List *tokens = NIL; |
337 | |
338 | do |
339 | { |
340 | if (!next_token(lineptr, buf, sizeof(buf), |
341 | &initial_quote, &trailing_comma, |
342 | elevel, err_msg)) |
343 | break; |
344 | |
345 | /* Is this referencing a file? */ |
346 | if (!initial_quote && buf[0] == '@' && buf[1] != '\0') |
347 | tokens = tokenize_inc_file(tokens, filename, buf + 1, |
348 | elevel, err_msg); |
349 | else |
350 | tokens = lappend(tokens, make_hba_token(buf, initial_quote)); |
351 | } while (trailing_comma && (*err_msg == NULL)); |
352 | |
353 | return tokens; |
354 | } |
355 | |
356 | /* |
357 | * tokenize_inc_file |
358 | * Expand a file included from another file into an hba "field" |
359 | * |
360 | * Opens and tokenises a file included from another HBA config file with @, |
361 | * and returns all values found therein as a flat list of HbaTokens. If a |
362 | * @-token is found, recursively expand it. The newly read tokens are |
363 | * appended to "tokens" (so that foo,bar,@baz does what you expect). |
364 | * All new tokens are allocated in caller's memory context. |
365 | * |
366 | * In event of an error, log a message at ereport level elevel, and also |
367 | * set *err_msg to a string describing the error. Note that the result |
368 | * may be non-NIL anyway, so *err_msg must be tested to determine whether |
369 | * there was an error. |
370 | */ |
371 | static List * |
372 | tokenize_inc_file(List *tokens, |
373 | const char *outer_filename, |
374 | const char *inc_filename, |
375 | int elevel, |
376 | char **err_msg) |
377 | { |
378 | char *inc_fullname; |
379 | FILE *inc_file; |
380 | List *inc_lines; |
381 | ListCell *inc_line; |
382 | MemoryContext linecxt; |
383 | |
384 | if (is_absolute_path(inc_filename)) |
385 | { |
386 | /* absolute path is taken as-is */ |
387 | inc_fullname = pstrdup(inc_filename); |
388 | } |
389 | else |
390 | { |
391 | /* relative path is relative to dir of calling file */ |
392 | inc_fullname = (char *) palloc(strlen(outer_filename) + 1 + |
393 | strlen(inc_filename) + 1); |
394 | strcpy(inc_fullname, outer_filename); |
395 | get_parent_directory(inc_fullname); |
396 | join_path_components(inc_fullname, inc_fullname, inc_filename); |
397 | canonicalize_path(inc_fullname); |
398 | } |
399 | |
400 | inc_file = AllocateFile(inc_fullname, "r" ); |
401 | if (inc_file == NULL) |
402 | { |
403 | int save_errno = errno; |
404 | |
405 | ereport(elevel, |
406 | (errcode_for_file_access(), |
407 | errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m" , |
408 | inc_filename, inc_fullname))); |
409 | *err_msg = psprintf("could not open secondary authentication file \"@%s\" as \"%s\": %s" , |
410 | inc_filename, inc_fullname, strerror(save_errno)); |
411 | pfree(inc_fullname); |
412 | return tokens; |
413 | } |
414 | |
415 | /* There is possible recursion here if the file contains @ */ |
416 | linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, elevel); |
417 | |
418 | FreeFile(inc_file); |
419 | pfree(inc_fullname); |
420 | |
421 | /* Copy all tokens found in the file and append to the tokens list */ |
422 | foreach(inc_line, inc_lines) |
423 | { |
424 | TokenizedLine *tok_line = (TokenizedLine *) lfirst(inc_line); |
425 | ListCell *inc_field; |
426 | |
427 | /* If any line has an error, propagate that up to caller */ |
428 | if (tok_line->err_msg) |
429 | { |
430 | *err_msg = pstrdup(tok_line->err_msg); |
431 | break; |
432 | } |
433 | |
434 | foreach(inc_field, tok_line->fields) |
435 | { |
436 | List *inc_tokens = lfirst(inc_field); |
437 | ListCell *inc_token; |
438 | |
439 | foreach(inc_token, inc_tokens) |
440 | { |
441 | HbaToken *token = lfirst(inc_token); |
442 | |
443 | tokens = lappend(tokens, copy_hba_token(token)); |
444 | } |
445 | } |
446 | } |
447 | |
448 | MemoryContextDelete(linecxt); |
449 | return tokens; |
450 | } |
451 | |
452 | /* |
453 | * Tokenize the given file. |
454 | * |
455 | * The output is a list of TokenizedLine structs; see struct definition above. |
456 | * |
457 | * filename: the absolute path to the target file |
458 | * file: the already-opened target file |
459 | * tok_lines: receives output list |
460 | * elevel: message logging level |
461 | * |
462 | * Errors are reported by logging messages at ereport level elevel and by |
463 | * adding TokenizedLine structs containing non-null err_msg fields to the |
464 | * output list. |
465 | * |
466 | * Return value is a memory context which contains all memory allocated by |
467 | * this function (it's a child of caller's context). |
468 | */ |
469 | static MemoryContext |
470 | tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel) |
471 | { |
472 | int line_number = 1; |
473 | MemoryContext linecxt; |
474 | MemoryContext oldcxt; |
475 | |
476 | linecxt = AllocSetContextCreate(CurrentMemoryContext, |
477 | "tokenize_file" , |
478 | ALLOCSET_SMALL_SIZES); |
479 | oldcxt = MemoryContextSwitchTo(linecxt); |
480 | |
481 | *tok_lines = NIL; |
482 | |
483 | while (!feof(file) && !ferror(file)) |
484 | { |
485 | char rawline[MAX_LINE]; |
486 | char *lineptr; |
487 | List *current_line = NIL; |
488 | char *err_msg = NULL; |
489 | |
490 | if (!fgets(rawline, sizeof(rawline), file)) |
491 | { |
492 | int save_errno = errno; |
493 | |
494 | if (!ferror(file)) |
495 | break; /* normal EOF */ |
496 | /* I/O error! */ |
497 | ereport(elevel, |
498 | (errcode_for_file_access(), |
499 | errmsg("could not read file \"%s\": %m" , filename))); |
500 | err_msg = psprintf("could not read file \"%s\": %s" , |
501 | filename, strerror(save_errno)); |
502 | rawline[0] = '\0'; |
503 | } |
504 | if (strlen(rawline) == MAX_LINE - 1) |
505 | { |
506 | /* Line too long! */ |
507 | ereport(elevel, |
508 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
509 | errmsg("authentication file line too long" ), |
510 | errcontext("line %d of configuration file \"%s\"" , |
511 | line_number, filename))); |
512 | err_msg = "authentication file line too long" ; |
513 | } |
514 | |
515 | /* Strip trailing linebreak from rawline */ |
516 | lineptr = rawline + strlen(rawline) - 1; |
517 | while (lineptr >= rawline && (*lineptr == '\n' || *lineptr == '\r')) |
518 | *lineptr-- = '\0'; |
519 | |
520 | /* Parse fields */ |
521 | lineptr = rawline; |
522 | while (*lineptr && err_msg == NULL) |
523 | { |
524 | List *current_field; |
525 | |
526 | current_field = next_field_expand(filename, &lineptr, |
527 | elevel, &err_msg); |
528 | /* add field to line, unless we are at EOL or comment start */ |
529 | if (current_field != NIL) |
530 | current_line = lappend(current_line, current_field); |
531 | } |
532 | |
533 | /* Reached EOL; emit line to TokenizedLine list unless it's boring */ |
534 | if (current_line != NIL || err_msg != NULL) |
535 | { |
536 | TokenizedLine *tok_line; |
537 | |
538 | tok_line = (TokenizedLine *) palloc(sizeof(TokenizedLine)); |
539 | tok_line->fields = current_line; |
540 | tok_line->line_num = line_number; |
541 | tok_line->raw_line = pstrdup(rawline); |
542 | tok_line->err_msg = err_msg; |
543 | *tok_lines = lappend(*tok_lines, tok_line); |
544 | } |
545 | |
546 | line_number++; |
547 | } |
548 | |
549 | MemoryContextSwitchTo(oldcxt); |
550 | |
551 | return linecxt; |
552 | } |
553 | |
554 | |
555 | /* |
556 | * Does user belong to role? |
557 | * |
558 | * userid is the OID of the role given as the attempted login identifier. |
559 | * We check to see if it is a member of the specified role name. |
560 | */ |
561 | static bool |
562 | is_member(Oid userid, const char *role) |
563 | { |
564 | Oid roleid; |
565 | |
566 | if (!OidIsValid(userid)) |
567 | return false; /* if user not exist, say "no" */ |
568 | |
569 | roleid = get_role_oid(role, true); |
570 | |
571 | if (!OidIsValid(roleid)) |
572 | return false; /* if target role not exist, say "no" */ |
573 | |
574 | /* |
575 | * See if user is directly or indirectly a member of role. For this |
576 | * purpose, a superuser is not considered to be automatically a member of |
577 | * the role, so group auth only applies to explicit membership. |
578 | */ |
579 | return is_member_of_role_nosuper(userid, roleid); |
580 | } |
581 | |
582 | /* |
583 | * Check HbaToken list for a match to role, allowing group names. |
584 | */ |
585 | static bool |
586 | check_role(const char *role, Oid roleid, List *tokens) |
587 | { |
588 | ListCell *cell; |
589 | HbaToken *tok; |
590 | |
591 | foreach(cell, tokens) |
592 | { |
593 | tok = lfirst(cell); |
594 | if (!tok->quoted && tok->string[0] == '+') |
595 | { |
596 | if (is_member(roleid, tok->string + 1)) |
597 | return true; |
598 | } |
599 | else if (token_matches(tok, role) || |
600 | token_is_keyword(tok, "all" )) |
601 | return true; |
602 | } |
603 | return false; |
604 | } |
605 | |
606 | /* |
607 | * Check to see if db/role combination matches HbaToken list. |
608 | */ |
609 | static bool |
610 | check_db(const char *dbname, const char *role, Oid roleid, List *tokens) |
611 | { |
612 | ListCell *cell; |
613 | HbaToken *tok; |
614 | |
615 | foreach(cell, tokens) |
616 | { |
617 | tok = lfirst(cell); |
618 | if (am_walsender && !am_db_walsender) |
619 | { |
620 | /* |
621 | * physical replication walsender connections can only match |
622 | * replication keyword |
623 | */ |
624 | if (token_is_keyword(tok, "replication" )) |
625 | return true; |
626 | } |
627 | else if (token_is_keyword(tok, "all" )) |
628 | return true; |
629 | else if (token_is_keyword(tok, "sameuser" )) |
630 | { |
631 | if (strcmp(dbname, role) == 0) |
632 | return true; |
633 | } |
634 | else if (token_is_keyword(tok, "samegroup" ) || |
635 | token_is_keyword(tok, "samerole" )) |
636 | { |
637 | if (is_member(roleid, dbname)) |
638 | return true; |
639 | } |
640 | else if (token_is_keyword(tok, "replication" )) |
641 | continue; /* never match this if not walsender */ |
642 | else if (token_matches(tok, dbname)) |
643 | return true; |
644 | } |
645 | return false; |
646 | } |
647 | |
648 | static bool |
649 | ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b) |
650 | { |
651 | return (a->sin_addr.s_addr == b->sin_addr.s_addr); |
652 | } |
653 | |
654 | #ifdef HAVE_IPV6 |
655 | |
656 | static bool |
657 | ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b) |
658 | { |
659 | int i; |
660 | |
661 | for (i = 0; i < 16; i++) |
662 | if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i]) |
663 | return false; |
664 | |
665 | return true; |
666 | } |
667 | #endif /* HAVE_IPV6 */ |
668 | |
669 | /* |
670 | * Check whether host name matches pattern. |
671 | */ |
672 | static bool |
673 | hostname_match(const char *pattern, const char *actual_hostname) |
674 | { |
675 | if (pattern[0] == '.') /* suffix match */ |
676 | { |
677 | size_t plen = strlen(pattern); |
678 | size_t hlen = strlen(actual_hostname); |
679 | |
680 | if (hlen < plen) |
681 | return false; |
682 | |
683 | return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0); |
684 | } |
685 | else |
686 | return (pg_strcasecmp(pattern, actual_hostname) == 0); |
687 | } |
688 | |
689 | /* |
690 | * Check to see if a connecting IP matches a given host name. |
691 | */ |
692 | static bool |
693 | check_hostname(hbaPort *port, const char *hostname) |
694 | { |
695 | struct addrinfo *gai_result, |
696 | *gai; |
697 | int ret; |
698 | bool found; |
699 | |
700 | /* Quick out if remote host name already known bad */ |
701 | if (port->remote_hostname_resolv < 0) |
702 | return false; |
703 | |
704 | /* Lookup remote host name if not already done */ |
705 | if (!port->remote_hostname) |
706 | { |
707 | char remote_hostname[NI_MAXHOST]; |
708 | |
709 | ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, |
710 | remote_hostname, sizeof(remote_hostname), |
711 | NULL, 0, |
712 | NI_NAMEREQD); |
713 | if (ret != 0) |
714 | { |
715 | /* remember failure; don't complain in the postmaster log yet */ |
716 | port->remote_hostname_resolv = -2; |
717 | port->remote_hostname_errcode = ret; |
718 | return false; |
719 | } |
720 | |
721 | port->remote_hostname = pstrdup(remote_hostname); |
722 | } |
723 | |
724 | /* Now see if remote host name matches this pg_hba line */ |
725 | if (!hostname_match(hostname, port->remote_hostname)) |
726 | return false; |
727 | |
728 | /* If we already verified the forward lookup, we're done */ |
729 | if (port->remote_hostname_resolv == +1) |
730 | return true; |
731 | |
732 | /* Lookup IP from host name and check against original IP */ |
733 | ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result); |
734 | if (ret != 0) |
735 | { |
736 | /* remember failure; don't complain in the postmaster log yet */ |
737 | port->remote_hostname_resolv = -2; |
738 | port->remote_hostname_errcode = ret; |
739 | return false; |
740 | } |
741 | |
742 | found = false; |
743 | for (gai = gai_result; gai; gai = gai->ai_next) |
744 | { |
745 | if (gai->ai_addr->sa_family == port->raddr.addr.ss_family) |
746 | { |
747 | if (gai->ai_addr->sa_family == AF_INET) |
748 | { |
749 | if (ipv4eq((struct sockaddr_in *) gai->ai_addr, |
750 | (struct sockaddr_in *) &port->raddr.addr)) |
751 | { |
752 | found = true; |
753 | break; |
754 | } |
755 | } |
756 | #ifdef HAVE_IPV6 |
757 | else if (gai->ai_addr->sa_family == AF_INET6) |
758 | { |
759 | if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr, |
760 | (struct sockaddr_in6 *) &port->raddr.addr)) |
761 | { |
762 | found = true; |
763 | break; |
764 | } |
765 | } |
766 | #endif |
767 | } |
768 | } |
769 | |
770 | if (gai_result) |
771 | freeaddrinfo(gai_result); |
772 | |
773 | if (!found) |
774 | elog(DEBUG2, "pg_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client" , |
775 | hostname); |
776 | |
777 | port->remote_hostname_resolv = found ? +1 : -1; |
778 | |
779 | return found; |
780 | } |
781 | |
782 | /* |
783 | * Check to see if a connecting IP matches the given address and netmask. |
784 | */ |
785 | static bool |
786 | check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask) |
787 | { |
788 | if (raddr->addr.ss_family == addr->sa_family && |
789 | pg_range_sockaddr(&raddr->addr, |
790 | (struct sockaddr_storage *) addr, |
791 | (struct sockaddr_storage *) mask)) |
792 | return true; |
793 | return false; |
794 | } |
795 | |
796 | /* |
797 | * pg_foreach_ifaddr callback: does client addr match this machine interface? |
798 | */ |
799 | static void |
800 | check_network_callback(struct sockaddr *addr, struct sockaddr *netmask, |
801 | void *cb_data) |
802 | { |
803 | check_network_data *cn = (check_network_data *) cb_data; |
804 | struct sockaddr_storage mask; |
805 | |
806 | /* Already found a match? */ |
807 | if (cn->result) |
808 | return; |
809 | |
810 | if (cn->method == ipCmpSameHost) |
811 | { |
812 | /* Make an all-ones netmask of appropriate length for family */ |
813 | pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family); |
814 | cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) &mask); |
815 | } |
816 | else |
817 | { |
818 | /* Use the netmask of the interface itself */ |
819 | cn->result = check_ip(cn->raddr, addr, netmask); |
820 | } |
821 | } |
822 | |
823 | /* |
824 | * Use pg_foreach_ifaddr to check a samehost or samenet match |
825 | */ |
826 | static bool |
827 | check_same_host_or_net(SockAddr *raddr, IPCompareMethod method) |
828 | { |
829 | check_network_data cn; |
830 | |
831 | cn.method = method; |
832 | cn.raddr = raddr; |
833 | cn.result = false; |
834 | |
835 | errno = 0; |
836 | if (pg_foreach_ifaddr(check_network_callback, &cn) < 0) |
837 | { |
838 | elog(LOG, "error enumerating network interfaces: %m" ); |
839 | return false; |
840 | } |
841 | |
842 | return cn.result; |
843 | } |
844 | |
845 | |
846 | /* |
847 | * Macros used to check and report on invalid configuration options. |
848 | * On error: log a message at level elevel, set *err_msg, and exit the function. |
849 | * These macros are not as general-purpose as they look, because they know |
850 | * what the calling function's error-exit value is. |
851 | * |
852 | * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's |
853 | * not supported. |
854 | * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the |
855 | * method is actually the one specified. Used as a shortcut when |
856 | * the option is only valid for one authentication method. |
857 | * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method, |
858 | * reporting error if it's not. |
859 | */ |
860 | #define INVALID_AUTH_OPTION(optname, validmethods) \ |
861 | do { \ |
862 | ereport(elevel, \ |
863 | (errcode(ERRCODE_CONFIG_FILE_ERROR), \ |
864 | /* translator: the second %s is a list of auth methods */ \ |
865 | errmsg("authentication option \"%s\" is only valid for authentication methods %s", \ |
866 | optname, _(validmethods)), \ |
867 | errcontext("line %d of configuration file \"%s\"", \ |
868 | line_num, HbaFileName))); \ |
869 | *err_msg = psprintf("authentication option \"%s\" is only valid for authentication methods %s", \ |
870 | optname, validmethods); \ |
871 | return false; \ |
872 | } while (0) |
873 | |
874 | #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) \ |
875 | do { \ |
876 | if (hbaline->auth_method != methodval) \ |
877 | INVALID_AUTH_OPTION(optname, validmethods); \ |
878 | } while (0) |
879 | |
880 | #define MANDATORY_AUTH_ARG(argvar, argname, authname) \ |
881 | do { \ |
882 | if (argvar == NULL) { \ |
883 | ereport(elevel, \ |
884 | (errcode(ERRCODE_CONFIG_FILE_ERROR), \ |
885 | errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \ |
886 | authname, argname), \ |
887 | errcontext("line %d of configuration file \"%s\"", \ |
888 | line_num, HbaFileName))); \ |
889 | *err_msg = psprintf("authentication method \"%s\" requires argument \"%s\" to be set", \ |
890 | authname, argname); \ |
891 | return NULL; \ |
892 | } \ |
893 | } while (0) |
894 | |
895 | /* |
896 | * Macros for handling pg_ident problems. |
897 | * Much as above, but currently the message level is hardwired as LOG |
898 | * and there is no provision for an err_msg string. |
899 | * |
900 | * IDENT_FIELD_ABSENT: |
901 | * Log a message and exit the function if the given ident field ListCell is |
902 | * not populated. |
903 | * |
904 | * IDENT_MULTI_VALUE: |
905 | * Log a message and exit the function if the given ident token List has more |
906 | * than one element. |
907 | */ |
908 | #define IDENT_FIELD_ABSENT(field) \ |
909 | do { \ |
910 | if (!field) { \ |
911 | ereport(LOG, \ |
912 | (errcode(ERRCODE_CONFIG_FILE_ERROR), \ |
913 | errmsg("missing entry in file \"%s\" at end of line %d", \ |
914 | IdentFileName, line_num))); \ |
915 | return NULL; \ |
916 | } \ |
917 | } while (0) |
918 | |
919 | #define IDENT_MULTI_VALUE(tokens) \ |
920 | do { \ |
921 | if (tokens->length > 1) { \ |
922 | ereport(LOG, \ |
923 | (errcode(ERRCODE_CONFIG_FILE_ERROR), \ |
924 | errmsg("multiple values in ident field"), \ |
925 | errcontext("line %d of configuration file \"%s\"", \ |
926 | line_num, IdentFileName))); \ |
927 | return NULL; \ |
928 | } \ |
929 | } while (0) |
930 | |
931 | |
932 | /* |
933 | * Parse one tokenised line from the hba config file and store the result in a |
934 | * HbaLine structure. |
935 | * |
936 | * If parsing fails, log a message at ereport level elevel, store an error |
937 | * string in tok_line->err_msg, and return NULL. (Some non-error conditions |
938 | * can also result in such messages.) |
939 | * |
940 | * Note: this function leaks memory when an error occurs. Caller is expected |
941 | * to have set a memory context that will be reset if this function returns |
942 | * NULL. |
943 | */ |
944 | static HbaLine * |
945 | parse_hba_line(TokenizedLine *tok_line, int elevel) |
946 | { |
947 | int line_num = tok_line->line_num; |
948 | char **err_msg = &tok_line->err_msg; |
949 | char *str; |
950 | struct addrinfo *gai_result; |
951 | struct addrinfo hints; |
952 | int ret; |
953 | char *cidr_slash; |
954 | char *unsupauth; |
955 | ListCell *field; |
956 | List *tokens; |
957 | ListCell *tokencell; |
958 | HbaToken *token; |
959 | HbaLine *parsedline; |
960 | |
961 | parsedline = palloc0(sizeof(HbaLine)); |
962 | parsedline->linenumber = line_num; |
963 | parsedline->rawline = pstrdup(tok_line->raw_line); |
964 | |
965 | /* Check the record type. */ |
966 | Assert(tok_line->fields != NIL); |
967 | field = list_head(tok_line->fields); |
968 | tokens = lfirst(field); |
969 | if (tokens->length > 1) |
970 | { |
971 | ereport(elevel, |
972 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
973 | errmsg("multiple values specified for connection type" ), |
974 | errhint("Specify exactly one connection type per line." ), |
975 | errcontext("line %d of configuration file \"%s\"" , |
976 | line_num, HbaFileName))); |
977 | *err_msg = "multiple values specified for connection type" ; |
978 | return NULL; |
979 | } |
980 | token = linitial(tokens); |
981 | if (strcmp(token->string, "local" ) == 0) |
982 | { |
983 | #ifdef HAVE_UNIX_SOCKETS |
984 | parsedline->conntype = ctLocal; |
985 | #else |
986 | ereport(elevel, |
987 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
988 | errmsg("local connections are not supported by this build" ), |
989 | errcontext("line %d of configuration file \"%s\"" , |
990 | line_num, HbaFileName))); |
991 | *err_msg = "local connections are not supported by this build" ; |
992 | return NULL; |
993 | #endif |
994 | } |
995 | else if (strcmp(token->string, "host" ) == 0 || |
996 | strcmp(token->string, "hostssl" ) == 0 || |
997 | strcmp(token->string, "hostnossl" ) == 0 || |
998 | strcmp(token->string, "hostgssenc" ) == 0 || |
999 | strcmp(token->string, "hostnogssenc" ) == 0) |
1000 | { |
1001 | |
1002 | if (token->string[4] == 's') /* "hostssl" */ |
1003 | { |
1004 | parsedline->conntype = ctHostSSL; |
1005 | /* Log a warning if SSL support is not active */ |
1006 | #ifdef USE_SSL |
1007 | if (!EnableSSL) |
1008 | { |
1009 | ereport(elevel, |
1010 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1011 | errmsg("hostssl record cannot match because SSL is disabled" ), |
1012 | errhint("Set ssl = on in postgresql.conf." ), |
1013 | errcontext("line %d of configuration file \"%s\"" , |
1014 | line_num, HbaFileName))); |
1015 | *err_msg = "hostssl record cannot match because SSL is disabled" ; |
1016 | } |
1017 | #else |
1018 | ereport(elevel, |
1019 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1020 | errmsg("hostssl record cannot match because SSL is not supported by this build" ), |
1021 | errhint("Compile with --with-openssl to use SSL connections." ), |
1022 | errcontext("line %d of configuration file \"%s\"" , |
1023 | line_num, HbaFileName))); |
1024 | *err_msg = "hostssl record cannot match because SSL is not supported by this build" ; |
1025 | #endif |
1026 | } |
1027 | else if (token->string[4] == 'g') /* "hostgssenc" */ |
1028 | { |
1029 | parsedline->conntype = ctHostGSS; |
1030 | #ifndef ENABLE_GSS |
1031 | ereport(elevel, |
1032 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1033 | errmsg("hostgssenc record cannot match because GSSAPI is not supported by this build" ), |
1034 | errhint("Compile with --with-gssapi to use GSSAPI connections." ), |
1035 | errcontext("line %d of configuration file \"%s\"" , |
1036 | line_num, HbaFileName))); |
1037 | *err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build" ; |
1038 | #endif |
1039 | } |
1040 | else if (token->string[4] == 'n' && token->string[6] == 's') |
1041 | parsedline->conntype = ctHostNoSSL; |
1042 | else if (token->string[4] == 'n' && token->string[6] == 'g') |
1043 | parsedline->conntype = ctHostNoGSS; |
1044 | else |
1045 | { |
1046 | /* "host" */ |
1047 | parsedline->conntype = ctHost; |
1048 | } |
1049 | } /* record type */ |
1050 | else |
1051 | { |
1052 | ereport(elevel, |
1053 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1054 | errmsg("invalid connection type \"%s\"" , |
1055 | token->string), |
1056 | errcontext("line %d of configuration file \"%s\"" , |
1057 | line_num, HbaFileName))); |
1058 | *err_msg = psprintf("invalid connection type \"%s\"" , token->string); |
1059 | return NULL; |
1060 | } |
1061 | |
1062 | /* Get the databases. */ |
1063 | field = lnext(field); |
1064 | if (!field) |
1065 | { |
1066 | ereport(elevel, |
1067 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1068 | errmsg("end-of-line before database specification" ), |
1069 | errcontext("line %d of configuration file \"%s\"" , |
1070 | line_num, HbaFileName))); |
1071 | *err_msg = "end-of-line before database specification" ; |
1072 | return NULL; |
1073 | } |
1074 | parsedline->databases = NIL; |
1075 | tokens = lfirst(field); |
1076 | foreach(tokencell, tokens) |
1077 | { |
1078 | parsedline->databases = lappend(parsedline->databases, |
1079 | copy_hba_token(lfirst(tokencell))); |
1080 | } |
1081 | |
1082 | /* Get the roles. */ |
1083 | field = lnext(field); |
1084 | if (!field) |
1085 | { |
1086 | ereport(elevel, |
1087 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1088 | errmsg("end-of-line before role specification" ), |
1089 | errcontext("line %d of configuration file \"%s\"" , |
1090 | line_num, HbaFileName))); |
1091 | *err_msg = "end-of-line before role specification" ; |
1092 | return NULL; |
1093 | } |
1094 | parsedline->roles = NIL; |
1095 | tokens = lfirst(field); |
1096 | foreach(tokencell, tokens) |
1097 | { |
1098 | parsedline->roles = lappend(parsedline->roles, |
1099 | copy_hba_token(lfirst(tokencell))); |
1100 | } |
1101 | |
1102 | if (parsedline->conntype != ctLocal) |
1103 | { |
1104 | /* Read the IP address field. (with or without CIDR netmask) */ |
1105 | field = lnext(field); |
1106 | if (!field) |
1107 | { |
1108 | ereport(elevel, |
1109 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1110 | errmsg("end-of-line before IP address specification" ), |
1111 | errcontext("line %d of configuration file \"%s\"" , |
1112 | line_num, HbaFileName))); |
1113 | *err_msg = "end-of-line before IP address specification" ; |
1114 | return NULL; |
1115 | } |
1116 | tokens = lfirst(field); |
1117 | if (tokens->length > 1) |
1118 | { |
1119 | ereport(elevel, |
1120 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1121 | errmsg("multiple values specified for host address" ), |
1122 | errhint("Specify one address range per line." ), |
1123 | errcontext("line %d of configuration file \"%s\"" , |
1124 | line_num, HbaFileName))); |
1125 | *err_msg = "multiple values specified for host address" ; |
1126 | return NULL; |
1127 | } |
1128 | token = linitial(tokens); |
1129 | |
1130 | if (token_is_keyword(token, "all" )) |
1131 | { |
1132 | parsedline->ip_cmp_method = ipCmpAll; |
1133 | } |
1134 | else if (token_is_keyword(token, "samehost" )) |
1135 | { |
1136 | /* Any IP on this host is allowed to connect */ |
1137 | parsedline->ip_cmp_method = ipCmpSameHost; |
1138 | } |
1139 | else if (token_is_keyword(token, "samenet" )) |
1140 | { |
1141 | /* Any IP on the host's subnets is allowed to connect */ |
1142 | parsedline->ip_cmp_method = ipCmpSameNet; |
1143 | } |
1144 | else |
1145 | { |
1146 | /* IP and netmask are specified */ |
1147 | parsedline->ip_cmp_method = ipCmpMask; |
1148 | |
1149 | /* need a modifiable copy of token */ |
1150 | str = pstrdup(token->string); |
1151 | |
1152 | /* Check if it has a CIDR suffix and if so isolate it */ |
1153 | cidr_slash = strchr(str, '/'); |
1154 | if (cidr_slash) |
1155 | *cidr_slash = '\0'; |
1156 | |
1157 | /* Get the IP address either way */ |
1158 | hints.ai_flags = AI_NUMERICHOST; |
1159 | hints.ai_family = AF_UNSPEC; |
1160 | hints.ai_socktype = 0; |
1161 | hints.ai_protocol = 0; |
1162 | hints.ai_addrlen = 0; |
1163 | hints.ai_canonname = NULL; |
1164 | hints.ai_addr = NULL; |
1165 | hints.ai_next = NULL; |
1166 | |
1167 | ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result); |
1168 | if (ret == 0 && gai_result) |
1169 | memcpy(&parsedline->addr, gai_result->ai_addr, |
1170 | gai_result->ai_addrlen); |
1171 | else if (ret == EAI_NONAME) |
1172 | parsedline->hostname = str; |
1173 | else |
1174 | { |
1175 | ereport(elevel, |
1176 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1177 | errmsg("invalid IP address \"%s\": %s" , |
1178 | str, gai_strerror(ret)), |
1179 | errcontext("line %d of configuration file \"%s\"" , |
1180 | line_num, HbaFileName))); |
1181 | *err_msg = psprintf("invalid IP address \"%s\": %s" , |
1182 | str, gai_strerror(ret)); |
1183 | if (gai_result) |
1184 | pg_freeaddrinfo_all(hints.ai_family, gai_result); |
1185 | return NULL; |
1186 | } |
1187 | |
1188 | pg_freeaddrinfo_all(hints.ai_family, gai_result); |
1189 | |
1190 | /* Get the netmask */ |
1191 | if (cidr_slash) |
1192 | { |
1193 | if (parsedline->hostname) |
1194 | { |
1195 | ereport(elevel, |
1196 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1197 | errmsg("specifying both host name and CIDR mask is invalid: \"%s\"" , |
1198 | token->string), |
1199 | errcontext("line %d of configuration file \"%s\"" , |
1200 | line_num, HbaFileName))); |
1201 | *err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"" , |
1202 | token->string); |
1203 | return NULL; |
1204 | } |
1205 | |
1206 | if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1, |
1207 | parsedline->addr.ss_family) < 0) |
1208 | { |
1209 | ereport(elevel, |
1210 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1211 | errmsg("invalid CIDR mask in address \"%s\"" , |
1212 | token->string), |
1213 | errcontext("line %d of configuration file \"%s\"" , |
1214 | line_num, HbaFileName))); |
1215 | *err_msg = psprintf("invalid CIDR mask in address \"%s\"" , |
1216 | token->string); |
1217 | return NULL; |
1218 | } |
1219 | pfree(str); |
1220 | } |
1221 | else if (!parsedline->hostname) |
1222 | { |
1223 | /* Read the mask field. */ |
1224 | pfree(str); |
1225 | field = lnext(field); |
1226 | if (!field) |
1227 | { |
1228 | ereport(elevel, |
1229 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1230 | errmsg("end-of-line before netmask specification" ), |
1231 | errhint("Specify an address range in CIDR notation, or provide a separate netmask." ), |
1232 | errcontext("line %d of configuration file \"%s\"" , |
1233 | line_num, HbaFileName))); |
1234 | *err_msg = "end-of-line before netmask specification" ; |
1235 | return NULL; |
1236 | } |
1237 | tokens = lfirst(field); |
1238 | if (tokens->length > 1) |
1239 | { |
1240 | ereport(elevel, |
1241 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1242 | errmsg("multiple values specified for netmask" ), |
1243 | errcontext("line %d of configuration file \"%s\"" , |
1244 | line_num, HbaFileName))); |
1245 | *err_msg = "multiple values specified for netmask" ; |
1246 | return NULL; |
1247 | } |
1248 | token = linitial(tokens); |
1249 | |
1250 | ret = pg_getaddrinfo_all(token->string, NULL, |
1251 | &hints, &gai_result); |
1252 | if (ret || !gai_result) |
1253 | { |
1254 | ereport(elevel, |
1255 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1256 | errmsg("invalid IP mask \"%s\": %s" , |
1257 | token->string, gai_strerror(ret)), |
1258 | errcontext("line %d of configuration file \"%s\"" , |
1259 | line_num, HbaFileName))); |
1260 | *err_msg = psprintf("invalid IP mask \"%s\": %s" , |
1261 | token->string, gai_strerror(ret)); |
1262 | if (gai_result) |
1263 | pg_freeaddrinfo_all(hints.ai_family, gai_result); |
1264 | return NULL; |
1265 | } |
1266 | |
1267 | memcpy(&parsedline->mask, gai_result->ai_addr, |
1268 | gai_result->ai_addrlen); |
1269 | pg_freeaddrinfo_all(hints.ai_family, gai_result); |
1270 | |
1271 | if (parsedline->addr.ss_family != parsedline->mask.ss_family) |
1272 | { |
1273 | ereport(elevel, |
1274 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1275 | errmsg("IP address and mask do not match" ), |
1276 | errcontext("line %d of configuration file \"%s\"" , |
1277 | line_num, HbaFileName))); |
1278 | *err_msg = "IP address and mask do not match" ; |
1279 | return NULL; |
1280 | } |
1281 | } |
1282 | } |
1283 | } /* != ctLocal */ |
1284 | |
1285 | /* Get the authentication method */ |
1286 | field = lnext(field); |
1287 | if (!field) |
1288 | { |
1289 | ereport(elevel, |
1290 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1291 | errmsg("end-of-line before authentication method" ), |
1292 | errcontext("line %d of configuration file \"%s\"" , |
1293 | line_num, HbaFileName))); |
1294 | *err_msg = "end-of-line before authentication method" ; |
1295 | return NULL; |
1296 | } |
1297 | tokens = lfirst(field); |
1298 | if (tokens->length > 1) |
1299 | { |
1300 | ereport(elevel, |
1301 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1302 | errmsg("multiple values specified for authentication type" ), |
1303 | errhint("Specify exactly one authentication type per line." ), |
1304 | errcontext("line %d of configuration file \"%s\"" , |
1305 | line_num, HbaFileName))); |
1306 | *err_msg = "multiple values specified for authentication type" ; |
1307 | return NULL; |
1308 | } |
1309 | token = linitial(tokens); |
1310 | |
1311 | unsupauth = NULL; |
1312 | if (strcmp(token->string, "trust" ) == 0) |
1313 | parsedline->auth_method = uaTrust; |
1314 | else if (strcmp(token->string, "ident" ) == 0) |
1315 | parsedline->auth_method = uaIdent; |
1316 | else if (strcmp(token->string, "peer" ) == 0) |
1317 | parsedline->auth_method = uaPeer; |
1318 | else if (strcmp(token->string, "password" ) == 0) |
1319 | parsedline->auth_method = uaPassword; |
1320 | else if (strcmp(token->string, "gss" ) == 0) |
1321 | #ifdef ENABLE_GSS |
1322 | parsedline->auth_method = uaGSS; |
1323 | #else |
1324 | unsupauth = "gss" ; |
1325 | #endif |
1326 | else if (strcmp(token->string, "sspi" ) == 0) |
1327 | #ifdef ENABLE_SSPI |
1328 | parsedline->auth_method = uaSSPI; |
1329 | #else |
1330 | unsupauth = "sspi" ; |
1331 | #endif |
1332 | else if (strcmp(token->string, "reject" ) == 0) |
1333 | parsedline->auth_method = uaReject; |
1334 | else if (strcmp(token->string, "md5" ) == 0) |
1335 | { |
1336 | if (Db_user_namespace) |
1337 | { |
1338 | ereport(elevel, |
1339 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1340 | errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled" ), |
1341 | errcontext("line %d of configuration file \"%s\"" , |
1342 | line_num, HbaFileName))); |
1343 | *err_msg = "MD5 authentication is not supported when \"db_user_namespace\" is enabled" ; |
1344 | return NULL; |
1345 | } |
1346 | parsedline->auth_method = uaMD5; |
1347 | } |
1348 | else if (strcmp(token->string, "scram-sha-256" ) == 0) |
1349 | parsedline->auth_method = uaSCRAM; |
1350 | else if (strcmp(token->string, "pam" ) == 0) |
1351 | #ifdef USE_PAM |
1352 | parsedline->auth_method = uaPAM; |
1353 | #else |
1354 | unsupauth = "pam" ; |
1355 | #endif |
1356 | else if (strcmp(token->string, "bsd" ) == 0) |
1357 | #ifdef USE_BSD_AUTH |
1358 | parsedline->auth_method = uaBSD; |
1359 | #else |
1360 | unsupauth = "bsd" ; |
1361 | #endif |
1362 | else if (strcmp(token->string, "ldap" ) == 0) |
1363 | #ifdef USE_LDAP |
1364 | parsedline->auth_method = uaLDAP; |
1365 | #else |
1366 | unsupauth = "ldap" ; |
1367 | #endif |
1368 | else if (strcmp(token->string, "cert" ) == 0) |
1369 | #ifdef USE_SSL |
1370 | parsedline->auth_method = uaCert; |
1371 | #else |
1372 | unsupauth = "cert" ; |
1373 | #endif |
1374 | else if (strcmp(token->string, "radius" ) == 0) |
1375 | parsedline->auth_method = uaRADIUS; |
1376 | else |
1377 | { |
1378 | ereport(elevel, |
1379 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1380 | errmsg("invalid authentication method \"%s\"" , |
1381 | token->string), |
1382 | errcontext("line %d of configuration file \"%s\"" , |
1383 | line_num, HbaFileName))); |
1384 | *err_msg = psprintf("invalid authentication method \"%s\"" , |
1385 | token->string); |
1386 | return NULL; |
1387 | } |
1388 | |
1389 | if (unsupauth) |
1390 | { |
1391 | ereport(elevel, |
1392 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1393 | errmsg("invalid authentication method \"%s\": not supported by this build" , |
1394 | token->string), |
1395 | errcontext("line %d of configuration file \"%s\"" , |
1396 | line_num, HbaFileName))); |
1397 | *err_msg = psprintf("invalid authentication method \"%s\": not supported by this build" , |
1398 | token->string); |
1399 | return NULL; |
1400 | } |
1401 | |
1402 | /* |
1403 | * XXX: When using ident on local connections, change it to peer, for |
1404 | * backwards compatibility. |
1405 | */ |
1406 | if (parsedline->conntype == ctLocal && |
1407 | parsedline->auth_method == uaIdent) |
1408 | parsedline->auth_method = uaPeer; |
1409 | |
1410 | /* Invalid authentication combinations */ |
1411 | if (parsedline->conntype == ctLocal && |
1412 | parsedline->auth_method == uaGSS) |
1413 | { |
1414 | ereport(elevel, |
1415 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1416 | errmsg("gssapi authentication is not supported on local sockets" ), |
1417 | errcontext("line %d of configuration file \"%s\"" , |
1418 | line_num, HbaFileName))); |
1419 | *err_msg = "gssapi authentication is not supported on local sockets" ; |
1420 | return NULL; |
1421 | } |
1422 | if (parsedline->conntype == ctHostGSS && |
1423 | parsedline->auth_method != uaGSS && |
1424 | parsedline->auth_method != uaReject && |
1425 | parsedline->auth_method != uaTrust) |
1426 | { |
1427 | ereport(elevel, |
1428 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1429 | errmsg("GSSAPI encryption only supports gss, trust, or reject authentication" ), |
1430 | errcontext("line %d of configuration file \"%s\"" , |
1431 | line_num, HbaFileName))); |
1432 | *err_msg = "GSSAPI encryption only supports gss, trust, or reject authentication" ; |
1433 | return NULL; |
1434 | } |
1435 | |
1436 | if (parsedline->conntype != ctLocal && |
1437 | parsedline->auth_method == uaPeer) |
1438 | { |
1439 | ereport(elevel, |
1440 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1441 | errmsg("peer authentication is only supported on local sockets" ), |
1442 | errcontext("line %d of configuration file \"%s\"" , |
1443 | line_num, HbaFileName))); |
1444 | *err_msg = "peer authentication is only supported on local sockets" ; |
1445 | return NULL; |
1446 | } |
1447 | |
1448 | /* |
1449 | * SSPI authentication can never be enabled on ctLocal connections, |
1450 | * because it's only supported on Windows, where ctLocal isn't supported. |
1451 | */ |
1452 | |
1453 | |
1454 | if (parsedline->conntype != ctHostSSL && |
1455 | parsedline->auth_method == uaCert) |
1456 | { |
1457 | ereport(elevel, |
1458 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1459 | errmsg("cert authentication is only supported on hostssl connections" ), |
1460 | errcontext("line %d of configuration file \"%s\"" , |
1461 | line_num, HbaFileName))); |
1462 | *err_msg = "cert authentication is only supported on hostssl connections" ; |
1463 | return NULL; |
1464 | } |
1465 | |
1466 | /* |
1467 | * For GSS and SSPI, set the default value of include_realm to true. |
1468 | * Having include_realm set to false is dangerous in multi-realm |
1469 | * situations and is generally considered bad practice. We keep the |
1470 | * capability around for backwards compatibility, but we might want to |
1471 | * remove it at some point in the future. Users who still need to strip |
1472 | * the realm off would be better served by using an appropriate regex in a |
1473 | * pg_ident.conf mapping. |
1474 | */ |
1475 | if (parsedline->auth_method == uaGSS || |
1476 | parsedline->auth_method == uaSSPI) |
1477 | parsedline->include_realm = true; |
1478 | |
1479 | /* |
1480 | * For SSPI, include_realm defaults to the SAM-compatible domain (aka |
1481 | * NetBIOS name) and user names instead of the Kerberos principal name for |
1482 | * compatibility. |
1483 | */ |
1484 | if (parsedline->auth_method == uaSSPI) |
1485 | { |
1486 | parsedline->compat_realm = true; |
1487 | parsedline->upn_username = false; |
1488 | } |
1489 | |
1490 | /* Parse remaining arguments */ |
1491 | while ((field = lnext(field)) != NULL) |
1492 | { |
1493 | tokens = lfirst(field); |
1494 | foreach(tokencell, tokens) |
1495 | { |
1496 | char *val; |
1497 | |
1498 | token = lfirst(tokencell); |
1499 | |
1500 | str = pstrdup(token->string); |
1501 | val = strchr(str, '='); |
1502 | if (val == NULL) |
1503 | { |
1504 | /* |
1505 | * Got something that's not a name=value pair. |
1506 | */ |
1507 | ereport(elevel, |
1508 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1509 | errmsg("authentication option not in name=value format: %s" , token->string), |
1510 | errcontext("line %d of configuration file \"%s\"" , |
1511 | line_num, HbaFileName))); |
1512 | *err_msg = psprintf("authentication option not in name=value format: %s" , |
1513 | token->string); |
1514 | return NULL; |
1515 | } |
1516 | |
1517 | *val++ = '\0'; /* str now holds "name", val holds "value" */ |
1518 | if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg)) |
1519 | /* parse_hba_auth_opt already logged the error message */ |
1520 | return NULL; |
1521 | pfree(str); |
1522 | } |
1523 | } |
1524 | |
1525 | /* |
1526 | * Check if the selected authentication method has any mandatory arguments |
1527 | * that are not set. |
1528 | */ |
1529 | if (parsedline->auth_method == uaLDAP) |
1530 | { |
1531 | #ifndef HAVE_LDAP_INITIALIZE |
1532 | /* Not mandatory for OpenLDAP, because it can use DNS SRV records */ |
1533 | MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver" , "ldap" ); |
1534 | #endif |
1535 | |
1536 | /* |
1537 | * LDAP can operate in two modes: either with a direct bind, using |
1538 | * ldapprefix and ldapsuffix, or using a search+bind, using |
1539 | * ldapbasedn, ldapbinddn, ldapbindpasswd and one of |
1540 | * ldapsearchattribute or ldapsearchfilter. Disallow mixing these |
1541 | * parameters. |
1542 | */ |
1543 | if (parsedline->ldapprefix || parsedline->ldapsuffix) |
1544 | { |
1545 | if (parsedline->ldapbasedn || |
1546 | parsedline->ldapbinddn || |
1547 | parsedline->ldapbindpasswd || |
1548 | parsedline->ldapsearchattribute || |
1549 | parsedline->ldapsearchfilter) |
1550 | { |
1551 | ereport(elevel, |
1552 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1553 | errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, ldapsearchfilter, or ldapurl together with ldapprefix" ), |
1554 | errcontext("line %d of configuration file \"%s\"" , |
1555 | line_num, HbaFileName))); |
1556 | *err_msg = "cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, ldapsearchfilter, or ldapurl together with ldapprefix" ; |
1557 | return NULL; |
1558 | } |
1559 | } |
1560 | else if (!parsedline->ldapbasedn) |
1561 | { |
1562 | ereport(elevel, |
1563 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1564 | errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set" ), |
1565 | errcontext("line %d of configuration file \"%s\"" , |
1566 | line_num, HbaFileName))); |
1567 | *err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set" ; |
1568 | return NULL; |
1569 | } |
1570 | |
1571 | /* |
1572 | * When using search+bind, you can either use a simple attribute |
1573 | * (defaulting to "uid") or a fully custom search filter. You can't |
1574 | * do both. |
1575 | */ |
1576 | if (parsedline->ldapsearchattribute && parsedline->ldapsearchfilter) |
1577 | { |
1578 | ereport(elevel, |
1579 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1580 | errmsg("cannot use ldapsearchattribute together with ldapsearchfilter" ), |
1581 | errcontext("line %d of configuration file \"%s\"" , |
1582 | line_num, HbaFileName))); |
1583 | *err_msg = "cannot use ldapsearchattribute together with ldapsearchfilter" ; |
1584 | return NULL; |
1585 | } |
1586 | } |
1587 | |
1588 | if (parsedline->auth_method == uaRADIUS) |
1589 | { |
1590 | MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers" , "radius" ); |
1591 | MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets" , "radius" ); |
1592 | |
1593 | if (list_length(parsedline->radiusservers) < 1) |
1594 | { |
1595 | ereport(LOG, |
1596 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1597 | errmsg("list of RADIUS servers cannot be empty" ), |
1598 | errcontext("line %d of configuration file \"%s\"" , |
1599 | line_num, HbaFileName))); |
1600 | return NULL; |
1601 | } |
1602 | |
1603 | if (list_length(parsedline->radiussecrets) < 1) |
1604 | { |
1605 | ereport(LOG, |
1606 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1607 | errmsg("list of RADIUS secrets cannot be empty" ), |
1608 | errcontext("line %d of configuration file \"%s\"" , |
1609 | line_num, HbaFileName))); |
1610 | return NULL; |
1611 | } |
1612 | |
1613 | /* |
1614 | * Verify length of option lists - each can be 0 (except for secrets, |
1615 | * but that's already checked above), 1 (use the same value |
1616 | * everywhere) or the same as the number of servers. |
1617 | */ |
1618 | if (!verify_option_list_length(parsedline->radiussecrets, |
1619 | "RADIUS secrets" , |
1620 | parsedline->radiusservers, |
1621 | "RADIUS servers" , |
1622 | line_num)) |
1623 | return NULL; |
1624 | if (!verify_option_list_length(parsedline->radiusports, |
1625 | "RADIUS ports" , |
1626 | parsedline->radiusservers, |
1627 | "RADIUS servers" , |
1628 | line_num)) |
1629 | return NULL; |
1630 | if (!verify_option_list_length(parsedline->radiusidentifiers, |
1631 | "RADIUS identifiers" , |
1632 | parsedline->radiusservers, |
1633 | "RADIUS servers" , |
1634 | line_num)) |
1635 | return NULL; |
1636 | } |
1637 | |
1638 | /* |
1639 | * Enforce any parameters implied by other settings. |
1640 | */ |
1641 | if (parsedline->auth_method == uaCert) |
1642 | { |
1643 | parsedline->clientcert = clientCertCA; |
1644 | } |
1645 | |
1646 | return parsedline; |
1647 | } |
1648 | |
1649 | |
1650 | static bool |
1651 | verify_option_list_length(List *options, const char *optionname, List *masters, const char *mastername, int line_num) |
1652 | { |
1653 | if (list_length(options) == 0 || |
1654 | list_length(options) == 1 || |
1655 | list_length(options) == list_length(masters)) |
1656 | return true; |
1657 | |
1658 | ereport(LOG, |
1659 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1660 | errmsg("the number of %s (%d) must be 1 or the same as the number of %s (%d)" , |
1661 | optionname, |
1662 | list_length(options), |
1663 | mastername, |
1664 | list_length(masters) |
1665 | ), |
1666 | errcontext("line %d of configuration file \"%s\"" , |
1667 | line_num, HbaFileName))); |
1668 | return false; |
1669 | } |
1670 | |
1671 | /* |
1672 | * Parse one name-value pair as an authentication option into the given |
1673 | * HbaLine. Return true if we successfully parse the option, false if we |
1674 | * encounter an error. In the event of an error, also log a message at |
1675 | * ereport level elevel, and store a message string into *err_msg. |
1676 | */ |
1677 | static bool |
1678 | parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, |
1679 | int elevel, char **err_msg) |
1680 | { |
1681 | int line_num = hbaline->linenumber; |
1682 | |
1683 | #ifdef USE_LDAP |
1684 | hbaline->ldapscope = LDAP_SCOPE_SUBTREE; |
1685 | #endif |
1686 | |
1687 | if (strcmp(name, "map" ) == 0) |
1688 | { |
1689 | if (hbaline->auth_method != uaIdent && |
1690 | hbaline->auth_method != uaPeer && |
1691 | hbaline->auth_method != uaGSS && |
1692 | hbaline->auth_method != uaSSPI && |
1693 | hbaline->auth_method != uaCert) |
1694 | INVALID_AUTH_OPTION("map" , gettext_noop("ident, peer, gssapi, sspi, and cert" )); |
1695 | hbaline->usermap = pstrdup(val); |
1696 | } |
1697 | else if (strcmp(name, "clientcert" ) == 0) |
1698 | { |
1699 | if (hbaline->conntype != ctHostSSL) |
1700 | { |
1701 | ereport(elevel, |
1702 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1703 | errmsg("clientcert can only be configured for \"hostssl\" rows" ), |
1704 | errcontext("line %d of configuration file \"%s\"" , |
1705 | line_num, HbaFileName))); |
1706 | *err_msg = "clientcert can only be configured for \"hostssl\" rows" ; |
1707 | return false; |
1708 | } |
1709 | if (strcmp(val, "1" ) == 0 |
1710 | || strcmp(val, "verify-ca" ) == 0) |
1711 | { |
1712 | hbaline->clientcert = clientCertCA; |
1713 | } |
1714 | else if (strcmp(val, "verify-full" ) == 0) |
1715 | { |
1716 | hbaline->clientcert = clientCertFull; |
1717 | } |
1718 | else if (strcmp(val, "0" ) == 0 |
1719 | || strcmp(val, "no-verify" ) == 0) |
1720 | { |
1721 | if (hbaline->auth_method == uaCert) |
1722 | { |
1723 | ereport(elevel, |
1724 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1725 | errmsg("clientcert can not be set to \"no-verify\" when using \"cert\" authentication" ), |
1726 | errcontext("line %d of configuration file \"%s\"" , |
1727 | line_num, HbaFileName))); |
1728 | *err_msg = "clientcert can not be set to \"no-verify\" when using \"cert\" authentication" ; |
1729 | return false; |
1730 | } |
1731 | hbaline->clientcert = clientCertOff; |
1732 | } |
1733 | else |
1734 | { |
1735 | ereport(elevel, |
1736 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1737 | errmsg("invalid value for clientcert: \"%s\"" , val), |
1738 | errcontext("line %d of configuration file \"%s\"" , |
1739 | line_num, HbaFileName))); |
1740 | return false; |
1741 | } |
1742 | } |
1743 | else if (strcmp(name, "pamservice" ) == 0) |
1744 | { |
1745 | REQUIRE_AUTH_OPTION(uaPAM, "pamservice" , "pam" ); |
1746 | hbaline->pamservice = pstrdup(val); |
1747 | } |
1748 | else if (strcmp(name, "pam_use_hostname" ) == 0) |
1749 | { |
1750 | REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname" , "pam" ); |
1751 | if (strcmp(val, "1" ) == 0) |
1752 | hbaline->pam_use_hostname = true; |
1753 | else |
1754 | hbaline->pam_use_hostname = false; |
1755 | |
1756 | } |
1757 | else if (strcmp(name, "ldapurl" ) == 0) |
1758 | { |
1759 | #ifdef LDAP_API_FEATURE_X_OPENLDAP |
1760 | LDAPURLDesc *urldata; |
1761 | int rc; |
1762 | #endif |
1763 | |
1764 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl" , "ldap" ); |
1765 | #ifdef LDAP_API_FEATURE_X_OPENLDAP |
1766 | rc = ldap_url_parse(val, &urldata); |
1767 | if (rc != LDAP_SUCCESS) |
1768 | { |
1769 | ereport(elevel, |
1770 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1771 | errmsg("could not parse LDAP URL \"%s\": %s" , val, ldap_err2string(rc)))); |
1772 | *err_msg = psprintf("could not parse LDAP URL \"%s\": %s" , |
1773 | val, ldap_err2string(rc)); |
1774 | return false; |
1775 | } |
1776 | |
1777 | if (strcmp(urldata->lud_scheme, "ldap" ) != 0 && |
1778 | strcmp(urldata->lud_scheme, "ldaps" ) != 0) |
1779 | { |
1780 | ereport(elevel, |
1781 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1782 | errmsg("unsupported LDAP URL scheme: %s" , urldata->lud_scheme))); |
1783 | *err_msg = psprintf("unsupported LDAP URL scheme: %s" , |
1784 | urldata->lud_scheme); |
1785 | ldap_free_urldesc(urldata); |
1786 | return false; |
1787 | } |
1788 | |
1789 | if (urldata->lud_scheme) |
1790 | hbaline->ldapscheme = pstrdup(urldata->lud_scheme); |
1791 | if (urldata->lud_host) |
1792 | hbaline->ldapserver = pstrdup(urldata->lud_host); |
1793 | hbaline->ldapport = urldata->lud_port; |
1794 | if (urldata->lud_dn) |
1795 | hbaline->ldapbasedn = pstrdup(urldata->lud_dn); |
1796 | |
1797 | if (urldata->lud_attrs) |
1798 | hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]); /* only use first one */ |
1799 | hbaline->ldapscope = urldata->lud_scope; |
1800 | if (urldata->lud_filter) |
1801 | hbaline->ldapsearchfilter = pstrdup(urldata->lud_filter); |
1802 | ldap_free_urldesc(urldata); |
1803 | #else /* not OpenLDAP */ |
1804 | ereport(elevel, |
1805 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1806 | errmsg("LDAP URLs not supported on this platform" ))); |
1807 | *err_msg = "LDAP URLs not supported on this platform" ; |
1808 | #endif /* not OpenLDAP */ |
1809 | } |
1810 | else if (strcmp(name, "ldaptls" ) == 0) |
1811 | { |
1812 | REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls" , "ldap" ); |
1813 | if (strcmp(val, "1" ) == 0) |
1814 | hbaline->ldaptls = true; |
1815 | else |
1816 | hbaline->ldaptls = false; |
1817 | } |
1818 | else if (strcmp(name, "ldapscheme" ) == 0) |
1819 | { |
1820 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapscheme" , "ldap" ); |
1821 | if (strcmp(val, "ldap" ) != 0 && strcmp(val, "ldaps" ) != 0) |
1822 | ereport(elevel, |
1823 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1824 | errmsg("invalid ldapscheme value: \"%s\"" , val), |
1825 | errcontext("line %d of configuration file \"%s\"" , |
1826 | line_num, HbaFileName))); |
1827 | hbaline->ldapscheme = pstrdup(val); |
1828 | } |
1829 | else if (strcmp(name, "ldapserver" ) == 0) |
1830 | { |
1831 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver" , "ldap" ); |
1832 | hbaline->ldapserver = pstrdup(val); |
1833 | } |
1834 | else if (strcmp(name, "ldapport" ) == 0) |
1835 | { |
1836 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapport" , "ldap" ); |
1837 | hbaline->ldapport = atoi(val); |
1838 | if (hbaline->ldapport == 0) |
1839 | { |
1840 | ereport(elevel, |
1841 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1842 | errmsg("invalid LDAP port number: \"%s\"" , val), |
1843 | errcontext("line %d of configuration file \"%s\"" , |
1844 | line_num, HbaFileName))); |
1845 | *err_msg = psprintf("invalid LDAP port number: \"%s\"" , val); |
1846 | return false; |
1847 | } |
1848 | } |
1849 | else if (strcmp(name, "ldapbinddn" ) == 0) |
1850 | { |
1851 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn" , "ldap" ); |
1852 | hbaline->ldapbinddn = pstrdup(val); |
1853 | } |
1854 | else if (strcmp(name, "ldapbindpasswd" ) == 0) |
1855 | { |
1856 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd" , "ldap" ); |
1857 | hbaline->ldapbindpasswd = pstrdup(val); |
1858 | } |
1859 | else if (strcmp(name, "ldapsearchattribute" ) == 0) |
1860 | { |
1861 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute" , "ldap" ); |
1862 | hbaline->ldapsearchattribute = pstrdup(val); |
1863 | } |
1864 | else if (strcmp(name, "ldapsearchfilter" ) == 0) |
1865 | { |
1866 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchfilter" , "ldap" ); |
1867 | hbaline->ldapsearchfilter = pstrdup(val); |
1868 | } |
1869 | else if (strcmp(name, "ldapbasedn" ) == 0) |
1870 | { |
1871 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn" , "ldap" ); |
1872 | hbaline->ldapbasedn = pstrdup(val); |
1873 | } |
1874 | else if (strcmp(name, "ldapprefix" ) == 0) |
1875 | { |
1876 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix" , "ldap" ); |
1877 | hbaline->ldapprefix = pstrdup(val); |
1878 | } |
1879 | else if (strcmp(name, "ldapsuffix" ) == 0) |
1880 | { |
1881 | REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix" , "ldap" ); |
1882 | hbaline->ldapsuffix = pstrdup(val); |
1883 | } |
1884 | else if (strcmp(name, "krb_realm" ) == 0) |
1885 | { |
1886 | if (hbaline->auth_method != uaGSS && |
1887 | hbaline->auth_method != uaSSPI) |
1888 | INVALID_AUTH_OPTION("krb_realm" , gettext_noop("gssapi and sspi" )); |
1889 | hbaline->krb_realm = pstrdup(val); |
1890 | } |
1891 | else if (strcmp(name, "include_realm" ) == 0) |
1892 | { |
1893 | if (hbaline->auth_method != uaGSS && |
1894 | hbaline->auth_method != uaSSPI) |
1895 | INVALID_AUTH_OPTION("include_realm" , gettext_noop("gssapi and sspi" )); |
1896 | if (strcmp(val, "1" ) == 0) |
1897 | hbaline->include_realm = true; |
1898 | else |
1899 | hbaline->include_realm = false; |
1900 | } |
1901 | else if (strcmp(name, "compat_realm" ) == 0) |
1902 | { |
1903 | if (hbaline->auth_method != uaSSPI) |
1904 | INVALID_AUTH_OPTION("compat_realm" , gettext_noop("sspi" )); |
1905 | if (strcmp(val, "1" ) == 0) |
1906 | hbaline->compat_realm = true; |
1907 | else |
1908 | hbaline->compat_realm = false; |
1909 | } |
1910 | else if (strcmp(name, "upn_username" ) == 0) |
1911 | { |
1912 | if (hbaline->auth_method != uaSSPI) |
1913 | INVALID_AUTH_OPTION("upn_username" , gettext_noop("sspi" )); |
1914 | if (strcmp(val, "1" ) == 0) |
1915 | hbaline->upn_username = true; |
1916 | else |
1917 | hbaline->upn_username = false; |
1918 | } |
1919 | else if (strcmp(name, "radiusservers" ) == 0) |
1920 | { |
1921 | struct addrinfo *gai_result; |
1922 | struct addrinfo hints; |
1923 | int ret; |
1924 | List *parsed_servers; |
1925 | ListCell *l; |
1926 | char *dupval = pstrdup(val); |
1927 | |
1928 | REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers" , "radius" ); |
1929 | |
1930 | if (!SplitIdentifierString(dupval, ',', &parsed_servers)) |
1931 | { |
1932 | /* syntax error in list */ |
1933 | ereport(elevel, |
1934 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1935 | errmsg("could not parse RADIUS server list \"%s\"" , |
1936 | val), |
1937 | errcontext("line %d of configuration file \"%s\"" , |
1938 | line_num, HbaFileName))); |
1939 | return false; |
1940 | } |
1941 | |
1942 | /* For each entry in the list, translate it */ |
1943 | foreach(l, parsed_servers) |
1944 | { |
1945 | MemSet(&hints, 0, sizeof(hints)); |
1946 | hints.ai_socktype = SOCK_DGRAM; |
1947 | hints.ai_family = AF_UNSPEC; |
1948 | |
1949 | ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result); |
1950 | if (ret || !gai_result) |
1951 | { |
1952 | ereport(elevel, |
1953 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1954 | errmsg("could not translate RADIUS server name \"%s\" to address: %s" , |
1955 | (char *) lfirst(l), gai_strerror(ret)), |
1956 | errcontext("line %d of configuration file \"%s\"" , |
1957 | line_num, HbaFileName))); |
1958 | if (gai_result) |
1959 | pg_freeaddrinfo_all(hints.ai_family, gai_result); |
1960 | |
1961 | list_free(parsed_servers); |
1962 | return false; |
1963 | } |
1964 | pg_freeaddrinfo_all(hints.ai_family, gai_result); |
1965 | } |
1966 | |
1967 | /* All entries are OK, so store them */ |
1968 | hbaline->radiusservers = parsed_servers; |
1969 | hbaline->radiusservers_s = pstrdup(val); |
1970 | } |
1971 | else if (strcmp(name, "radiusports" ) == 0) |
1972 | { |
1973 | List *parsed_ports; |
1974 | ListCell *l; |
1975 | char *dupval = pstrdup(val); |
1976 | |
1977 | REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports" , "radius" ); |
1978 | |
1979 | if (!SplitIdentifierString(dupval, ',', &parsed_ports)) |
1980 | { |
1981 | ereport(elevel, |
1982 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1983 | errmsg("could not parse RADIUS port list \"%s\"" , |
1984 | val), |
1985 | errcontext("line %d of configuration file \"%s\"" , |
1986 | line_num, HbaFileName))); |
1987 | *err_msg = psprintf("invalid RADIUS port number: \"%s\"" , val); |
1988 | return false; |
1989 | } |
1990 | |
1991 | foreach(l, parsed_ports) |
1992 | { |
1993 | if (atoi(lfirst(l)) == 0) |
1994 | { |
1995 | ereport(elevel, |
1996 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
1997 | errmsg("invalid RADIUS port number: \"%s\"" , val), |
1998 | errcontext("line %d of configuration file \"%s\"" , |
1999 | line_num, HbaFileName))); |
2000 | |
2001 | return false; |
2002 | } |
2003 | } |
2004 | hbaline->radiusports = parsed_ports; |
2005 | hbaline->radiusports_s = pstrdup(val); |
2006 | } |
2007 | else if (strcmp(name, "radiussecrets" ) == 0) |
2008 | { |
2009 | List *parsed_secrets; |
2010 | char *dupval = pstrdup(val); |
2011 | |
2012 | REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets" , "radius" ); |
2013 | |
2014 | if (!SplitIdentifierString(dupval, ',', &parsed_secrets)) |
2015 | { |
2016 | /* syntax error in list */ |
2017 | ereport(elevel, |
2018 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
2019 | errmsg("could not parse RADIUS secret list \"%s\"" , |
2020 | val), |
2021 | errcontext("line %d of configuration file \"%s\"" , |
2022 | line_num, HbaFileName))); |
2023 | return false; |
2024 | } |
2025 | |
2026 | hbaline->radiussecrets = parsed_secrets; |
2027 | hbaline->radiussecrets_s = pstrdup(val); |
2028 | } |
2029 | else if (strcmp(name, "radiusidentifiers" ) == 0) |
2030 | { |
2031 | List *parsed_identifiers; |
2032 | char *dupval = pstrdup(val); |
2033 | |
2034 | REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers" , "radius" ); |
2035 | |
2036 | if (!SplitIdentifierString(dupval, ',', &parsed_identifiers)) |
2037 | { |
2038 | /* syntax error in list */ |
2039 | ereport(elevel, |
2040 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
2041 | errmsg("could not parse RADIUS identifiers list \"%s\"" , |
2042 | val), |
2043 | errcontext("line %d of configuration file \"%s\"" , |
2044 | line_num, HbaFileName))); |
2045 | return false; |
2046 | } |
2047 | |
2048 | hbaline->radiusidentifiers = parsed_identifiers; |
2049 | hbaline->radiusidentifiers_s = pstrdup(val); |
2050 | } |
2051 | else |
2052 | { |
2053 | ereport(elevel, |
2054 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
2055 | errmsg("unrecognized authentication option name: \"%s\"" , |
2056 | name), |
2057 | errcontext("line %d of configuration file \"%s\"" , |
2058 | line_num, HbaFileName))); |
2059 | *err_msg = psprintf("unrecognized authentication option name: \"%s\"" , |
2060 | name); |
2061 | return false; |
2062 | } |
2063 | return true; |
2064 | } |
2065 | |
2066 | /* |
2067 | * Scan the pre-parsed hba file, looking for a match to the port's connection |
2068 | * request. |
2069 | */ |
2070 | static void |
2071 | check_hba(hbaPort *port) |
2072 | { |
2073 | Oid roleid; |
2074 | ListCell *line; |
2075 | HbaLine *hba; |
2076 | |
2077 | /* Get the target role's OID. Note we do not error out for bad role. */ |
2078 | roleid = get_role_oid(port->user_name, true); |
2079 | |
2080 | foreach(line, parsed_hba_lines) |
2081 | { |
2082 | hba = (HbaLine *) lfirst(line); |
2083 | |
2084 | /* Check connection type */ |
2085 | if (hba->conntype == ctLocal) |
2086 | { |
2087 | if (!IS_AF_UNIX(port->raddr.addr.ss_family)) |
2088 | continue; |
2089 | } |
2090 | else |
2091 | { |
2092 | if (IS_AF_UNIX(port->raddr.addr.ss_family)) |
2093 | continue; |
2094 | |
2095 | /* Check SSL state */ |
2096 | if (port->ssl_in_use) |
2097 | { |
2098 | /* Connection is SSL, match both "host" and "hostssl" */ |
2099 | if (hba->conntype == ctHostNoSSL) |
2100 | continue; |
2101 | } |
2102 | else |
2103 | { |
2104 | /* Connection is not SSL, match both "host" and "hostnossl" */ |
2105 | if (hba->conntype == ctHostSSL) |
2106 | continue; |
2107 | } |
2108 | |
2109 | /* Check GSSAPI state */ |
2110 | #ifdef ENABLE_GSS |
2111 | if (port->gss->enc && hba->conntype == ctHostNoGSS) |
2112 | continue; |
2113 | else if (!port->gss->enc && hba->conntype == ctHostGSS) |
2114 | continue; |
2115 | #else |
2116 | if (hba->conntype == ctHostGSS) |
2117 | continue; |
2118 | #endif |
2119 | |
2120 | /* Check IP address */ |
2121 | switch (hba->ip_cmp_method) |
2122 | { |
2123 | case ipCmpMask: |
2124 | if (hba->hostname) |
2125 | { |
2126 | if (!check_hostname(port, |
2127 | hba->hostname)) |
2128 | continue; |
2129 | } |
2130 | else |
2131 | { |
2132 | if (!check_ip(&port->raddr, |
2133 | (struct sockaddr *) &hba->addr, |
2134 | (struct sockaddr *) &hba->mask)) |
2135 | continue; |
2136 | } |
2137 | break; |
2138 | case ipCmpAll: |
2139 | break; |
2140 | case ipCmpSameHost: |
2141 | case ipCmpSameNet: |
2142 | if (!check_same_host_or_net(&port->raddr, |
2143 | hba->ip_cmp_method)) |
2144 | continue; |
2145 | break; |
2146 | default: |
2147 | /* shouldn't get here, but deem it no-match if so */ |
2148 | continue; |
2149 | } |
2150 | } /* != ctLocal */ |
2151 | |
2152 | /* Check database and role */ |
2153 | if (!check_db(port->database_name, port->user_name, roleid, |
2154 | hba->databases)) |
2155 | continue; |
2156 | |
2157 | if (!check_role(port->user_name, roleid, hba->roles)) |
2158 | continue; |
2159 | |
2160 | /* Found a record that matched! */ |
2161 | port->hba = hba; |
2162 | return; |
2163 | } |
2164 | |
2165 | /* If no matching entry was found, then implicitly reject. */ |
2166 | hba = palloc0(sizeof(HbaLine)); |
2167 | hba->auth_method = uaImplicitReject; |
2168 | port->hba = hba; |
2169 | } |
2170 | |
2171 | /* |
2172 | * Read the config file and create a List of HbaLine records for the contents. |
2173 | * |
2174 | * The configuration is read into a temporary list, and if any parse error |
2175 | * occurs the old list is kept in place and false is returned. Only if the |
2176 | * whole file parses OK is the list replaced, and the function returns true. |
2177 | * |
2178 | * On a false result, caller will take care of reporting a FATAL error in case |
2179 | * this is the initial startup. If it happens on reload, we just keep running |
2180 | * with the old data. |
2181 | */ |
2182 | bool |
2183 | load_hba(void) |
2184 | { |
2185 | FILE *file; |
2186 | List *hba_lines = NIL; |
2187 | ListCell *line; |
2188 | List *new_parsed_lines = NIL; |
2189 | bool ok = true; |
2190 | MemoryContext linecxt; |
2191 | MemoryContext oldcxt; |
2192 | MemoryContext hbacxt; |
2193 | |
2194 | file = AllocateFile(HbaFileName, "r" ); |
2195 | if (file == NULL) |
2196 | { |
2197 | ereport(LOG, |
2198 | (errcode_for_file_access(), |
2199 | errmsg("could not open configuration file \"%s\": %m" , |
2200 | HbaFileName))); |
2201 | return false; |
2202 | } |
2203 | |
2204 | linecxt = tokenize_file(HbaFileName, file, &hba_lines, LOG); |
2205 | FreeFile(file); |
2206 | |
2207 | /* Now parse all the lines */ |
2208 | Assert(PostmasterContext); |
2209 | hbacxt = AllocSetContextCreate(PostmasterContext, |
2210 | "hba parser context" , |
2211 | ALLOCSET_SMALL_SIZES); |
2212 | oldcxt = MemoryContextSwitchTo(hbacxt); |
2213 | foreach(line, hba_lines) |
2214 | { |
2215 | TokenizedLine *tok_line = (TokenizedLine *) lfirst(line); |
2216 | HbaLine *newline; |
2217 | |
2218 | /* don't parse lines that already have errors */ |
2219 | if (tok_line->err_msg != NULL) |
2220 | { |
2221 | ok = false; |
2222 | continue; |
2223 | } |
2224 | |
2225 | if ((newline = parse_hba_line(tok_line, LOG)) == NULL) |
2226 | { |
2227 | /* Parse error; remember there's trouble */ |
2228 | ok = false; |
2229 | |
2230 | /* |
2231 | * Keep parsing the rest of the file so we can report errors on |
2232 | * more than the first line. Error has already been logged, no |
2233 | * need for more chatter here. |
2234 | */ |
2235 | continue; |
2236 | } |
2237 | |
2238 | new_parsed_lines = lappend(new_parsed_lines, newline); |
2239 | } |
2240 | |
2241 | /* |
2242 | * A valid HBA file must have at least one entry; else there's no way to |
2243 | * connect to the postmaster. But only complain about this if we didn't |
2244 | * already have parsing errors. |
2245 | */ |
2246 | if (ok && new_parsed_lines == NIL) |
2247 | { |
2248 | ereport(LOG, |
2249 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
2250 | errmsg("configuration file \"%s\" contains no entries" , |
2251 | HbaFileName))); |
2252 | ok = false; |
2253 | } |
2254 | |
2255 | /* Free tokenizer memory */ |
2256 | MemoryContextDelete(linecxt); |
2257 | MemoryContextSwitchTo(oldcxt); |
2258 | |
2259 | if (!ok) |
2260 | { |
2261 | /* File contained one or more errors, so bail out */ |
2262 | MemoryContextDelete(hbacxt); |
2263 | return false; |
2264 | } |
2265 | |
2266 | /* Loaded new file successfully, replace the one we use */ |
2267 | if (parsed_hba_context != NULL) |
2268 | MemoryContextDelete(parsed_hba_context); |
2269 | parsed_hba_context = hbacxt; |
2270 | parsed_hba_lines = new_parsed_lines; |
2271 | |
2272 | return true; |
2273 | } |
2274 | |
2275 | /* |
2276 | * This macro specifies the maximum number of authentication options |
2277 | * that are possible with any given authentication method that is supported. |
2278 | * Currently LDAP supports 11, and there are 3 that are not dependent on |
2279 | * the auth method here. It may not actually be possible to set all of them |
2280 | * at the same time, but we'll set the macro value high enough to be |
2281 | * conservative and avoid warnings from static analysis tools. |
2282 | */ |
2283 | #define MAX_HBA_OPTIONS 14 |
2284 | |
2285 | /* |
2286 | * Create a text array listing the options specified in the HBA line. |
2287 | * Return NULL if no options are specified. |
2288 | */ |
2289 | static ArrayType * |
2290 | gethba_options(HbaLine *hba) |
2291 | { |
2292 | int noptions; |
2293 | Datum options[MAX_HBA_OPTIONS]; |
2294 | |
2295 | noptions = 0; |
2296 | |
2297 | if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI) |
2298 | { |
2299 | if (hba->include_realm) |
2300 | options[noptions++] = |
2301 | CStringGetTextDatum("include_realm=true" ); |
2302 | |
2303 | if (hba->krb_realm) |
2304 | options[noptions++] = |
2305 | CStringGetTextDatum(psprintf("krb_realm=%s" , hba->krb_realm)); |
2306 | } |
2307 | |
2308 | if (hba->usermap) |
2309 | options[noptions++] = |
2310 | CStringGetTextDatum(psprintf("map=%s" , hba->usermap)); |
2311 | |
2312 | if (hba->clientcert != clientCertOff) |
2313 | options[noptions++] = |
2314 | CStringGetTextDatum(psprintf("clientcert=%s" , (hba->clientcert == clientCertCA) ? "verify-ca" : "verify-full" )); |
2315 | |
2316 | if (hba->pamservice) |
2317 | options[noptions++] = |
2318 | CStringGetTextDatum(psprintf("pamservice=%s" , hba->pamservice)); |
2319 | |
2320 | if (hba->auth_method == uaLDAP) |
2321 | { |
2322 | if (hba->ldapserver) |
2323 | options[noptions++] = |
2324 | CStringGetTextDatum(psprintf("ldapserver=%s" , hba->ldapserver)); |
2325 | |
2326 | if (hba->ldapport) |
2327 | options[noptions++] = |
2328 | CStringGetTextDatum(psprintf("ldapport=%d" , hba->ldapport)); |
2329 | |
2330 | if (hba->ldaptls) |
2331 | options[noptions++] = |
2332 | CStringGetTextDatum("ldaptls=true" ); |
2333 | |
2334 | if (hba->ldapprefix) |
2335 | options[noptions++] = |
2336 | CStringGetTextDatum(psprintf("ldapprefix=%s" , hba->ldapprefix)); |
2337 | |
2338 | if (hba->ldapsuffix) |
2339 | options[noptions++] = |
2340 | CStringGetTextDatum(psprintf("ldapsuffix=%s" , hba->ldapsuffix)); |
2341 | |
2342 | if (hba->ldapbasedn) |
2343 | options[noptions++] = |
2344 | CStringGetTextDatum(psprintf("ldapbasedn=%s" , hba->ldapbasedn)); |
2345 | |
2346 | if (hba->ldapbinddn) |
2347 | options[noptions++] = |
2348 | CStringGetTextDatum(psprintf("ldapbinddn=%s" , hba->ldapbinddn)); |
2349 | |
2350 | if (hba->ldapbindpasswd) |
2351 | options[noptions++] = |
2352 | CStringGetTextDatum(psprintf("ldapbindpasswd=%s" , |
2353 | hba->ldapbindpasswd)); |
2354 | |
2355 | if (hba->ldapsearchattribute) |
2356 | options[noptions++] = |
2357 | CStringGetTextDatum(psprintf("ldapsearchattribute=%s" , |
2358 | hba->ldapsearchattribute)); |
2359 | |
2360 | if (hba->ldapsearchfilter) |
2361 | options[noptions++] = |
2362 | CStringGetTextDatum(psprintf("ldapsearchfilter=%s" , |
2363 | hba->ldapsearchfilter)); |
2364 | |
2365 | if (hba->ldapscope) |
2366 | options[noptions++] = |
2367 | CStringGetTextDatum(psprintf("ldapscope=%d" , hba->ldapscope)); |
2368 | } |
2369 | |
2370 | if (hba->auth_method == uaRADIUS) |
2371 | { |
2372 | if (hba->radiusservers_s) |
2373 | options[noptions++] = |
2374 | CStringGetTextDatum(psprintf("radiusservers=%s" , hba->radiusservers_s)); |
2375 | |
2376 | if (hba->radiussecrets_s) |
2377 | options[noptions++] = |
2378 | CStringGetTextDatum(psprintf("radiussecrets=%s" , hba->radiussecrets_s)); |
2379 | |
2380 | if (hba->radiusidentifiers_s) |
2381 | options[noptions++] = |
2382 | CStringGetTextDatum(psprintf("radiusidentifiers=%s" , hba->radiusidentifiers_s)); |
2383 | |
2384 | if (hba->radiusports_s) |
2385 | options[noptions++] = |
2386 | CStringGetTextDatum(psprintf("radiusports=%s" , hba->radiusports_s)); |
2387 | } |
2388 | |
2389 | /* If you add more options, consider increasing MAX_HBA_OPTIONS. */ |
2390 | Assert(noptions <= MAX_HBA_OPTIONS); |
2391 | |
2392 | if (noptions > 0) |
2393 | return construct_array(options, noptions, TEXTOID, -1, false, 'i'); |
2394 | else |
2395 | return NULL; |
2396 | } |
2397 | |
2398 | /* Number of columns in pg_hba_file_rules view */ |
2399 | #define NUM_PG_HBA_FILE_RULES_ATTS 9 |
2400 | |
2401 | /* |
2402 | * fill_hba_line: build one row of pg_hba_file_rules view, add it to tuplestore |
2403 | * |
2404 | * tuple_store: where to store data |
2405 | * tupdesc: tuple descriptor for the view |
2406 | * lineno: pg_hba.conf line number (must always be valid) |
2407 | * hba: parsed line data (can be NULL, in which case err_msg should be set) |
2408 | * err_msg: error message (NULL if none) |
2409 | * |
2410 | * Note: leaks memory, but we don't care since this is run in a short-lived |
2411 | * memory context. |
2412 | */ |
2413 | static void |
2414 | fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, |
2415 | int lineno, HbaLine *hba, const char *err_msg) |
2416 | { |
2417 | Datum values[NUM_PG_HBA_FILE_RULES_ATTS]; |
2418 | bool nulls[NUM_PG_HBA_FILE_RULES_ATTS]; |
2419 | char buffer[NI_MAXHOST]; |
2420 | HeapTuple tuple; |
2421 | int index; |
2422 | ListCell *lc; |
2423 | const char *typestr; |
2424 | const char *addrstr; |
2425 | const char *maskstr; |
2426 | ArrayType *options; |
2427 | |
2428 | Assert(tupdesc->natts == NUM_PG_HBA_FILE_RULES_ATTS); |
2429 | |
2430 | memset(values, 0, sizeof(values)); |
2431 | memset(nulls, 0, sizeof(nulls)); |
2432 | index = 0; |
2433 | |
2434 | /* line_number */ |
2435 | values[index++] = Int32GetDatum(lineno); |
2436 | |
2437 | if (hba != NULL) |
2438 | { |
2439 | /* type */ |
2440 | /* Avoid a default: case so compiler will warn about missing cases */ |
2441 | typestr = NULL; |
2442 | switch (hba->conntype) |
2443 | { |
2444 | case ctLocal: |
2445 | typestr = "local" ; |
2446 | break; |
2447 | case ctHost: |
2448 | typestr = "host" ; |
2449 | break; |
2450 | case ctHostSSL: |
2451 | typestr = "hostssl" ; |
2452 | break; |
2453 | case ctHostNoSSL: |
2454 | typestr = "hostnossl" ; |
2455 | break; |
2456 | case ctHostGSS: |
2457 | typestr = "hostgssenc" ; |
2458 | break; |
2459 | case ctHostNoGSS: |
2460 | typestr = "hostnogssenc" ; |
2461 | break; |
2462 | } |
2463 | if (typestr) |
2464 | values[index++] = CStringGetTextDatum(typestr); |
2465 | else |
2466 | nulls[index++] = true; |
2467 | |
2468 | /* database */ |
2469 | if (hba->databases) |
2470 | { |
2471 | /* |
2472 | * Flatten HbaToken list to string list. It might seem that we |
2473 | * should re-quote any quoted tokens, but that has been rejected |
2474 | * on the grounds that it makes it harder to compare the array |
2475 | * elements to other system catalogs. That makes entries like |
2476 | * "all" or "samerole" formally ambiguous ... but users who name |
2477 | * databases/roles that way are inflicting their own pain. |
2478 | */ |
2479 | List *names = NIL; |
2480 | |
2481 | foreach(lc, hba->databases) |
2482 | { |
2483 | HbaToken *tok = lfirst(lc); |
2484 | |
2485 | names = lappend(names, tok->string); |
2486 | } |
2487 | values[index++] = PointerGetDatum(strlist_to_textarray(names)); |
2488 | } |
2489 | else |
2490 | nulls[index++] = true; |
2491 | |
2492 | /* user */ |
2493 | if (hba->roles) |
2494 | { |
2495 | /* Flatten HbaToken list to string list; see comment above */ |
2496 | List *roles = NIL; |
2497 | |
2498 | foreach(lc, hba->roles) |
2499 | { |
2500 | HbaToken *tok = lfirst(lc); |
2501 | |
2502 | roles = lappend(roles, tok->string); |
2503 | } |
2504 | values[index++] = PointerGetDatum(strlist_to_textarray(roles)); |
2505 | } |
2506 | else |
2507 | nulls[index++] = true; |
2508 | |
2509 | /* address and netmask */ |
2510 | /* Avoid a default: case so compiler will warn about missing cases */ |
2511 | addrstr = maskstr = NULL; |
2512 | switch (hba->ip_cmp_method) |
2513 | { |
2514 | case ipCmpMask: |
2515 | if (hba->hostname) |
2516 | { |
2517 | addrstr = hba->hostname; |
2518 | } |
2519 | else |
2520 | { |
2521 | if (pg_getnameinfo_all(&hba->addr, sizeof(hba->addr), |
2522 | buffer, sizeof(buffer), |
2523 | NULL, 0, |
2524 | NI_NUMERICHOST) == 0) |
2525 | { |
2526 | clean_ipv6_addr(hba->addr.ss_family, buffer); |
2527 | addrstr = pstrdup(buffer); |
2528 | } |
2529 | if (pg_getnameinfo_all(&hba->mask, sizeof(hba->mask), |
2530 | buffer, sizeof(buffer), |
2531 | NULL, 0, |
2532 | NI_NUMERICHOST) == 0) |
2533 | { |
2534 | clean_ipv6_addr(hba->mask.ss_family, buffer); |
2535 | maskstr = pstrdup(buffer); |
2536 | } |
2537 | } |
2538 | break; |
2539 | case ipCmpAll: |
2540 | addrstr = "all" ; |
2541 | break; |
2542 | case ipCmpSameHost: |
2543 | addrstr = "samehost" ; |
2544 | break; |
2545 | case ipCmpSameNet: |
2546 | addrstr = "samenet" ; |
2547 | break; |
2548 | } |
2549 | if (addrstr) |
2550 | values[index++] = CStringGetTextDatum(addrstr); |
2551 | else |
2552 | nulls[index++] = true; |
2553 | if (maskstr) |
2554 | values[index++] = CStringGetTextDatum(maskstr); |
2555 | else |
2556 | nulls[index++] = true; |
2557 | |
2558 | /* |
2559 | * Make sure UserAuthName[] tracks additions to the UserAuth enum |
2560 | */ |
2561 | StaticAssertStmt(lengthof(UserAuthName) == USER_AUTH_LAST + 1, |
2562 | "UserAuthName[] must match the UserAuth enum" ); |
2563 | |
2564 | /* auth_method */ |
2565 | values[index++] = CStringGetTextDatum(UserAuthName[hba->auth_method]); |
2566 | |
2567 | /* options */ |
2568 | options = gethba_options(hba); |
2569 | if (options) |
2570 | values[index++] = PointerGetDatum(options); |
2571 | else |
2572 | nulls[index++] = true; |
2573 | } |
2574 | else |
2575 | { |
2576 | /* no parsing result, so set relevant fields to nulls */ |
2577 | memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool)); |
2578 | } |
2579 | |
2580 | /* error */ |
2581 | if (err_msg) |
2582 | values[NUM_PG_HBA_FILE_RULES_ATTS - 1] = CStringGetTextDatum(err_msg); |
2583 | else |
2584 | nulls[NUM_PG_HBA_FILE_RULES_ATTS - 1] = true; |
2585 | |
2586 | tuple = heap_form_tuple(tupdesc, values, nulls); |
2587 | tuplestore_puttuple(tuple_store, tuple); |
2588 | } |
2589 | |
2590 | /* |
2591 | * Read the pg_hba.conf file and fill the tuplestore with view records. |
2592 | */ |
2593 | static void |
2594 | fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) |
2595 | { |
2596 | FILE *file; |
2597 | List *hba_lines = NIL; |
2598 | ListCell *line; |
2599 | MemoryContext linecxt; |
2600 | MemoryContext hbacxt; |
2601 | MemoryContext oldcxt; |
2602 | |
2603 | /* |
2604 | * In the unlikely event that we can't open pg_hba.conf, we throw an |
2605 | * error, rather than trying to report it via some sort of view entry. |
2606 | * (Most other error conditions should result in a message in a view |
2607 | * entry.) |
2608 | */ |
2609 | file = AllocateFile(HbaFileName, "r" ); |
2610 | if (file == NULL) |
2611 | ereport(ERROR, |
2612 | (errcode_for_file_access(), |
2613 | errmsg("could not open configuration file \"%s\": %m" , |
2614 | HbaFileName))); |
2615 | |
2616 | linecxt = tokenize_file(HbaFileName, file, &hba_lines, DEBUG3); |
2617 | FreeFile(file); |
2618 | |
2619 | /* Now parse all the lines */ |
2620 | hbacxt = AllocSetContextCreate(CurrentMemoryContext, |
2621 | "hba parser context" , |
2622 | ALLOCSET_SMALL_SIZES); |
2623 | oldcxt = MemoryContextSwitchTo(hbacxt); |
2624 | foreach(line, hba_lines) |
2625 | { |
2626 | TokenizedLine *tok_line = (TokenizedLine *) lfirst(line); |
2627 | HbaLine *hbaline = NULL; |
2628 | |
2629 | /* don't parse lines that already have errors */ |
2630 | if (tok_line->err_msg == NULL) |
2631 | hbaline = parse_hba_line(tok_line, DEBUG3); |
2632 | |
2633 | fill_hba_line(tuple_store, tupdesc, tok_line->line_num, |
2634 | hbaline, tok_line->err_msg); |
2635 | } |
2636 | |
2637 | /* Free tokenizer memory */ |
2638 | MemoryContextDelete(linecxt); |
2639 | /* Free parse_hba_line memory */ |
2640 | MemoryContextSwitchTo(oldcxt); |
2641 | MemoryContextDelete(hbacxt); |
2642 | } |
2643 | |
2644 | /* |
2645 | * SQL-accessible SRF to return all the entries in the pg_hba.conf file. |
2646 | */ |
2647 | Datum |
2648 | pg_hba_file_rules(PG_FUNCTION_ARGS) |
2649 | { |
2650 | Tuplestorestate *tuple_store; |
2651 | TupleDesc tupdesc; |
2652 | MemoryContext old_cxt; |
2653 | ReturnSetInfo *rsi; |
2654 | |
2655 | /* |
2656 | * We must use the Materialize mode to be safe against HBA file changes |
2657 | * while the cursor is open. It's also more efficient than having to look |
2658 | * up our current position in the parsed list every time. |
2659 | */ |
2660 | rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
2661 | |
2662 | /* Check to see if caller supports us returning a tuplestore */ |
2663 | if (rsi == NULL || !IsA(rsi, ReturnSetInfo)) |
2664 | ereport(ERROR, |
2665 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2666 | errmsg("set-valued function called in context that cannot accept a set" ))); |
2667 | if (!(rsi->allowedModes & SFRM_Materialize)) |
2668 | ereport(ERROR, |
2669 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
2670 | errmsg("materialize mode required, but it is not " \ |
2671 | "allowed in this context" ))); |
2672 | |
2673 | rsi->returnMode = SFRM_Materialize; |
2674 | |
2675 | /* Build a tuple descriptor for our result type */ |
2676 | if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) |
2677 | elog(ERROR, "return type must be a row type" ); |
2678 | |
2679 | /* Build tuplestore to hold the result rows */ |
2680 | old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); |
2681 | |
2682 | tuple_store = |
2683 | tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, |
2684 | false, work_mem); |
2685 | rsi->setDesc = tupdesc; |
2686 | rsi->setResult = tuple_store; |
2687 | |
2688 | MemoryContextSwitchTo(old_cxt); |
2689 | |
2690 | /* Fill the tuplestore */ |
2691 | fill_hba_view(tuple_store, tupdesc); |
2692 | |
2693 | PG_RETURN_NULL(); |
2694 | } |
2695 | |
2696 | |
2697 | /* |
2698 | * Parse one tokenised line from the ident config file and store the result in |
2699 | * an IdentLine structure. |
2700 | * |
2701 | * If parsing fails, log a message and return NULL. |
2702 | * |
2703 | * If ident_user is a regular expression (ie. begins with a slash), it is |
2704 | * compiled and stored in IdentLine structure. |
2705 | * |
2706 | * Note: this function leaks memory when an error occurs. Caller is expected |
2707 | * to have set a memory context that will be reset if this function returns |
2708 | * NULL. |
2709 | */ |
2710 | static IdentLine * |
2711 | parse_ident_line(TokenizedLine *tok_line) |
2712 | { |
2713 | int line_num = tok_line->line_num; |
2714 | ListCell *field; |
2715 | List *tokens; |
2716 | HbaToken *token; |
2717 | IdentLine *parsedline; |
2718 | |
2719 | Assert(tok_line->fields != NIL); |
2720 | field = list_head(tok_line->fields); |
2721 | |
2722 | parsedline = palloc0(sizeof(IdentLine)); |
2723 | parsedline->linenumber = line_num; |
2724 | |
2725 | /* Get the map token (must exist) */ |
2726 | tokens = lfirst(field); |
2727 | IDENT_MULTI_VALUE(tokens); |
2728 | token = linitial(tokens); |
2729 | parsedline->usermap = pstrdup(token->string); |
2730 | |
2731 | /* Get the ident user token */ |
2732 | field = lnext(field); |
2733 | IDENT_FIELD_ABSENT(field); |
2734 | tokens = lfirst(field); |
2735 | IDENT_MULTI_VALUE(tokens); |
2736 | token = linitial(tokens); |
2737 | parsedline->ident_user = pstrdup(token->string); |
2738 | |
2739 | /* Get the PG rolename token */ |
2740 | field = lnext(field); |
2741 | IDENT_FIELD_ABSENT(field); |
2742 | tokens = lfirst(field); |
2743 | IDENT_MULTI_VALUE(tokens); |
2744 | token = linitial(tokens); |
2745 | parsedline->pg_role = pstrdup(token->string); |
2746 | |
2747 | if (parsedline->ident_user[0] == '/') |
2748 | { |
2749 | /* |
2750 | * When system username starts with a slash, treat it as a regular |
2751 | * expression. Pre-compile it. |
2752 | */ |
2753 | int r; |
2754 | pg_wchar *wstr; |
2755 | int wlen; |
2756 | |
2757 | wstr = palloc((strlen(parsedline->ident_user + 1) + 1) * sizeof(pg_wchar)); |
2758 | wlen = pg_mb2wchar_with_len(parsedline->ident_user + 1, |
2759 | wstr, strlen(parsedline->ident_user + 1)); |
2760 | |
2761 | r = pg_regcomp(&parsedline->re, wstr, wlen, REG_ADVANCED, C_COLLATION_OID); |
2762 | if (r) |
2763 | { |
2764 | char errstr[100]; |
2765 | |
2766 | pg_regerror(r, &parsedline->re, errstr, sizeof(errstr)); |
2767 | ereport(LOG, |
2768 | (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), |
2769 | errmsg("invalid regular expression \"%s\": %s" , |
2770 | parsedline->ident_user + 1, errstr))); |
2771 | |
2772 | pfree(wstr); |
2773 | return NULL; |
2774 | } |
2775 | pfree(wstr); |
2776 | } |
2777 | |
2778 | return parsedline; |
2779 | } |
2780 | |
2781 | /* |
2782 | * Process one line from the parsed ident config lines. |
2783 | * |
2784 | * Compare input parsed ident line to the needed map, pg_role and ident_user. |
2785 | * *found_p and *error_p are set according to our results. |
2786 | */ |
2787 | static void |
2788 | check_ident_usermap(IdentLine *identLine, const char *usermap_name, |
2789 | const char *pg_role, const char *ident_user, |
2790 | bool case_insensitive, bool *found_p, bool *error_p) |
2791 | { |
2792 | *found_p = false; |
2793 | *error_p = false; |
2794 | |
2795 | if (strcmp(identLine->usermap, usermap_name) != 0) |
2796 | /* Line does not match the map name we're looking for, so just abort */ |
2797 | return; |
2798 | |
2799 | /* Match? */ |
2800 | if (identLine->ident_user[0] == '/') |
2801 | { |
2802 | /* |
2803 | * When system username starts with a slash, treat it as a regular |
2804 | * expression. In this case, we process the system username as a |
2805 | * regular expression that returns exactly one match. This is replaced |
2806 | * for \1 in the database username string, if present. |
2807 | */ |
2808 | int r; |
2809 | regmatch_t matches[2]; |
2810 | pg_wchar *wstr; |
2811 | int wlen; |
2812 | char *ofs; |
2813 | char *regexp_pgrole; |
2814 | |
2815 | wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar)); |
2816 | wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user)); |
2817 | |
2818 | r = pg_regexec(&identLine->re, wstr, wlen, 0, NULL, 2, matches, 0); |
2819 | if (r) |
2820 | { |
2821 | char errstr[100]; |
2822 | |
2823 | if (r != REG_NOMATCH) |
2824 | { |
2825 | /* REG_NOMATCH is not an error, everything else is */ |
2826 | pg_regerror(r, &identLine->re, errstr, sizeof(errstr)); |
2827 | ereport(LOG, |
2828 | (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), |
2829 | errmsg("regular expression match for \"%s\" failed: %s" , |
2830 | identLine->ident_user + 1, errstr))); |
2831 | *error_p = true; |
2832 | } |
2833 | |
2834 | pfree(wstr); |
2835 | return; |
2836 | } |
2837 | pfree(wstr); |
2838 | |
2839 | if ((ofs = strstr(identLine->pg_role, "\\1" )) != NULL) |
2840 | { |
2841 | int offset; |
2842 | |
2843 | /* substitution of the first argument requested */ |
2844 | if (matches[1].rm_so < 0) |
2845 | { |
2846 | ereport(LOG, |
2847 | (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), |
2848 | errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"" , |
2849 | identLine->ident_user + 1, identLine->pg_role))); |
2850 | *error_p = true; |
2851 | return; |
2852 | } |
2853 | |
2854 | /* |
2855 | * length: original length minus length of \1 plus length of match |
2856 | * plus null terminator |
2857 | */ |
2858 | regexp_pgrole = palloc0(strlen(identLine->pg_role) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1); |
2859 | offset = ofs - identLine->pg_role; |
2860 | memcpy(regexp_pgrole, identLine->pg_role, offset); |
2861 | memcpy(regexp_pgrole + offset, |
2862 | ident_user + matches[1].rm_so, |
2863 | matches[1].rm_eo - matches[1].rm_so); |
2864 | strcat(regexp_pgrole, ofs + 2); |
2865 | } |
2866 | else |
2867 | { |
2868 | /* no substitution, so copy the match */ |
2869 | regexp_pgrole = pstrdup(identLine->pg_role); |
2870 | } |
2871 | |
2872 | /* |
2873 | * now check if the username actually matched what the user is trying |
2874 | * to connect as |
2875 | */ |
2876 | if (case_insensitive) |
2877 | { |
2878 | if (pg_strcasecmp(regexp_pgrole, pg_role) == 0) |
2879 | *found_p = true; |
2880 | } |
2881 | else |
2882 | { |
2883 | if (strcmp(regexp_pgrole, pg_role) == 0) |
2884 | *found_p = true; |
2885 | } |
2886 | pfree(regexp_pgrole); |
2887 | |
2888 | return; |
2889 | } |
2890 | else |
2891 | { |
2892 | /* Not regular expression, so make complete match */ |
2893 | if (case_insensitive) |
2894 | { |
2895 | if (pg_strcasecmp(identLine->pg_role, pg_role) == 0 && |
2896 | pg_strcasecmp(identLine->ident_user, ident_user) == 0) |
2897 | *found_p = true; |
2898 | } |
2899 | else |
2900 | { |
2901 | if (strcmp(identLine->pg_role, pg_role) == 0 && |
2902 | strcmp(identLine->ident_user, ident_user) == 0) |
2903 | *found_p = true; |
2904 | } |
2905 | } |
2906 | return; |
2907 | } |
2908 | |
2909 | |
2910 | /* |
2911 | * Scan the (pre-parsed) ident usermap file line by line, looking for a match |
2912 | * |
2913 | * See if the user with ident username "auth_user" is allowed to act |
2914 | * as Postgres user "pg_role" according to usermap "usermap_name". |
2915 | * |
2916 | * Special case: Usermap NULL, equivalent to what was previously called |
2917 | * "sameuser" or "samerole", means don't look in the usermap file. |
2918 | * That's an implied map wherein "pg_role" must be identical to |
2919 | * "auth_user" in order to be authorized. |
2920 | * |
2921 | * Iff authorized, return STATUS_OK, otherwise return STATUS_ERROR. |
2922 | */ |
2923 | int |
2924 | check_usermap(const char *usermap_name, |
2925 | const char *pg_role, |
2926 | const char *auth_user, |
2927 | bool case_insensitive) |
2928 | { |
2929 | bool found_entry = false, |
2930 | error = false; |
2931 | |
2932 | if (usermap_name == NULL || usermap_name[0] == '\0') |
2933 | { |
2934 | if (case_insensitive) |
2935 | { |
2936 | if (pg_strcasecmp(pg_role, auth_user) == 0) |
2937 | return STATUS_OK; |
2938 | } |
2939 | else |
2940 | { |
2941 | if (strcmp(pg_role, auth_user) == 0) |
2942 | return STATUS_OK; |
2943 | } |
2944 | ereport(LOG, |
2945 | (errmsg("provided user name (%s) and authenticated user name (%s) do not match" , |
2946 | pg_role, auth_user))); |
2947 | return STATUS_ERROR; |
2948 | } |
2949 | else |
2950 | { |
2951 | ListCell *line_cell; |
2952 | |
2953 | foreach(line_cell, parsed_ident_lines) |
2954 | { |
2955 | check_ident_usermap(lfirst(line_cell), usermap_name, |
2956 | pg_role, auth_user, case_insensitive, |
2957 | &found_entry, &error); |
2958 | if (found_entry || error) |
2959 | break; |
2960 | } |
2961 | } |
2962 | if (!found_entry && !error) |
2963 | { |
2964 | ereport(LOG, |
2965 | (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"" , |
2966 | usermap_name, pg_role, auth_user))); |
2967 | } |
2968 | return found_entry ? STATUS_OK : STATUS_ERROR; |
2969 | } |
2970 | |
2971 | |
2972 | /* |
2973 | * Read the ident config file and create a List of IdentLine records for |
2974 | * the contents. |
2975 | * |
2976 | * This works the same as load_hba(), but for the user config file. |
2977 | */ |
2978 | bool |
2979 | load_ident(void) |
2980 | { |
2981 | FILE *file; |
2982 | List *ident_lines = NIL; |
2983 | ListCell *line_cell, |
2984 | *parsed_line_cell; |
2985 | List *new_parsed_lines = NIL; |
2986 | bool ok = true; |
2987 | MemoryContext linecxt; |
2988 | MemoryContext oldcxt; |
2989 | MemoryContext ident_context; |
2990 | IdentLine *newline; |
2991 | |
2992 | file = AllocateFile(IdentFileName, "r" ); |
2993 | if (file == NULL) |
2994 | { |
2995 | /* not fatal ... we just won't do any special ident maps */ |
2996 | ereport(LOG, |
2997 | (errcode_for_file_access(), |
2998 | errmsg("could not open usermap file \"%s\": %m" , |
2999 | IdentFileName))); |
3000 | return false; |
3001 | } |
3002 | |
3003 | linecxt = tokenize_file(IdentFileName, file, &ident_lines, LOG); |
3004 | FreeFile(file); |
3005 | |
3006 | /* Now parse all the lines */ |
3007 | Assert(PostmasterContext); |
3008 | ident_context = AllocSetContextCreate(PostmasterContext, |
3009 | "ident parser context" , |
3010 | ALLOCSET_SMALL_SIZES); |
3011 | oldcxt = MemoryContextSwitchTo(ident_context); |
3012 | foreach(line_cell, ident_lines) |
3013 | { |
3014 | TokenizedLine *tok_line = (TokenizedLine *) lfirst(line_cell); |
3015 | |
3016 | /* don't parse lines that already have errors */ |
3017 | if (tok_line->err_msg != NULL) |
3018 | { |
3019 | ok = false; |
3020 | continue; |
3021 | } |
3022 | |
3023 | if ((newline = parse_ident_line(tok_line)) == NULL) |
3024 | { |
3025 | /* Parse error; remember there's trouble */ |
3026 | ok = false; |
3027 | |
3028 | /* |
3029 | * Keep parsing the rest of the file so we can report errors on |
3030 | * more than the first line. Error has already been logged, no |
3031 | * need for more chatter here. |
3032 | */ |
3033 | continue; |
3034 | } |
3035 | |
3036 | new_parsed_lines = lappend(new_parsed_lines, newline); |
3037 | } |
3038 | |
3039 | /* Free tokenizer memory */ |
3040 | MemoryContextDelete(linecxt); |
3041 | MemoryContextSwitchTo(oldcxt); |
3042 | |
3043 | if (!ok) |
3044 | { |
3045 | /* |
3046 | * File contained one or more errors, so bail out, first being careful |
3047 | * to clean up whatever we allocated. Most stuff will go away via |
3048 | * MemoryContextDelete, but we have to clean up regexes explicitly. |
3049 | */ |
3050 | foreach(parsed_line_cell, new_parsed_lines) |
3051 | { |
3052 | newline = (IdentLine *) lfirst(parsed_line_cell); |
3053 | if (newline->ident_user[0] == '/') |
3054 | pg_regfree(&newline->re); |
3055 | } |
3056 | MemoryContextDelete(ident_context); |
3057 | return false; |
3058 | } |
3059 | |
3060 | /* Loaded new file successfully, replace the one we use */ |
3061 | if (parsed_ident_lines != NIL) |
3062 | { |
3063 | foreach(parsed_line_cell, parsed_ident_lines) |
3064 | { |
3065 | newline = (IdentLine *) lfirst(parsed_line_cell); |
3066 | if (newline->ident_user[0] == '/') |
3067 | pg_regfree(&newline->re); |
3068 | } |
3069 | } |
3070 | if (parsed_ident_context != NULL) |
3071 | MemoryContextDelete(parsed_ident_context); |
3072 | |
3073 | parsed_ident_context = ident_context; |
3074 | parsed_ident_lines = new_parsed_lines; |
3075 | |
3076 | return true; |
3077 | } |
3078 | |
3079 | |
3080 | |
3081 | /* |
3082 | * Determine what authentication method should be used when accessing database |
3083 | * "database" from frontend "raddr", user "user". Return the method and |
3084 | * an optional argument (stored in fields of *port), and STATUS_OK. |
3085 | * |
3086 | * If the file does not contain any entry matching the request, we return |
3087 | * method = uaImplicitReject. |
3088 | */ |
3089 | void |
3090 | hba_getauthmethod(hbaPort *port) |
3091 | { |
3092 | check_hba(port); |
3093 | } |
3094 | |