Coverage Report

Created: 2026-08-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/libpq/hba.c
Line
Count
Source
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
0
#define token_has_regexp(t) (t->regex != NULL)
70
0
#define token_is_member_check(t)  (!t->quoted && t->string[0] == '+')
71
0
#define token_is_keyword(t, k)  (!t->quoted && strcmp(t->string, k) == 0)
72
0
#define token_matches(t, k)  (strcmp(t->string, k) == 0)
73
0
#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
141
pg_isblank(const char c)
142
0
{
143
  /* don't accept non-ASCII data */
144
0
  return (!IS_HIGHBIT_SET(c) && isblank(c));
145
0
}
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
183
next_token(char **lineptr, StringInfo buf,
184
       bool *initial_quote, bool *terminating_comma)
185
0
{
186
0
  int     c;
187
0
  bool    in_quote = false;
188
0
  bool    was_quote = false;
189
0
  bool    saw_quote = false;
190
191
  /* Initialize output parameters */
192
0
  resetStringInfo(buf);
193
0
  *initial_quote = false;
194
0
  *terminating_comma = false;
195
196
  /* Move over any whitespace and commas preceding the next token */
197
0
  while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
198
0
    ;
199
200
  /*
201
   * Build a token in buf of next characters up to EOL, unquoted comma, or
202
   * unquoted whitespace.
203
   */
204
0
  while (c != '\0' &&
205
0
       (!pg_isblank(c) || in_quote))
206
0
  {
207
    /* skip comments to EOL */
208
0
    if (c == '#' && !in_quote)
209
0
    {
210
0
      while ((c = (*(*lineptr)++)) != '\0')
211
0
        ;
212
0
      break;
213
0
    }
214
215
    /* we do not pass back a terminating comma in the token */
216
0
    if (c == ',' && !in_quote)
217
0
    {
218
0
      *terminating_comma = true;
219
0
      break;
220
0
    }
221
222
0
    if (c != '"' || was_quote)
223
0
      appendStringInfoChar(buf, c);
224
225
    /* Literal double-quote is two double-quotes */
226
0
    if (in_quote && c == '"')
227
0
      was_quote = !was_quote;
228
0
    else
229
0
      was_quote = false;
230
231
0
    if (c == '"')
232
0
    {
233
0
      in_quote = !in_quote;
234
0
      saw_quote = true;
235
0
      if (buf->len == 0)
236
0
        *initial_quote = true;
237
0
    }
238
239
0
    c = *(*lineptr)++;
240
0
  }
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
0
  (*lineptr)--;
247
248
0
  return (saw_quote || buf->len > 0);
249
0
}
250
251
/*
252
 * Construct a palloc'd AuthToken struct, copying the given string.
253
 */
254
static AuthToken *
255
make_auth_token(const char *token, bool quoted)
256
0
{
257
0
  AuthToken  *authtoken;
258
0
  int     toklen;
259
260
0
  toklen = strlen(token);
261
  /* we copy string into same palloc block as the struct */
262
0
  authtoken = (AuthToken *) palloc0(sizeof(AuthToken) + toklen + 1);
263
0
  authtoken->string = (char *) authtoken + sizeof(AuthToken);
264
0
  authtoken->quoted = quoted;
265
0
  authtoken->regex = NULL;
266
0
  memcpy(authtoken->string, token, toklen + 1);
267
268
0
  return authtoken;
269
0
}
270
271
/*
272
 * Free an AuthToken, that may include a regular expression that needs
273
 * to be cleaned up explicitly.
274
 */
275
static void
276
free_auth_token(AuthToken *token)
277
0
{
278
0
  if (token_has_regexp(token))
279
0
    pg_regfree(token->regex);
280
0
}
281
282
/*
283
 * Copy a AuthToken struct into freshly palloc'd memory.
284
 */
285
static AuthToken *
286
copy_auth_token(AuthToken *in)
287
0
{
288
0
  AuthToken  *out = make_auth_token(in->string, in->quoted);
289
290
0
  return out;
291
0
}
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
299
regcomp_auth_token(AuthToken *token, char *filename, int line_num,
300
           char **err_msg, int elevel)
301
0
{
302
0
  pg_wchar   *wstr;
303
0
  int     wlen;
304
0
  int     rc;
305
306
0
  Assert(token->regex == NULL);
307
308
0
  if (token->string[0] != '/')
309
0
    return 0;       /* nothing to compile */
310
311
0
  token->regex = palloc0_object(regex_t);
312
0
  wstr = palloc((strlen(token->string + 1) + 1) * sizeof(pg_wchar));
313
0
  wlen = pg_mb2wchar_with_len(token->string + 1,
314
0
                wstr, strlen(token->string + 1));
315
316
0
  rc = pg_regcomp(token->regex, wstr, wlen, REG_ADVANCED, C_COLLATION_OID);
317
318
0
  if (rc)
319
0
  {
320
0
    char    errstr[100];
321
322
0
    pg_regerror(rc, token->regex, errstr, sizeof(errstr));
323
0
    ereport(elevel,
324
0
        (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
325
0
         errmsg("invalid regular expression \"%s\": %s",
326
0
            token->string + 1, errstr),
327
0
         errcontext("line %d of configuration file \"%s\"",
328
0
              line_num, filename)));
329
330
0
    *err_msg = psprintf("invalid regular expression \"%s\": %s",
331
0
              token->string + 1, errstr);
332
0
  }
333
334
0
  pfree(wstr);
335
0
  return rc;
336
0
}
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
regexec_auth_token(const char *match, AuthToken *token, size_t nmatch,
345
           regmatch_t pmatch[])
346
0
{
347
0
  pg_wchar   *wmatchstr;
348
0
  int     wmatchlen;
349
0
  int     r;
350
351
0
  Assert(token->string[0] == '/' && token->regex);
352
353
0
  wmatchstr = palloc((strlen(match) + 1) * sizeof(pg_wchar));
354
0
  wmatchlen = pg_mb2wchar_with_len(match, wmatchstr, strlen(match));
355
356
0
  r = pg_regexec(token->regex, wmatchstr, wmatchlen, 0, NULL, nmatch, pmatch, 0);
357
358
0
  pfree(wmatchstr);
359
0
  return r;
360
0
}
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 *
377
next_field_expand(const char *filename, char **lineptr,
378
          int elevel, int depth, char **err_msg)
379
0
{
380
0
  StringInfoData buf;
381
0
  bool    trailing_comma;
382
0
  bool    initial_quote;
383
0
  List     *tokens = NIL;
384
385
0
  initStringInfo(&buf);
386
387
0
  do
388
0
  {
389
0
    if (!next_token(lineptr, &buf,
390
0
            &initial_quote, &trailing_comma))
391
0
      break;
392
393
    /* Is this referencing a file? */
394
0
    if (!initial_quote && buf.len > 1 && buf.data[0] == '@')
395
0
      tokens = tokenize_expand_file(tokens, filename, buf.data + 1,
396
0
                      elevel, depth + 1, err_msg);
397
0
    else
398
0
    {
399
0
      MemoryContext oldcxt;
400
401
      /*
402
       * lappend() may do its own allocations, so move to the context
403
       * for the list of tokens.
404
       */
405
0
      oldcxt = MemoryContextSwitchTo(tokenize_context);
406
0
      tokens = lappend(tokens, make_auth_token(buf.data, initial_quote));
407
0
      MemoryContextSwitchTo(oldcxt);
408
0
    }
409
0
  } while (trailing_comma && (*err_msg == NULL));
410
411
0
  pfree(buf.data);
412
413
0
  return tokens;
414
0
}
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
436
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
0
{
444
0
  char     *inc_fullname;
445
0
  FILE     *inc_file;
446
447
0
  inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
448
0
  inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
449
450
0
  if (!inc_file)
451
0
  {
452
0
    if (errno == ENOENT && missing_ok)
453
0
    {
454
0
      ereport(elevel,
455
0
          (errmsg("skipping missing authentication file \"%s\"",
456
0
              inc_fullname)));
457
0
      *err_msg = NULL;
458
0
      pfree(inc_fullname);
459
0
      return;
460
0
    }
461
462
    /* error in err_msg, so leave and report */
463
0
    pfree(inc_fullname);
464
0
    Assert(err_msg);
465
0
    return;
466
0
  }
467
468
0
  tokenize_auth_file(inc_fullname, inc_file, tok_lines, elevel,
469
0
             depth);
470
0
  free_auth_file(inc_file, depth);
471
0
  pfree(inc_fullname);
472
0
}
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
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
0
{
498
0
  char     *inc_fullname;
499
0
  FILE     *inc_file;
500
0
  List     *inc_lines = NIL;
501
0
  ListCell   *inc_line;
502
503
0
  inc_fullname = AbsoluteConfigLocation(inc_filename, outer_filename);
504
0
  inc_file = open_auth_file(inc_fullname, elevel, depth, err_msg);
505
506
0
  if (inc_file == NULL)
507
0
  {
508
    /* error already logged */
509
0
    pfree(inc_fullname);
510
0
    return tokens;
511
0
  }
512
513
  /*
514
   * There is possible recursion here if the file contains @ or an include
515
   * record.
516
   */
517
0
  tokenize_auth_file(inc_fullname, inc_file, &inc_lines, elevel,
518
0
             depth);
519
520
0
  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
   */
526
0
  foreach(inc_line, inc_lines)
527
0
  {
528
0
    TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(inc_line);
529
0
    ListCell   *inc_field;
530
531
    /* If any line has an error, propagate that up to caller */
532
0
    if (tok_line->err_msg)
533
0
    {
534
0
      *err_msg = pstrdup(tok_line->err_msg);
535
0
      break;
536
0
    }
537
538
0
    foreach(inc_field, tok_line->fields)
539
0
    {
540
0
      List     *inc_tokens = lfirst(inc_field);
541
0
      ListCell   *inc_token;
542
543
0
      foreach(inc_token, inc_tokens)
544
0
      {
545
0
        AuthToken  *token = lfirst(inc_token);
546
0
        MemoryContext oldcxt;
547
548
        /*
549
         * lappend() may do its own allocations, so move to the
550
         * context for the list of tokens.
551
         */
552
0
        oldcxt = MemoryContextSwitchTo(tokenize_context);
553
0
        tokens = lappend(tokens, token);
554
0
        MemoryContextSwitchTo(oldcxt);
555
0
      }
556
0
    }
557
0
  }
558
559
0
  free_auth_file(inc_file, depth);
560
0
  return tokens;
561
0
}
562
563
/*
564
 * free_auth_file
565
 *    Free a file opened by open_auth_file().
566
 */
567
void
568
free_auth_file(FILE *file, int depth)
569
0
{
570
0
  FreeFile(file);
571
572
  /* If this is the last cleanup, remove the tokenization context */
573
0
  if (depth == CONF_FILE_START_DEPTH)
574
0
  {
575
0
    MemoryContextDelete(tokenize_context);
576
0
    tokenize_context = NULL;
577
0
  }
578
0
}
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 *
593
open_auth_file(const char *filename, int elevel, int depth,
594
         char **err_msg)
