~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/libpq/hba.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 possibly using ident).
 
7
 *
 
8
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
9
 * Portions Copyright (c) 1994, Regents of the University of California
 
10
 *
 
11
 *
 
12
 * IDENTIFICATION
 
13
 *        $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.136 2004-12-31 21:59:50 pgsql Exp $
 
14
 *
 
15
 *-------------------------------------------------------------------------
 
16
 */
 
17
#include "postgres.h"
 
18
 
 
19
#include <errno.h>
 
20
#include <pwd.h>
 
21
#include <fcntl.h>
 
22
#include <sys/param.h>
 
23
#include <sys/socket.h>
 
24
#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
 
25
#include <sys/uio.h>
 
26
#include <sys/ucred.h>
 
27
#endif
 
28
#include <netinet/in.h>
 
29
#include <arpa/inet.h>
 
30
#include <unistd.h>
 
31
 
 
32
#include "commands/user.h"
 
33
#include "libpq/crypt.h"
 
34
#include "libpq/libpq.h"
 
35
#include "miscadmin.h"
 
36
#include "nodes/pg_list.h"
 
37
#include "storage/fd.h"
 
38
#include "utils/guc.h"
 
39
 
 
40
 
 
41
/* Max size of username ident server can return */
 
42
#define IDENT_USERNAME_MAX 512
 
43
 
 
44
/* Standard TCP port number for Ident service.  Assigned by IANA */
 
45
#define IDENT_PORT 113
 
46
 
 
47
/* This is used to separate values in multi-valued column strings */
 
48
#define MULTI_VALUE_SEP "\001"
 
49
 
 
50
#define MAX_TOKEN       256
 
51
 
 
52
/*
 
53
 * These variables hold the pre-parsed contents of the hba and ident
 
54
 * configuration files.  Each is a list of sublists, one sublist for
 
55
 * each (non-empty, non-comment) line of the file.      Each sublist's
 
56
 * first item is an integer line number (so we can give somewhat-useful
 
57
 * location info in error messages).  Remaining items are palloc'd strings,
 
58
 * one string per token on the line.  Note there will always be at least
 
59
 * one token, since blank lines are not entered in the data structure.
 
60
 */
 
61
 
 
62
/* pre-parsed content of HBA config file and corresponding line #s */
 
63
static List *hba_lines = NIL;
 
64
static List *hba_line_nums = NIL;
 
65
 
 
66
/* pre-parsed content of ident usermap file and corresponding line #s */
 
67
static List *ident_lines = NIL;
 
68
static List *ident_line_nums = NIL;
 
69
 
 
70
/* pre-parsed content of group file and corresponding line #s */
 
71
static List *group_lines = NIL;
 
72
static List *group_line_nums = NIL;
 
73
 
 
74
/* pre-parsed content of user passwd file and corresponding line #s */
 
75
static List *user_lines = NIL;
 
76
static List *user_line_nums = NIL;
 
77
 
 
78
/* sorted entries so we can do binary search lookups */
 
79
static List **user_sorted = NULL;               /* sorted user list, for bsearch() */
 
80
static List **group_sorted = NULL;              /* sorted group list, for
 
81
                                                                                 * bsearch() */
 
82
static int      user_length;
 
83
static int      group_length;
 
84
 
 
85
static void tokenize_file(const char *filename, FILE *file,
 
86
                                                  List **lines, List **line_nums);
 
87
static char *tokenize_inc_file(const char *outer_filename,
 
88
                                                           const char *inc_filename);
 
89
 
 
90
/*
 
91
 * isblank() exists in the ISO C99 spec, but it's not very portable yet,
 
92
 * so provide our own version.
 
93
 */
 
94
static bool
 
95
pg_isblank(const char c)
 
96
{
 
97
        return c == ' ' || c == '\t' || c == '\r';
 
98
}
 
99
 
 
100
 
 
101
/*
 
102
 * Grab one token out of fp. Tokens are strings of non-blank
 
103
 * characters bounded by blank characters, beginning of line, and
 
104
 * end of line. Blank means space or tab. Return the token as
 
105
 * *buf. Leave file positioned at the character immediately after the
 
106
 * token or EOF, whichever comes first. If no more tokens on line,
 
107
 * return empty string as *buf and position the file to the beginning
 
108
 * of the next line or EOF, whichever comes first. Allow spaces in
 
109
 * quoted strings. Terminate on unquoted commas. Handle
 
110
 * comments. Treat unquoted keywords that might be user names or
 
111
 * database names specially, by appending a newline to them.
 
112
 */
 
113
static void
 
114
next_token(FILE *fp, char *buf, int bufsz)
 
115
{
 
116
        int                     c;
 
117
        char       *start_buf = buf;
 
118
        char       *end_buf = buf + (bufsz - 2);
 
119
        bool            in_quote = false;
 
120
        bool            was_quote = false;
 
121
        bool            saw_quote = false;
 
122
 
 
123
        Assert(end_buf > start_buf);
 
124
 
 
125
        /* Move over initial whitespace and commas */
 
126
        while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
 
127
                ;
 
128
 
 
129
        if (c == EOF || c == '\n')
 
130
        {
 
131
                *buf = '\0';
 
132
                return;
 
133
        }
 
134
 
 
135
        /*
 
136
         * Build a token in buf of next characters up to EOF, EOL, unquoted
 
137
         * comma, or unquoted whitespace.
 
138
         */
 
139
        while (c != EOF && c != '\n' &&
 
140
                   (!pg_isblank(c) || in_quote == true))
 
141
        {
 
142
                /* skip comments to EOL */
 
143
                if (c == '#' && !in_quote)
 
144
                {
 
145
                        while ((c = getc(fp)) != EOF && c != '\n')
 
146
                                ;
 
147
                        /* If only comment, consume EOL too; return EOL */
 
148
                        if (c != EOF && buf == start_buf)
 
149
                                c = getc(fp);
 
150
                        break;
 
151
                }
 
152
 
 
153
                if (buf >= end_buf)
 
154
                {
 
155
                        *buf = '\0';
 
156
                        ereport(LOG,
 
157
                                        (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
158
                        errmsg("authentication file token too long, skipping: \"%s\"",
 
159
                                   start_buf)));
 
160
                        /* Discard remainder of line */
 
161
                        while ((c = getc(fp)) != EOF && c != '\n')
 
162
                                ;
 
163
                        break;
 
164
                }
 
165
 
 
166
                if (c != '"' || (c == '"' && was_quote))
 
167
                        *buf++ = c;
 
168
 
 
169
                /* We pass back the comma so the caller knows there is more */
 
170
                if ((pg_isblank(c) || c == ',') && !in_quote)
 
171
                        break;
 
172
 
 
173
                /* Literal double-quote is two double-quotes */
 
174
                if (in_quote && c == '"')
 
175
                        was_quote = !was_quote;
 
176
                else
 
177
                        was_quote = false;
 
178
 
 
179
                if (c == '"')
 
180
                {
 
181
                        in_quote = !in_quote;
 
182
                        saw_quote = true;
 
183
                }
 
184
 
 
185
                c = getc(fp);
 
186
        }
 
187
 
 
188
        /*
 
189
         * Put back the char right after the token (critical in case it is
 
190
         * EOL, since we need to detect end-of-line at next call).
 
191
         */
 
192
        if (c != EOF)
 
193
                ungetc(c, fp);
 
194
 
 
195
        *buf = '\0';
 
196
 
 
197
        if (!saw_quote &&
 
198
                (strcmp(start_buf, "all") == 0 ||
 
199
                 strcmp(start_buf, "sameuser") == 0 ||
 
200
                 strcmp(start_buf, "samegroup") == 0))
 
201
        {
 
202
                /* append newline to a magical keyword */
 
203
                *buf++ = '\n';
 
204
                *buf = '\0';
 
205
        }
 
206
}
 
