OSDN Git Service

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