595
0
{
596
0
  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
   */
603
0
  if (depth > CONF_FILE_MAX_DEPTH)
604
0
  {
605
0
    ereport(elevel,
606
0
        (errcode_for_file_access(),
607
0
         errmsg("could not open file \"%s\": maximum nesting depth exceeded",
608
0
            filename)));
609
0
    if (err_msg)
610
0
      *err_msg = psprintf("could not open file \"%s\": maximum nesting depth exceeded",
611
0
                filename);
612
0
    return NULL;
613
0
  }
614
615
0
  file = AllocateFile(filename, "r");
616
0
  if (file == NULL)
617
0
  {
618
0
    int     save_errno = errno;
619
620
0
    ereport(elevel,
621
0
        (errcode_for_file_access(),
622
0
         errmsg("could not open file \"%s\": %m",
623
0
            filename)));
624
0
    if (err_msg)
625
0
    {
626
0
      errno = save_errno;
627
0
      *err_msg = psprintf("could not open file \"%s\": %m",
628
0
                filename);
629
0
    }
630
    /* the caller may care about some specific errno */
631
0
    errno = save_errno;
632
0
    return NULL;
633
0
  }
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
   */
640
0
  if (depth == CONF_FILE_START_DEPTH)
641
0
  {
642
    /*
643
     * A context may be present, but assume that it has been eliminated
644
     * already.
645
     */
646
0
    tokenize_context = AllocSetContextCreate(CurrentMemoryContext,
647
0
                         "tokenize_context",
648
0
                         ALLOCSET_START_SMALL_SIZES);
649
0
  }
650
651
0
  return file;
652
0
}
653
654
/*
655
 * error context callback for tokenize_auth_file()
656
 */
657
static void
658
tokenize_error_callback(void *arg)
659
0
{
660
0
  tokenize_error_callback_arg *callback_arg = (tokenize_error_callback_arg *) arg;
661
662
0
  errcontext("line %d of configuration file \"%s\"",
663
0
         callback_arg->linenum, callback_arg->filename);
664
0
}
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
687
tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
688
           int elevel, int depth)
689
0
{
690
0
  int     line_number = 1;
691
0
  StringInfoData buf;
692
0
  MemoryContext linecxt;
693
0
  MemoryContext funccxt;    /* context of this function's caller */
694
0
  ErrorContextCallback tokenerrcontext;
695
0
  tokenize_error_callback_arg callback_arg;
696
697
0
  Assert(tokenize_context);
698
699
0
  callback_arg.filename = filename;
700
0
  callback_arg.linenum = line_number;
701
702
0
  tokenerrcontext.callback = tokenize_error_callback;
703
0
  tokenerrcontext.arg = &callback_arg;
704
0
  tokenerrcontext.previous = error_context_stack;
705
0
  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
   */
711
0
  linecxt = AllocSetContextCreate(CurrentMemoryContext,
712
0
                  "tokenize_auth_file",
713
0
                  ALLOCSET_SMALL_SIZES);
714
0
  funccxt = MemoryContextSwitchTo(linecxt);
715
716
0
  initStringInfo(&buf);
717
718
0
  if (depth == CONF_FILE_START_DEPTH)
719
0
    *tok_lines = NIL;
720
721
0
  while (!feof(file) && !ferror(file))
722
0
  {
723
0
    TokenizedAuthLine *tok_line;
724
0
    MemoryContext oldcxt;
725
0
    char     *lineptr;
726
0
    List     *current_line = NIL;
727
0
    char     *err_msg = NULL;
728
0
    int     last_backslash_buflen = 0;
729
0
    int     continuations = 0;
730
731
    /* Collect the next input line, handling backslash continuations */
732
0
    resetStringInfo(&buf);
733
734
0
    while (pg_get_line_append(file, &buf, NULL))
735
0
    {
736
      /* Strip trailing newline, including \r in case we're on Windows */
737
0
      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
0
      if (buf.len > last_backslash_buflen &&
745
0
        buf.data[buf.len - 1] == '\\')
746
0
      {
747
        /* Continuation, so strip it and keep reading */
748
0
        buf.data[--buf.len] = '\0';
749
0
        last_backslash_buflen = buf.len;
750
0
        continuations++;
751
0
        continue;
752
0
      }
753
754
      /* Nope, so we have the whole line */
755
0
      break;
756
0
    }
757
758
0
    if (ferror(file))
759
0
    {
760
      /* I/O error! */
761
0
      int     save_errno = errno;
762
763
0
      ereport(elevel,
764
0
          (errcode_for_file_access(),
765
0
           errmsg("could not read file \"%s\": %m", filename)));
766
0
      errno = save_errno;
767
0
      err_msg = psprintf("could not read file \"%s\": %m",
768
0
                 filename);
769
0
      break;
770
0
    }
771
772
    /* Parse fields */
773
0
    lineptr = buf.data;
774
0
    while (*lineptr && err_msg == NULL)
775
0
    {
776
0
      List     *current_field;
777
778
0
      current_field = next_field_expand(filename, &lineptr,
779
0
                        elevel, depth, &err_msg);
780
      /* add field to line, unless we are at EOL or comment start */
781
0
      if (current_field != NIL)
782
0
      {
783
        /*
784
         * lappend() may do its own allocations, so move to the
785
         * context for the list of tokens.
786
         */
787
0
        oldcxt = MemoryContextSwitchTo(tokenize_context);
788
0
        current_line = lappend(current_line, current_field);
789
0
        MemoryContextSwitchTo(oldcxt);
790
0
      }
791
0
    }
792
793
    /*
794
     * Reached EOL; no need to emit line to TokenizedAuthLine list if it's
795
     * boring.
796
     */
797
0
    if (current_line == NIL && err_msg == NULL)
798
0
      goto next_line;
799
800
    /* If the line is valid, check if that's an include directive */
801
0
    if (err_msg == NULL && list_length(current_line) == 2)
802
0
    {
803
0
      AuthToken  *first,
804
0
             *second;
805
806
0
      first = linitial(linitial_node(List, current_line));
807
0
      second = linitial(lsecond_node(List, current_line));
808
809
0
      if (strcmp(first->string, "include") == 0)
810
0
      {
811
0
        tokenize_include_file(filename, second->string, tok_lines,
812
0
                    elevel, depth + 1, false, &err_msg);
813
814
0
        if (err_msg)
815
0
          goto process_line;
816
817
        /*
818
         * tokenize_auth_file() has taken care of creating the
819
         * TokenizedAuthLines.
820
         */
821
0
        goto next_line;
822
0
      }
823
0
      else if (strcmp(first->string, "include_dir") == 0)
824
0
      {
825
0
        char    **filenames;
826
0
        char     *dir_name = second->string;
827
0
        int     num_filenames;
828
0
        StringInfoData err_buf;
829
830
0
        filenames = GetConfFilesInDir(dir_name, filename, elevel,
831
0
                        &num_filenames, &err_msg);
832
833
0
        if (!filenames)
834
0
        {
835
          /* the error is in err_msg, so create an entry */
836
0
          goto process_line;
837
0
        }
838
839
0
        initStringInfo(&err_buf);
840
0
        for (int i = 0; i < num_filenames; i++)
841
0
        {
842
0
          tokenize_include_file(filename, filenames[i], tok_lines,
843
0
                      elevel, depth + 1, false, &err_msg);
844
          /* cumulate errors if any */
845
0
          if (err_msg)
846
0
          {
847
0
            if (err_buf.len > 0)
848
0
              appendStringInfoChar(&err_buf, '\n');
849
0
            appendStringInfoString(&err_buf, err_msg);
850
0
          }
851
0
        }
852
853
        /* clean up things */
854
0
        for (int i = 0; i < num_filenames; i++)
855
0
          pfree(filenames[i]);
856
0
        pfree(filenames);
857
858
        /*
859
         * If there were no errors, the line is fully processed,
860
         * bypass the general TokenizedAuthLine processing.
861
         */
862
0
        if (err_buf.len == 0)
863
0
          goto next_line;
864
865
        /* Otherwise, process the cumulated errors, if any. */
866
0
        err_msg = err_buf.data;
867
0
        goto process_line;
868
0
      }
869
0
      else if (strcmp(first->string, "include_if_exists") == 0)
870
0
      {
871
872
0
        tokenize_include_file(filename, second->string, tok_lines,
873
0
                    elevel, depth + 1, true, &err_msg);
874
0
        if (err_msg)
875
0
          goto process_line;
876
877
        /*
878
         * tokenize_auth_file() has taken care of creating the
879
         * TokenizedAuthLines.
880
         */
881
0
        goto next_line;
882
0
      }
883
0
    }
884
885
0
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
0
    oldcxt = MemoryContextSwitchTo(tokenize_context);
893
0
    tok_line = palloc0_object(TokenizedAuthLine);
894
0
    tok_line->fields = current_line;
895
0
    tok_line->file_name = pstrdup(filename);
896
0
    tok_line->line_num = line_number;
897
0
    tok_line->raw_line = pstrdup(buf.data);
898
0
    tok_line->err_msg = err_msg ? pstrdup(err_msg) : NULL;
899
0
    *tok_lines = lappend(*tok_lines, tok_line);
900
0
    MemoryContextSwitchTo(oldcxt);
901
902
0
next_line:
903
0
    line_number += continuations + 1;
904
0
    callback_arg.linenum = line_number;
905
0
  }
906
907
0
  MemoryContextSwitchTo(funccxt);
908
0
  MemoryContextDelete(linecxt);
909
910
0
  error_context_stack = tokenerrcontext.previous;
911
0
}
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
921
is_member(Oid userid, const char *role)
922
0
{
923
0
  Oid     roleid;
924
925
0
  if (!OidIsValid(userid))
926
0
    return false;     /* if user not exist, say "no" */
927
928
0
  roleid = get_role_oid(role, true);
929
930
0
  if (!OidIsValid(roleid))
931
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
   */
938
0
  return is_member_of_role_nosuper(userid, roleid);
939
0
}
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
950
check_role(const char *role, Oid roleid, List *tokens, bool case_insensitive)
951
0
{
952
0
  ListCell   *cell;
953
0
  AuthToken  *tok;
954
955
0
  foreach(cell, tokens)
956
0
  {
957
0
    tok = lfirst(cell);
958
0
    if (token_is_member_check(tok))
959
0
    {
960
0
      if (is_member(roleid, tok->string + 1))
961
0
        return true;
962
0
    }
963
0
    else if (token_is_keyword(tok, "all"))
964
0
      return true;
965
0
    else if (token_has_regexp(tok))
966
0
    {
967
0
      if (regexec_auth_token(role, tok, 0, NULL) == REG_OKAY)
968
0
        return true;
969
0
    }
970
0
    else if (case_insensitive)
971
0
    {
972
0
      if (token_matches_insensitive(tok, role))
973
0
        return true;
974
0
    }
975
0
    else if (token_matches(tok, role))
976
0
      return true;
977
0
  }
978
0
  return false;
979
0
}
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
989
check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
990
0
{
991
0
  ListCell   *cell;
992
0
  AuthToken  *tok;
993
994
0
  foreach(cell, tokens)
995
0
  {
996
0
    tok = lfirst(cell);
997
0
    if (am_walsender && !am_db_walsender)
998
0
    {
999
      /*
1000
       * physical replication walsender connections can only match
1001
       * replication keyword
1002
       */
1003
0
      if (token_is_keyword(tok, "replication"))
1004
0
        return true;
1005
0
    }
1006
0
    else if (token_is_keyword(tok, "all"))
1007
0
      return true;
1008
0
    else if (token_is_keyword(tok, "sameuser"))
1009
0
    {
1010
0
      if (strcmp(dbname, role) == 0)
1011
0
        return true;
1012
0
    }
1013
0
    else if (token_is_keyword(tok, "samegroup") ||
1014
0
         token_is_keyword(tok, "samerole"))
1015
0
    {
1016
0
      if (is_member(roleid, dbname))
1017
0
        return true;
1018
0
    }
1019
0
    else if (token_is_keyword(tok, "replication"))
1020
0
      continue;     /* never match this if not walsender */
1021
0
    else if (token_has_regexp(tok))
1022
0
    {
1023
0
      if (regexec_auth_token(dbname, tok, 0, NULL) == REG_OKAY)
1024
0
        return true;
1025
0
    }
1026
0
    else if (token_matches(tok, dbname))
1027
0
      return true;
1028
0
  }
1029
0
  return false;
1030
0
}
1031
1032
static bool
1033
ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
1034
0
{
1035
0
  return (a->sin_addr.s_addr == b->sin_addr.s_addr);
1036
0
}
1037
1038
static bool
1039
ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
1040
0
{
1041
0
  int     i;
1042
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
0
}
1049
1050
/*
1051
 * Check whether host name matches pattern.
1052
 */
