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