207
 
 
208
/*
 
209
 *       Tokenize file and handle file inclusion and comma lists. We have
 
210
 *       to  break      apart  the      commas  to      expand  any  file names then
 
211
 *       reconstruct with commas.
 
212
 *
 
213
 * The result is always a palloc'd string.  If it's zero-length then
 
214
 * we have reached EOL.
 
215
 */
 
216
static char *
 
217
next_token_expand(const char *filename, FILE *file)
 
218
{
 
219
        char            buf[MAX_TOKEN];
 
220
        char       *comma_str = pstrdup("");
 
221
        bool            trailing_comma;
 
222
        char       *incbuf;
 
223
        int                     needed;
 
224
 
 
225
        do
 
226
        {
 
227
                next_token(file, buf, sizeof(buf));
 
228
                if (!buf[0])
 
229
                        break;
 
230
 
 
231
                if (buf[strlen(buf) - 1] == ',')
 
232
                {
 
233
                        trailing_comma = true;
 
234
                        buf[strlen(buf) - 1] = '\0';
 
235
                }
 
236
                else
 
237
                        trailing_comma = false;
 
238
 
 
239
                /* Is this referencing a file? */
 
240
                if (buf[0] == '@')
 
241
                        incbuf = tokenize_inc_file(filename, buf + 1);
 
242
                else
 
243
                        incbuf = pstrdup(buf);
 
244
 
 
245
                needed = strlen(comma_str) + strlen(incbuf) + 1;
 
246
                if (trailing_comma)
 
247
                        needed++;
 
248
                comma_str = repalloc(comma_str, needed);
 
249
                strcat(comma_str, incbuf);
 
250
                if (trailing_comma)
 
251
                        strcat(comma_str, MULTI_VALUE_SEP);
 
252
                pfree(incbuf);
 
253
        } while (trailing_comma);
 
254
 
 
255
        return comma_str;
 
256
}
 
257
 
 
258
 
 
259
/*
 
260
 * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
 
261
 */
 
262
static void
 
263
free_lines(List **lines, List **line_nums)
 
264
{
 
265
        /*
 
266
         * Either both must be non-NULL, or both must be NULL
 
267
         */
 
268
        Assert((*lines != NIL && *line_nums != NIL) ||
 
269
                   (*lines == NIL && *line_nums == NIL));
 
270
 
 
271
        if (*lines)
 
272
        {
 
273
                /*
 
274
                 * "lines" is a list of lists; each of those sublists consists of
 
275
                 * palloc'ed tokens, so we want to free each pointed-to token in a
 
276
                 * sublist, followed by the sublist itself, and finally the whole
 
277
                 * list.
 
278
                 */
 
279
                ListCell   *line;
 
280
 
 
281
                foreach(line, *lines)
 
282
                {
 
283
                        List       *ln = lfirst(line);
 
284
                        ListCell   *token;
 
285
 
 
286
                        foreach(token, ln)
 
287
                                pfree(lfirst(token));
 
288
                        /* free the sublist structure itself */
 
289
                        list_free(ln);
 
290
                }
 
291
                /* free the list structure itself */
 
292
                list_free(*lines);
 
293
                /* clear the static variable */
 
294
                *lines = NIL;
 
295
        }
 
296
 
 
297
        if (*line_nums)
 
298
        {
 
299
                list_free(*line_nums);
 
300
                *line_nums = NIL;
 
301
        }
 
302
}
 
303
 
 
304
 
 
305
static char *
 
306
tokenize_inc_file(const char *outer_filename,
 
307
                                  const char *inc_filename)
 
308
{
 
309
        char       *inc_fullname;
 
310
        FILE       *inc_file;
 
311
        List       *inc_lines;
 
312
        List       *inc_line_nums;
 
313
        ListCell   *line;
 
314
        char       *comma_str;
 
315
 
 
316
        if (is_absolute_path(inc_filename))
 
317
        {
 
318
                /* absolute path is taken as-is */
 
319
                inc_fullname = pstrdup(inc_filename);
 
320
        }
 
321
        else
 
322
        {
 
323
                /* relative path is relative to dir of calling file */
 
324
                inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
 
325
                                                                           strlen(inc_filename) + 1);
 
326
                strcpy(inc_fullname, outer_filename);
 
327
                get_parent_directory(inc_fullname);
 
328
                join_path_components(inc_fullname, inc_fullname, inc_filename);
 
329
                canonicalize_path(inc_fullname);
 
330
        }
 
331
 
 
332
        inc_file = AllocateFile(inc_fullname, "r");
 
333
        if (inc_file == NULL)
 
334
        {
 
335
                ereport(LOG,
 
336
                                (errcode_for_file_access(),
 
337
                                 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
 
338
                                                inc_filename, inc_fullname)));
 
339
                pfree(inc_fullname);
 
340
 
 
341
                /* return single space, it matches nothing */
 
342
                return pstrdup(" ");
 
343
        }
 
344
 
 
345
        /* There is possible recursion here if the file contains @ */
 
346
        tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
 
347
 
 
348
        FreeFile(inc_file);
 
349
        pfree(inc_fullname);
 
350
 
 
351
        /* Create comma-separated string from List */
 
352
        comma_str = pstrdup("");
 
353
        foreach(line, inc_lines)
 
354
        {
 
355
                List       *token_list = (List *) lfirst(line);
 
356
                ListCell   *token;
 
357
 
 
358
                foreach(token, token_list)
 
359
                {
 
360
                        int             oldlen = strlen(comma_str);
 
361
                        int             needed;
 
362
 
 
363
                        needed = oldlen + strlen(lfirst(token)) + 1;
 
364
                        if (oldlen > 0)
 
365
                                needed++;
 
366
                        comma_str = repalloc(comma_str, needed);
 
367
                        if (oldlen > 0)
 
368
                                strcat(comma_str, MULTI_VALUE_SEP);
 
369
                        strcat(comma_str, lfirst(token));
 
370
                }
 
371
        }
 
372
 
 
373
        free_lines(&inc_lines, &inc_line_nums);
 
374
 
 
375
        /* if file is empty, return single space rather than empty string */
 
376
        if (strlen(comma_str) == 0)
 
377
        {
 
378
                pfree(comma_str);
 
379
                return pstrdup(" ");
 
380
        }
 
381
 
 
382
        return comma_str;
 
383
}
 
384
 
 
385
 
 
386
/*
 
387
 * Tokenize the given file, storing the resulting data into two lists:
 
388
 * a list of sublists, each sublist containing the tokens in a line of
 
389
 * the file, and a list of line numbers.
 
390
 *
 
391
 * filename must be the absolute path to the target file.
 
392
 */
 
393
static void
 
394
tokenize_file(const char *filename, FILE *file,
 
395
                          List **lines, List **line_nums)
 
396
{
 
397
        List       *current_line = NIL;
 
398
        int                     line_number = 1;
 
399
        char       *buf;
 
400
 
 
401
        *lines = *line_nums = NIL;
 
402
 
 
403
        while (!feof(file))
 
404
        {
 
405
                buf = next_token_expand(filename, file);
 
406
 
 
407
                /* add token to list, unless we are at EOL or comment start */
 
408
                if (buf[0])
 
409
                {
 
410
                        if (current_line == NIL)
 
411
                        {
 
412
                                /* make a new line List, record its line number */
 
413
                                current_line = lappend(current_line, buf);
 
414
                                *lines = lappend(*lines, current_line);
 
415
                                *line_nums = lappend_int(*line_nums, line_number);
 
416
                        }
 
417
                        else
 
418
                        {
 
419
                                /* append token to current line's list */
 
420
                                current_line = lappend(current_line, buf);
 
421
                        }
 
422
                }
 
423
                else
 
424
                {
 
425
                        /* we are at real or logical EOL, so force a new line List */
 
426
                        current_line = NIL;
 
427
                        /* Advance line number whenever we reach EOL */
 
428
                        line_number++;
 
429
                        /* Don't forget to pfree the next_token_expand result */
 
430
                        pfree(buf);
 
431
                }
 
432
        }
 
433
}
 