1053
static bool
1054
hostname_match(const char *pattern, const char *actual_hostname)
1055
0
{
1056
0
  if (pattern[0] == '.')   /* suffix match */
1057
0
  {
1058
0
    size_t    plen = strlen(pattern);
1059
0
    size_t    hlen = strlen(actual_hostname);
1060
1061
0
    if (hlen < plen)
1062
0
      return false;
1063
1064
0
    return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
1065
0
  }
1066
0
  else
1067
0
    return (pg_strcasecmp(pattern, actual_hostname) == 0);
1068
0
}
1069
1070
/*
1071
 * Check to see if a connecting IP matches a given host name.
1072
 */
1073
static bool
1074
check_hostname(Port *port, const char *hostname)
1075
0
{
1076
0
  struct addrinfo *gai_result,
1077
0
         *gai;
1078
0
  int     ret;
1079
0
  bool    found;
1080
1081
  /* Quick out if remote host name already known bad */
1082
0
  if (port->remote_hostname_resolv < 0)
1083
0
    return false;
1084
1085
  /* Lookup remote host name if not already done */
1086
0
  if (!port->remote_hostname)
1087
0
  {
1088
0
    char    remote_hostname[NI_MAXHOST];
1089
1090
0
    ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
1091
0
                 remote_hostname, sizeof(remote_hostname),
1092
0
                 NULL, 0,
1093
0
                 NI_NAMEREQD);
1094
0
    if (ret != 0)
1095
0
    {
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;
1099
0
      return false;
1100
0
    }
1101
1102
0
    port->remote_hostname = pstrdup(remote_hostname);
1103
0
  }
1104
1105
  /* Now see if remote host name matches this pg_hba line */
1106
0
  if (!hostname_match(hostname, port->remote_hostname))
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
0
  {
1117
    /* remember failure; don't complain in the postmaster log yet */
1118
0
    port->remote_hostname_resolv = -2;
1119
0
    port->remote_hostname_errcode = ret;
1120
0
    return false;
1121
0
  }
1122
1123
0
  found = false;
1124
0
  for (gai = gai_result; gai; gai = gai->ai_next)
1125
0
  {
1126
0
    if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
1127
0
    {
1128
0
      if (gai->ai_addr->sa_family == AF_INET)
1129
0
      {
1130
0
        if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
1131
0
               (struct sockaddr_in *) &port->raddr.addr))
1132
0
        {
1133
0
          found = true;
1134
0
          break;
1135
0
        }
1136
0
      }
1137
0
      else if (gai->ai_addr->sa_family == AF_INET6)
1138
0
      {
1139
0
        if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
1140
0
               (struct sockaddr_in6 *) &port->raddr.addr))
1141
0
        {
1142
0
          found = true;
1143
0
          break;
1144
0
        }
1145
0
      }
1146
0
    }
1147
0
  }
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
0
       hostname);
1155
1156
0
  port->remote_hostname_resolv = found ? +1 : -1;
1157
1158
0
  return found;
1159
0
}
1160
1161
/*
1162
 * Check to see if a connecting IP matches the given address and netmask.
1163
 */
1164
static bool
1165
check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
1166
0
{
1167
0
  if (raddr->addr.ss_family == addr->sa_family &&
1168
0
    pg_range_sockaddr(&raddr->addr,
1169
0
              (struct sockaddr_storage *) addr,
1170
0
              (struct sockaddr_storage *) mask))
1171
0
    return true;
1172
0
  return false;
1173
0
}
1174
1175
/*
1176
 * pg_foreach_ifaddr callback: does client addr match this machine interface?
1177
 */
1178
static void
1179
check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
1180
             void *cb_data)
1181
0
{
1182
0
  check_network_data *cn = (check_network_data *) cb_data;
1183
0
  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
0
  {
1191
    /* Make an all-ones netmask of appropriate length for family */
1192
0
    pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
1193
0
    cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) &mask);
1194
0
  }
1195
0
  else
1196
0
  {
1197
    /* Use the netmask of the interface itself */
1198
0
    cn->result = check_ip(cn->raddr, addr, netmask);
1199
0
  }
1200
0
}
1201
1202
/*
1203
 * Use pg_foreach_ifaddr to check a samehost or samenet match
1204
 */
1205
static bool
1206
check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
1207
{
1208
  check_network_data cn;
1209
1210
  cn.method = method;
1211
  cn.raddr = raddr;
1212
  cn.result = false;
1213
1214
  errno = 0;
1215
  if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
1216
  {
1217
    ereport(LOG,
1218
        (errmsg("error enumerating network interfaces: %m")));
1219
    return false;
1220
  }
1221
1222
  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
0
#define INVALID_AUTH_OPTION(optname, validmethods) \
1241
0
do { \
1242
0
  ereport(elevel, \
1243
0
      (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1244
0
       /* translator: the second %s is a list of auth methods */ \
1245
0
       errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
1246
0
          optname, _(validmethods)), \
1247
0
       errcontext("line %d of configuration file \"%s\"", \
1248
0
          line_num, file_name))); \
1249
0
  *err_msg = psprintf("authentication option \"%s\" is only valid for authentication methods %s", \
1250
0
            optname, validmethods); \
1251
0
  return false; \
1252
0
} while (0)
1253
1254
0
#define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) \
1255
0
do { \
1256
0
  if (hbaline->auth_method != methodval) \
1257
0
    INVALID_AUTH_OPTION(optname, validmethods); \
1258
0
} while (0)
1259
1260
0
#define MANDATORY_AUTH_ARG(argvar, argname, authname) \
1261
0
do { \
1262
0
  if (argvar == NULL) { \
1263
0
    ereport(elevel, \
1264
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1265
0
         errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
1266
0
            authname, argname), \
1267
0
         errcontext("line %d of configuration file \"%s\"", \
1268
0
            line_num, file_name))); \
1269
0
    *err_msg = psprintf("authentication method \"%s\" requires argument \"%s\" to be set", \
1270
0
              authname, argname); \
1271
0
    return NULL; \
1272
0
  } \
1273
0
} 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
0
#define IDENT_FIELD_ABSENT(field) \
1285
0
do { \
1286
0
  if (!field) { \
1287
0
    ereport(elevel, \
1288
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1289
0
         errmsg("missing entry at end of line"), \
1290
0
         errcontext("line %d of configuration file \"%s\"", \
1291
0
              line_num, file_name))); \
1292
0
    *err_msg = pstrdup("missing entry at end of line"); \
1293
0
    return NULL; \
1294
0
  } \
1295
0
} while (0)
1296
1297
0
#define IDENT_MULTI_VALUE(tokens) \
1298
0
do { \
1299
0
  if (tokens->length > 1) { \
1300
0
    ereport(elevel, \
1301
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR), \
1302
0
         errmsg("multiple values in ident field"), \
1303
0
         errcontext("line %d of configuration file \"%s\"", \
1304
0
              line_num, file_name))); \
1305
0
    *err_msg = pstrdup("multiple values in ident field"); \
1306
0
    return NULL; \
1307
0
  } \
1308
0
} 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 *
1324
parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
1325
0
{
1326
0
  int     line_num = tok_line->line_num;
1327
0
  char     *file_name = tok_line->file_name;
1328
0
  char    **err_msg = &tok_line->err_msg;
1329
0
  char     *str;
1330
0
  struct addrinfo *gai_result;
1331
0
  struct addrinfo hints;
1332
0
  int     ret;
1333
0
  char     *cidr_slash;
1334
0
  char     *unsupauth;
1335
0
  ListCell   *field;
1336
0
  List     *tokens;
1337
0
  ListCell   *tokencell;
1338
0
  AuthToken  *token;
1339
0
  HbaLine    *parsedline;
1340
1341
0
  parsedline = palloc0_object(HbaLine);
1342
0
  parsedline->sourcefile = pstrdup(file_name);
1343
0
  parsedline->linenumber = line_num;
1344
0
  parsedline->rawline = pstrdup(tok_line->raw_line);
1345
1346
  /* Check the record type. */
1347
0
  Assert(tok_line->fields != NIL);
1348
0
  field = list_head(tok_line->fields);
1349
0
  tokens = lfirst(field);
1350
0
  if (tokens->length > 1)
1351
0
  {
1352
0
    ereport(elevel,
1353
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1354
0
         errmsg("multiple values specified for connection type"),
1355
0
         errhint("Specify exactly one connection type per line."),
1356
0
         errcontext("line %d of configuration file \"%s\"",
1357
0
              line_num, file_name)));
1358
0
    *err_msg = "multiple values specified for connection type";
1359
0
    return NULL;
1360
0
  }
1361
0
  token = linitial(tokens);
1362
0
  if (strcmp(token->string, "local") == 0)
1363
0
  {
1364
0
    parsedline->conntype = ctLocal;
1365
0
  }
1366
0
  else if (strcmp(token->string, "host") == 0 ||
1367
0
       strcmp(token->string, "hostssl") == 0 ||
1368
0
       strcmp(token->string, "hostnossl") == 0 ||
1369
0
       strcmp(token->string, "hostgssenc") == 0 ||
1370
0
       strcmp(token->string, "hostnogssenc") == 0)
1371
0
  {
1372
1373
0
    if (token->string[4] == 's') /* "hostssl" */
1374
0
    {
1375
0
      parsedline->conntype = ctHostSSL;
1376
      /* Log a warning if SSL support is not active */
1377
#ifdef USE_SSL
1378
      if (!EnableSSL)
1379
      {
1380
        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
        *err_msg = "hostssl record cannot match because SSL is disabled";
1387
      }
1388
#else
1389
0
      ereport(elevel,
1390
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
1391
0
           errmsg("hostssl record cannot match because SSL is not supported by this build"),
1392
0
           errcontext("line %d of configuration file \"%s\"",
1393
0
                line_num, file_name)));
1394
0
      *err_msg = "hostssl record cannot match because SSL is not supported by this build";
1395
0
#endif
1396
0
    }
