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