434
 
 
435
 
 
436
/*
 
437
 * Compare two lines based on their user/group names.
 
438
 *
 
439
 * Used for qsort() sorting.
 
440
 */
 
441
static int
 
442
user_group_qsort_cmp(const void *list1, const void *list2)
 
443
{
 
444
        char       *user1 = linitial(*(List **) list1);
 
445
        char       *user2 = linitial(*(List **) list2);
 
446
 
 
447
        return strcmp(user1, user2);
 
448
}
 
449
 
 
450
 
 
451
/*
 
452
 * Compare two lines based on their user/group names.
 
453
 *
 
454
 * Used for bsearch() lookup.
 
455
 */
 
456
static int
 
457
user_group_bsearch_cmp(const void *user, const void *list)
 
458
{
 
459
        char       *user2 = linitial(*(List **) list);
 
460
 
 
461
        return strcmp(user, user2);
 
462
}
 
463
 
 
464
 
 
465
/*
 
466
 * Lookup a group name in the pg_group file
 
467
 */
 
468
static List **
 
469
get_group_line(const char *group)
 
470
{
 
471
        /* On some versions of Solaris, bsearch of zero items dumps core */
 
472
        if (group_length == 0)
 
473
                return NULL;
 
474
 
 
475
        return (List **) bsearch((void *) group,
 
476
                                                         (void *) group_sorted,
 
477
                                                         group_length,
 
478
                                                         sizeof(List *),
 
479
                                                         user_group_bsearch_cmp);
 
480
}
 
481
 
 
482
 
 
483
/*
 
484
 * Lookup a user name in the pg_shadow file
 
485
 */
 
486
List      **
 
487
get_user_line(const char *user)
 
488
{
 
489
        /* On some versions of Solaris, bsearch of zero items dumps core */
 
490
        if (user_length == 0)
 
491
                return NULL;
 
492
 
 
493
        return (List **) bsearch((void *) user,
 
494
                                                         (void *) user_sorted,
 
495
                                                         user_length,
 
496
                                                         sizeof(List *),
 
497
                                                         user_group_bsearch_cmp);
 
498
}
 
499
 
 
500
 
 
501
/*
 
502
 * Check group for a specific user.
 
503
 */
 
504
static bool
 
505
check_group(char *group, char *user)
 
506
{
 
507
        List      **line;
 
508
 
 
509
        if ((line = get_group_line(group)) != NULL)
 
510
        {
 
511
                ListCell   *line_item;
 
512
 
 
513
                /* skip over the group name */
 
514
                for_each_cell(line_item, lnext(list_head(*line)))
 
515
                {
 
516
                        if (strcmp(lfirst(line_item), user) == 0)
 
517
                                return true;
 
518
                }
 
519
        }
 
520
 
 
521
        return false;
 
522
}
 
523
 
 
524
/*
 
525
 * Check comma user list for a specific user, handle group names.
 
526
 */
 
527
static bool
 
528
check_user(char *user, char *param_str)
 
529
{
 
530
        char       *tok;
 
531
 
 
532
        for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
 
533
        {
 
534
                if (tok[0] == '+')
 
535
                {
 
536
                        if (check_group(tok + 1, user))
 
537
                                return true;
 
538
                }
 
539
                else if (strcmp(tok, user) == 0 ||
 
540
                                 strcmp(tok, "all\n") == 0)
 
541
                        return true;
 
542
        }
 
543
 
 
544
        return false;
 
545
}
 
546
 
 
547
/*
 
548
 * Check to see if db/user combination matches param string.
 
549
 */
 
550
static bool
 
551
check_db(char *dbname, char *user, char *param_str)
 
552
{
 
553
        char       *tok;
 
554
 
 
555
        for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
 
556
        {
 
557
                if (strcmp(tok, "all\n") == 0)
 
558
                        return true;
 
559
                else if (strcmp(tok, "sameuser\n") == 0)
 
560
                {
 
561
                        if (strcmp(dbname, user) == 0)
 
562
                                return true;
 
563
                }
 
564
                else if (strcmp(tok, "samegroup\n") == 0)
 
565
                {
 
566
                        if (check_group(dbname, user))
 
567
                                return true;
 
568
                }
 
569
                else if (strcmp(tok, dbname) == 0)
 
570
                        return true;
 
571
        }
 
572
        return false;
 
573
}
 
574
 
 
575
 
 
576
/*
 
577
 *      Scan the rest of a host record (after the mask field)
 
578
 *      and return the interpretation of it as *userauth_p, *auth_arg_p, and
 
579
 *      *error_p.  *line_item points to the next token of the line, and is
 
580
 *      advanced over successfully-read tokens.
 
581
 */
 
582
static void
 
583
parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
 
584
                           char **auth_arg_p, bool *error_p)
 
585
{
 
586
        char       *token;
 
587
 
 
588
        *auth_arg_p = NULL;
 
589
 
 
590
        if (!*line_item)
 
591
        {
 
592
                *error_p = true;
 
593
                return;
 
594
        }
 
595
 
 
596
        token = lfirst(*line_item);
 
597
        if (strcmp(token, "trust") == 0)
 
598
                *userauth_p = uaTrust;
 
599
        else if (strcmp(token, "ident") == 0)
 
600
                *userauth_p = uaIdent;
 
601
        else if (strcmp(token, "password") == 0)
 
602
                *userauth_p = uaPassword;
 
603
        else if (strcmp(token, "krb4") == 0)
 
604
                *userauth_p = uaKrb4;
 
605
        else if (strcmp(token, "krb5") == 0)
 
606
                *userauth_p = uaKrb5;
 
607
        else if (strcmp(token, "reject") == 0)
 
608
                *userauth_p = uaReject;
 
609
        else if (strcmp(token, "md5") == 0)
 
610
                *userauth_p = uaMD5;
 
611
        else if (strcmp(token, "crypt") == 0)
 
612
                *userauth_p = uaCrypt;
 
613
#ifdef USE_PAM
 
614
        else if (strcmp(token, "pam") == 0)
 
615
                *userauth_p = uaPAM;
 
616
#endif
 
617
        else
 
618
        {
 
619
                *error_p = true;
 
620
                return;
 
621
        }
 
622
        *line_item = lnext(*line_item);
 
623
 
 
624
        /* Get the authentication argument token, if any */
 
625
        if (*line_item)
 
626
        {
 
627
                token = lfirst(*line_item);
 
628
                *auth_arg_p = pstrdup(token);
 
629
                *line_item = lnext(*line_item);
 
630
                /* If there is more on the line, it is an error */
 
631
                if (*line_item)
 
632
                        *error_p = true;
 
633
        }
 
634
}
 
635
 
 
636
 
 
637
/*
 
638
 *      Process one line from the hba config file.
 
639
 *
 
640
 *      See if it applies to a connection from a host with IP address port->raddr
 
641
 *      to a database named port->database.  If so, return *found_p true
 
642
 *      and fill in the auth arguments into the appropriate port fields.
 
643
 *      If not, leave *found_p as it was.  If the record has a syntax error,
 
644
 *      return *error_p true, after issuing a message to the log.  If no error,
 
645
 *      leave *error_p as it was.
 
646
 */
 
647
static void
 
648
parse_hba(List *line, int line_num, hbaPort *port,
 
649
                  bool *found_p, bool *error_p)
 