1397
0
    else if (token->string[4] == 'g') /* "hostgssenc" */
1398
0
    {
1399
0
      parsedline->conntype = ctHostGSS;
1400
0
#ifndef ENABLE_GSS
1401
0
      ereport(elevel,
1402
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
1403
0
           errmsg("hostgssenc record cannot match because GSSAPI is not supported by this build"),
1404
0
           errcontext("line %d of configuration file \"%s\"",
1405
0
                line_num, file_name)));
1406
0
      *err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build";
1407
0
#endif
1408
0
    }
1409
0
    else if (token->string[4] == 'n' && token->string[6] == 's')
1410
0
      parsedline->conntype = ctHostNoSSL;
1411
0
    else if (token->string[4] == 'n' && token->string[6] == 'g')
1412
0
      parsedline->conntype = ctHostNoGSS;
1413
0
    else
1414
0
    {
1415
      /* "host" */
1416
0
      parsedline->conntype = ctHost;
1417
0
    }
1418
0
  }             /* record type */
1419
0
  else
1420
0
  {
1421
0
    ereport(elevel,
1422
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1423
0
         errmsg("invalid connection type \"%s\"",
1424
0
            token->string),
1425
0
         errcontext("line %d of configuration file \"%s\"",
1426
0
              line_num, file_name)));
1427
0
    *err_msg = psprintf("invalid connection type \"%s\"", token->string);
1428
0
    return NULL;
1429
0
  }
1430
1431
  /* Get the databases. */
1432
0
  field = lnext(tok_line->fields, field);
1433
0
  if (!field)
1434
0
  {
1435
0
    ereport(elevel,
1436
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1437
0
         errmsg("end-of-line before database specification"),
1438
0
         errcontext("line %d of configuration file \"%s\"",
1439
0
              line_num, file_name)));
1440
0
    *err_msg = "end-of-line before database specification";
1441
0
    return NULL;
1442
0
  }
1443
0
  parsedline->databases = NIL;
1444
0
  tokens = lfirst(field);
1445
0
  foreach(tokencell, tokens)
1446
0
  {
1447
0
    AuthToken  *tok = copy_auth_token(lfirst(tokencell));
1448
1449
    /* Compile a regexp for the database token, if necessary */
1450
0
    if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
1451
0
      return NULL;
1452
1453
0
    parsedline->databases = lappend(parsedline->databases, tok);
1454
0
  }
1455
1456
  /* Get the roles. */
1457
0
  field = lnext(tok_line->fields, field);
1458
0
  if (!field)
1459
0
  {
1460
0
    ereport(elevel,
1461
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1462
0
         errmsg("end-of-line before role specification"),
1463
0
         errcontext("line %d of configuration file \"%s\"",
1464
0
              line_num, file_name)));
1465
0
    *err_msg = "end-of-line before role specification";
1466
0
    return NULL;
1467
0
  }
1468
0
  parsedline->roles = NIL;
1469
0
  tokens = lfirst(field);
1470
0
  foreach(tokencell, tokens)
1471
0
  {
1472
0
    AuthToken  *tok = copy_auth_token(lfirst(tokencell));
1473
1474
    /* Compile a regexp from the role token, if necessary */
1475
0
    if (regcomp_auth_token(tok, file_name, line_num, err_msg, elevel))
1476
0
      return NULL;
1477
1478
0
    parsedline->roles = lappend(parsedline->roles, tok);
1479
0
  }
1480
1481
0
  if (parsedline->conntype != ctLocal)
1482
0
  {
1483
    /* Read the IP address field. (with or without CIDR netmask) */
1484
0
    field = lnext(tok_line->fields, field);
1485
0
    if (!field)
1486
0
    {
1487
0
      ereport(elevel,
1488
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
1489
0
           errmsg("end-of-line before IP address specification"),
1490
0
           errcontext("line %d of configuration file \"%s\"",
1491
0
                line_num, file_name)));
1492
0
      *err_msg = "end-of-line before IP address specification";
1493
0
      return NULL;
1494
0
    }
1495
0
    tokens = lfirst(field);
1496
0
    if (tokens->length > 1)
1497
0
    {
1498
0
      ereport(elevel,
1499
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
1500
0
           errmsg("multiple values specified for host address"),
1501
0
           errhint("Specify one address range per line."),
1502
0
           errcontext("line %d of configuration file \"%s\"",
1503
0
                line_num, file_name)));
1504
0
      *err_msg = "multiple values specified for host address";
1505
0
      return NULL;
1506
0
    }
1507
0
    token = linitial(tokens);
1508
1509
0
    if (token_is_keyword(token, "all"))
1510
0
    {
1511
0
      parsedline->ip_cmp_method = ipCmpAll;
1512
0
    }
1513
0
    else if (token_is_keyword(token, "samehost"))
1514
0
    {
1515
      /* Any IP on this host is allowed to connect */
1516
0
      parsedline->ip_cmp_method = ipCmpSameHost;
1517
0
    }
1518
0
    else if (token_is_keyword(token, "samenet"))
1519
0
    {
1520
      /* Any IP on the host's subnets is allowed to connect */
1521
0
      parsedline->ip_cmp_method = ipCmpSameNet;
1522
0
    }
1523
0
    else
1524
0
    {
1525
      /* IP and netmask are specified */
1526
0
      parsedline->ip_cmp_method = ipCmpMask;
1527
1528
      /* need a modifiable copy of token */
1529
0
      str = pstrdup(token->string);
1530
1531
      /* Check if it has a CIDR suffix and if so isolate it */
1532
0
      cidr_slash = strchr(str, '/');
1533
0
      if (cidr_slash)
1534
0
        *cidr_slash = '\0';
1535
1536
      /* Get the IP address either way */
1537
0
      hints.ai_flags = AI_NUMERICHOST;
1538
0
      hints.ai_family = AF_UNSPEC;
1539
0
      hints.ai_socktype = 0;
1540
0
      hints.ai_protocol = 0;
1541
0
      hints.ai_addrlen = 0;
1542
0
      hints.ai_canonname = NULL;
1543
0
      hints.ai_addr = NULL;
1544
0
      hints.ai_next = NULL;
1545
1546
0
      ret = pg_getaddrinfo_all(str, NULL, &hints, &gai_result);
1547
0
      if (ret == 0 && gai_result)
1548
0
      {
1549
0
        memcpy(&parsedline->addr, gai_result->ai_addr,
1550
0
             gai_result->ai_addrlen);
1551
0
        parsedline->addrlen = gai_result->ai_addrlen;
1552
0
      }
1553
0
      else if (ret == EAI_NONAME)
1554
0
        parsedline->hostname = str;
1555
0
      else
1556
0
      {
1557
0
        ereport(elevel,
1558
0
            (errcode(ERRCODE_CONFIG_FILE_ERROR),
1559
0
             errmsg("invalid IP address \"%s\": %s",
1560
0
                str, gai_strerror(ret)),
1561
0
             errcontext("line %d of configuration file \"%s\"",
1562
0
                  line_num, file_name)));
1563
0
        *err_msg = psprintf("invalid IP address \"%s\": %s",
1564
0
                  str, gai_strerror(ret));
1565
0
        if (gai_result)
1566
0
          pg_freeaddrinfo_all(hints.ai_family, gai_result);
1567
0
        return NULL;
1568
0
      }
1569
1570
0
      pg_freeaddrinfo_all(hints.ai_family, gai_result);
1571
1572
      /* Get the netmask */
1573
0
      if (cidr_slash)
1574
0
      {
1575
0
        if (parsedline->hostname)
1576
0
        {
1577
0
          ereport(elevel,
1578
0
              (errcode(ERRCODE_CONFIG_FILE_ERROR),
1579
0
               errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
1580
0
                  token->string),
1581
0
               errcontext("line %d of configuration file \"%s\"",
1582
0
                    line_num, file_name)));
1583
0
          *err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
1584
0
                    token->string);
1585
0
          return NULL;
1586
0
        }
1587
1588
0
        if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
1589
0
                      parsedline->addr.ss_family) < 0)
1590
0
        {
1591
0
          ereport(elevel,
1592
0
              (errcode(ERRCODE_CONFIG_FILE_ERROR),
1593
0
               errmsg("invalid CIDR mask in address \"%s\"",
1594
0
                  token->string),
1595
0
               errcontext("line %d of configuration file \"%s\"",
1596
0
                    line_num, file_name)));
1597
0
          *err_msg = psprintf("invalid CIDR mask in address \"%s\"",
1598
0
                    token->string);
1599
0
          return NULL;
1600
0
        }
1601
0
        parsedline->masklen = parsedline->addrlen;
1602
0
        pfree(str);
1603
0
      }
1604
0
      else if (!parsedline->hostname)
1605
0
      {
1606
        /* Read the mask field. */
1607
0
        pfree(str);
1608
0
        field = lnext(tok_line->fields, field);
1609
0
        if (!field)
1610
0
        {
1611
0
          ereport(elevel,
1612
0
              (errcode(ERRCODE_CONFIG_FILE_ERROR),
1613
0
               errmsg("end-of-line before netmask specification"),
1614
0
               errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
1615
0
               errcontext("line %d of configuration file \"%s\"",
1616
0
                    line_num, file_name)));
1617
0
          *err_msg = "end-of-line before netmask specification";
1618
0
          return NULL;
1619
0
        }
1620
0
        tokens = lfirst(field);
1621
0
        if (tokens->length > 1)
1622
0
        {
1623
0
          ereport(elevel,
1624
0
              (errcode(ERRCODE_CONFIG_FILE_ERROR),
1625
0
               errmsg("multiple values specified for netmask"),
1626
0
               errcontext("line %d of configuration file \"%s\"",
1627
0
                    line_num, file_name)));
1628
0
          *err_msg = "multiple values specified for netmask";
1629
0
          return NULL;
1630
0
        }
1631
0
        token = linitial(tokens);
1632
1633
0
        ret = pg_getaddrinfo_all(token->string, NULL,
1634
0
                     &hints, &gai_result);
1635
0
        if (ret || !gai_result)
1636
0
        {
1637
0
          ereport(elevel,
1638
0
              (errcode(ERRCODE_CONFIG_FILE_ERROR),
1639
0
               errmsg("invalid IP mask \"%s\": %s",
1640
0
                  token->string, gai_strerror(ret)),
1641
0
               errcontext("line %d of configuration file \"%s\"",
1642
0
                    line_num, file_name)));
1643
0
          *err_msg = psprintf("invalid IP mask \"%s\": %s",
1644
0
                    token->string, gai_strerror(ret));
1645
0
          if (gai_result)
1646
0
            pg_freeaddrinfo_all(hints.ai_family, gai_result);
1647
0
          return NULL;
1648
0
        }
1649
1650
0
        memcpy(&parsedline->mask, gai_result->ai_addr,
1651
0
             gai_result->ai_addrlen);
1652
0
        parsedline->masklen = gai_result->ai_addrlen;
1653
0
        pg_freeaddrinfo_all(hints.ai_family, gai_result);
1654
1655
0
        if (parsedline->addr.ss_family != parsedline->mask.ss_family)
1656
0
        {
1657
0
          ereport(elevel,
1658
0
              (errcode(ERRCODE_CONFIG_FILE_ERROR),
1659
0
               errmsg("IP address and mask do not match"),
1660
0
               errcontext("line %d of configuration file \"%s\"",
1661
0
                    line_num, file_name)));
1662
0
          *err_msg = "IP address and mask do not match";
1663
0
          return NULL;
1664
0
        }
1665
0
      }
