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-2005, 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.136 2004-12-31 21:59:50 pgsql 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
/* This is used to separate values in multi-valued column strings */
48
#define MULTI_VALUE_SEP "\001"
53
* These variables hold the pre-parsed contents of the hba and ident
54
* configuration files. Each is a list of sublists, one sublist for
55
* each (non-empty, non-comment) line of the file. Each sublist's
56
* first item is an integer line number (so we can give somewhat-useful
57
* location info in error messages). Remaining items are palloc'd strings,
58
* one string per token on the line. Note there will always be at least
59
* one token, since blank lines are not entered in the data structure.
62
/* pre-parsed content of HBA config file and corresponding line #s */
63
static List *hba_lines = NIL;
64
static List *hba_line_nums = NIL;
66
/* pre-parsed content of ident usermap file and corresponding line #s */
67
static List *ident_lines = NIL;
68
static List *ident_line_nums = NIL;
70
/* pre-parsed content of group file and corresponding line #s */
71
static List *group_lines = NIL;
72
static List *group_line_nums = NIL;
74
/* pre-parsed content of user passwd file and corresponding line #s */
75
static List *user_lines = NIL;
76
static List *user_line_nums = NIL;
78
/* sorted entries so we can do binary search lookups */
79
static List **user_sorted = NULL; /* sorted user list, for bsearch() */
80
static List **group_sorted = NULL; /* sorted group list, for
82
static int user_length;
83
static int group_length;
85
static void tokenize_file(const char *filename, FILE *file,
86
List **lines, List **line_nums);
87
static char *tokenize_inc_file(const char *outer_filename,
88
const char *inc_filename);
91
* isblank() exists in the ISO C99 spec, but it's not very portable yet,
92
* so provide our own version.
95
pg_isblank(const char c)
97
return c == ' ' || c == '\t' || c == '\r';
102
* Grab one token out of fp. Tokens are strings of non-blank
103
* characters bounded by blank characters, beginning of line, and
104
* end of line. Blank means space or tab. Return the token as
105
* *buf. Leave file positioned at the character immediately after the
106
* token or EOF, whichever comes first. If no more tokens on line,
107
* return empty string as *buf and position the file to the beginning
108
* of the next line or EOF, whichever comes first. Allow spaces in
109
* quoted strings. Terminate on unquoted commas. Handle
110
* comments. Treat unquoted keywords that might be user names or
111
* database names specially, by appending a newline to them.
114
next_token(FILE *fp, char *buf, int bufsz)
117
char *start_buf = buf;
118
char *end_buf = buf + (bufsz - 2);
119
bool in_quote = false;
120
bool was_quote = false;
121
bool saw_quote = false;
123
Assert(end_buf > start_buf);
125
/* Move over initial whitespace and commas */
126
while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
129
if (c == EOF || c == '\n')
136
* Build a token in buf of next characters up to EOF, EOL, unquoted
137
* comma, or unquoted whitespace.
139
while (c != EOF && c != '\n' &&
140
(!pg_isblank(c) || in_quote == true))
142
/* skip comments to EOL */
143
if (c == '#' && !in_quote)
145
while ((c = getc(fp)) != EOF && c != '\n')
147
/* If only comment, consume EOL too; return EOL */
148
if (c != EOF && buf == start_buf)
157
(errcode(ERRCODE_CONFIG_FILE_ERROR),
158
errmsg("authentication file token too long, skipping: \"%s\"",
160
/* Discard remainder of line */
161
while ((c = getc(fp)) != EOF && c != '\n')
166
if (c != '"' || (c == '"' && was_quote))
169
/* We pass back the comma so the caller knows there is more */
170
if ((pg_isblank(c) || c == ',') && !in_quote)
173
/* Literal double-quote is two double-quotes */
174
if (in_quote && c == '"')
175
was_quote = !was_quote;
181
in_quote = !in_quote;
189
* Put back the char right after the token (critical in case it is
190
* EOL, since we need to detect end-of-line at next call).
198
(strcmp(start_buf, "all") == 0 ||
199
strcmp(start_buf, "sameuser") == 0 ||
200
strcmp(start_buf, "samegroup") == 0))
202
/* append newline to a magical keyword */
209
* Tokenize file and handle file inclusion and comma lists. We have
210
* to break apart the commas to expand any file names then
211
* reconstruct with commas.
213
* The result is always a palloc'd string. If it's zero-length then
214
* we have reached EOL.
217
next_token_expand(const char *filename, FILE *file)
220
char *comma_str = pstrdup("");
227
next_token(file, buf, sizeof(buf));
231
if (buf[strlen(buf) - 1] == ',')
233
trailing_comma = true;
234
buf[strlen(buf) - 1] = '\0';
237
trailing_comma = false;
239
/* Is this referencing a file? */
241
incbuf = tokenize_inc_file(filename, buf + 1);
243
incbuf = pstrdup(buf);
245
needed = strlen(comma_str) + strlen(incbuf) + 1;
248
comma_str = repalloc(comma_str, needed);
249
strcat(comma_str, incbuf);
251
strcat(comma_str, MULTI_VALUE_SEP);
253
} while (trailing_comma);
260
* Free memory used by lines/tokens (i.e., structure built by tokenize_file)
263
free_lines(List **lines, List **line_nums)
266
* Either both must be non-NULL, or both must be NULL
268
Assert((*lines != NIL && *line_nums != NIL) ||
269
(*lines == NIL && *line_nums == NIL));
274
* "lines" is a list of lists; each of those sublists consists of
275
* palloc'ed tokens, so we want to free each pointed-to token in a
276
* sublist, followed by the sublist itself, and finally the whole
281
foreach(line, *lines)
283
List *ln = lfirst(line);
287
pfree(lfirst(token));
288
/* free the sublist structure itself */
291
/* free the list structure itself */
293
/* clear the static variable */
299
list_free(*line_nums);
306
tokenize_inc_file(const char *outer_filename,
307
const char *inc_filename)
316
if (is_absolute_path(inc_filename))
318
/* absolute path is taken as-is */
319
inc_fullname = pstrdup(inc_filename);
323
/* relative path is relative to dir of calling file */
324
inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
325
strlen(inc_filename) + 1);
326
strcpy(inc_fullname, outer_filename);
327
get_parent_directory(inc_fullname);
328
join_path_components(inc_fullname, inc_fullname, inc_filename);
329
canonicalize_path(inc_fullname);
332
inc_file = AllocateFile(inc_fullname, "r");
333
if (inc_file == NULL)
336
(errcode_for_file_access(),
337
errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
338
inc_filename, inc_fullname)));
341
/* return single space, it matches nothing */
345
/* There is possible recursion here if the file contains @ */
346
tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
351
/* Create comma-separated string from List */
352
comma_str = pstrdup("");
353
foreach(line, inc_lines)
355
List *token_list = (List *) lfirst(line);
358
foreach(token, token_list)
360
int oldlen = strlen(comma_str);
363
needed = oldlen + strlen(lfirst(token)) + 1;
366
comma_str = repalloc(comma_str, needed);
368
strcat(comma_str, MULTI_VALUE_SEP);
369
strcat(comma_str, lfirst(token));
373
free_lines(&inc_lines, &inc_line_nums);
375
/* if file is empty, return single space rather than empty string */
376
if (strlen(comma_str) == 0)
387
* Tokenize the given file, storing the resulting data into two lists:
388
* a list of sublists, each sublist containing the tokens in a line of
389
* the file, and a list of line numbers.
391
* filename must be the absolute path to the target file.
394
tokenize_file(const char *filename, FILE *file,
395
List **lines, List **line_nums)
397
List *current_line = NIL;
401
*lines = *line_nums = NIL;
405
buf = next_token_expand(filename, file);
407
/* add token to list, unless we are at EOL or comment start */
410
if (current_line == NIL)
412
/* make a new line List, record its line number */
413
current_line = lappend(current_line, buf);
414
*lines = lappend(*lines, current_line);
415
*line_nums = lappend_int(*line_nums, line_number);
419
/* append token to current line's list */
420
current_line = lappend(current_line, buf);
425
/* we are at real or logical EOL, so force a new line List */
427
/* Advance line number whenever we reach EOL */
429
/* Don't forget to pfree the next_token_expand result */
437
* Compare two lines based on their user/group names.
439
* Used for qsort() sorting.
442
user_group_qsort_cmp(const void *list1, const void *list2)
444
char *user1 = linitial(*(List **) list1);
445
char *user2 = linitial(*(List **) list2);
447
return strcmp(user1, user2);
452
* Compare two lines based on their user/group names.
454
* Used for bsearch() lookup.
457
user_group_bsearch_cmp(const void *user, const void *list)
459
char *user2 = linitial(*(List **) list);
461
return strcmp(user, user2);
466
* Lookup a group name in the pg_group file
469
get_group_line(const char *group)
471
/* On some versions of Solaris, bsearch of zero items dumps core */
472
if (group_length == 0)
475
return (List **) bsearch((void *) group,
476
(void *) group_sorted,
479
user_group_bsearch_cmp);
484
* Lookup a user name in the pg_shadow file
487
get_user_line(const char *user)
489
/* On some versions of Solaris, bsearch of zero items dumps core */
490
if (user_length == 0)
493
return (List **) bsearch((void *) user,
494
(void *) user_sorted,
497
user_group_bsearch_cmp);
502
* Check group for a specific user.
505
check_group(char *group, char *user)
509
if ((line = get_group_line(group)) != NULL)
513
/* skip over the group name */
514
for_each_cell(line_item, lnext(list_head(*line)))
516
if (strcmp(lfirst(line_item), user) == 0)
525
* Check comma user list for a specific user, handle group names.
528
check_user(char *user, char *param_str)
532
for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
536
if (check_group(tok + 1, user))
539
else if (strcmp(tok, user) == 0 ||
540
strcmp(tok, "all\n") == 0)
548
* Check to see if db/user combination matches param string.
551
check_db(char *dbname, char *user, char *param_str)
555
for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
557
if (strcmp(tok, "all\n") == 0)
559
else if (strcmp(tok, "sameuser\n") == 0)
561
if (strcmp(dbname, user) == 0)
564
else if (strcmp(tok, "samegroup\n") == 0)
566
if (check_group(dbname, user))
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, "krb4") == 0)
604
*userauth_p = uaKrb4;
605
else if (strcmp(token, "krb5") == 0)
606
*userauth_p = uaKrb5;
607
else if (strcmp(token, "reject") == 0)
608
*userauth_p = uaReject;
609
else if (strcmp(token, "md5") == 0)
611
else if (strcmp(token, "crypt") == 0)
612
*userauth_p = uaCrypt;
614
else if (strcmp(token, "pam") == 0)
622
*line_item = lnext(*line_item);
624
/* Get the authentication argument token, if any */
627
token = lfirst(*line_item);
628
*auth_arg_p = pstrdup(token);
629
*line_item = lnext(*line_item);
630
/* If there is more on the line, it is an error */
638
* Process one line from the hba config file.
640
* See if it applies to a connection from a host with IP address port->raddr
641
* to a database named port->database. If so, return *found_p true
642
* and fill in the auth arguments into the appropriate port fields.
643
* If not, leave *found_p as it was. If the record has a syntax error,
644
* return *error_p true, after issuing a message to the log. If no error,
645
* leave *error_p as it was.
648
parse_hba(List *line, int line_num, hbaPort *port,
649
bool *found_p, bool *error_p)
654
struct addrinfo *gai_result;
655
struct addrinfo hints;
657
struct sockaddr_storage addr;
658
struct sockaddr_storage mask;
662
line_item = list_head(line);
663
/* Check the record type. */
664
token = lfirst(line_item);
665
if (strcmp(token, "local") == 0)
667
/* Get the database. */
668
line_item = lnext(line_item);
671
db = lfirst(line_item);
674
line_item = lnext(line_item);
677
user = lfirst(line_item);
679
line_item = lnext(line_item);
683
/* Read the rest of the line. */
684
parse_hba_auth(&line_item, &port->auth_method,
685
&port->auth_arg, error_p);
689
/* Disallow auth methods that always need TCP/IP sockets to work */
690
if (port->auth_method == uaKrb4 ||
691
port->auth_method == uaKrb5)
694
/* Does not match if connection isn't AF_UNIX */
695
if (!IS_AF_UNIX(port->raddr.addr.ss_family))
698
else if (strcmp(token, "host") == 0
699
|| strcmp(token, "hostssl") == 0
700
|| strcmp(token, "hostnossl") == 0)
703
if (token[4] == 's') /* "hostssl" */
706
/* Record does not match if we are not on an SSL connection */
710
/* Placeholder to require specific SSL level, perhaps? */
711
/* Or a client certificate */
713
/* Since we were on SSL, proceed as with normal 'host' mode */
715
/* We don't accept this keyword at all if no SSL support */
720
else if (token[4] == 'n') /* "hostnossl" */
722
/* Record does not match if we are on an SSL connection */
728
/* Get the database. */
729
line_item = lnext(line_item);
732
db = lfirst(line_item);
735
line_item = lnext(line_item);
738
user = lfirst(line_item);
740
/* Read the IP address field. (with or without CIDR netmask) */
741
line_item = lnext(line_item);
744
token = lfirst(line_item);
746
/* Check if it has a CIDR suffix and if so isolate it */
747
cidr_slash = strchr(token, '/');
751
/* Get the IP address either way */
752
hints.ai_flags = AI_NUMERICHOST;
753
hints.ai_family = PF_UNSPEC;
754
hints.ai_socktype = 0;
755
hints.ai_protocol = 0;
756
hints.ai_addrlen = 0;
757
hints.ai_canonname = NULL;
758
hints.ai_addr = NULL;
759
hints.ai_next = NULL;
761
ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
762
if (ret || !gai_result)
765
(errcode(ERRCODE_CONFIG_FILE_ERROR),
766
errmsg("invalid IP address \"%s\" in file \"%s\" line %d: %s",
767
token, HbaFileName, line_num,
768
gai_strerror(ret))));
772
freeaddrinfo_all(hints.ai_family, gai_result);
773
goto hba_other_error;
779
memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
780
freeaddrinfo_all(hints.ai_family, gai_result);
782
/* Get the netmask */
785
if (SockAddr_cidr_mask(&mask, cidr_slash + 1, addr.ss_family) < 0)
790
/* Read the mask field. */
791
line_item = lnext(line_item);
794
token = lfirst(line_item);
796
ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
797
if (ret || !gai_result)
800
(errcode(ERRCODE_CONFIG_FILE_ERROR),
801
errmsg("invalid IP mask \"%s\" in file \"%s\" line %d: %s",
802
token, HbaFileName, line_num,
803
gai_strerror(ret))));
805
freeaddrinfo_all(hints.ai_family, gai_result);
806
goto hba_other_error;
809
memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
810
freeaddrinfo_all(hints.ai_family, gai_result);
812
if (addr.ss_family != mask.ss_family)
815
(errcode(ERRCODE_CONFIG_FILE_ERROR),
816
errmsg("IP address and mask do not match in file \"%s\" line %d",
817
HbaFileName, line_num)));
818
goto hba_other_error;
822
if (addr.ss_family != port->raddr.addr.ss_family)
825
* Wrong address family. We allow only one case: if the file
826
* has IPv4 and the port is IPv6, promote the file address to
827
* IPv6 and try to match that way.
830
if (addr.ss_family == AF_INET &&
831
port->raddr.addr.ss_family == AF_INET6)
833
promote_v4_to_v6_addr(&addr);
834
promote_v4_to_v6_mask(&mask);
837
#endif /* HAVE_IPV6 */
839
/* Line doesn't match client port, so ignore it. */
844
/* Ignore line if client port is not in the matching addr range. */
845
if (!rangeSockAddr(&port->raddr.addr, &addr, &mask))
848
/* Read the rest of the line. */
849
line_item = lnext(line_item);
852
parse_hba_auth(&line_item, &port->auth_method,
853
&port->auth_arg, error_p);
860
/* Does the entry match database and user? */
861
if (!check_db(port->database_name, port->user_name, db))
863
if (!check_user(port->user_name, user))
873
(errcode(ERRCODE_CONFIG_FILE_ERROR),
874
errmsg("invalid entry in file \"%s\" at line %d, token \"%s\"",
875
HbaFileName, line_num,
876
(char *) lfirst(line_item))));
879
(errcode(ERRCODE_CONFIG_FILE_ERROR),
880
errmsg("missing field in file \"%s\" at end of line %d",
881
HbaFileName, line_num)));
883
/* Come here if suitable message already logged */
890
* Scan the (pre-parsed) hba file line by line, looking for a match
891
* to the port's connection request.
894
check_hba(hbaPort *port)
896
bool found_entry = false;
901
forboth(line, hba_lines, line_num, hba_line_nums)
903
parse_hba(lfirst(line), lfirst_int(line_num),
904
port, &found_entry, &error);
905
if (found_entry || error)
911
/* If no matching entry was found, synthesize 'reject' entry. */
913
port->auth_method = uaReject;
922
* Load group/user name mapping file
930
/* Discard any old data */
931
if (group_lines || group_line_nums)
932
free_lines(&group_lines, &group_line_nums);
938
/* Read in the file contents */
939
filename = group_getfilename();
940
group_file = AllocateFile(filename, "r");
942
if (group_file == NULL)
944
/* no complaint if not there */
947
(errcode_for_file_access(),
948
errmsg("could not open file \"%s\": %m", filename)));
953
tokenize_file(filename, group_file, &group_lines, &group_line_nums);
955
FreeFile(group_file);
958
/* create sorted lines for binary searching */
959
group_length = list_length(group_lines);
965
group_sorted = palloc(group_length * sizeof(List *));
967
foreach(line, group_lines)
968
group_sorted[i++] = lfirst(line);
970
qsort((void *) group_sorted,
973
user_group_qsort_cmp);
979
* Load user/password mapping file
987
/* Discard any old data */
988
if (user_lines || user_line_nums)
989
free_lines(&user_lines, &user_line_nums);
995
/* Read in the file contents */
996
filename = user_getfilename();
997
user_file = AllocateFile(filename, "r");
999
if (user_file == NULL)
1001
/* no complaint if not there */
1002
if (errno != ENOENT)
1004
(errcode_for_file_access(),
1005
errmsg("could not open file \"%s\": %m", filename)));
1010
tokenize_file(filename, user_file, &user_lines, &user_line_nums);
1012
FreeFile(user_file);
1015
/* create sorted lines for binary searching */
1016
user_length = list_length(user_lines);
1022
user_sorted = palloc(user_length * sizeof(List *));
1024
foreach(line, user_lines)
1025
user_sorted[i++] = lfirst(line);
1027
qsort((void *) user_sorted,
1030
user_group_qsort_cmp);
1036
* Read the config file and create a List of Lists of tokens in the file.
1043
if (hba_lines || hba_line_nums)
1044
free_lines(&hba_lines, &hba_line_nums);
1046
file = AllocateFile(HbaFileName, "r");
1047
/* Failure is fatal since with no HBA entries we can do nothing... */
1050
(errcode_for_file_access(),
1051
errmsg("could not open configuration file \"%s\": %m",
1054
tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
1060
* Process one line from the ident config file.
1062
* Take the line and compare it to the needed map, pg_user and ident_user.
1063
* *found_p and *error_p are set according to our results.
1066
parse_ident_usermap(List *line, int line_number, const char *usermap_name,
1067
const char *pg_user, const char *ident_user,
1068
bool *found_p, bool *error_p)
1070
ListCell *line_item;
1074
char *file_ident_user;
1079
Assert(line != NIL);
1080
line_item = list_head(line);
1082
/* Get the map token (must exist) */
1083
token = lfirst(line_item);
1086
/* Get the ident user token (must be provided) */
1087
line_item = lnext(line_item);
1090
token = lfirst(line_item);
1091
file_ident_user = token;
1093
/* Get the PG username token */
1094
line_item = lnext(line_item);
1097
token = lfirst(line_item);
1098
file_pguser = token;
1101
if (strcmp(file_map, usermap_name) == 0 &&
1102
strcmp(file_pguser, pg_user) == 0 &&
1103
strcmp(file_ident_user, ident_user) == 0)
1110
(errcode(ERRCODE_CONFIG_FILE_ERROR),
1111
errmsg("invalid entry in file \"%s\" at line %d, token \"%s\"",
1112
IdentFileName, line_number,
1113
(const char *) lfirst(line_item))));
1116
(errcode(ERRCODE_CONFIG_FILE_ERROR),
1117
errmsg("missing entry in file \"%s\" at end of line %d",
1118
IdentFileName, line_number)));
1125
* Scan the (pre-parsed) ident usermap file line by line, looking for a match
1127
* See if the user with ident username "ident_user" is allowed to act
1128
* as Postgres user "pguser" according to usermap "usermap_name".
1130
* Special case: For usermap "sameuser", don't look in the usermap
1131
* file. That's an implied map where "pguser" must be identical to
1132
* "ident_user" in order to be authorized.
1134
* Iff authorized, return true.
1137
check_ident_usermap(const char *usermap_name,
1138
const char *pg_user,
1139
const char *ident_user)
1141
bool found_entry = false,
1144
if (usermap_name == NULL || usermap_name[0] == '\0')
1147
(errcode(ERRCODE_CONFIG_FILE_ERROR),
1148
errmsg("cannot use Ident authentication without usermap field")));
1149
found_entry = false;
1151
else if (strcmp(usermap_name, "sameuser\n") == 0)
1153
if (strcmp(pg_user, ident_user) == 0)
1156
found_entry = false;
1160
ListCell *line_cell,
1163
forboth(line_cell, ident_lines, num_cell, ident_line_nums)
1165
parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
1166
usermap_name, pg_user, ident_user,
1167
&found_entry, &error);
1168
if (found_entry || error)
1177
* Read the ident config file and create a List of Lists of tokens in the file.
1184
if (ident_lines || ident_line_nums)
1185
free_lines(&ident_lines, &ident_line_nums);
1187
file = AllocateFile(IdentFileName, "r");
1190
/* not fatal ... we just won't do any special ident maps */
1192
(errcode_for_file_access(),
1193
errmsg("could not open Ident usermap file \"%s\": %m",
1198
tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
1205
* Parse the string "*ident_response" as a response from a query to an Ident
1206
* server. If it's a normal response indicating a user name, return true
1207
* and store the user name at *ident_user. If it's anything else,
1211
interpret_ident_response(const char *ident_response,
1214
const char *cursor = ident_response; /* Cursor into
1215
* *ident_response */
1218
* Ident's response, in the telnet tradition, should end in crlf
1221
if (strlen(ident_response) < 2)
1223
else if (ident_response[strlen(ident_response) - 2] != '\r')
1227
while (*cursor != ':' && *cursor != '\r')
1228
cursor++; /* skip port field */
1234
/* We're positioned to colon before response type field */
1235
char response_type[80];
1236
int i; /* Index into *response_type */
1238
cursor++; /* Go over colon */
1239
while (pg_isblank(*cursor))
1240
cursor++; /* skip blanks */
1242
while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) &&
1243
i < (int) (sizeof(response_type) - 1))
1244
response_type[i++] = *cursor++;
1245
response_type[i] = '\0';
1246
while (pg_isblank(*cursor))
1247
cursor++; /* skip blanks */
1248
if (strcmp(response_type, "USERID") != 0)
1253
* It's a USERID response. Good. "cursor" should be
1254
* pointing to the colon that precedes the operating
1261
cursor++; /* Go over colon */
1262
/* Skip over operating system field. */
1263
while (*cursor != ':' && *cursor != '\r')
1269
int i; /* Index into *ident_user */
1271
cursor++; /* Go over colon */
1272
while (pg_isblank(*cursor))
1273
cursor++; /* skip blanks */
1274
/* Rest of line is user name. Copy it over. */
1276
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
1277
ident_user[i++] = *cursor++;
1278
ident_user[i] = '\0';
1289
* Talk to the ident server on host "remote_ip_addr" and find out who
1290
* owns the tcp connection from his port "remote_port" to port
1291
* "local_port_addr" on host "local_ip_addr". Return the user name the
1292
* ident server gives as "*ident_user".
1294
* IP addresses and port numbers are in network byte order.
1296
* But iff we're unable to get the information from ident, return false.
1299
ident_inet(const SockAddr remote_addr,
1300
const SockAddr local_addr,
1303
int sock_fd, /* File descriptor for socket on which we
1305
rc; /* Return code from a locally called
1308
char remote_addr_s[NI_MAXHOST];
1309
char remote_port[NI_MAXSERV];
1310
char local_addr_s[NI_MAXHOST];
1311
char local_port[NI_MAXSERV];
1312
char ident_port[NI_MAXSERV];
1313
char ident_query[80];
1314
char ident_response[80 + IDENT_USERNAME_MAX];
1315
struct addrinfo *ident_serv = NULL,
1320
* Might look a little weird to first convert it to text and then back
1321
* to sockaddr, but it's protocol independent.
1323
getnameinfo_all(&remote_addr.addr, remote_addr.salen,
1324
remote_addr_s, sizeof(remote_addr_s),
1325
remote_port, sizeof(remote_port),
1326
NI_NUMERICHOST | NI_NUMERICSERV);
1327
getnameinfo_all(&local_addr.addr, local_addr.salen,
1328
local_addr_s, sizeof(local_addr_s),
1329
local_port, sizeof(local_port),
1330
NI_NUMERICHOST | NI_NUMERICSERV);
1332
snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT);
1333
hints.ai_flags = AI_NUMERICHOST;
1334
hints.ai_family = remote_addr.addr.ss_family;
1335
hints.ai_socktype = SOCK_STREAM;
1336
hints.ai_protocol = 0;
1337
hints.ai_addrlen = 0;
1338
hints.ai_canonname = NULL;
1339
hints.ai_addr = NULL;
1340
hints.ai_next = NULL;
1341
rc = getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);
1342
if (rc || !ident_serv)
1345
freeaddrinfo_all(hints.ai_family, ident_serv);
1346
return false; /* we don't expect this to happen */
1349
hints.ai_flags = AI_NUMERICHOST;
1350
hints.ai_family = local_addr.addr.ss_family;
1351
hints.ai_socktype = SOCK_STREAM;
1352
hints.ai_protocol = 0;
1353
hints.ai_addrlen = 0;
1354
hints.ai_canonname = NULL;
1355
hints.ai_addr = NULL;
1356
hints.ai_next = NULL;
1357
rc = getaddrinfo_all(local_addr_s, NULL, &hints, &la);
1361
freeaddrinfo_all(hints.ai_family, la);
1362
return false; /* we don't expect this to happen */
1365
sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype,
1366
ident_serv->ai_protocol);
1370
(errcode_for_socket_access(),
1371
errmsg("could not create socket for Ident connection: %m")));
1372
ident_return = false;
1373
goto ident_inet_done;
1377
* Bind to the address which the client originally contacted,
1378
* otherwise the ident server won't be able to match up the right
1379
* connection. This is necessary if the PostgreSQL server is running
1382
rc = bind(sock_fd, la->ai_addr, la->ai_addrlen);
1386
(errcode_for_socket_access(),
1387
errmsg("could not bind to local address \"%s\": %m",
1389
ident_return = false;
1390
goto ident_inet_done;
1393
rc = connect(sock_fd, ident_serv->ai_addr,
1394
ident_serv->ai_addrlen);
1398
(errcode_for_socket_access(),
1399
errmsg("could not connect to Ident server at address \"%s\", port %s: %m",
1400
remote_addr_s, ident_port)));
1401
ident_return = false;
1402
goto ident_inet_done;
1405
/* The query we send to the Ident server */
1406
snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n",
1407
remote_port, local_port);
1409
/* loop in case send is interrupted */
1412
rc = send(sock_fd, ident_query, strlen(ident_query), 0);
1413
} while (rc < 0 && errno == EINTR);
1418
(errcode_for_socket_access(),
1419
errmsg("could not send query to Ident server at address \"%s\", port %s: %m",
1420
remote_addr_s, ident_port)));
1421
ident_return = false;
1422
goto ident_inet_done;
1427
rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
1428
} while (rc < 0 && errno == EINTR);
1433
(errcode_for_socket_access(),
1434
errmsg("could not receive response from Ident server at address \"%s\", port %s: %m",
1435
remote_addr_s, ident_port)));
1436
ident_return = false;
1437
goto ident_inet_done;
1440
ident_response[rc] = '\0';
1441
ident_return = interpret_ident_response(ident_response, ident_user);
1444
(errmsg("invalidly formatted response from Ident server: \"%s\"",
1449
closesocket(sock_fd);
1450
freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);
1451
freeaddrinfo_all(local_addr.addr.ss_family, la);
1452
return ident_return;
1456
* Ask kernel about the credentials of the connecting process and
1457
* determine the symbolic name of the corresponding user.
1459
* Returns either true and the username put into "ident_user",
1460
* or false if we were unable to determine the username.
1462
#ifdef HAVE_UNIX_SOCKETS
1465
ident_unix(int sock, char *ident_user)
1467
#if defined(HAVE_GETPEEREID)
1468
/* OpenBSD style: */
1471
struct passwd *pass;
1474
if (getpeereid(sock, &uid, &gid) != 0)
1476
/* We didn't get a valid credentials struct. */
1478
(errcode_for_socket_access(),
1479
errmsg("could not get peer credentials: %m")));
1483
pass = getpwuid(uid);
1488
(errmsg("local user with ID %d does not exist",
1493
StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
1497
#elif defined(SO_PEERCRED)
1498
/* Linux style: use getsockopt(SO_PEERCRED) */
1499
struct ucred peercred;
1500
ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
1501
struct passwd *pass;
1504
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
1505
so_len != sizeof(peercred))
1507
/* We didn't get a valid credentials struct. */
1509
(errcode_for_socket_access(),
1510
errmsg("could not get peer credentials: %m")));
1514
pass = getpwuid(peercred.uid);
1519
(errmsg("local user with ID %d does not exist",
1520
(int) peercred.uid)));
1524
StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
1528
#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
1531
/* Credentials structure */
1532
#if defined(HAVE_STRUCT_CMSGCRED)
1533
typedef struct cmsgcred Cred;
1535
#define cruid cmcred_uid
1536
#elif defined(HAVE_STRUCT_FCRED)
1537
typedef struct fcred Cred;
1539
#define cruid fc_uid
1540
#elif defined(HAVE_STRUCT_SOCKCRED)
1541
typedef struct sockcred Cred;
1543
#define cruid sc_uid
1547
/* Compute size without padding */
1548
char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */
1550
/* Point to start of first structure */
1551
struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
1557
memset(&msg, 0, sizeof(msg));
1560
msg.msg_control = (char *) cmsg;
1561
msg.msg_controllen = sizeof(cmsgmem);
1562
memset(cmsg, 0, sizeof(cmsgmem));
1565
* The one character which is received here is not meaningful; its
1566
* purposes is only to make sure that recvmsg() blocks long enough for
1567
* the other side to send its credentials.
1569
iov.iov_base = &buf;
1572
if (recvmsg(sock, &msg, 0) < 0 ||
1573
cmsg->cmsg_len < sizeof(cmsgmem) ||
1574
cmsg->cmsg_type != SCM_CREDS)
1577
(errcode_for_socket_access(),
1578
errmsg("could not get peer credentials: %m")));
1582
cred = (Cred *) CMSG_DATA(cmsg);
1584
pw = getpwuid(cred->cruid);
1589
(errmsg("local user with ID %d does not exist",
1590
(int) cred->cruid)));
1594
StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1);
1600
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1601
errmsg("Ident authentication is not supported on local connections on this platform")));
1606
#endif /* HAVE_UNIX_SOCKETS */
1610
* Determine the username of the initiator of the connection described
1611
* by "port". Then look in the usermap file under the usermap
1612
* port->auth_arg and see if that user is equivalent to Postgres user
1615
* Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info).
1618
authident(hbaPort *port)
1620
char ident_user[IDENT_USERNAME_MAX + 1];
1622
switch (port->raddr.addr.ss_family)
1628
if (!ident_inet(port->raddr, port->laddr, ident_user))
1629
return STATUS_ERROR;
1632
#ifdef HAVE_UNIX_SOCKETS
1634
if (!ident_unix(port->sock, ident_user))
1635
return STATUS_ERROR;
1640
return STATUS_ERROR;
1644
(errmsg("Ident protocol identifies remote user as \"%s\"",
1647
if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
1650
return STATUS_ERROR;
1655
* Determine what authentication method should be used when accessing database
1656
* "database" from frontend "raddr", user "user". Return the method and
1657
* an optional argument (stored in fields of *port), and STATUS_OK.
1659
* Note that STATUS_ERROR indicates a problem with the hba config file.
1660
* If the file is OK but does not contain any entry matching the request,
1661
* we return STATUS_OK and method = uaReject.
1664
hba_getauthmethod(hbaPort *port)
1666
if (check_hba(port))
1669
return STATUS_ERROR;