650
{
 
651
        char       *token;
 
652
        char       *db;
 
653
        char       *user;
 
654
        struct addrinfo *gai_result;
 
655
        struct addrinfo hints;
 
656
        int                     ret;
 
657
        struct sockaddr_storage addr;
 
658
        struct sockaddr_storage mask;
 
659
        char       *cidr_slash;
 
660
        ListCell   *line_item;
 
661
 
 
662
        line_item = list_head(line);
 
663
        /* Check the record type. */
 
664
        token = lfirst(line_item);
 
665
        if (strcmp(token, "local") == 0)
 
666
        {
 
667
                /* Get the database. */
 
668
                line_item = lnext(line_item);
 
669
                if (!line_item)
 
670
                        goto hba_syntax;
 
671
                db = lfirst(line_item);
 
672
 
 
673
                /* Get the user. */
 
674
                line_item = lnext(line_item);
 
675
                if (!line_item)
 
676
                        goto hba_syntax;
 
677
                user = lfirst(line_item);
 
678
 
 
679
                line_item = lnext(line_item);
 
680
                if (!line_item)
 
681
                        goto hba_syntax;
 
682
 
 
683
                /* Read the rest of the line. */
 
684
                parse_hba_auth(&line_item, &port->auth_method,
 
685
                                           &port->auth_arg, error_p);
 
686
                if (*error_p)
 
687
                        goto hba_syntax;
 
688
 
 
689
                /* Disallow auth methods that always need TCP/IP sockets to work */
 
690
                if (port->auth_method == uaKrb4 ||
 
691
                        port->auth_method == uaKrb5)
 
692
                        goto hba_syntax;
 
693
 
 
694
                /* Does not match if connection isn't AF_UNIX */
 
695
                if (!IS_AF_UNIX(port->raddr.addr.ss_family))
 
696
                        return;
 
697
        }
 
698
        else if (strcmp(token, "host") == 0
 
699
                         || strcmp(token, "hostssl") == 0
 
700
                         || strcmp(token, "hostnossl") == 0)
 
701
        {
 
702
 
 
703
                if (token[4] == 's')    /* "hostssl" */
 
704
                {
 
705
#ifdef USE_SSL
 
706
                        /* Record does not match if we are not on an SSL connection */
 
707
                        if (!port->ssl)
 
708
                                return;
 
709
 
 
710
                        /* Placeholder to require specific SSL level, perhaps? */
 
711
                        /* Or a client certificate */
 
712
 
 
713
                        /* Since we were on SSL, proceed as with normal 'host' mode */
 
714
#else
 
715
                        /* We don't accept this keyword at all if no SSL support */
 
716
                        goto hba_syntax;
 
717
#endif
 
718
                }
 
719
#ifdef USE_SSL
 
720
                else if (token[4] == 'n')               /* "hostnossl" */
 
721
                {
 
722
                        /* Record does not match if we are on an SSL connection */
 
723
                        if (port->ssl)
 
724
                                return;
 
725
                }
 
726
#endif
 
727
 
 
728
                /* Get the database. */
 
729
                line_item = lnext(line_item);
 
730
                if (!line_item)
 
731
                        goto hba_syntax;
 
732
                db = lfirst(line_item);
 
733
 
 
734
                /* Get the user. */
 
735
                line_item = lnext(line_item);
 
736
                if (!line_item)
 
737
                        goto hba_syntax;
 
738
                user = lfirst(line_item);
 
739
 
 
740
                /* Read the IP address field. (with or without CIDR netmask) */
 
741
                line_item = lnext(line_item);
 
742
                if (!line_item)
 
743
                        goto hba_syntax;
 
744
                token = lfirst(line_item);
 
745
 
 
746
                /* Check if it has a CIDR suffix and if so isolate it */
 
747
                cidr_slash = strchr(token, '/');
 
748
                if (cidr_slash)
 
749
                        *cidr_slash = '\0';
 
750
 
 
751
                /* Get the IP address either way */
 
752
                hints.ai_flags = AI_NUMERICHOST;
 
753
                hints.ai_family = PF_UNSPEC;
 
754
                hints.ai_socktype = 0;
 
755
                hints.ai_protocol = 0;
 
756
                hints.ai_addrlen = 0;
 
757
                hints.ai_canonname = NULL;
 
758
                hints.ai_addr = NULL;
 
759
                hints.ai_next = NULL;
 
760
 
 
761
                ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
 
762
                if (ret || !gai_result)
 
763
                {
 
764
                        ereport(LOG,
 
765
                                        (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
766
                                         errmsg("invalid IP address \"%s\" in file \"%s\" line %d: %s",
 
767
                                                        token, HbaFileName, line_num,
 
768
                                                        gai_strerror(ret))));
 
769
                        if (cidr_slash)
 
770
                                *cidr_slash = '/';
 
771
                        if (gai_result)
 
772
                                freeaddrinfo_all(hints.ai_family, gai_result);
 
773
                        goto hba_other_error;
 
774
                }
 
775
 
 
776
                if (cidr_slash)
 
777
                        *cidr_slash = '/';
 
778
 
 
779
                memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
 
780
                freeaddrinfo_all(hints.ai_family, gai_result);
 
781
 
 
782
                /* Get the netmask */
 
783
                if (cidr_slash)
 
784
                {
 
785
                        if (SockAddr_cidr_mask(&mask, cidr_slash + 1, addr.ss_family) < 0)
 
786
                                goto hba_syntax;
 
787
                }
 
788
                else
 
789
                {
 
790
                        /* Read the mask field. */
 
791
                        line_item = lnext(line_item);
 
792
                        if (!line_item)
 
793
                                goto hba_syntax;
 
794
                        token = lfirst(line_item);
 
795
 
 
796
                        ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
 
797
                        if (ret || !gai_result)
 
798
                        {
 
799
                                ereport(LOG,
 
800
                                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
801
                                                 errmsg("invalid IP mask \"%s\" in file \"%s\" line %d: %s",
 
802
                                                                token, HbaFileName, line_num,
 
803
                                                                gai_strerror(ret))));
 
804
                                if (gai_result)
 
805
                                        freeaddrinfo_all(hints.ai_family, gai_result);
 
806
                                goto hba_other_error;
 
807
                        }
 
808
 
 
809
                        memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
 
810
                        freeaddrinfo_all(hints.ai_family, gai_result);
 
811
 
 
812
                        if (addr.ss_family != mask.ss_family)
 
813
                        {
 
814
                                ereport(LOG,
 
815
                                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
816
                                                 errmsg("IP address and mask do not match in file \"%s\" line %d",
 
817
                                                                HbaFileName, line_num)));
 
818
                                goto hba_other_error;
 
819
                        }
 
820
                }
 
821
 
 
822
                if (addr.ss_family != port->raddr.addr.ss_family)
 
823
                {
 
824
                        /*
 
825
                         * Wrong address family.  We allow only one case: if the file
 
826
                         * has IPv4 and the port is IPv6, promote the file address to
 
827
                         * IPv6 and try to match that way.
 
828
                         */
 
829
#ifdef HAVE_IPV6
 
830
                        if (addr.ss_family == AF_INET &&
 
831
                                port->raddr.addr.ss_family == AF_INET6)
 
832
                        {
 
833
                                promote_v4_to_v6_addr(&addr);
 
834
                                promote_v4_to_v6_mask(&mask);
 
835
                        }
 
836
                        else
 
837
#endif   /* HAVE_IPV6 */
 
838
                        {
 
839
                                /* Line doesn't match client port, so ignore it. */
 
840
                                return;
 
841
                        }
 
842
                }
 
843
 
 
844
                /* Ignore line if client port is not in the matching addr range. */
 
845
                if (!rangeSockAddr(&port->raddr.addr, &addr, &mask))
 
846
                        return;
 
847
 
 
848
                /* Read the rest of the line. */
 
849
                line_item = lnext(line_item);
 
850
                if (!line_item)
 
851
                        goto hba_syntax;
 
852
                parse_hba_auth(&line_item, &port->auth_method,
 
853
                                           &port->auth_arg, error_p);
 
854
                if (*error_p)
 
855
                        goto hba_syntax;
 
856
        }
 
857
        else
 
858
                goto hba_syntax;
 
859
 
 
860
        /* Does the entry match database and user? */
 