1666
0
    }
1667
0
  }             /* != ctLocal */
1668
1669
  /* Get the authentication method */
1670
0
  field = lnext(tok_line->fields, field);
1671
0
  if (!field)
1672
0
  {
1673
0
    ereport(elevel,
1674
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1675
0
         errmsg("end-of-line before authentication method"),
1676
0
         errcontext("line %d of configuration file \"%s\"",
1677
0
              line_num, file_name)));
1678
0
    *err_msg = "end-of-line before authentication method";
1679
0
    return NULL;
1680
0
  }
1681
0
  tokens = lfirst(field);
1682
0
  if (tokens->length > 1)
1683
0
  {
1684
0
    ereport(elevel,
1685
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1686
0
         errmsg("multiple values specified for authentication type"),
1687
0
         errhint("Specify exactly one authentication type per line."),
1688
0
         errcontext("line %d of configuration file \"%s\"",
1689
0
              line_num, file_name)));
1690
0
    *err_msg = "multiple values specified for authentication type";
1691
0
    return NULL;
1692
0
  }
1693
0
  token = linitial(tokens);
1694
1695
0
  unsupauth = NULL;
1696
0
  if (strcmp(token->string, "trust") == 0)
1697
0
    parsedline->auth_method = uaTrust;
1698
0
  else if (strcmp(token->string, "ident") == 0)
1699
0
    parsedline->auth_method = uaIdent;
1700
0
  else if (strcmp(token->string, "peer") == 0)
1701
0
    parsedline->auth_method = uaPeer;
1702
0
  else if (strcmp(token->string, "password") == 0)
1703
0
    parsedline->auth_method = uaPassword;
1704
0
  else if (strcmp(token->string, "gss") == 0)
1705
#ifdef ENABLE_GSS
1706
    parsedline->auth_method = uaGSS;
1707
#else
1708
0
    unsupauth = "gss";
1709
0
#endif
1710
0
  else if (strcmp(token->string, "sspi") == 0)
1711
#ifdef ENABLE_SSPI
1712
    parsedline->auth_method = uaSSPI;
1713
#else
1714
0
    unsupauth = "sspi";
1715
0
#endif
1716
0
  else if (strcmp(token->string, "reject") == 0)
1717
0
    parsedline->auth_method = uaReject;
1718
0
  else if (strcmp(token->string, "md5") == 0)
1719
0
    parsedline->auth_method = uaMD5;
1720
0
  else if (strcmp(token->string, "scram-sha-256") == 0)
1721
0
    parsedline->auth_method = uaSCRAM;
1722
0
  else if (strcmp(token->string, "pam") == 0)
1723
#ifdef USE_PAM
1724
    parsedline->auth_method = uaPAM;
1725
#else
1726
0
    unsupauth = "pam";
1727
0
#endif
1728
0
  else if (strcmp(token->string, "bsd") == 0)
1729
#ifdef USE_BSD_AUTH
1730
    parsedline->auth_method = uaBSD;
1731
#else
1732
0
    unsupauth = "bsd";
1733
0
#endif
1734
0
  else if (strcmp(token->string, "ldap") == 0)
1735
#ifdef USE_LDAP
1736
    parsedline->auth_method = uaLDAP;
1737
#else
1738
0
    unsupauth = "ldap";
1739
0
#endif
1740
0
  else if (strcmp(token->string, "cert") == 0)
1741
#ifdef USE_SSL
1742
    parsedline->auth_method = uaCert;
1743
#else
1744
0
    unsupauth = "cert";
1745
0
#endif
1746
0
  else if (strcmp(token->string, "oauth") == 0)
1747
0
    parsedline->auth_method = uaOAuth;
1748
0
  else
1749
0
  {
1750
0
    ereport(elevel,
1751
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1752
0
         errmsg("invalid authentication method \"%s\"",
1753
0
            token->string),
1754
0
         errcontext("line %d of configuration file \"%s\"",
1755
0
              line_num, file_name)));
1756
0
    *err_msg = psprintf("invalid authentication method \"%s\"",
1757
0
              token->string);
1758
0
    return NULL;
1759
0
  }
1760
1761
0
  if (unsupauth)
1762
0
  {
1763
0
    ereport(elevel,
1764
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1765
0
         errmsg("invalid authentication method \"%s\": not supported by this build",
1766
0
            token->string),
1767
0
         errcontext("line %d of configuration file \"%s\"",
1768
0
              line_num, file_name)));
1769
0
    *err_msg = psprintf("invalid authentication method \"%s\": not supported by this build",
1770
0
              token->string);
1771
0
    return NULL;
1772
0
  }
1773
1774
  /*
1775
   * XXX: When using ident on local connections, change it to peer, for
1776
   * backwards compatibility.
1777
   */
1778
0
  if (parsedline->conntype == ctLocal &&
1779
0
    parsedline->auth_method == uaIdent)
1780
0
    parsedline->auth_method = uaPeer;
1781
1782
  /* Invalid authentication combinations */
1783
0
  if (parsedline->conntype == ctLocal &&
1784
0
    parsedline->auth_method == uaGSS)
1785
0
  {
1786
0
    ereport(elevel,
1787
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1788
0
         errmsg("gssapi authentication is not supported on local sockets"),
1789
0
         errcontext("line %d of configuration file \"%s\"",
1790
0
              line_num, file_name)));
1791
0
    *err_msg = "gssapi authentication is not supported on local sockets";
1792
0
    return NULL;
1793
0
  }
1794
1795
0
  if (parsedline->conntype != ctLocal &&
1796
0
    parsedline->auth_method == uaPeer)
1797
0
  {
1798
0
    ereport(elevel,
1799
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1800
0
         errmsg("peer authentication is only supported on local sockets"),
1801
0
         errcontext("line %d of configuration file \"%s\"",
1802
0
              line_num, file_name)));
1803
0
    *err_msg = "peer authentication is only supported on local sockets";
1804
0
    return NULL;
1805
0
  }
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
1813
0
  if (parsedline->conntype != ctHostSSL &&
1814
0
    parsedline->auth_method == uaCert)
1815
0
  {
1816
0
    ereport(elevel,
1817
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
1818
0
         errmsg("cert authentication is only supported on hostssl connections"),
1819
0
         errcontext("line %d of configuration file \"%s\"",
1820
0
              line_num, file_name)));
1821
0
    *err_msg = "cert authentication is only supported on hostssl connections";
1822
0
    return NULL;
1823
0
  }
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
   */
1834
0
  if (parsedline->auth_method == uaGSS ||
1835
0
    parsedline->auth_method == uaSSPI)
1836
0
    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
   */
1843
0
  if (parsedline->auth_method == uaSSPI)
1844
0
  {
1845
0
    parsedline->compat_realm = true;
1846
0
    parsedline->upn_username = false;
1847
0
  }
1848
1849
  /* Parse remaining arguments */
1850
0
  while ((field = lnext(tok_line->fields, field)) != NULL)
1851
0
  {
1852
0
    tokens = lfirst(field);
1853
0
    foreach(tokencell, tokens)
1854
0
    {
1855
0
      char     *val;
1856
1857
0
      token = lfirst(tokencell);
1858
1859
0
      str = pstrdup(token->string);
1860
0
      val = strchr(str, '=');
1861
0
      if (val == NULL)
1862
0
      {
1863
        /*
1864
         * Got something that's not a name=value pair.
1865
         */
1866
0
        ereport(elevel,
1867
0
            (errcode(ERRCODE_CONFIG_FILE_ERROR),
1868
0
             errmsg("authentication option not in name=value format: %s", token->string),
1869
0
             errcontext("line %d of configuration file \"%s\"",
1870
0
                  line_num, file_name)));
1871
0
        *err_msg = psprintf("authentication option not in name=value format: %s",
1872
0
                  token->string);
1873
0
        return NULL;
1874
0
      }
1875
1876
0
      *val++ = '\0';    /* str now holds "name", val holds "value" */
1877
0
      if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
1878
        /* parse_hba_auth_opt already logged the error message */
1879
0
        return NULL;
1880
0
      pfree(str);
1881
0
    }
1882
0
  }
1883
1884
  /*
1885
   * Check if the selected authentication method has any mandatory arguments
1886
   * that are not set.
1887
   */
1888
0
  if (parsedline->auth_method == uaLDAP)
1889
0
  {
1890
0
#ifndef HAVE_LDAP_INITIALIZE
1891
    /* Not mandatory for OpenLDAP, because it can use DNS SRV records */
1892
0
    MANDATORY_AUTH_ARG(parsedline->ldapserver, "ldapserver", "ldap");
1893
0
#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
     */
1902
0
    if (parsedline->ldapprefix || parsedline->ldapsuffix)
1903
0
    {
1904
0
      if (parsedline->ldapbasedn ||
1905
0
        parsedline->ldapbinddn ||
1906
0
        parsedline->ldapbindpasswd ||
1907
0
        parsedline->ldapsearchattribute ||
1908
0
        parsedline->ldapsearchfilter)
1909
0
      {
1910
0
        ereport(elevel,
1911
0
            (errcode(ERRCODE_CONFIG_FILE_ERROR),
1912
0
             errmsg("cannot mix options for simple bind and search+bind modes"),
1913
0
             errcontext("line %d of configuration file \"%s\"",
1914
0
                  line_num, file_name)));
1915
0
        *err_msg = "cannot mix options for simple bind and search+bind modes";
1916
0
        return NULL;
1917
0
      }
1918
0
    }
1919
0
    else if (!parsedline->ldapbasedn)
1920
0
    {
1921
0
      ereport(elevel,
1922
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
1923
0
           errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"),
1924
0
           errcontext("line %d of configuration file \"%s\"",
1925
0
                line_num, file_name)));
1926
0
      *err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set";
1927
0
      return NULL;
1928
0
    }
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
     */
1935
0
    if (parsedline->ldapsearchattribute && parsedline->ldapsearchfilter)
1936
0
    {
1937
0
      ereport(elevel,
1938
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
1939
0
           errmsg("cannot use ldapsearchattribute together with ldapsearchfilter"),
1940
0
           errcontext("line %d of configuration file \"%s\"",
1941
0
                line_num, file_name)));
1942
0
      *err_msg = "cannot use ldapsearchattribute together with ldapsearchfilter";
1943
0
      return NULL;
1944
0
    }
1945
0
  }
1946
1947
  /*
1948
   * Enforce any parameters implied by other settings.
1949
   */
1950
0
  if (parsedline->auth_method == uaCert)
