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-2006, 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.155 2006/07/14 14:52:19 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>
33 #include "libpq/libpq.h"
34 #include "storage/fd.h"
35 #include "utils/flatfiles.h"
36 #include "utils/guc.h"
40 #define atooid(x) ((Oid) strtoul((x), NULL, 10))
41 #define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))
43 /* Max size of username ident server can return */
44 #define IDENT_USERNAME_MAX 512
46 /* Standard TCP port number for Ident service. Assigned by IANA */
47 #define IDENT_PORT 113
49 /* This is used to separate values in multi-valued column strings */
50 #define MULTI_VALUE_SEP "\001"
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.
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;
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;
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;
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;
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);
87 * isblank() exists in the ISO C99 spec, but it's not very portable yet,
88 * so provide our own version.
91 pg_isblank(const char c)
93 return c == ' ' || c == '\t' || c == '\r';
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).
103 * The token, if any, is returned at *buf (a buffer of size bufsz).
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.
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.
112 * Handle comments. Treat unquoted keywords that might be role names or
113 * database names specially, by appending a newline to them.
116 next_token(FILE *fp, char *buf, int bufsz)
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;
125 Assert(end_buf > start_buf);
127 /* Move over initial whitespace and commas */
128 while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
131 if (c == EOF || c == '\n')
138 * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
139 * or unquoted whitespace.
141 while (c != EOF && c != '\n' &&
142 (!pg_isblank(c) || in_quote == true))
144 /* skip comments to EOL */
145 if (c == '#' && !in_quote)
147 while ((c = getc(fp)) != EOF && c != '\n')
149 /* If only comment, consume EOL too; return EOL */
150 if (c != EOF && buf == start_buf)
159 (errcode(ERRCODE_CONFIG_FILE_ERROR),
160 errmsg("authentication file token too long, skipping: \"%s\"",
162 /* Discard remainder of line */
163 while ((c = getc(fp)) != EOF && c != '\n')
168 if (c != '"' || (c == '"' && was_quote))
171 /* We pass back the comma so the caller knows there is more */
172 if ((pg_isblank(c) || c == ',') && !in_quote)
175 /* Literal double-quote is two double-quotes */
176 if (in_quote && c == '"')
177 was_quote = !was_quote;
183 in_quote = !in_quote;
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).
200 (strcmp(start_buf, "all") == 0 ||
201 strcmp(start_buf, "sameuser") == 0 ||
202 strcmp(start_buf, "samegroup") == 0 ||
203 strcmp(start_buf, "samerole") == 0))
205 /* append newline to a magical keyword */
210 return (saw_quote || buf > start_buf);
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.
218 * The result is a palloc'd string, or NULL if we have reached EOL.
221 next_token_expand(const char *filename, FILE *file)
224 char *comma_str = pstrdup("");
225 bool got_something = false;
232 if (!next_token(file, buf, sizeof(buf)))
235 got_something = true;
237 if (strlen(buf) > 0 && buf[strlen(buf) - 1] == ',')
239 trailing_comma = true;
240 buf[strlen(buf) - 1] = '\0';
243 trailing_comma = false;
245 /* Is this referencing a file? */
247 incbuf = tokenize_inc_file(filename, buf + 1);
249 incbuf = pstrdup(buf);
251 needed = strlen(comma_str) + strlen(incbuf) + 1;
254 comma_str = repalloc(comma_str, needed);
255 strcat(comma_str, incbuf);
257 strcat(comma_str, MULTI_VALUE_SEP);
259 } while (trailing_comma);
272 * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
275 free_lines(List **lines, List **line_nums)
278 * Either both must be non-NULL, or both must be NULL
280 Assert((*lines != NIL && *line_nums != NIL) ||
281 (*lines == NIL && *line_nums == NIL));
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
293 foreach(line, *lines)
295 List *ln = lfirst(line);
299 pfree(lfirst(token));
300 /* free the sublist structure itself */
303 /* free the list structure itself */
305 /* clear the static variable */
311 list_free(*line_nums);
318 tokenize_inc_file(const char *outer_filename,
319 const char *inc_filename)
328 if (is_absolute_path(inc_filename))
330 /* absolute path is taken as-is */
331 inc_fullname = pstrdup(inc_filename);
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);
344 inc_file = AllocateFile(inc_fullname, "r");
345 if (inc_file == NULL)
348 (errcode_for_file_access(),
349 errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
350 inc_filename, inc_fullname)));
353 /* return single space, it matches nothing */
357 /* There is possible recursion here if the file contains @ */
358 tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
363 /* Create comma-separated string from List */
364 comma_str = pstrdup("");
365 foreach(line, inc_lines)
367 List *token_list = (List *) lfirst(line);
370 foreach(token, token_list)
372 int oldlen = strlen(comma_str);
375 needed = oldlen + strlen(lfirst(token)) + 1;
378 comma_str = repalloc(comma_str, needed);
380 strcat(comma_str, MULTI_VALUE_SEP);
381 strcat(comma_str, lfirst(token));
385 free_lines(&inc_lines, &inc_line_nums);
387 /* if file is empty, return single space rather than empty string */
388 if (strlen(comma_str) == 0)
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.
403 * filename must be the absolute path to the target file.
406 tokenize_file(const char *filename, FILE *file,
407 List **lines, List **line_nums)
409 List *current_line = NIL;
413 *lines = *line_nums = NIL;
417 buf = next_token_expand(filename, file);
419 /* add token to list, unless we are at EOL or comment start */
422 if (current_line == NIL)
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);
431 /* append token to current line's list */
432 current_line = lappend(current_line, buf);
437 /* we are at real or logical EOL, so force a new line List */
439 /* Advance line number whenever we reach EOL */
446 * Compare two lines based on their role/member names.
448 * Used for bsearch() lookup.
451 role_bsearch_cmp(const void *role, const void *list)
453 char *role2 = linitial(*(List **) list);
455 return strcmp(role, role2);
460 * Lookup a role name in the pg_auth file
463 get_role_line(const char *role)
465 /* On some versions of Solaris, bsearch of zero items dumps core */
466 if (role_length == 0)
469 return (List **) bsearch((void *) role,
470 (void *) role_sorted,
478 * Does user belong to role?
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.
484 is_member(const char *user, const char *role)
489 if ((line = get_role_line(user)) == NULL)
490 return false; /* if user not exist, say "no" */
492 /* A user always belongs to its own role */
493 if (strcmp(user, role) == 0)
497 * skip over the role name, password, valuntil, examine all the membership
500 if (list_length(*line) < 4)
502 for_each_cell(line_item, lnext(lnext(lnext(list_head(*line)))))
504 if (strcmp((char *) lfirst(line_item), role) == 0)
512 * Check comma-separated list for a match to role, allowing group names.
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.
519 check_role(const char *role, char *param_str)
523 for (tok = strtok(param_str, MULTI_VALUE_SEP);
525 tok = strtok(NULL, MULTI_VALUE_SEP))
529 if (is_member(role, tok + 1))
532 else if (strcmp(tok, role) == 0 ||
533 strcmp(tok, "all\n") == 0)
541 * Check to see if db/role combination matches param string.
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.
548 check_db(const char *dbname, const char *role, char *param_str)
552 for (tok = strtok(param_str, MULTI_VALUE_SEP);
554 tok = strtok(NULL, MULTI_VALUE_SEP))
556 if (strcmp(tok, "all\n") == 0)
558 else if (strcmp(tok, "sameuser\n") == 0)
560 if (strcmp(dbname, role) == 0)
563 else if (strcmp(tok, "samegroup\n") == 0 ||
564 strcmp(tok, "samerole\n") == 0)
566 if (is_member(role, dbname))
569 else if (strcmp(tok, dbname) == 0)
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.
583 parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
584 char **auth_arg_p, bool *error_p)
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)
609 else if (strcmp(token, "crypt") == 0)
610 *userauth_p = uaCrypt;
612 else if (strcmp(token, "pam") == 0)
616 else if (strcmp(token,"ldap") == 0)
617 *userauth_p = uaLDAP;
624 *line_item = lnext(*line_item);
626 /* Get the authentication argument token, if any */
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 */
640 * Process one line from the hba config file.
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.
650 parse_hba(List *line, int line_num, hbaPort *port,
651 bool *found_p, bool *error_p)
656 struct addrinfo *gai_result;
657 struct addrinfo hints;
659 struct sockaddr_storage addr;
660 struct sockaddr_storage mask;
664 line_item = list_head(line);
665 /* Check the record type. */
666 token = lfirst(line_item);
667 if (strcmp(token, "local") == 0)
669 /* Get the database. */
670 line_item = lnext(line_item);
673 db = lfirst(line_item);
676 line_item = lnext(line_item);
679 role = lfirst(line_item);
681 line_item = lnext(line_item);
685 /* Read the rest of the line. */
686 parse_hba_auth(&line_item, &port->auth_method,
687 &port->auth_arg, error_p);
691 /* Disallow auth methods that always need TCP/IP sockets to work */
692 if (port->auth_method == uaKrb5)
695 /* Does not match if connection isn't AF_UNIX */
696 if (!IS_AF_UNIX(port->raddr.addr.ss_family))
699 else if (strcmp(token, "host") == 0
700 || strcmp(token, "hostssl") == 0
701 || strcmp(token, "hostnossl") == 0)
704 if (token[4] == 's') /* "hostssl" */
707 /* Record does not match if we are not on an SSL connection */
711 /* Placeholder to require specific SSL level, perhaps? */
712 /* Or a client certificate */
714 /* Since we were on SSL, proceed as with normal 'host' mode */
716 /* We don't accept this keyword at all if no SSL support */
721 else if (token[4] == 'n') /* "hostnossl" */
723 /* Record does not match if we are on an SSL connection */
729 /* Get the database. */
730 line_item = lnext(line_item);
733 db = lfirst(line_item);
736 line_item = lnext(line_item);
739 role = lfirst(line_item);
741 /* Read the IP address field. (with or without CIDR netmask) */
742 line_item = lnext(line_item);
745 token = lfirst(line_item);
747 /* Check if it has a CIDR suffix and if so isolate it */
748 cidr_slash = strchr(token, '/');
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;
762 ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
763 if (ret || !gai_result)
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))));
773 pg_freeaddrinfo_all(hints.ai_family, gai_result);
774 goto hba_other_error;
780 memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
781 pg_freeaddrinfo_all(hints.ai_family, gai_result);
783 /* Get the netmask */
786 if (pg_sockaddr_cidr_mask(&mask, cidr_slash + 1,
792 /* Read the mask field. */
793 line_item = lnext(line_item);
796 token = lfirst(line_item);
798 ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
799 if (ret || !gai_result)
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))));
807 pg_freeaddrinfo_all(hints.ai_family, gai_result);
808 goto hba_other_error;
811 memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
812 pg_freeaddrinfo_all(hints.ai_family, gai_result);
814 if (addr.ss_family != mask.ss_family)
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;
824 if (addr.ss_family != port->raddr.addr.ss_family)
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.
832 if (addr.ss_family == AF_INET &&
833 port->raddr.addr.ss_family == AF_INET6)
835 pg_promote_v4_to_v6_addr(&addr);
836 pg_promote_v4_to_v6_mask(&mask);
839 #endif /* HAVE_IPV6 */
841 /* Line doesn't match client port, so ignore it. */
846 /* Ignore line if client port is not in the matching addr range. */
847 if (!pg_range_sockaddr(&port->raddr.addr, &addr, &mask))
850 /* Read the rest of the line. */
851 line_item = lnext(line_item);
854 parse_hba_auth(&line_item, &port->auth_method,
855 &port->auth_arg, error_p);
862 /* Does the entry match database and role? */
863 if (!check_db(port->database_name, port->user_name, db))
865 if (!check_role(port->user_name, role))
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))));
881 (errcode(ERRCODE_CONFIG_FILE_ERROR),
882 errmsg("missing field in file \"%s\" at end of line %d",
883 HbaFileName, line_num)));
885 /* Come here if suitable message already logged */
892 * Scan the (pre-parsed) hba file line by line, looking for a match
893 * to the port's connection request.
896 check_hba(hbaPort *port)
898 bool found_entry = false;
903 forboth(line, hba_lines, line_num, hba_line_nums)
905 parse_hba(lfirst(line), lfirst_int(line_num),
906 port, &found_entry, &error);
907 if (found_entry || error)
913 /* If no matching entry was found, synthesize 'reject' entry. */
915 port->auth_method = uaReject;
924 * Load role/password mapping file
932 /* Discard any old data */
933 if (role_lines || role_line_nums)
934 free_lines(&role_lines, &role_line_nums);
940 /* Read in the file contents */
941 filename = auth_getflatfilename();
942 role_file = AllocateFile(filename, "r");
944 if (role_file == NULL)
946 /* no complaint if not there */
949 (errcode_for_file_access(),
950 errmsg("could not open file \"%s\": %m", filename)));
955 tokenize_file(filename, role_file, &role_lines, &role_line_nums);
960 /* create array for binary searching */
961 role_length = list_length(role_lines);
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);
976 * Read the config file and create a List of Lists of tokens in the file.
983 if (hba_lines || hba_line_nums)
984 free_lines(&hba_lines, &hba_line_nums);
986 file = AllocateFile(HbaFileName, "r");
987 /* Failure is fatal since with no HBA entries we can do nothing... */
990 (errcode_for_file_access(),
991 errmsg("could not open configuration file \"%s\": %m",
994 tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
999 * Read and parse one line from the flat pg_database file.
1001 * Returns TRUE on success, FALSE if EOF; bad data causes elog(FATAL).
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
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.
1014 read_pg_database_line(FILE *fp, char *dbname, Oid *dboid,
1015 Oid *dbtablespace, TransactionId *dbminxid,
1016 TransactionId *dbvacuumxid)
1018 char buf[MAX_TOKEN];
1022 if (!next_token(fp, buf, sizeof(buf)))
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");
1050 * Process one line from the ident config file.
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.
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)
1060 ListCell *line_item;
1064 char *file_ident_user;
1069 Assert(line != NIL);
1070 line_item = list_head(line);
1072 /* Get the map token (must exist) */
1073 token = lfirst(line_item);
1076 /* Get the ident user token */
1077 line_item = lnext(line_item);
1080 token = lfirst(line_item);
1081 file_ident_user = token;
1083 /* Get the PG rolename token */
1084 line_item = lnext(line_item);
1087 token = lfirst(line_item);
1088 file_pgrole = token;
1091 if (strcmp(file_map, usermap_name) == 0 &&
1092 strcmp(file_pgrole, pg_role) == 0 &&
1093 strcmp(file_ident_user, ident_user) == 0)
1100 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1101 errmsg("missing entry in file \"%s\" at end of line %d",
1102 IdentFileName, line_number)));
1108 * Scan the (pre-parsed) ident usermap file line by line, looking for a match
1110 * See if the user with ident username "ident_user" is allowed to act
1111 * as Postgres user "pgrole" according to usermap "usermap_name".
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.
1117 * Iff authorized, return true.
1120 check_ident_usermap(const char *usermap_name,
1121 const char *pg_role,
1122 const char *ident_user)
1124 bool found_entry = false,
1127 if (usermap_name == NULL || usermap_name[0] == '\0')
1130 (errcode(ERRCODE_CONFIG_FILE_ERROR),
1131 errmsg("cannot use Ident authentication without usermap field")));
1132 found_entry = false;
1134 else if (strcmp(usermap_name, "sameuser\n") == 0 ||
1135 strcmp(usermap_name, "samerole\n") == 0)
1137 if (strcmp(pg_role, ident_user) == 0)
1140 found_entry = false;
1144 ListCell *line_cell,
1147 forboth(line_cell, ident_lines, num_cell, ident_line_nums)
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)
1161 * Read the ident config file and create a List of Lists of tokens in the file.
1168 if (ident_lines || ident_line_nums)
1169 free_lines(&ident_lines, &ident_line_nums);
1171 file = AllocateFile(IdentFileName, "r");
1174 /* not fatal ... we just won't do any special ident maps */
1176 (errcode_for_file_access(),
1177 errmsg("could not open Ident usermap file \"%s\": %m",
1182 tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
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,
1195 interpret_ident_response(const char *ident_response,
1198 const char *cursor = ident_response; /* Cursor into *ident_response */
1201 * Ident's response, in the telnet tradition, should end in crlf (\r\n).
1203 if (strlen(ident_response) < 2)
1205 else if (ident_response[strlen(ident_response) - 2] != '\r')
1209 while (*cursor != ':' && *cursor != '\r')
1210 cursor++; /* skip port field */
1216 /* We're positioned to colon before response type field */
1217 char response_type[80];
1218 int i; /* Index into *response_type */
1220 cursor++; /* Go over colon */
1221 while (pg_isblank(*cursor))
1222 cursor++; /* skip blanks */
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)
1235 * It's a USERID response. Good. "cursor" should be pointing
1236 * to the colon that precedes the operating system type.
1242 cursor++; /* Go over colon */
1243 /* Skip over operating system field. */
1244 while (*cursor != ':' && *cursor != '\r')
1250 int i; /* Index into *ident_user */
1252 cursor++; /* Go over colon */
1253 while (pg_isblank(*cursor))
1254 cursor++; /* skip blanks */
1255 /* Rest of line is user name. Copy it over. */
1257 while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
1258 ident_user[i++] = *cursor++;
1259 ident_user[i] = '\0';
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".
1275 * IP addresses and port numbers are in network byte order.
1277 * But iff we're unable to get the information from ident, return false.
1280 ident_inet(const SockAddr remote_addr,
1281 const SockAddr local_addr,
1284 int sock_fd, /* File descriptor for socket on which we talk
1286 rc; /* Return code from a locally called function */
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,
1300 * Might look a little weird to first convert it to text and then back to
1301 * sockaddr, but it's protocol independent.
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);
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)
1325 pg_freeaddrinfo_all(hints.ai_family, ident_serv);
1326 return false; /* we don't expect this to happen */
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);
1341 pg_freeaddrinfo_all(hints.ai_family, la);
1342 return false; /* we don't expect this to happen */
1345 sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
1346 ident_serv->ai_protocol);
1350 (errcode_for_socket_access(),
1351 errmsg("could not create socket for Ident connection: %m")));
1352 ident_return = false;
1353 goto ident_inet_done;
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.
1361 rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
1365 (errcode_for_socket_access(),
1366 errmsg("could not bind to local address \"%s\": %m",
1368 ident_return = false;
1369 goto ident_inet_done;
1372 rc = connect(sock_fd, ident_serv->ai_addr,
1373 ident_serv->ai_addrlen);
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;
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);
1388 /* loop in case send is interrupted */
1391 rc = send(sock_fd, ident_query, strlen(ident_query), 0);
1392 } while (rc < 0 && errno == EINTR);
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;
1406 rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
1407 } while (rc < 0 && errno == EINTR);
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;
1419 ident_response[rc] = '\0';
1420 ident_return = interpret_ident_response(ident_response, ident_user);
1423 (errmsg("invalidly formatted response from Ident server: \"%s\"",
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;
1435 * Ask kernel about the credentials of the connecting process and
1436 * determine the symbolic name of the corresponding user.
1438 * Returns either true and the username put into "ident_user",
1439 * or false if we were unable to determine the username.
1441 #ifdef HAVE_UNIX_SOCKETS
1444 ident_unix(int sock, char *ident_user)
1446 #if defined(HAVE_GETPEEREID)
1447 /* OpenBSD style: */
1450 struct passwd *pass;
1453 if (getpeereid(sock, &uid, &gid) != 0)
1455 /* We didn't get a valid credentials struct. */
1457 (errcode_for_socket_access(),
1458 errmsg("could not get peer credentials: %m")));
1462 pass = getpwuid(uid);
1467 (errmsg("local user with ID %d does not exist",
1472 StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
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;
1482 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
1483 so_len != sizeof(peercred))
1485 /* We didn't get a valid credentials struct. */
1487 (errcode_for_socket_access(),
1488 errmsg("could not get peer credentials: %m")));
1492 pass = getpwuid(peercred.uid);
1497 (errmsg("local user with ID %d does not exist",
1498 (int) peercred.uid)));
1502 StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
1505 #elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
1508 /* Credentials structure */
1509 #if defined(HAVE_STRUCT_CMSGCRED)
1510 typedef struct cmsgcred Cred;
1512 #define cruid cmcred_uid
1513 #elif defined(HAVE_STRUCT_FCRED)
1514 typedef struct fcred Cred;
1516 #define cruid fc_uid
1517 #elif defined(HAVE_STRUCT_SOCKCRED)
1518 typedef struct sockcred Cred;
1520 #define cruid sc_uid
1524 /* Compute size without padding */
1525 char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */
1527 /* Point to start of first structure */
1528 struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
1534 memset(&msg, 0, sizeof(msg));
1537 msg.msg_control = (char *) cmsg;
1538 msg.msg_controllen = sizeof(cmsgmem);
1539 memset(cmsg, 0, sizeof(cmsgmem));
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.
1546 iov.iov_base = &buf;
1549 if (recvmsg(sock, &msg, 0) < 0 ||
1550 cmsg->cmsg_len < sizeof(cmsgmem) ||
1551 cmsg->cmsg_type != SCM_CREDS)
1554 (errcode_for_socket_access(),
1555 errmsg("could not get peer credentials: %m")));
1559 cred = (Cred *) CMSG_DATA(cmsg);
1561 pw = getpwuid(cred->cruid);
1566 (errmsg("local user with ID %d does not exist",
1567 (int) cred->cruid)));
1571 StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1);
1576 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1577 errmsg("Ident authentication is not supported on local connections on this platform")));
1582 #endif /* HAVE_UNIX_SOCKETS */
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
1591 * Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info).
1594 authident(hbaPort *port)
1596 char ident_user[IDENT_USERNAME_MAX + 1];
1598 switch (port->raddr.addr.ss_family)
1604 if (!ident_inet(port->raddr, port->laddr, ident_user))
1605 return STATUS_ERROR;
1608 #ifdef HAVE_UNIX_SOCKETS
1610 if (!ident_unix(port->sock, ident_user))
1611 return STATUS_ERROR;
1616 return STATUS_ERROR;
1620 (errmsg("Ident protocol identifies remote user as \"%s\"",
1623 if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
1626 return STATUS_ERROR;
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.
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.
1640 hba_getauthmethod(hbaPort *port)
1642 if (check_hba(port))
1645 return STATUS_ERROR;