861
        if (!check_db(port->database_name, port->user_name, db))
 
862
                return;
 
863
        if (!check_user(port->user_name, user))
 
864
                return;
 
865
 
 
866
        /* Success */
 
867
        *found_p = true;
 
868
        return;
 
869
 
 
870
hba_syntax:
 
871
        if (line_item)
 
872
                ereport(LOG,
 
873
                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
874
                                 errmsg("invalid entry in file \"%s\" at line %d, token \"%s\"",
 
875
                                                HbaFileName, line_num,
 
876
                                                (char *) lfirst(line_item))));
 
877
        else
 
878
                ereport(LOG,
 
879
                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
880
                        errmsg("missing field in file \"%s\" at end of line %d",
 
881
                                   HbaFileName, line_num)));
 
882
 
 
883
        /* Come here if suitable message already logged */
 
884
hba_other_error:
 
885
        *error_p = true;
 
886
}
 
887
 
 
888
 
 
889
/*
 
890
 *      Scan the (pre-parsed) hba file line by line, looking for a match
 
891
 *      to the port's connection request.
 
892
 */
 
893
static bool
 
894
check_hba(hbaPort *port)
 
895
{
 
896
        bool            found_entry = false;
 
897
        bool            error = false;
 
898
        ListCell   *line;
 
899
        ListCell   *line_num;
 
900
 
 
901
        forboth(line, hba_lines, line_num, hba_line_nums)
 
902
        {
 
903
                parse_hba(lfirst(line), lfirst_int(line_num),
 
904
                                  port, &found_entry, &error);
 
905
                if (found_entry || error)
 
906
                        break;
 
907
        }
 
908
 
 
909
        if (!error)
 
910
        {
 
911
                /* If no matching entry was found, synthesize 'reject' entry. */
 
912
                if (!found_entry)
 
913
                        port->auth_method = uaReject;
 
914
                return true;
 
915
        }
 
916
        else
 
917
                return false;
 
918
}
 
919
 
 
920
 
 
921
/*
 
922
 *       Load group/user name mapping file
 
923
 */
 
924
void
 
925
load_group(void)
 
926
{
 
927
        char       *filename;
 
928
        FILE       *group_file;
 
929
 
 
930
        /* Discard any old data */
 
931
        if (group_lines || group_line_nums)
 
932
                free_lines(&group_lines, &group_line_nums);
 
933
        if (group_sorted)
 
934
                pfree(group_sorted);
 
935
        group_sorted = NULL;
 
936
        group_length = 0;
 
937
 
 
938
        /* Read in the file contents */
 
939
        filename = group_getfilename();
 
940
        group_file = AllocateFile(filename, "r");
 
941
 
 
942
        if (group_file == NULL)
 
943
        {
 
944
                /* no complaint if not there */
 
945
                if (errno != ENOENT)
 
946
                        ereport(LOG,
 
947
                                        (errcode_for_file_access(),
 
948
                                         errmsg("could not open file \"%s\": %m", filename)));
 
949
                pfree(filename);
 
950
                return;
 
951
        }
 
952
 
 
953
        tokenize_file(filename, group_file, &group_lines, &group_line_nums);
 
954
 
 
955
        FreeFile(group_file);
 
956
        pfree(filename);
 
957
 
 
958
        /* create sorted lines for binary searching */
 
959
        group_length = list_length(group_lines);
 
960
        if (group_length)
 
961
        {
 
962
                int                     i = 0;
 
963
                ListCell   *line;
 
964
 
 
965
                group_sorted = palloc(group_length * sizeof(List *));
 
966
 
 
967
                foreach(line, group_lines)
 
968
                        group_sorted[i++] = lfirst(line);
 
969
 
 
970
                qsort((void *) group_sorted,
 
971
                          group_length,
 
972
                          sizeof(List *),
 
973
                          user_group_qsort_cmp);
 
974
        }
 
975
}
 
976
 
 
977
 
 
978
/*
 
979
 *       Load user/password mapping file
 
980
 */
 
981
void
 
982
load_user(void)
 
983
{
 
984
        char       *filename;
 
985
        FILE       *user_file;
 
986
 
 
987
        /* Discard any old data */
 
988
        if (user_lines || user_line_nums)
 
989
                free_lines(&user_lines, &user_line_nums);
 
990
        if (user_sorted)
 
991
                pfree(user_sorted);
 
992
        user_sorted = NULL;
 
993
        user_length = 0;
 
994
 
 
995
        /* Read in the file contents */
 
996
        filename = user_getfilename();
 
997
        user_file = AllocateFile(filename, "r");
 
998
 
 
999
        if (user_file == NULL)
 
1000
        {
 
1001
                /* no complaint if not there */
 
1002
                if (errno != ENOENT)
 
1003
                        ereport(LOG,
 
1004
                                        (errcode_for_file_access(),
 
1005
                                         errmsg("could not open file \"%s\": %m", filename)));
 
1006
                pfree(filename);
 
1007
                return;
 
1008
        }
 
1009
 
 
1010
        tokenize_file(filename, user_file, &user_lines, &user_line_nums);
 
1011
 
 
1012
        FreeFile(user_file);
 
1013
        pfree(filename);
 
1014
 
 
1015
        /* create sorted lines for binary searching */
 
1016
        user_length = list_length(user_lines);
 
1017
        if (user_length)
 
1018
        {
 
1019
                int                     i = 0;
 
1020
                ListCell   *line;
 
1021
 
 
1022
                user_sorted = palloc(user_length * sizeof(List *));
 
1023
 
 
1024
                foreach(line, user_lines)
 
1025
                        user_sorted[i++] = lfirst(line);
 
1026
 
 
1027
                qsort((void *) user_sorted,
 
1028
                          user_length,
 
1029
                          sizeof(List *),
 
1030
                          user_group_qsort_cmp);
 
1031
        }
 
1032
}
 
1033
 
 
1034
 
 
1035
/*
 
1036
 * Read the config file and create a List of Lists of tokens in the file.
 
1037
 */
 
1038
void
 
1039
load_hba(void)
 
1040
{
 
1041
        FILE       *file;
 
1042
 
 
1043
        if (hba_lines || hba_line_nums)
 
1044
                free_lines(&hba_lines, &hba_line_nums);
 
1045
 
 
1046
        file = AllocateFile(HbaFileName, "r");
 
1047
        /* Failure is fatal since with no HBA entries we can do nothing... */
 
1048
        if (file == NULL)
 
1049
                ereport(FATAL,
 
1050
                                (errcode_for_file_access(),
 
1051
                                 errmsg("could not open configuration file \"%s\": %m",
 
1052
                                                HbaFileName)));
 
1053
 
 
1054
        tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
 
1055
        FreeFile(file);
 
1056
}
 
1057
 
 
1058
 
 
1059
/*
 
1060
 *      Process one line from the ident config file.
 
1061
 *
 
1062
 *      Take the line and compare it to the needed map, pg_user and ident_user.
 
1063
 *      *found_p and *error_p are set according to our results.
 
1064
 */
 
1065
static void
 
1066
parse_ident_usermap(List *line, int line_number, const char *usermap_name,
 
1067
                                        const char *pg_user, const char *ident_user,
 
1068
                                        bool *found_p, bool *error_p)
 