1951
0
  {
1952
    /*
1953
     * For auth method cert, client certificate validation is mandatory,
1954
     * and it implies the level of verify-full.
1955
     */
1956
0
    parsedline->clientcert = clientCertFull;
1957
0
  }
1958
1959
  /*
1960
   * Enforce proper configuration of OAuth authentication.
1961
   */
1962
0
  if (parsedline->auth_method == uaOAuth)
1963
0
  {
1964
0
    MANDATORY_AUTH_ARG(parsedline->oauth_scope, "scope", "oauth");
1965
0
    MANDATORY_AUTH_ARG(parsedline->oauth_issuer, "issuer", "oauth");
1966
1967
    /* Ensure a validator library is set and permitted by the config. */
1968
0
    if (!check_oauth_validator(parsedline, elevel, err_msg))
1969
0
      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
0
    if (parsedline->oauth_skip_usermap && parsedline->usermap != NULL)
1976
0
    {
1977
0
      ereport(elevel,
1978
0
          errcode(ERRCODE_CONFIG_FILE_ERROR),
1979
      /* translator: strings are replaced with hba options */
1980
0
          errmsg("%s cannot be used in combination with %s",
1981
0
               "map", "delegate_ident_mapping"),
1982
0
          errcontext("line %d of configuration file \"%s\"",
1983
0
                 line_num, file_name));
1984
0
      *err_msg = "map cannot be used in combination with delegate_ident_mapping";
1985
0
      return NULL;
1986
0
    }
1987
0
  }
1988
1989
0
  return parsedline;
1990
0
}
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
2000
parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
2001
           int elevel, char **err_msg)
2002
0
{
2003
0
  int     line_num = hbaline->linenumber;
2004
0
  char     *file_name = hbaline->sourcefile;
2005
2006
#ifdef USE_LDAP
2007
  hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
2008
#endif
2009
2010
0
  if (strcmp(name, "map") == 0)
2011
0
  {
2012
0
    if (hbaline->auth_method != uaIdent &&
2013
0
      hbaline->auth_method != uaPeer &&
2014
0
      hbaline->auth_method != uaGSS &&
2015
0
      hbaline->auth_method != uaSSPI &&
2016
0
      hbaline->auth_method != uaCert &&
2017
0
      hbaline->auth_method != uaOAuth)
2018
0
      INVALID_AUTH_OPTION("map", gettext_noop("ident, peer, gssapi, sspi, cert, and oauth"));
2019
0
    hbaline->usermap = pstrdup(val);
2020
0
  }
2021
0
  else if (strcmp(name, "clientcert") == 0)
2022
0
  {
2023
0
    if (hbaline->conntype != ctHostSSL)
2024
0
    {
2025
0
      ereport(elevel,
2026
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2027
0
           errmsg("clientcert can only be configured for \"hostssl\" rows"),
2028
0
           errcontext("line %d of configuration file \"%s\"",
2029
0
                line_num, file_name)));
2030
0
      *err_msg = "clientcert can only be configured for \"hostssl\" rows";
2031
0
      return false;
2032
0
    }
2033
2034
0
    if (strcmp(val, "verify-full") == 0)
2035
0
    {
2036
0
      hbaline->clientcert = clientCertFull;
2037
0
    }
2038
0
    else if (strcmp(val, "verify-ca") == 0)
2039
0
    {
2040
0
      if (hbaline->auth_method == uaCert)
2041
0
      {
2042
0
        ereport(elevel,
2043
0
            (errcode(ERRCODE_CONFIG_FILE_ERROR),
2044
0
             errmsg("clientcert only accepts \"verify-full\" when using \"cert\" authentication"),
2045
0
             errcontext("line %d of configuration file \"%s\"",
2046
0
                  line_num, file_name)));
2047
0
        *err_msg = "clientcert can only be set to \"verify-full\" when using \"cert\" authentication";
2048
0
        return false;
2049
0
      }
2050
2051
0
      hbaline->clientcert = clientCertCA;
2052
0
    }
2053
0
    else
2054
0
    {
2055
0
      ereport(elevel,
2056
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2057
0
           errmsg("invalid value for clientcert: \"%s\"", val),
2058
0
           errcontext("line %d of configuration file \"%s\"",
2059
0
                line_num, file_name)));
2060
0
      return false;
2061
0
    }
2062
0
  }
2063
0
  else if (strcmp(name, "clientname") == 0)
2064
0
  {
2065
0
    if (hbaline->conntype != ctHostSSL)
2066
0
    {
2067
0
      ereport(elevel,
2068
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2069
0
           errmsg("clientname can only be configured for \"hostssl\" rows"),
2070
0
           errcontext("line %d of configuration file \"%s\"",
2071
0
                line_num, file_name)));
2072
0
      *err_msg = "clientname can only be configured for \"hostssl\" rows";
2073
0
      return false;
2074
0
    }
2075
2076
0
    if (strcmp(val, "CN") == 0)
2077
0
    {
2078
0
      hbaline->clientcertname = clientCertCN;
2079
0
    }
2080
0
    else if (strcmp(val, "DN") == 0)
2081
0
    {
2082
0
      hbaline->clientcertname = clientCertDN;
2083
0
    }
2084
0
    else
2085
0
    {
2086
0
      ereport(elevel,
2087
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2088
0
           errmsg("invalid value for clientname: \"%s\"", val),
2089
0
           errcontext("line %d of configuration file \"%s\"",
2090
0
                line_num, file_name)));
2091
0
      return false;
2092
0
    }
2093
0
  }
2094
0
  else if (strcmp(name, "pamservice") == 0)
2095
0
  {
2096
0
    REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
2097
0
    hbaline->pamservice = pstrdup(val);
2098
0
  }
2099
0
  else if (strcmp(name, "pam_use_hostname") == 0)
2100
0
  {
2101
0
    REQUIRE_AUTH_OPTION(uaPAM, "pam_use_hostname", "pam");
2102
0
    if (strcmp(val, "1") == 0)
2103
0
      hbaline->pam_use_hostname = true;
2104
0
    else
2105
0
      hbaline->pam_use_hostname = false;
2106
0
  }
2107
0
  else if (strcmp(name, "ldapurl") == 0)
2108
0
  {
2109
#ifdef LDAP_API_FEATURE_X_OPENLDAP
2110
    LDAPURLDesc *urldata;
2111
    int     rc;
2112
#endif
2113
2114
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapurl", "ldap");
2115
#ifdef LDAP_API_FEATURE_X_OPENLDAP
2116
    rc = ldap_url_parse(val, &urldata);
2117
    if (rc != LDAP_SUCCESS)
2118
    {
2119
      ereport(elevel,
2120
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2121
           errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc))));
2122
      *err_msg = psprintf("could not parse LDAP URL \"%s\": %s",
2123
                val, ldap_err2string(rc));
2124
      return false;
2125
    }
2126
2127
    if (strcmp(urldata->lud_scheme, "ldap") != 0 &&
2128
      strcmp(urldata->lud_scheme, "ldaps") != 0)
2129
    {
2130
      ereport(elevel,
2131
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2132
           errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme)));
2133
      *err_msg = psprintf("unsupported LDAP URL scheme: %s",
2134
                urldata->lud_scheme);
2135
      ldap_free_urldesc(urldata);
2136
      return false;
2137
    }
2138
2139
    if (urldata->lud_scheme)
2140
      hbaline->ldapscheme = pstrdup(urldata->lud_scheme);
2141
    if (urldata->lud_host)
2142
      hbaline->ldapserver = pstrdup(urldata->lud_host);
2143
    hbaline->ldapport = urldata->lud_port;
2144
    if (urldata->lud_dn)
2145
      hbaline->ldapbasedn = pstrdup(urldata->lud_dn);
2146
2147
    if (urldata->lud_attrs)
2148
      hbaline->ldapsearchattribute = pstrdup(urldata->lud_attrs[0]);  /* only use first one */
2149
    hbaline->ldapscope = urldata->lud_scope;
2150
    if (urldata->lud_filter)
2151
      hbaline->ldapsearchfilter = pstrdup(urldata->lud_filter);
2152
    ldap_free_urldesc(urldata);
2153
#else             /* not OpenLDAP */
2154
0
    ereport(elevel,
2155
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2156
0
         errmsg("LDAP URLs not supported on this platform")));
2157
0
    *err_msg = "LDAP URLs not supported on this platform";
2158
0
#endif              /* not OpenLDAP */
2159
0
  }
2160
0
  else if (strcmp(name, "ldaptls") == 0)
2161
0
  {
2162
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldaptls", "ldap");
2163
0
    if (strcmp(val, "1") == 0)
2164
0
      hbaline->ldaptls = true;
2165
0
    else
2166
0
      hbaline->ldaptls = false;
2167
0
  }
2168
0
  else if (strcmp(name, "ldapscheme") == 0)
2169
0
  {
2170
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapscheme", "ldap");
2171
0
    if (strcmp(val, "ldap") != 0 && strcmp(val, "ldaps") != 0)
2172
0
      ereport(elevel,
2173
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2174
0
           errmsg("invalid ldapscheme value: \"%s\"", val),
2175
0
           errcontext("line %d of configuration file \"%s\"",
2176
0
                line_num, file_name)));
2177
0
    hbaline->ldapscheme = pstrdup(val);
2178
0
  }
2179
0
  else if (strcmp(name, "ldapserver") == 0)
2180
0
  {
2181
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapserver", "ldap");
2182
0
    hbaline->ldapserver = pstrdup(val);
2183
0
  }
2184
0
  else if (strcmp(name, "ldapport") == 0)
2185
0
  {
2186
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapport", "ldap");
2187
0
    hbaline->ldapport = atoi(val);
2188
0
    if (hbaline->ldapport == 0)
2189
0
    {
2190
0
      ereport(elevel,
2191
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2192
0
           errmsg("invalid LDAP port number: \"%s\"", val),
2193
0
           errcontext("line %d of configuration file \"%s\"",
2194
0
                line_num, file_name)));
2195
0
      *err_msg = psprintf("invalid LDAP port number: \"%s\"", val);
2196
0
      return false;
2197
0
    }
2198
0
  }
2199
0
  else if (strcmp(name, "ldapbinddn") == 0)
2200
0
  {
2201
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapbinddn", "ldap");
2202
0
    hbaline->ldapbinddn = pstrdup(val);
2203
0
  }
2204
0
  else if (strcmp(name, "ldapbindpasswd") == 0)
2205
0
  {
2206
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapbindpasswd", "ldap");
2207
0
    hbaline->ldapbindpasswd = pstrdup(val);
2208
0
  }
2209
0
  else if (strcmp(name, "ldapsearchattribute") == 0)
2210
0
  {
2211
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchattribute", "ldap");
2212
0
    hbaline->ldapsearchattribute = pstrdup(val);
2213
0
  }
2214
0
  else if (strcmp(name, "ldapsearchfilter") == 0)
2215
0
  {
2216
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapsearchfilter", "ldap");
2217
0
    hbaline->ldapsearchfilter = pstrdup(val);
2218
0
  }
2219
0
  else if (strcmp(name, "ldapbasedn") == 0)
