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