1069
{
 
1070
        ListCell   *line_item;
 
1071
        char       *token;
 
1072
        char       *file_map;
 
1073
        char       *file_pguser;
 
1074
        char       *file_ident_user;
 
1075
 
 
1076
        *found_p = false;
 
1077
        *error_p = false;
 
1078
 
 
1079
        Assert(line != NIL);
 
1080
        line_item = list_head(line);
 
1081
 
 
1082
        /* Get the map token (must exist) */
 
1083
        token = lfirst(line_item);
 
1084
        file_map = token;
 
1085
 
 
1086
        /* Get the ident user token (must be provided) */
 
1087
        line_item = lnext(line_item);
 
1088
        if (!line)
 
1089
                goto ident_syntax;
 
1090
        token = lfirst(line_item);
 
1091
        file_ident_user = token;
 
1092
 
 
1093
        /* Get the PG username token */
 
1094
        line_item = lnext(line_item);
 
1095
        if (!line)
 
1096
                goto ident_syntax;
 
1097
        token = lfirst(line_item);
 
1098
        file_pguser = token;
 
1099
 
 
1100
        /* Match? */
 
1101
        if (strcmp(file_map, usermap_name) == 0 &&
 
1102
                strcmp(file_pguser, pg_user) == 0 &&
 
1103
                strcmp(file_ident_user, ident_user) == 0)
 
1104
                *found_p = true;
 
1105
        return;
 
1106
 
 
1107
ident_syntax:
 
1108
        if (line_item)
 
1109
                ereport(LOG,
 
1110
                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
1111
                                 errmsg("invalid entry in file \"%s\" at line %d, token \"%s\"",
 
1112
                                                IdentFileName, line_number,
 
1113
                                                (const char *) lfirst(line_item))));
 
1114
        else
 
1115
                ereport(LOG,
 
1116
                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
1117
                  errmsg("missing entry in file \"%s\" at end of line %d",
 
1118
                                 IdentFileName, line_number)));
 
1119
 
 
1120
        *error_p = true;
 
1121
}
 
1122
 
 
1123
 
 
1124
/*
 
1125
 *      Scan the (pre-parsed) ident usermap file line by line, looking for a match
 
1126
 *
 
1127
 *      See if the user with ident username "ident_user" is allowed to act
 
1128
 *      as Postgres user "pguser" according to usermap "usermap_name".
 
1129
 *
 
1130
 *      Special case: For usermap "sameuser", don't look in the usermap
 
1131
 *      file.  That's an implied map where "pguser" must be identical to
 
1132
 *      "ident_user" in order to be authorized.
 
1133
 *
 
1134
 *      Iff authorized, return true.
 
1135
 */
 
1136
static bool
 
1137
check_ident_usermap(const char *usermap_name,
 
1138
                                        const char *pg_user,
 
1139
                                        const char *ident_user)
 
1140
{
 
1141
        bool            found_entry = false,
 
1142
                                error = false;
 
1143
 
 
1144
        if (usermap_name == NULL || usermap_name[0] == '\0')
 
1145
        {
 
1146
                ereport(LOG,
 
1147
                                (errcode(ERRCODE_CONFIG_FILE_ERROR),
 
1148
                errmsg("cannot use Ident authentication without usermap field")));
 
1149
                found_entry = false;
 
1150
        }
 
1151
        else if (strcmp(usermap_name, "sameuser\n") == 0)
 
1152
        {
 
1153
                if (strcmp(pg_user, ident_user) == 0)
 
1154
                        found_entry = true;
 
1155
                else
 
1156
                        found_entry = false;
 
1157
        }
 
1158
        else
 
1159
        {
 
1160
                ListCell   *line_cell,
 
1161
                                   *num_cell;
 
1162
 
 
1163
                forboth(line_cell, ident_lines, num_cell, ident_line_nums)
 
1164
                {
 
1165
                        parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
 
1166
                                                                usermap_name, pg_user, ident_user,
 
1167
                                                                &found_entry, &error);
 
1168
                        if (found_entry || error)
 
1169
                                break;
 
1170
                }
 
1171
        }
 
1172
        return found_entry;
 
1173
}
 
1174
 
 
1175
 
 
1176
/*
 
1177
 * Read the ident config file and create a List of Lists of tokens in the file.
 
1178
 */
 
1179
void
 
1180
load_ident(void)
 
1181
{
 
1182
        FILE       *file;
 
1183
 
 
1184
        if (ident_lines || ident_line_nums)
 
1185
                free_lines(&ident_lines, &ident_line_nums);
 
1186
 
 
1187
        file = AllocateFile(IdentFileName, "r");
 
1188
        if (file == NULL)
 
1189
        {
 
1190
                /* not fatal ... we just won't do any special ident maps */
 
1191
                ereport(LOG,
 
1192
                                (errcode_for_file_access(),
 
1193
                                 errmsg("could not open Ident usermap file \"%s\": %m",
 
1194
                                                IdentFileName)));
 
1195
        }
 
1196
        else
 
1197
        {
 
1198
                tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
 
1199
                FreeFile(file);
 
1200
        }
 
1201
}
 
1202
 
 
1203
 
 
1204
/*
 
1205
 *      Parse the string "*ident_response" as a response from a query to an Ident
 
1206
 *      server.  If it's a normal response indicating a user name, return true
 
1207
 *      and store the user name at *ident_user. If it's anything else,
 
1208
 *      return false.
 
1209
 */
 
1210
static bool
 
1211
interpret_ident_response(const char *ident_response,
 
1212
                                                 char *ident_user)
 
1213
{
 
1214
        const char *cursor = ident_response;            /* Cursor into
 
1215
                                                                                                 * *ident_response */
 
1216
 
 
1217
        /*
 
1218
         * Ident's response, in the telnet tradition, should end in crlf
 
1219
         * (\r\n).
 
1220
         */
 
1221
        if (strlen(ident_response) < 2)
 
1222
                return false;
 
1223
        else if (ident_response[strlen(ident_response) - 2] != '\r')
 
1224
                return false;
 
1225
        else
 
1226
        {
 
1227
                while (*cursor != ':' && *cursor != '\r')
 
1228
                        cursor++;                       /* skip port field */
 
1229
 
 
1230
                if (*cursor != ':')
 
1231
                        return false;
 
1232
                else
 
1233
                {
 
1234
                        /* We're positioned to colon before response type field */
 
1235
                        char            response_type[80];
 
1236
                        int                     i;              /* Index into *response_type */
 
1237
 
 
1238
                        cursor++;                       /* Go over colon */
 
1239
                        while (pg_isblank(*cursor))
 
1240
                                cursor++;               /* skip blanks */
 
1241
                        i = 0;
 
1242
                        while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) &&
 
1243
                                   i < (int) (sizeof(response_type) - 1))
 
1244
                                response_type[i++] = *cursor++;
 
1245
                        response_type[i] = '\0';
 
1246
                        while (pg_isblank(*cursor))
 
1247
                                cursor++;               /* skip blanks */
 
1248
                        if (strcmp(response_type, "USERID") != 0)
 
1249
                                return false;
 
1250
                        else
 
1251
                        {
 
1252
                                /*
 
1253
                                 * It's a USERID response.  Good.  "cursor" should be
 
1254
                                 * pointing to the colon that precedes the operating
 
1255
                                 * system type.
 
1256
                                 */
 
1257
                                if (*cursor != ':')
 
1258
                                        return false;
 
1259
                                else
 
1260
                                {
 
1261
                                        cursor++;       /* Go over colon */
 
1262
                                        /* Skip over operating system field. */
 
1263
                                        while (*cursor != ':' && *cursor != '\r')
 
1264
                                                cursor++;
 
1265
                                        if (*cursor != ':')
 
1266
                                                return false;
 
1267
                                        else
 
1268
                                        {
 
1269
                                                int                     i;      /* Index into *ident_user */
 
1270
 
 
1271
                                                cursor++;               /* Go over colon */
 
1272
                                                while (pg_isblank(*cursor))
 
1273
                                                        cursor++;       /* skip blanks */
 
1274
                                                /* Rest of line is user name.  Copy it over. */
 
1275
                                                i = 0;
 
1276
                                                while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
 
1277
                                                        ident_user[i++] = *cursor++;
 
1278
                                                ident_user[i] = '\0';
 
1279
                                                return true;
 
1280
                                        }
 
1281
                                }
 
1282
                        }
 
1283
                }
 
1284
        }
 
1285
}
 