2220
0
  {
2221
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapbasedn", "ldap");
2222
0
    hbaline->ldapbasedn = pstrdup(val);
2223
0
  }
2224
0
  else if (strcmp(name, "ldapprefix") == 0)
2225
0
  {
2226
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapprefix", "ldap");
2227
0
    hbaline->ldapprefix = pstrdup(val);
2228
0
  }
2229
0
  else if (strcmp(name, "ldapsuffix") == 0)
2230
0
  {
2231
0
    REQUIRE_AUTH_OPTION(uaLDAP, "ldapsuffix", "ldap");
2232
0
    hbaline->ldapsuffix = pstrdup(val);
2233
0
  }
2234
0
  else if (strcmp(name, "krb_realm") == 0)
2235
0
  {
2236
0
    if (hbaline->auth_method != uaGSS &&
2237
0
      hbaline->auth_method != uaSSPI)
2238
0
      INVALID_AUTH_OPTION("krb_realm", gettext_noop("gssapi and sspi"));
2239
0
    hbaline->krb_realm = pstrdup(val);
2240
0
  }
2241
0
  else if (strcmp(name, "include_realm") == 0)
2242
0
  {
2243
0
    if (hbaline->auth_method != uaGSS &&
2244
0
      hbaline->auth_method != uaSSPI)
2245
0
      INVALID_AUTH_OPTION("include_realm", gettext_noop("gssapi and sspi"));
2246
0
    if (strcmp(val, "1") == 0)
2247
0
      hbaline->include_realm = true;
2248
0
    else
2249
0
      hbaline->include_realm = false;
2250
0
  }
2251
0
  else if (strcmp(name, "compat_realm") == 0)
2252
0
  {
2253
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
0
    else
2258
0
      hbaline->compat_realm = false;
2259
0
  }
2260
0
  else if (strcmp(name, "upn_username") == 0)
2261
0
  {
2262
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
0
    else
2267
0
      hbaline->upn_username = false;
2268
0
  }
2269
0
  else if (strcmp(name, "issuer") == 0)
2270
0
  {
2271
0
    REQUIRE_AUTH_OPTION(uaOAuth, "issuer", "oauth");
2272
0
    hbaline->oauth_issuer = pstrdup(val);
2273
0
  }
2274
0
  else if (strcmp(name, "scope") == 0)
2275
0
  {
2276
0
    REQUIRE_AUTH_OPTION(uaOAuth, "scope", "oauth");
2277
0
    hbaline->oauth_scope = pstrdup(val);
2278
0
  }
2279
0
  else if (strcmp(name, "validator") == 0)
2280
0
  {
2281
0
    REQUIRE_AUTH_OPTION(uaOAuth, "validator", "oauth");
2282
0
    hbaline->oauth_validator = pstrdup(val);
2283
0
  }
2284
0
  else if (strncmp(name, "validator.", strlen("validator.")) == 0)
2285
0
  {
2286
0
    const char *key = name + strlen("validator.");
2287
2288
0
    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
0
    if (!valid_oauth_hba_option_name(key))
2298
0
    {
2299
0
      ereport(elevel,
2300
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
2301
0
           errmsg("invalid OAuth validator option name: \"%s\"", name),
2302
0
           errcontext("line %d of configuration file \"%s\"",
2303
0
                line_num, file_name)));
2304
0
      return false;
2305
0
    }
2306
2307
0
    hbaline->oauth_opt_keys = lappend(hbaline->oauth_opt_keys, pstrdup(key));
2308
0
    hbaline->oauth_opt_vals = lappend(hbaline->oauth_opt_vals, pstrdup(val));
2309
0
  }
2310
0
  else if (strcmp(name, "delegate_ident_mapping") == 0)
2311
0
  {
2312
0
    REQUIRE_AUTH_OPTION(uaOAuth, "delegate_ident_mapping", "oauth");
2313
0
    if (strcmp(val, "1") == 0)
2314
0
      hbaline->oauth_skip_usermap = true;
2315
0
    else
2316
0
      hbaline->oauth_skip_usermap = false;
2317
0
  }
2318
0
  else
2319
0
  {
2320
0
    ereport(elevel,
2321
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
2322
0
         errmsg("unrecognized authentication option name: \"%s\"",
2323
0
            name),
2324
0
         errcontext("line %d of configuration file \"%s\"",
2325
0
              line_num, file_name)));
2326
0
    *err_msg = psprintf("unrecognized authentication option name: \"%s\"",
2327
0
              name);
2328
0
    return false;
2329
0
  }
2330
0
  return true;
2331
0
}
2332
2333
/*
2334
 *  Scan the pre-parsed hba file, looking for a match to the port's connection
2335
 *  request.
2336
 */
