YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

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