1286
 
 
1287
 
 
1288
/*
 
1289
 *      Talk to the ident server on host "remote_ip_addr" and find out who
 
1290
 *      owns the tcp connection from his port "remote_port" to port
 
1291
 *      "local_port_addr" on host "local_ip_addr".      Return the user name the
 
1292
 *      ident server gives as "*ident_user".
 
1293
 *
 
1294
 *      IP addresses and port numbers are in network byte order.
 
1295
 *
 
1296
 *      But iff we're unable to get the information from ident, return false.
 
1297
 */
 
1298
static bool
 
1299
ident_inet(const SockAddr remote_addr,
 
1300
                   const SockAddr local_addr,
 
1301
                   char *ident_user)
 
1302
{
 
1303
        int                     sock_fd,                /* File descriptor for socket on which we
 
1304
                                                                 * talk to Ident */
 
1305
                                rc;                             /* Return code from a locally called
 
1306
                                                                 * function */
 
1307
        bool            ident_return;
 
1308
        char            remote_addr_s[NI_MAXHOST];
 
1309
        char            remote_port[NI_MAXSERV];
 
1310
        char            local_addr_s[NI_MAXHOST];
 
1311
        char            local_port[NI_MAXSERV];
 
1312
        char            ident_port[NI_MAXSERV];
 
1313
        char            ident_query[80];
 
1314
        char            ident_response[80 + IDENT_USERNAME_MAX];
 
1315
        struct addrinfo *ident_serv = NULL,
 
1316
                           *la = NULL,
 
1317
                                hints;
 
1318
 
 
1319
        /*
 
1320
         * Might look a little weird to first convert it to text and then back
 
1321
         * to sockaddr, but it's protocol independent.
 
1322
         */
 
1323
        getnameinfo_all(&remote_addr.addr, remote_addr.salen,
 
1324
                                        remote_addr_s, sizeof(remote_addr_s),
 
1325
                                        remote_port, sizeof(remote_port),
 
1326
                                        NI_NUMERICHOST | NI_NUMERICSERV);
 
1327
        getnameinfo_all(&local_addr.addr, local_addr.salen,
 
1328
                                        local_addr_s, sizeof(local_addr_s),
 
1329
                                        local_port, sizeof(local_port),
 
1330
                                        NI_NUMERICHOST | NI_NUMERICSERV);
 
1331
 
 
1332
        snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT);
 
1333
        hints.ai_flags = AI_NUMERICHOST;
 
1334
        hints.ai_family = remote_addr.addr.ss_family;
 
1335
        hints.ai_socktype = SOCK_STREAM;
 
1336
        hints.ai_protocol = 0;
 
1337
        hints.ai_addrlen = 0;
 
1338
        hints.ai_canonname = NULL;
 
1339
        hints.ai_addr = NULL;
 
1340
        hints.ai_next = NULL;
 
1341
        rc = getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);
 
1342
        if (rc || !ident_serv)
 
1343
        {
 
1344
                if (ident_serv)
 
1345
                        freeaddrinfo_all(hints.ai_family, ident_serv);
 
1346
                return false;                   /* we don't expect this to happen */
 
1347
        }
 
1348
 
 
1349
        hints.ai_flags = AI_NUMERICHOST;
 
1350
        hints.ai_family = local_addr.addr.ss_family;
 
1351
        hints.ai_socktype = SOCK_STREAM;
 
1352
        hints.ai_protocol = 0;
 
1353
        hints.ai_addrlen = 0;
 
1354
        hints.ai_canonname = NULL;
 
1355
        hints.ai_addr = NULL;
 
1356
        hints.ai_next = NULL;
 
1357
        rc = getaddrinfo_all(local_addr_s, NULL, &hints, &la);
 
1358
        if (rc || !la)
 
1359
        {
 
1360
                if (la)
 
1361
                        freeaddrinfo_all(hints.ai_family, la);
 
1362
                return false;                   /* we don't expect this to happen */
 
1363
        }
 
1364
 
 
1365
        sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
 
1366
                                         ident_serv->ai_protocol);
 
1367
        if (sock_fd < 0)
 
1368
        {
 
1369
                ereport(LOG,
 
1370
                                (errcode_for_socket_access(),
 
1371
                        errmsg("could not create socket for Ident connection: %m")));
 
1372
                ident_return = false;
 
1373
                goto ident_inet_done;
 
1374
        }
 
1375
 
 
1376
        /*
 
1377
         * Bind to the address which the client originally contacted,
 
1378
         * otherwise the ident server won't be able to match up the right
 
1379
         * connection. This is necessary if the PostgreSQL server is running
 
1380
         * on an IP alias.
 
1381
         */
 
1382
        rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
 
1383
        if (rc != 0)
 
1384
        {
 
1385
                ereport(LOG,
 
1386
                                (errcode_for_socket_access(),
 
1387
                                 errmsg("could not bind to local address \"%s\": %m",
 
1388
                                                local_addr_s)));
 
1389
                ident_return = false;
 
1390
                goto ident_inet_done;
 
1391
        }
 
1392
 
 
1393
        rc = connect(sock_fd, ident_serv->ai_addr,
 
1394
                                 ident_serv->ai_addrlen);
 
1395
        if (rc != 0)
 
1396
        {
 
1397
                ereport(LOG,
 
1398
                                (errcode_for_socket_access(),
 
1399
                                 errmsg("could not connect to Ident server at address \"%s\", port %s: %m",
 
1400
                                                remote_addr_s, ident_port)));
 
1401
                ident_return = false;
 
1402
                goto ident_inet_done;
 
1403
        }
 
1404
 
 
1405
        /* The query we send to the Ident server */
 
1406
        snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n",
 
1407
                         remote_port, local_port);
 
1408
 
 
1409
        /* loop in case send is interrupted */
 
1410
        do
 
1411
        {
 
1412
                rc = send(sock_fd, ident_query, strlen(ident_query), 0);
 
1413
        } while (rc < 0 && errno == EINTR);
 
1414
 
 
1415
        if (rc < 0)
 
1416
        {
 
1417
                ereport(LOG,
 
1418
                                (errcode_for_socket_access(),
 
1419
                                 errmsg("could not send query to Ident server at address \"%s\", port %s: %m",
 
1420
                                                remote_addr_s, ident_port)));
 
1421
                ident_return = false;
 
1422
                goto ident_inet_done;
 
1423
        }
 
1424
 
 
1425
        do
 
1426
        {
 
1427
                rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
 
1428
        } while (rc < 0 && errno == EINTR);
 
1429
 
 
1430
        if (rc < 0)
 
1431
        {
 
1432
                ereport(LOG,
 
1433
                                (errcode_for_socket_access(),
 
1434
                                 errmsg("could not receive response from Ident server at address \"%s\", port %s: %m",
 
1435
                                                remote_addr_s, ident_port)));
 
1436
                ident_return = false;
 
1437
                goto ident_inet_done;
 
1438
        }
 
1439
 
 
1440
        ident_response[rc] = '\0';
 
1441
        ident_return = interpret_ident_response(ident_response, ident_user);
 
1442
        if (!ident_return)
 
1443
                ereport(LOG,
 
1444
                (errmsg("invalidly formatted response from Ident server: \"%s\"",
 
1445
                                ident_response)));
 
1446
 
 
1447
ident_inet_done:
 
1448
        if (sock_fd >= 0)
 
1449
                closesocket(sock_fd);
 
1450
        freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
 
1451
        freeaddrinfo_all(local_addr.addr.ss_family, la);
 
1452
        return ident_return;
 
1453
}
 
1454
 
 
1455
/*
 
1456
 *      Ask kernel about the credentials of the connecting process and
 
1457
 *      determine the symbolic name of the corresponding user.
 
1458
 *
 
1459
 *      Returns either true and the username put into "ident_user",
 
1460
 *      or false if we were unable to determine the username.
 
1461
 */
 