2337
static void
2338
check_hba(Port *port)
2339
0
{
2340
0
  Oid     roleid;
2341
0
  ListCell   *line;
2342
0
  HbaLine    *hba;
2343
2344
  /* Get the target role's OID.  Note we do not error out for bad role. */
2345
0
  roleid = get_role_oid(port->user_name, true);
2346
2347
0
  foreach(line, parsed_hba_lines)
2348
0
  {
2349
0
    hba = (HbaLine *) lfirst(line);
2350
2351
    /* Check connection type */
2352
0
    if (hba->conntype == ctLocal)
2353
0
    {
2354
0
      if (port->raddr.addr.ss_family != AF_UNIX)
2355
0
        continue;
2356
0
    }
2357
0
    else
2358
0
    {
2359
0
      if (port->raddr.addr.ss_family == AF_UNIX)
2360
0
        continue;
2361
2362
      /* Check SSL state */
2363
0
      if (port->ssl_in_use)
2364
0
      {
2365
        /* Connection is SSL, match both "host" and "hostssl" */
2366
0
        if (hba->conntype == ctHostNoSSL)
2367
0
          continue;
2368
0
      }
2369
0
      else
2370
0
      {
2371
        /* Connection is not SSL, match both "host" and "hostnossl" */
2372
0
        if (hba->conntype == ctHostSSL)
2373
0
          continue;
2374
0
      }
2375
2376
      /* Check GSSAPI state */
2377
#ifdef ENABLE_GSS
2378
      if (port->gss && port->gss->enc &&
2379
        hba->conntype == ctHostNoGSS)
2380
        continue;
2381
      else if (!(port->gss && port->gss->enc) &&
2382
           hba->conntype == ctHostGSS)
2383
        continue;
2384
#else
2385
0
      if (hba->conntype == ctHostGSS)
2386
0
        continue;
2387
0
#endif
2388
2389
      /* Check IP address */
2390
0
      switch (hba->ip_cmp_method)
2391
0
      {
2392
0
        case ipCmpMask:
2393
0
          if (hba->hostname)
2394
0
          {
2395
0
            if (!check_hostname(port,
2396
0
                      hba->hostname))
2397
0
              continue;
2398
0
          }
2399
0
          else
2400
0
          {
2401
0
            if (!check_ip(&port->raddr,
2402
0
                    (struct sockaddr *) &hba->addr,
2403
0
                    (struct sockaddr *) &hba->mask))
2404
0
              continue;
2405
0
          }
2406
0
          break;
2407
0
        case ipCmpAll:
2408
0
          break;
2409
0
        case ipCmpSameHost:
2410
0
        case ipCmpSameNet:
2411
0
          if (!check_same_host_or_net(&port->raddr,
2412
0
                        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 */
2417
0
          continue;
2418
0
      }
2419
0
    }           /* != ctLocal */
2420
2421
    /* Check database and role */
2422
0
    if (!check_db(port->database_name, port->user_name, roleid,
2423
0
            hba->databases))
2424
0
      continue;
2425
2426
0
    if (!check_role(port->user_name, roleid, hba->roles, false))
2427
0
      continue;
2428
2429
    /* Found a record that matched! */
2430
0
    port->hba = hba;
2431
0
    return;
2432
0
  }
2433
2434
  /* If no matching entry was found, then implicitly reject. */
2435
0
  hba = palloc0_object(HbaLine);
2436
0
  hba->auth_method = uaImplicitReject;
2437
0
  port->hba = hba;
2438
0
}
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
2452
load_hba(void)
2453
0
{
2454
0
  FILE     *file;
2455
0
  List     *hba_lines = NIL;
2456
0
  ListCell   *line;
2457
0
  List     *new_parsed_lines = NIL;
2458
0
  bool    ok = true;
2459
0
  MemoryContext oldcxt;
2460
0
  MemoryContext hbacxt;
2461
2462
0
  file = open_auth_file(HbaFileName, LOG, 0, NULL);
2463
0
  if (file == NULL)
2464
0
  {
2465
    /* error already logged */
2466
0
    return false;
2467
0
  }
2468
2469
0
  tokenize_auth_file(HbaFileName, file, &hba_lines, LOG, 0);
2470
2471
  /* Now parse all the lines */
2472
0
  Assert(PostmasterContext);
2473
0
  hbacxt = AllocSetContextCreate(PostmasterContext,
2474
0
                   "hba parser context",
2475
0
                   ALLOCSET_SMALL_SIZES);
2476
0
  oldcxt = MemoryContextSwitchTo(hbacxt);
2477
0
  foreach(line, hba_lines)
2478
0
  {
2479
0
    TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
2480
0
    HbaLine    *newline;
2481
2482
    /* don't parse lines that already have errors */
2483
0
    if (tok_line->err_msg != NULL)
2484
0
    {
2485
0
      ok = false;
2486
0
      continue;
2487
0
    }
2488
2489
0
    if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
2490
0
    {
2491
      /* Parse error; remember there's trouble */
2492
0
      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
       */
2499
0
      continue;
2500
0
    }
2501
2502
0
    new_parsed_lines = lappend(new_parsed_lines, newline);
2503
0
  }
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
   */
2510
0
  if (ok && new_parsed_lines == NIL)
2511
0
  {
2512
0
    ereport(LOG,
2513
0
        (errcode(ERRCODE_CONFIG_FILE_ERROR),
2514
0
         errmsg("configuration file \"%s\" contains no entries",
2515
0
            HbaFileName)));
2516
0
    ok = false;
2517
0
  }
2518
2519
  /* Free tokenizer memory */
2520
0
  free_auth_file(file, 0);
2521
0
  MemoryContextSwitchTo(oldcxt);
2522
2523
0
  if (!ok)
2524
0
  {
2525
    /*
2526
     * File contained one or more errors, so bail out. MemoryContextDelete
2527
     * is enough to clean up everything, including regexes.
2528
     */
2529
0
    MemoryContextDelete(hbacxt);
2530
0
    return false;
2531
0
  }
2532
2533
  /* Loaded new file successfully, replace the one we use */
2534
0
  if (parsed_hba_context != NULL)
2535
0
    MemoryContextDelete(parsed_hba_context);
2536
0
  parsed_hba_context = hbacxt;
2537
0
  parsed_hba_lines = new_parsed_lines;
2538
2539
0
  return true;
2540
0
}
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 *
2558
parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
2559
0
{
2560
0
  int     line_num = tok_line->line_num;
2561
0
  char     *file_name = tok_line->file_name;
2562
0
  char    **err_msg = &tok_line->err_msg;
2563
0
  ListCell   *field;
2564
0
  List     *tokens;
2565
0
  AuthToken  *token;
2566
0
  IdentLine  *parsedline;
2567
2568
0
  Assert(tok_line->fields != NIL);
2569
0
  field = list_head(tok_line->fields);
2570
2571
0
  parsedline = palloc0_object(IdentLine);
2572
0
  parsedline->linenumber = line_num;
2573
2574
  /* Get the map token (must exist) */
2575
0
  tokens = lfirst(field);
2576
0
  IDENT_MULTI_VALUE(tokens);
2577
0
  token = linitial(tokens);
2578
0
  parsedline->usermap = pstrdup(token->string);
2579
2580
  /* Get the ident user token */
2581
0
  field = lnext(tok_line->fields, field);
2582
0
  IDENT_FIELD_ABSENT(field);
2583
0
  tokens = lfirst(field);
2584
0
  IDENT_MULTI_VALUE(tokens);
2585
0
  token = linitial(tokens);
2586
2587
  /* Copy the ident user token */
2588
0
  parsedline->system_user = copy_auth_token(token);
2589
2590
  /* Get the PG rolename token */
2591
0
  field = lnext(tok_line->fields, field);
2592
0
  IDENT_FIELD_ABSENT(field);
2593
0
  tokens = lfirst(field);
2594
0
  IDENT_MULTI_VALUE(tokens);
2595
0
  token = linitial(tokens);
2596
0
  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
   */
2602
0
  if (regcomp_auth_token(parsedline->system_user, file_name, line_num,
2603
0
               err_msg, elevel))
2604
0
  {
2605
    /* err_msg includes the error to report */
2606
0
    return NULL;
2607
0
  }
2608
2609
0
  if (regcomp_auth_token(parsedline->pg_user, file_name, line_num,
2610
0
               err_msg, elevel))
2611
0
  {
2612
    /* err_msg includes the error to report */
2613
0
    return NULL;
2614
0
  }
2615
2616
0
  return parsedline;
2617
0
}
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
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
0
{
2630
0
  Oid     roleid;
2631
2632
0
  *found_p = false;
2633
0
  *error_p = false;
2634
2635
0
  if (strcmp(identLine->usermap, usermap_name) != 0)
2636
    /* Line does not match the map name we're looking for, so just abort */
2637
0
    return;
2638
2639
  /* Get the target role's OID.  Note we do not error out for bad role. */
2640
0
  roleid = get_role_oid(pg_user, true);
2641
2642
  /* Match? */
2643
0
  if (token_has_regexp(identLine->system_user))
2644
0
  {
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
0
    int     r;
2651
0
    regmatch_t  matches[2];
2652
0
    char     *ofs;
2653
0
    AuthToken  *expanded_pg_user_token;
2654
0
    bool    created_temporary_token = false;
2655
2656
0
    r = regexec_auth_token(system_user, identLine->system_user, 2, matches);
2657
0
    if (r)
2658
0
    {
2659
0
      char    errstr[100];
2660
2661
0
      if (r != REG_NOMATCH)
2662
0
      {
2663
        /* REG_NOMATCH is not an error, everything else is */
2664
0
        pg_regerror(r, identLine->system_user->regex, errstr, sizeof(errstr));
2665
0
        ereport(LOG,
2666
0
            (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2667
0
             errmsg("regular expression match for \"%s\" failed: %s",
2668
0
                identLine->system_user->string + 1, errstr)));
2669
0
        *error_p = true;
2670
0
      }
2671
0
      return;
2672
0
    }
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
     */
2679
0
    if (!token_is_member_check(identLine->pg_user) &&
2680
0
      !token_has_regexp(identLine->pg_user) &&
2681
0
      (ofs = strstr(identLine->pg_user->string, "\\1")) != NULL)
2682
0
    {
2683
0
      const char *repl_str;
2684
0
      size_t    repl_len;
2685
0
      char     *old_pg_user;
2686
0
      char     *expanded_pg_user;
2687
0
      size_t    offset;
2688
2689
      /* substitution of the first argument requested */
2690
0
      if (matches[1].rm_so < 0)
2691
0
      {
2692
0
        ereport(LOG,
2693
0
            (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
2694
0
             errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
2695
0
                identLine->system_user->string + 1, identLine->pg_user->string)));
2696
0
        *error_p = true;
2697
0
        return;
2698
0
      }
2699
0
      repl_str = system_user + matches[1].rm_so;
2700
0
      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
0
      old_pg_user = identLine->pg_user->string;
2710
0
      do
2711
0
      {
2712
        /*
2713
         * length: current length minus length of \1 plus length of
2714
         * replacement plus null terminator
2715
         */
2716
0
        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
0
        offset = ofs - old_pg_user;
2719
0
        memcpy(expanded_pg_user, old_pg_user, offset);
2720
0
        memcpy(expanded_pg_user + offset, repl_str, repl_len);
2721
0
        strcpy(expanded_pg_user + offset + repl_len, ofs + 2);
2722
0
        if (old_pg_user != identLine->pg_user->string)
2723
0
          pfree(old_pg_user);
2724
0
        old_pg_user = expanded_pg_user;
2725
0
      } 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
       */
2732
0
      expanded_pg_user_token = make_auth_token(expanded_pg_user, true);
2733
0
      created_temporary_token = true;
2734
0
      pfree(expanded_pg_user);
2735
0
    }
2736
0
    else
2737
0
    {
2738
0
      expanded_pg_user_token = identLine->pg_user;
2739
0
    }
2740
2741
    /* check the Postgres user */
2742
0
    *found_p = check_role(pg_user, roleid,
2743
0
                list_make1(expanded_pg_user_token),
2744
0
                case_insensitive);
2745
2746
0
    if (created_temporary_token)
2747
0
      free_auth_token(expanded_pg_user_token);
2748
2749
0
    return;
2750
0
  }
2751
0
  else
2752
0
  {
2753
    /*
2754
     * Not a regular expression, so make a complete match.  If the system
2755
     * user does not match, just leave.
2756
     */
2757
0
    if (case_insensitive)
2758
0
    {
2759
0
      if (!token_matches_insensitive(identLine->system_user,
2760
0
                       system_user))
2761
0
        return;
2762
0
    }
2763
0
    else
2764
0
    {
2765
0
      if (!token_matches(identLine->system_user, system_user))
2766
0
        return;
2767
0
    }
2768
2769
    /* check the Postgres user */
2770
0
    *found_p = check_role(pg_user, roleid,
2771
0
                list_make1(identLine->pg_user),
2772
0
                case_insensitive);
2773
0
  }
2774
0
}
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
2791
check_usermap(const char *usermap_name,
2792
        const char *pg_user,
2793
        const char *system_user,
2794
        bool case_insensitive)
2795
0
{
2796
0
  bool    found_entry = false,
2797
0
        error = false;
2798
2799
0
  if (usermap_name == NULL || usermap_name[0] == '\0')
2800
0
  {
2801
0
    if (case_insensitive)
2802
0
    {
2803
0
      if (pg_strcasecmp(pg_user, system_user) == 0)
2804
0
        return STATUS_OK;
2805
0
    }
2806
0
    else
2807
0
    {
2808
0
      if (strcmp(pg_user, system_user) == 0)
2809
0
        return STATUS_OK;
2810
0
    }
2811
0
    ereport(LOG,
2812
0
        (errmsg("provided user name (%s) and authenticated user name (%s) do not match",
2813
0
            pg_user, system_user)));
2814
0
    return STATUS_ERROR;
2815
0
  }
2816
0
  else
2817
0
  {
2818
0
    ListCell   *line_cell;
2819
2820
0
    foreach(line_cell, parsed_ident_lines)
2821
0
    {
2822
0
      check_ident_usermap(lfirst(line_cell), usermap_name,
2823
0
                pg_user, system_user, case_insensitive,
2824
0
                &found_entry, &error);
2825
0
      if (found_entry || error)
2826
0
        break;
2827
0
    }
2828
0
  }
2829
0
  if (!found_entry && !error)
2830
0
  {
2831
0
    ereport(LOG,
2832
0
        (errmsg("no match in usermap \"%s\" for user \"%s\" authenticated as \"%s\"",
2833
0
            usermap_name, pg_user, system_user)));
2834
0
  }
2835
0
  return found_entry ? STATUS_OK : STATUS_ERROR;
2836
0
}
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
2846
load_ident(void)
2847
0
{
2848
0
  FILE     *file;
2849
0
  List     *ident_lines = NIL;
2850
0
  ListCell   *line_cell;
2851
0
  List     *new_parsed_lines = NIL;
2852
0
  bool    ok = true;
2853
0
  MemoryContext oldcxt;
2854
0
  MemoryContext ident_context;
2855
0
  IdentLine  *newline;
2856
2857
  /* not FATAL ... we just won't do any special ident maps */
2858
0
  file = open_auth_file(IdentFileName, LOG, 0, NULL);
2859
0
  if (file == NULL)
2860
0
  {
2861
    /* error already logged */
2862
0
    return false;
2863
0
  }
2864
2865
0
  tokenize_auth_file(IdentFileName, file, &ident_lines, LOG, 0);
2866
2867
  /* Now parse all the lines */
2868
0
  Assert(PostmasterContext);
2869
0
  ident_context = AllocSetContextCreate(PostmasterContext,
2870
0
                      "ident parser context",
2871
0
                      ALLOCSET_SMALL_SIZES);
2872
0
  oldcxt = MemoryContextSwitchTo(ident_context);
2873
0
  foreach(line_cell, ident_lines)
2874
0
  {
2875
0
    TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line_cell);
2876
2877
    /* don't parse lines that already have errors */
2878
0
    if (tok_line->err_msg != NULL)
2879
0
    {
2880
0
      ok = false;
2881
0
      continue;
2882
0
    }
2883
2884
0
    if ((newline = parse_ident_line(tok_line, LOG)) == NULL)
2885
0
    {
2886
      /* Parse error; remember there's trouble */
2887
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
0
    }
2896
2897
0
    new_parsed_lines = lappend(new_parsed_lines, newline);
2898
0
  }
2899
2900
  /* Free tokenizer memory */
2901
0
  free_auth_file(file, 0);
2902
0
  MemoryContextSwitchTo(oldcxt);
2903
2904
0
  if (!ok)
2905
0
  {
2906
    /*
2907
     * File contained one or more errors, so bail out. MemoryContextDelete
2908
     * is enough to clean up everything, including regexes.
2909
     */
2910
0
    MemoryContextDelete(ident_context);
2911
0
    return false;
2912
0
  }
2913
2914
  /* Loaded new file successfully, replace the one we use */
2915
0
  if (parsed_ident_context != NULL)
2916
0
    MemoryContextDelete(parsed_ident_context);
2917
2918
0
  parsed_ident_context = ident_context;
2919
0
  parsed_ident_lines = new_parsed_lines;
2920
2921
0
  return true;
2922
0
}
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
2935
hba_getauthmethod(Port *port)
2936
0
{
2937
0
  check_hba(port);
2938
0
}
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 *
2948
hba_authname(UserAuth auth_method)
2949
0
{
2950
0
  return UserAuthName[auth_method];
2951
0
}