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