1462
#ifdef HAVE_UNIX_SOCKETS
 
1463
 
 
1464
static bool
 
1465
ident_unix(int sock, char *ident_user)
 
1466
{
 
1467
#if defined(HAVE_GETPEEREID)
 
1468
        /* OpenBSD style:  */
 
1469
        uid_t           uid;
 
1470
        gid_t           gid;
 
1471
        struct passwd *pass;
 
1472
 
 
1473
        errno = 0;
 
1474
        if (getpeereid(sock, &uid, &gid) != 0)
 
1475
        {
 
1476
                /* We didn't get a valid credentials struct. */
 
1477
                ereport(LOG,
 
1478
                                (errcode_for_socket_access(),
 
1479
                                 errmsg("could not get peer credentials: %m")));
 
1480
                return false;
 
1481
        }
 
1482
 
 
1483
        pass = getpwuid(uid);
 
1484
 
 
1485
        if (pass == NULL)
 
1486
        {
 
1487
                ereport(LOG,
 
1488
                                (errmsg("local user with ID %d does not exist",
 
1489
                                                (int) uid)));
 
1490
                return false;
 
1491
        }
 
1492
 
 
1493
        StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
 
1494
 
 
1495
        return true;
 
1496
 
 
1497
#elif defined(SO_PEERCRED)
 
1498
        /* Linux style: use getsockopt(SO_PEERCRED) */
 
1499
        struct ucred peercred;
 
1500
        ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
 
1501
        struct passwd *pass;
 
1502
 
 
1503
        errno = 0;
 
1504
        if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
 
1505
                so_len != sizeof(peercred))
 
1506
        {
 
1507
                /* We didn't get a valid credentials struct. */
 
1508
                ereport(LOG,
 
1509
                                (errcode_for_socket_access(),
 
1510
                                 errmsg("could not get peer credentials: %m")));
 
1511
                return false;
 
1512
        }
 
1513
 
 
1514
        pass = getpwuid(peercred.uid);
 
1515
 
 
1516
        if (pass == NULL)
 
1517
        {
 
1518
                ereport(LOG,
 
1519
                                (errmsg("local user with ID %d does not exist",
 
1520
                                                (int) peercred.uid)));
 
1521
                return false;
 
1522
        }
 
1523
 
 
1524
        StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
 
1525
 
 
1526
        return true;
 
1527
 
 
1528
#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
 
1529
        struct msghdr msg;
 
1530
 
 
1531
/* Credentials structure */
 
1532
#if defined(HAVE_STRUCT_CMSGCRED)
 
1533
        typedef struct cmsgcred Cred;
 
1534
 
 
1535
#define cruid cmcred_uid
 
1536
#elif defined(HAVE_STRUCT_FCRED)
 
1537
        typedef struct fcred Cred;
 
1538
 
 
1539
#define cruid fc_uid
 
1540
#elif defined(HAVE_STRUCT_SOCKCRED)
 
1541
        typedef struct sockcred Cred;
 
1542
 
 
1543
#define cruid sc_uid
 
1544
#endif
 
1545
        Cred       *cred;
 
1546
 
 
1547
        /* Compute size without padding */
 
1548
        char            cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))];   /* for NetBSD */
 
1549
 
 
1550
        /* Point to start of first structure */
 
1551
        struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
 
1552
 
 
1553
        struct iovec iov;
 
1554
        char            buf;
 
1555
        struct passwd *pw;
 
1556
 
 
1557
        memset(&msg, 0, sizeof(msg));
 
1558
        msg.msg_iov = &iov;
 
1559
        msg.msg_iovlen = 1;
 
1560
        msg.msg_control = (char *) cmsg;
 
1561
        msg.msg_controllen = sizeof(cmsgmem);
 
1562
        memset(cmsg, 0, sizeof(cmsgmem));
 
1563
 
 
1564
        /*
 
1565
         * The one character which is received here is not meaningful; its
 
1566
         * purposes is only to make sure that recvmsg() blocks long enough for
 
1567
         * the other side to send its credentials.
 
1568
         */
 
1569
        iov.iov_base = &buf;
 
1570
        iov.iov_len = 1;
 
1571
 
 
1572
        if (recvmsg(sock, &msg, 0) < 0 ||
 
1573
                cmsg->cmsg_len < sizeof(cmsgmem) ||
 
1574
                cmsg->cmsg_type != SCM_CREDS)
 
1575
        {
 
1576
                ereport(LOG,
 
1577
                                (errcode_for_socket_access(),
 
1578
                                 errmsg("could not get peer credentials: %m")));
 
1579
                return false;
 
1580
        }
 
1581
 
 
1582
        cred = (Cred *) CMSG_DATA(cmsg);
 
1583
 
 
1584
        pw = getpwuid(cred->cruid);
 
1585
 
 
1586
        if (pw == NULL)
 
1587
        {
 
1588
                ereport(LOG,
 
1589
                                (errmsg("local user with ID %d does not exist",
 
1590
                                                (int) cred->cruid)));
 
1591
                return false;
 
1592
        }
 
1593
 
 
1594
        StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1);
 
1595
 
 
1596
        return true;
 
1597
 
 
1598
#else
 
1599
        ereport(LOG,
 
1600
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
1601
                         errmsg("Ident authentication is not supported on local connections on this platform")));
 
1602
 
 
1603
        return false;
 
1604
#endif
 
1605
}
 
1606
#endif   /* HAVE_UNIX_SOCKETS */
 
1607
 
 
1608
 
 
1609
/*
 
1610
 *      Determine the username of the initiator of the connection described
 
1611
 *      by "port".      Then look in the usermap file under the usermap
 
1612
 *      port->auth_arg and see if that user is equivalent to Postgres user
 
1613
 *      port->user.
 
1614
 *
 
1615
 *      Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info).
 
1616
 */
 
1617
int
 
1618
authident(hbaPort *port)
 
1619
{
 
1620
        char            ident_user[IDENT_USERNAME_MAX + 1];
 
1621
 
 
1622
        switch (port->raddr.addr.ss_family)
 
1623
        {
 
1624
                case AF_INET:
 
1625
#ifdef  HAVE_IPV6
 
1626
                case AF_INET6:
 
1627
#endif
 
1628
                        if (!ident_inet(port->raddr, port->laddr, ident_user))
 
1629
                                return STATUS_ERROR;
 
1630
                        break;
 
1631
 
 
1632
#ifdef HAVE_UNIX_SOCKETS
 
1633
                case AF_UNIX:
 
1634
                        if (!ident_unix(port->sock, ident_user))
 
1635
                                return STATUS_ERROR;
 
1636
                        break;
 
1637
#endif
 
1638
 
 
1639
                default:
 
1640
                        return STATUS_ERROR;
 
1641
        }
 
1642
 
 
1643
        ereport(DEBUG1,
 
1644
                        (errmsg("Ident protocol identifies remote user as \"%s\"",
 
1645
                                        ident_user)));
 
1646
 
 
1647
        if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
 
1648
                return STATUS_OK;
 
1649
        else
 
1650
                return STATUS_ERROR;
 
1651
}
 
1652
 
 
1653
 
 
1654
/*
 
1655
 *      Determine what authentication method should be used when accessing database
 
1656
 *      "database" from frontend "raddr", user "user".  Return the method and
 
1657
 *      an optional argument (stored in fields of *port), and STATUS_OK.
 
1658
 *
 
1659
 *      Note that STATUS_ERROR indicates a problem with the hba config file.
 
1660
 *      If the file is OK but does not contain any entry matching the request,
 
1661
 *      we return STATUS_OK and method = uaReject.
 
1662
 */
 
1663
int
 
1664
hba_getauthmethod(hbaPort *port)
 
1665
{
 
1666
        if (check_hba(port))
 
1667
                return STATUS_OK;
 
1668
        else
 
1669
                return STATUS_ERROR;
 
1670
}