1 /*-------------------------------------------------------------------------
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).
8 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
13 * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.128 2004/08/29 04:12:32 momjian Exp $
15 *-------------------------------------------------------------------------
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
26 #include <sys/ucred.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
32 #include "commands/user.h"
33 #include "libpq/crypt.h"
34 #include "libpq/libpq.h"
35 #include "miscadmin.h"
36 #include "nodes/pg_list.h"
37 #include "storage/fd.h"
38 #include "utils/guc.h"
41 /* Max size of username ident server can return */
42 #define IDENT_USERNAME_MAX 512
44 /* Standard TCP port number for Ident service. Assigned by IANA */
45 #define IDENT_PORT 113
47 /* Name of the config file */
48 #define CONF_FILE "pg_hba.conf"
50 /* Name of the usermap file */
51 #define USERMAP_FILE "pg_ident.conf"
53 /* This is used to separate values in multi-valued column strings */
54 #define MULTI_VALUE_SEP "\001"
59 * These variables hold the pre-parsed contents of the hba and ident
60 * configuration files. Each is a list of sublists, one sublist for
61 * each (non-empty, non-comment) line of the file. Each sublist's
62 * first item is an integer line number (so we can give somewhat-useful
63 * location info in error messages). Remaining items are palloc'd strings,
64 * one string per token on the line. Note there will always be at least
65 * one token, since blank lines are not entered in the data structure.
68 /* pre-parsed content of CONF_FILE and corresponding line #s */
69 static List *hba_lines = NIL;
70 static List *hba_line_nums = NIL;
71 /* pre-parsed content of USERMAP_FILE and corresponding line #s */
72 static List *ident_lines = NIL;
73 static List *ident_line_nums = NIL;
74 /* pre-parsed content of group file and corresponding line #s */
75 static List *group_lines = NIL;
76 static List *group_line_nums = NIL;
77 /* pre-parsed content of user passwd file and corresponding line #s */
78 static List *user_lines = NIL;
79 static List *user_line_nums = NIL;
81 /* sorted entries so we can do binary search lookups */
82 static List **user_sorted = NULL; /* sorted user list, for bsearch() */
83 static List **group_sorted = NULL; /* sorted group list, for
85 static int user_length;
86 static int group_length;
88 static void tokenize_file(FILE *file, List **lines, List **line_nums);
89 static char *tokenize_inc_file(const char *inc_filename);
92 * isblank() exists in the ISO C99 spec, but it's not very portable yet,
93 * so provide our own version.
96 pg_isblank(const char c)
98 return c == ' ' || c == '\t' || c == '\r';
103 * Grab one token out of fp. Tokens are strings of non-blank
104 * characters bounded by blank characters, beginning of line, and
105 * end of line. Blank means space or tab. Return the token as
106 * *buf. Leave file positioned at the character immediately after the
107 * token or EOF, whichever comes first. If no more tokens on line,
108 * return empty string as *buf and position the file to the beginning
109 * of the next line or EOF, whichever comes first. Allow spaces in
110 * quoted strings. Terminate on unquoted commas. Handle
111 * comments. Treat unquoted keywords that might be user names or
112 * database names specially, by appending a newline to them.
115 next_token(FILE *fp, char *buf, int bufsz)
118 char *start_buf = buf;
119 char *end_buf = buf + (bufsz - 2);
120 bool in_quote = false;
121 bool was_quote = false;
122 bool saw_quote = false;
124 Assert(end_buf > start_buf);
126 /* Move over initial whitespace and commas */
127 while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
130 if (c == EOF || c == '\n')
137 * Build a token in buf of next characters up to EOF, EOL,
138 * unquoted comma, or unquoted whitespace.
140 while (c != EOF && c != '\n' &&
141 (!pg_isblank(c) || in_quote == true))
143 /* skip comments to EOL */
144 if (c == '#' && !in_quote)
146 while ((c = getc(fp)) != EOF && c != '\n')
148 /* If only comment, consume EOL too; return EOL */
149 if (c != EOF && buf == start_buf)
158 (errcode(ERRCODE_CONFIG_FILE_ERROR),
159 errmsg("authentication file token too long, skipping: \"%s\"",
161 /* Discard remainder of line */
162 while ((c = getc(fp)) != EOF && c != '\n')
167 if (c != '"' || (c == '"' && was_quote))
170 /* We pass back the comma so the caller knows there is more */
171 if ((pg_isblank(c) || c == ',') && !in_quote)
174 /* Literal double-quote is two double-quotes */
175 if (in_quote && c == '"')
176 was_quote = !was_quote;
182 in_quote = !in_quote;
190 * Put back the char right after the token (critical in case it is
191 * EOL, since we need to detect end-of-line at next call).
199 (strcmp(start_buf, "all") == 0 ||
200 strcmp(start_buf, "sameuser") == 0 ||
201 strcmp(start_buf, "samegroup") == 0))
203 /* append newline to a magical keyword */
210 * Tokenize file and handle file inclusion and comma lists. We have
211 * to break apart the commas to expand any file names then
212 * reconstruct with commas.
215 next_token_expand(FILE *file)
218 char *comma_str = pstrdup("");
224 next_token(file, buf, sizeof(buf));
228 if (buf[strlen(buf) - 1] == ',')
230 trailing_comma = true;
231 buf[strlen(buf) - 1] = '\0';
234 trailing_comma = false;
236 /* Is this referencing a file? */
238 incbuf = tokenize_inc_file(buf + 1);
240 incbuf = pstrdup(buf);
242 comma_str = repalloc(comma_str,
243 strlen(comma_str) + strlen(incbuf) + 1);
244 strcat(comma_str, incbuf);
249 comma_str = repalloc(comma_str, strlen(comma_str) + 1 + 1);
250 strcat(comma_str, MULTI_VALUE_SEP);
252 } while (trailing_comma);
259 * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
262 free_lines(List **lines, List **line_nums)
265 * Either both must be non-NULL, or both must be NULL
267 Assert((*lines != NIL && *line_nums != NIL) ||
268 (*lines == NIL && *line_nums == NIL));
273 * "lines" is a list of lists; each of those sublists consists
274 * of palloc'ed tokens, so we want to free each pointed-to
275 * token in a sublist, followed by the sublist itself, and
276 * finally the whole list.
280 foreach(line, *lines)
282 List *ln = lfirst(line);
286 pfree(lfirst(token));
287 /* free the sublist structure itself */
290 /* free the list structure itself */
292 /* clear the static variable */
298 list_free(*line_nums);
305 tokenize_inc_file(const char *inc_filename)
312 char *comma_str = pstrdup("");
314 inc_fullname = (char *) palloc(strlen(DataDir) + 1 +
315 strlen(inc_filename) + 1);
316 strcpy(inc_fullname, DataDir);
317 strcat(inc_fullname, "/");
318 strcat(inc_fullname, inc_filename);
320 inc_file = AllocateFile(inc_fullname, "r");
324 (errcode_for_file_access(),
325 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
326 inc_filename, inc_fullname)));
329 /* return empty string, it matches nothing */
334 /* There is possible recursion here if the file contains @ */
335 tokenize_file(inc_file, &inc_lines, &inc_line_nums);
338 /* Create comma-separate string from List */
339 foreach(line, inc_lines)
341 List *token_list = (List *) lfirst(line);
344 foreach(token, token_list)
346 if (strlen(comma_str))
348 comma_str = repalloc(comma_str, strlen(comma_str) + 1);
349 strcat(comma_str, MULTI_VALUE_SEP);
351 comma_str = repalloc(comma_str,
352 strlen(comma_str) + strlen(lfirst(token)) + 1);
353 strcat(comma_str, lfirst(token));
357 free_lines(&inc_lines, &inc_line_nums);
364 * Tokenize the given file, storing the resulting data into two lists:
365 * a list of sublists, each sublist containing the tokens in a line of
366 * the file, and a list of line numbers.
369 tokenize_file(FILE *file, List **lines, List **line_nums)
371 List *current_line = NIL;
375 *lines = *line_nums = NIL;
379 buf = next_token_expand(file);
381 /* add token to list, unless we are at EOL or comment start */
384 if (current_line == NIL)
386 /* make a new line List, record its line number */
387 current_line = lappend(current_line, buf);
388 *lines = lappend(*lines, current_line);
389 *line_nums = lappend_int(*line_nums, line_number);
393 /* append token to current line's list */
394 current_line = lappend(current_line, buf);
399 /* we are at real or logical EOL, so force a new line List */
401 /* Advance line number whenever we reach EOL */
409 * Compare two lines based on their user/group names.
411 * Used for qsort() sorting.
414 user_group_qsort_cmp(const void *list1, const void *list2)
416 char *user1 = linitial(*(List **) list1);
417 char *user2 = linitial(*(List **) list2);
419 return strcmp(user1, user2);
424 * Compare two lines based on their user/group names.
426 * Used for bsearch() lookup.
429 user_group_bsearch_cmp(const void *user, const void *list)
431 char *user2 = linitial(*(List **) list);
433 return strcmp(user, user2);
438 * Lookup a group name in the pg_group file
441 get_group_line(const char *group)
443 /* On some versions of Solaris, bsearch of zero items dumps core */
444 if (group_length == 0)
447 return (List **) bsearch((void *) group,
448 (void *) group_sorted,
451 user_group_bsearch_cmp);
456 * Lookup a user name in the pg_shadow file
459 get_user_line(const char *user)
461 /* On some versions of Solaris, bsearch of zero items dumps core */
462 if (user_length == 0)
465 return (List **) bsearch((void *) user,
466 (void *) user_sorted,
469 user_group_bsearch_cmp);
474 * Check group for a specific user.
477 check_group(char *group, char *user)
481 if ((line = get_group_line(group)) != NULL)
485 /* skip over the group name */
486 for_each_cell(line_item, lnext(list_head(*line)))
488 if (strcmp(lfirst(line_item), user) == 0)
497 * Check comma user list for a specific user, handle group names.
500 check_user(char *user, char *param_str)
504 for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
508 if (check_group(tok + 1, user))
511 else if (strcmp(tok, user) == 0 ||
512 strcmp(tok, "all\n") == 0)
520 * Check to see if db/user combination matches param string.
523 check_db(char *dbname, char *user, char *param_str)
527 for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
529 if (strcmp(tok, "all\n") == 0)
531 else if (strcmp(tok, "sameuser\n") == 0)
533 if (strcmp(dbname, user) == 0)
536 else if (strcmp(tok, "samegroup\n") == 0)
538 if (check_group(dbname, user))
541 else if (strcmp(tok, dbname) == 0)
549 * Scan the rest of a host record (after the mask field)
550 * and return the interpretation of it as *userauth_p, *auth_arg_p, and
551 * *error_p. *line_item points to the next token of the line, and is
552 * advanced over successfully-read tokens.
555 parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
556 char **auth_arg_p, bool *error_p)
568 token = lfirst(*line_item);
569 if (strcmp(token, "trust") == 0)
570 *userauth_p = uaTrust;
571 else if (strcmp(token, "ident") == 0)
572 *userauth_p = uaIdent;
573 else if (strcmp(token, "password") == 0)
574 *userauth_p = uaPassword;
575 else if (strcmp(token, "krb4") == 0)
576 *userauth_p = uaKrb4;
577 else if (strcmp(token, "krb5") == 0)
578 *userauth_p = uaKrb5;
579 else if (strcmp(token, "reject") == 0)
580 *userauth_p = uaReject;
581 else if (strcmp(token, "md5") == 0)
583 else if (strcmp(token, "crypt") == 0)
584 *userauth_p = uaCrypt;
586 else if (strcmp(token, "pam") == 0)
594 *line_item = lnext(*line_item);
596 /* Get the authentication argument token, if any */
599 token = lfirst(*line_item);
600 *auth_arg_p = pstrdup(token);
601 *line_item = lnext(*line_item);
602 /* If there is more on the line, it is an error */
610 * Process one line from the hba config file.
612 * See if it applies to a connection from a host with IP address port->raddr
613 * to a database named port->database. If so, return *found_p true
614 * and fill in the auth arguments into the appropriate port fields.
615 * If not, leave *found_p as it was. If the record has a syntax error,
616 * return *error_p true, after issuing a message to the log. If no error,
617 * leave *error_p as it was.
620 parse_hba(List *line, int line_num, hbaPort *port,
621 bool *found_p, bool *error_p)
626 struct addrinfo *gai_result;
627 struct addrinfo hints;
629 struct sockaddr_storage addr;
630 struct sockaddr_storage mask;
634 line_item = list_head(line);
635 /* Check the record type. */
636 token = lfirst(line_item);
637 if (strcmp(token, "local") == 0)
639 /* Get the database. */
640 line_item = lnext(line_item);
643 db = lfirst(line_item);
646 line_item = lnext(line_item);
649 user = lfirst(line_item);
651 line_item = lnext(line_item);
655 /* Read the rest of the line. */
656 parse_hba_auth(&line_item, &port->auth_method,
657 &port->auth_arg, error_p);
661 /* Disallow auth methods that always need TCP/IP sockets to work */
662 if (port->auth_method == uaKrb4 ||
663 port->auth_method == uaKrb5)
666 /* Does not match if connection isn't AF_UNIX */
667 if (!IS_AF_UNIX(port->raddr.addr.ss_family))
670 else if (strcmp(token, "host") == 0
671 || strcmp(token, "hostssl") == 0
672 || strcmp(token, "hostnossl") == 0)
675 if (token[4] == 's') /* "hostssl" */
678 /* Record does not match if we are not on an SSL connection */
682 /* Placeholder to require specific SSL level, perhaps? */
683 /* Or a client certificate */
685 /* Since we were on SSL, proceed as with normal 'host' mode */
687 /* We don't accept this keyword at all if no SSL support */
692 else if (token[4] == 'n') /* "hostnossl" */
694 /* Record does not match if we are on an SSL connection */
700 /* Get the database. */
701 line_item = lnext(line_item);
704 db = lfirst(line_item);
707 line_item = lnext(line_item);
710 user = lfirst(line_item);
712 /* Read the IP address field. (with or without CIDR netmask) */
713 line_item = lnext(line_item);
716 token = lfirst(line_item);
718 /* Check if it has a CIDR suffix and if so isolate it */
719 cidr_slash = strchr(token, '/');
723 /* Get the IP address either way */
724 hints.ai_flags = AI_NUMERICHOST;
725 hints.ai_family = PF_UNSPEC;
726 hints.ai_socktype = 0;
727 hints.ai_protocol = 0;
728 hints.ai_addrlen = 0;
729 hints.ai_canonname = NULL;
730 hints.ai_addr = NULL;
731 hints.ai_next = NULL;
733 ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
734 if (ret || !gai_result)
737 (errcode(ERRCODE_CONFIG_FILE_ERROR),
738 errmsg("invalid IP address \"%s\" in pg_hba.conf file line %d: %s",
739 token, line_num, gai_strerror(ret))));
743 freeaddrinfo_all(hints.ai_family, gai_result);
744 goto hba_other_error;
750 memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
751 freeaddrinfo_all(hints.ai_family, gai_result);
753 /* Get the netmask */
756 if (SockAddr_cidr_mask(&mask, cidr_slash + 1, addr.ss_family) < 0)
761 /* Read the mask field. */
762 line_item = lnext(line_item);
765 token = lfirst(line_item);
767 ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
768 if (ret || !gai_result)
771 (errcode(ERRCODE_CONFIG_FILE_ERROR),
772 errmsg("invalid IP mask \"%s\" in pg_hba.conf file line %d: %s",
773 token, line_num, gai_strerror(ret))));
775 freeaddrinfo_all(hints.ai_family, gai_result);
776 goto hba_other_error;
779 memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
780 freeaddrinfo_all(hints.ai_family, gai_result);
782 if (addr.ss_family != mask.ss_family)
785 (errcode(ERRCODE_CONFIG_FILE_ERROR),
786 errmsg("IP address and mask do not match in pg_hba.conf file line %d",
788 goto hba_other_error;
792 if (addr.ss_family != port->raddr.addr.ss_family)
795 * Wrong address family. We allow only one case: if the
796 * file has IPv4 and the port is IPv6, promote the file
797 * address to IPv6 and try to match that way.
800 if (addr.ss_family == AF_INET &&
801 port->raddr.addr.ss_family == AF_INET6)
803 promote_v4_to_v6_addr(&addr);
804 promote_v4_to_v6_mask(&mask);
807 #endif /* HAVE_IPV6 */
809 /* Line doesn't match client port, so ignore it. */
814 /* Ignore line if client port is not in the matching addr range. */
815 if (!rangeSockAddr(&port->raddr.addr, &addr, &mask))
818 /* Read the rest of the line. */
819 line_item = lnext(line_item);
822 parse_hba_auth(&line_item, &port->auth_method,
823 &port->auth_arg, error_p);
830 /* Does the entry match database and user? */
831 if (!check_db(port->database_name, port->user_name, db))
833 if (!check_user(port->user_name, user))
843 (errcode(ERRCODE_CONFIG_FILE_ERROR),
844 errmsg("invalid entry in pg_hba.conf file at line %d, token \"%s\"",
845 line_num, (char *) lfirst(line_item))));
848 (errcode(ERRCODE_CONFIG_FILE_ERROR),
849 errmsg("missing field in pg_hba.conf file at end of line %d",
852 /* Come here if suitable message already logged */
859 * Scan the (pre-parsed) hba file line by line, looking for a match
860 * to the port's connection request.
863 check_hba(hbaPort *port)
865 bool found_entry = false;
870 forboth(line, hba_lines, line_num, hba_line_nums)
872 parse_hba(lfirst(line), lfirst_int(line_num),
873 port, &found_entry, &error);
874 if (found_entry || error)
880 /* If no matching entry was found, synthesize 'reject' entry. */
882 port->auth_method = uaReject;
892 * Open the group file if possible (return NULL if not)
900 filename = group_getfilename();
901 groupfile = AllocateFile(filename, "r");
903 if (groupfile == NULL && errno != ENOENT)
905 (errcode_for_file_access(),
906 errmsg("could not open file \"%s\": %m", filename)));
916 * Open the password file if possible (return NULL if not)
924 filename = user_getfilename();
925 pwdfile = AllocateFile(filename, "r");
927 if (pwdfile == NULL && errno != ENOENT)
929 (errcode_for_file_access(),
930 errmsg("could not open file \"%s\": %m", filename)));
940 * Load group/user name mapping file
947 /* Discard any old data */
948 if (group_lines || group_line_nums)
949 free_lines(&group_lines, &group_line_nums);
955 group_file = group_openfile();
958 tokenize_file(group_file, &group_lines, &group_line_nums);
959 FreeFile(group_file);
961 /* create sorted lines for binary searching */
962 group_length = list_length(group_lines);
968 group_sorted = palloc(group_length * sizeof(List *));
970 foreach(line, group_lines)
971 group_sorted[i++] = lfirst(line);
973 qsort((void *) group_sorted,
976 user_group_qsort_cmp);
982 * Load user/password mapping file
989 /* Discard any old data */
990 if (user_lines || user_line_nums)
991 free_lines(&user_lines, &user_line_nums);
997 user_file = user_openfile();
1000 tokenize_file(user_file, &user_lines, &user_line_nums);
1001 FreeFile(user_file);
1003 /* create sorted lines for binary searching */
1004 user_length = list_length(user_lines);
1010 user_sorted = palloc(user_length * sizeof(List *));
1012 foreach(line, user_lines)
1013 user_sorted[i++] = lfirst(line);
1015 qsort((void *) user_sorted,
1018 user_group_qsort_cmp);
1024 * Read the config file and create a List of Lists of tokens in the file.
1025 * If we find a file by the old name of the config file (pg_hba), we issue
1026 * an error message because it probably needs to be converted. He didn't
1027 * follow directions and just installed his old hba file in the new database
1033 FILE *file; /* The config file we have to read */
1034 char *conf_file; /* The name of the config file */
1036 if (hba_lines || hba_line_nums)
1037 free_lines(&hba_lines, &hba_line_nums);
1039 /* HBA filename in config file */
1041 conf_file = pstrdup(guc_hbafile);
1044 char *confloc = (user_pgconfig_is_dir) ? user_pgconfig : DataDir;
1045 /* put together the full pathname to the config file */
1046 conf_file = palloc(strlen(confloc) + strlen(CONF_FILE) + 2);
1047 sprintf(conf_file, "%s/%s", confloc, CONF_FILE);
1050 file = AllocateFile(conf_file, "r");
1053 (errcode_for_file_access(),
1054 errmsg("could not open configuration file \"%s\": %m",
1057 tokenize_file(file, &hba_lines, &hba_line_nums);
1064 * Process one line from the ident config file.
1066 * Take the line and compare it to the needed map, pg_user and ident_user.
1067 * *found_p and *error_p are set according to our results.
1070 parse_ident_usermap(List *line, int line_number, const char *usermap_name,
1071 const char *pg_user, const char *ident_user,
1072 bool *found_p, bool *error_p)
1074 ListCell *line_item;
1078 char *file_ident_user;
1083 Assert(line != NIL);
1084 line_item = list_head(line);
1086 /* Get the map token (must exist) */
1087 token = lfirst(line_item);
1090 /* Get the ident user token (must be provided) */
1091 line_item = lnext(line_item);
1094 token = lfirst(line_item);
1095 file_ident_user = token;
1097 /* Get the PG username token */
1098 line_item = lnext(line_item);
1101 token = lfirst(line_item);
1102 file_pguser = token;
1105 if (strcmp(file_map, usermap_name) == 0 &&
1106 strcmp(file_pguser, pg_user) == 0 &&
1107 strcmp(file_ident_user, ident_user) == 0)
1114 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1115 errmsg("invalid entry in pg_ident.conf file at line %d, token \"%s\"",
1116 line_number, (const char *) lfirst(line_item))));
1119 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1120 errmsg("missing entry in pg_ident.conf file at end of line %d",
1128 * Scan the (pre-parsed) ident usermap file line by line, looking for a match
1130 * See if the user with ident username "ident_user" is allowed to act
1131 * as Postgres user "pguser" according to usermap "usermap_name".
1133 * Special case: For usermap "sameuser", don't look in the usermap
1134 * file. That's an implied map where "pguser" must be identical to
1135 * "ident_user" in order to be authorized.
1137 * Iff authorized, return true.
1140 check_ident_usermap(const char *usermap_name,
1141 const char *pg_user,
1142 const char *ident_user)
1144 bool found_entry = false,
1147 if (usermap_name == NULL || usermap_name[0] == '\0')
1150 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1151 errmsg("cannot use Ident authentication without usermap field")));
1152 found_entry = false;
1154 else if (strcmp(usermap_name, "sameuser\n") == 0)
1156 if (strcmp(pg_user, ident_user) == 0)
1159 found_entry = false;
1163 ListCell *line_cell, *num_cell;
1165 forboth(line_cell, ident_lines, num_cell, ident_line_nums)
1167 parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
1168 usermap_name, pg_user, ident_user,
1169 &found_entry, &error);
1170 if (found_entry || error)
1179 * Read the ident config file and create a List of Lists of tokens in the file.
1184 FILE *file; /* The map file we have to read */
1185 char *map_file; /* The name of the map file we have to
1187 if (ident_lines || ident_line_nums)
1188 free_lines(&ident_lines, &ident_line_nums);
1190 /* IDENT filename in config file */
1192 map_file = pstrdup(guc_identfile);
1195 /* put together the full pathname to the map file */
1196 char *confloc = (user_pgconfig_is_dir) ? user_pgconfig : DataDir;
1197 map_file = (char *) palloc(strlen(confloc) + strlen(USERMAP_FILE) + 2);
1198 sprintf(map_file, "%s/%s", confloc, USERMAP_FILE);
1201 file = AllocateFile(map_file, "r");
1205 (errcode_for_file_access(),
1206 errmsg("could not open Ident usermap file \"%s\": %m",
1211 tokenize_file(file, &ident_lines, &ident_line_nums);
1219 * Parse the string "*ident_response" as a response from a query to an Ident
1220 * server. If it's a normal response indicating a user name, return true
1221 * and store the user name at *ident_user. If it's anything else,
1225 interpret_ident_response(const char *ident_response,
1228 const char *cursor = ident_response; /* Cursor into
1229 * *ident_response */
1232 * Ident's response, in the telnet tradition, should end in crlf
1235 if (strlen(ident_response) < 2)
1237 else if (ident_response[strlen(ident_response) - 2] != '\r')
1241 while (*cursor != ':' && *cursor != '\r')
1242 cursor++; /* skip port field */
1248 /* We're positioned to colon before response type field */
1249 char response_type[80];
1250 int i; /* Index into *response_type */
1252 cursor++; /* Go over colon */
1253 while (pg_isblank(*cursor))
1254 cursor++; /* skip blanks */
1256 while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) &&
1257 i < (int) (sizeof(response_type) - 1))
1258 response_type[i++] = *cursor++;
1259 response_type[i] = '\0';
1260 while (pg_isblank(*cursor))
1261 cursor++; /* skip blanks */
1262 if (strcmp(response_type, "USERID") != 0)
1267 * It's a USERID response. Good. "cursor" should be
1268 * pointing to the colon that precedes the operating
1275 cursor++; /* Go over colon */
1276 /* Skip over operating system field. */
1277 while (*cursor != ':' && *cursor != '\r')
1283 int i; /* Index into *ident_user */
1285 cursor++; /* Go over colon */
1286 while (pg_isblank(*cursor))
1287 cursor++; /* skip blanks */
1288 /* Rest of line is user name. Copy it over. */
1290 while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
1291 ident_user[i++] = *cursor++;
1292 ident_user[i] = '\0';
1303 * Talk to the ident server on host "remote_ip_addr" and find out who
1304 * owns the tcp connection from his port "remote_port" to port
1305 * "local_port_addr" on host "local_ip_addr". Return the user name the
1306 * ident server gives as "*ident_user".
1308 * IP addresses and port numbers are in network byte order.
1310 * But iff we're unable to get the information from ident, return false.
1313 ident_inet(const SockAddr remote_addr,
1314 const SockAddr local_addr,
1317 int sock_fd, /* File descriptor for socket on which we
1319 rc; /* Return code from a locally called
1322 char remote_addr_s[NI_MAXHOST];
1323 char remote_port[NI_MAXSERV];
1324 char local_addr_s[NI_MAXHOST];
1325 char local_port[NI_MAXSERV];
1326 char ident_port[NI_MAXSERV];
1327 char ident_query[80];
1328 char ident_response[80 + IDENT_USERNAME_MAX];
1329 struct addrinfo *ident_serv = NULL,
1334 * Might look a little weird to first convert it to text and then back
1335 * to sockaddr, but it's protocol independent.
1337 getnameinfo_all(&remote_addr.addr, remote_addr.salen,
1338 remote_addr_s, sizeof(remote_addr_s),
1339 remote_port, sizeof(remote_port),
1340 NI_NUMERICHOST | NI_NUMERICSERV);
1341 getnameinfo_all(&local_addr.addr, local_addr.salen,
1342 local_addr_s, sizeof(local_addr_s),
1343 local_port, sizeof(local_port),
1344 NI_NUMERICHOST | NI_NUMERICSERV);
1346 snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT);
1347 hints.ai_flags = AI_NUMERICHOST;
1348 hints.ai_family = remote_addr.addr.ss_family;
1349 hints.ai_socktype = SOCK_STREAM;
1350 hints.ai_protocol = 0;
1351 hints.ai_addrlen = 0;
1352 hints.ai_canonname = NULL;
1353 hints.ai_addr = NULL;
1354 hints.ai_next = NULL;
1355 rc = getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);
1356 if (rc || !ident_serv) {
1358 freeaddrinfo_all(hints.ai_family, ident_serv);
1359 return false; /* we don't expect this to happen */
1362 hints.ai_flags = AI_NUMERICHOST;
1363 hints.ai_family = local_addr.addr.ss_family;
1364 hints.ai_socktype = SOCK_STREAM;
1365 hints.ai_protocol = 0;
1366 hints.ai_addrlen = 0;
1367 hints.ai_canonname = NULL;
1368 hints.ai_addr = NULL;
1369 hints.ai_next = NULL;
1370 rc = getaddrinfo_all(local_addr_s, NULL, &hints, &la);
1373 freeaddrinfo_all(hints.ai_family, la);
1374 return false; /* we don't expect this to happen */
1377 sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
1378 ident_serv->ai_protocol);
1382 (errcode_for_socket_access(),
1383 errmsg("could not create socket for Ident connection: %m")));
1384 ident_return = false;
1385 goto ident_inet_done;
1389 * Bind to the address which the client originally contacted,
1390 * otherwise the ident server won't be able to match up the right
1391 * connection. This is necessary if the PostgreSQL server is running
1394 rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
1398 (errcode_for_socket_access(),
1399 errmsg("could not bind to local address \"%s\": %m",
1401 ident_return = false;
1402 goto ident_inet_done;
1405 rc = connect(sock_fd, ident_serv->ai_addr,
1406 ident_serv->ai_addrlen);
1410 (errcode_for_socket_access(),
1411 errmsg("could not connect to Ident server at address \"%s\", port %s: %m",
1412 remote_addr_s, ident_port)));
1413 ident_return = false;
1414 goto ident_inet_done;
1417 /* The query we send to the Ident server */
1418 snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n",
1419 remote_port, local_port);
1421 /* loop in case send is interrupted */
1424 rc = send(sock_fd, ident_query, strlen(ident_query), 0);
1425 } while (rc < 0 && errno == EINTR);
1430 (errcode_for_socket_access(),
1431 errmsg("could not send query to Ident server at address \"%s\", port %s: %m",
1432 remote_addr_s, ident_port)));
1433 ident_return = false;
1434 goto ident_inet_done;
1439 rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
1440 } while (rc < 0 && errno == EINTR);
1445 (errcode_for_socket_access(),
1446 errmsg("could not receive response from Ident server at address \"%s\", port %s: %m",
1447 remote_addr_s, ident_port)));
1448 ident_return = false;
1449 goto ident_inet_done;
1452 ident_response[rc] = '\0';
1453 ident_return = interpret_ident_response(ident_response, ident_user);
1456 (errmsg("invalidly formatted response from Ident server: \"%s\"",
1461 closesocket(sock_fd);
1462 freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
1463 freeaddrinfo_all(local_addr.addr.ss_family, la);
1464 return ident_return;
1468 * Ask kernel about the credentials of the connecting process and
1469 * determine the symbolic name of the corresponding user.
1471 * Returns either true and the username put into "ident_user",
1472 * or false if we were unable to determine the username.
1474 #ifdef HAVE_UNIX_SOCKETS
1477 ident_unix(int sock, char *ident_user)
1479 #if defined(HAVE_GETPEEREID)
1480 /* OpenBSD style: */
1483 struct passwd *pass;
1486 if (getpeereid(sock, &uid, &gid) != 0)
1488 /* We didn't get a valid credentials struct. */
1490 (errcode_for_socket_access(),
1491 errmsg("could not get peer credentials: %m")));
1495 pass = getpwuid(uid);
1500 (errmsg("local user with ID %d does not exist",
1505 StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
1509 #elif defined(SO_PEERCRED)
1510 /* Linux style: use getsockopt(SO_PEERCRED) */
1511 struct ucred peercred;
1512 ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
1513 struct passwd *pass;
1516 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
1517 so_len != sizeof(peercred))
1519 /* We didn't get a valid credentials struct. */
1521 (errcode_for_socket_access(),
1522 errmsg("could not get peer credentials: %m")));
1526 pass = getpwuid(peercred.uid);
1531 (errmsg("local user with ID %d does not exist",
1532 (int) peercred.uid)));
1536 StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
1540 #elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
1543 /* Credentials structure */
1544 #if defined(HAVE_STRUCT_CMSGCRED)
1545 typedef struct cmsgcred Cred;
1547 #define cruid cmcred_uid
1548 #elif defined(HAVE_STRUCT_FCRED)
1549 typedef struct fcred Cred;
1551 #define cruid fc_uid
1552 #elif defined(HAVE_STRUCT_SOCKCRED)
1553 typedef struct sockcred Cred;
1555 #define cruid sc_uid
1559 /* Compute size without padding */
1560 char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */
1562 /* Point to start of first structure */
1563 struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
1569 memset(&msg, 0, sizeof(msg));
1572 msg.msg_control = (char *) cmsg;
1573 msg.msg_controllen = sizeof(cmsgmem);
1574 memset(cmsg, 0, sizeof(cmsgmem));
1577 * The one character which is received here is not meaningful; its
1578 * purposes is only to make sure that recvmsg() blocks long enough for
1579 * the other side to send its credentials.
1581 iov.iov_base = &buf;
1584 if (recvmsg(sock, &msg, 0) < 0 ||
1585 cmsg->cmsg_len < sizeof(cmsgmem) ||
1586 cmsg->cmsg_type != SCM_CREDS)
1589 (errcode_for_socket_access(),
1590 errmsg("could not get peer credentials: %m")));
1594 cred = (Cred *) CMSG_DATA(cmsg);
1596 pw = getpwuid(cred->cruid);
1601 (errmsg("local user with ID %d does not exist",
1602 (int) cred->cruid)));
1606 StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1);
1612 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1613 errmsg("Ident authentication is not supported on local connections on this platform")));
1618 #endif /* HAVE_UNIX_SOCKETS */
1622 * Determine the username of the initiator of the connection described
1623 * by "port". Then look in the usermap file under the usermap
1624 * port->auth_arg and see if that user is equivalent to Postgres user
1627 * Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info).
1630 authident(hbaPort *port)
1632 char ident_user[IDENT_USERNAME_MAX + 1];
1634 switch (port->raddr.addr.ss_family)
1640 if (!ident_inet(port->raddr, port->laddr, ident_user))
1641 return STATUS_ERROR;
1644 #ifdef HAVE_UNIX_SOCKETS
1646 if (!ident_unix(port->sock, ident_user))
1647 return STATUS_ERROR;
1652 return STATUS_ERROR;
1656 (errmsg("IDENT code identifies remote user as \"%s\"",
1659 if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
1662 return STATUS_ERROR;
1667 * Determine what authentication method should be used when accessing database
1668 * "database" from frontend "raddr", user "user". Return the method and
1669 * an optional argument (stored in fields of *port), and STATUS_OK.
1671 * Note that STATUS_ERROR indicates a problem with the hba config file.
1672 * If the file is OK but does not contain any entry matching the request,
1673 * we return STATUS_OK and method = uaReject.
1676 hba_getauthmethod(hbaPort *port)
1678 if (check_hba(port))
1681 return STATUS_ERROR;