mirror of https://github.com/postgres/postgres
parent
57026d6009
commit
4b5c977782
@ -0,0 +1,768 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* hba.c-- |
||||
* Routines to handle host based authentication (that's the scheme |
||||
* wherein you authenticate a user by seeing what IP address the system
|
||||
* says he comes from and possibly using ident). |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.1 1996/10/12 07:47:09 bryanh Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <pwd.h> |
||||
#include <ctype.h> /* isspace() declaration */ |
||||
#include <sys/stat.h> |
||||
#include <netinet/in.h> |
||||
#include <arpa/inet.h> |
||||
|
||||
#include <miscadmin.h> |
||||
#include <libpq/libpq.h> |
||||
#include <libpq/pqcomm.h> |
||||
#include <libpq/hba.h> |
||||
|
||||
|
||||
#define CONF_FILE "pg_hba.conf" |
||||
/* Name of the config file */ |
||||
|
||||
#define MAP_FILE "pg_ident.conf" |
||||
/* Name of the usermap file */ |
||||
|
||||
#define OLD_CONF_FILE "pg_hba" |
||||
/* Name of the config file in prior releases of Postgres. */ |
||||
|
||||
#define MAX_LINES 255 |
||||
/* Maximum number of config lines that can apply to one database */ |
||||
|
||||
#define MAX_TOKEN 80 |
||||
/* Maximum size of one token in the configuration file */ |
||||
|
||||
#define USERMAP_NAME_SIZE 16 /* Max size of a usermap name */ |
||||
|
||||
#define IDENT_PORT 113 |
||||
/* Standard TCP port number for Ident service. Assigned by IANA */ |
||||
|
||||
#define IDENT_USERNAME_MAX 512 |
||||
/* Max size of username ident server can return */ |
||||
|
||||
enum Userauth {Trust, Ident}; |
||||
|
||||
|
||||
|
||||
static void
|
||||
next_token(FILE *fp, char *buf, const int bufsz) { |
||||
/*--------------------------------------------------------------------------
|
||||
Grab one token out of fp. Tokens are strings of non-blank |
||||
characters bounded by blank characters, beginning of line, and end |
||||
of line. Blank means space or tab. Return the token as *buf. |
||||
Leave file positioned to character immediately after the token or |
||||
EOF, whichever comes first. If no more tokens on line, return null |
||||
string as *buf and position file to beginning of next line or EOF, |
||||
whichever comes first.
|
||||
--------------------------------------------------------------------------*/ |
||||
int c; |
||||
char *eb = buf+(bufsz-1); |
||||
|
||||
/* Move over inital token-delimiting blanks */ |
||||
while (isblank(c = getc(fp))) ; |
||||
|
||||
if (c != '\n') { |
||||
/* build a token in buf of next characters up to EOF, eol, or blank. */ |
||||
while (c != EOF && c != '\n' && !isblank(c)) { |
||||
if (buf < eb) *buf++ = c; |
||||
c = getc(fp); |
||||
/* Put back the char right after the token (putting back EOF is ok) */ |
||||
}
|
||||
(void) ungetc(c, fp); |
||||
} |
||||
*buf = '\0'; |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
read_through_eol(FILE *file) { |
||||
int c; |
||||
do
|
||||
c = getc(file);
|
||||
while (c != '\n' && c != EOF);
|
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
read_hba_entry2(FILE *file, enum Userauth *userauth_p, char usermap_name[],
|
||||
bool *error_p) { |
||||
/*--------------------------------------------------------------------------
|
||||
Read from file FILE the rest of a host record, after the mask field, |
||||
and return the interpretation of it as *userauth_p, usermap_name, and |
||||
*error_p. |
||||
---------------------------------------------------------------------------*/ |
||||
char buf[MAX_TOKEN]; |
||||
|
||||
bool userauth_valid; |
||||
|
||||
/* Get authentication type token. */ |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf[0] == '\0') { |
||||
*error_p = true; |
||||
read_through_eol(file); |
||||
} else { |
||||
if (strcmp(buf, "trust") == 0) { |
||||
userauth_valid = true; |
||||
*userauth_p = Trust; |
||||
} else if (strcmp(buf, "ident") == 0) { |
||||
userauth_valid = true; |
||||
*userauth_p = Ident; |
||||
} else userauth_valid = false; |
||||
if (!userauth_valid) { |
||||
*error_p = true; |
||||
read_through_eol(file); |
||||
} else { |
||||
/* Get the map name token, if any */ |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf[0] == '\0') { |
||||
*error_p = false; |
||||
usermap_name[0] = '\0'; |
||||
} else { |
||||
strncpy(usermap_name, buf, USERMAP_NAME_SIZE); |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf[0] != '\0') { |
||||
*error_p = true; |
||||
read_through_eol(file); |
||||
} else *error_p = false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
process_hba_record(FILE *file,
|
||||
const struct in_addr ip_addr, const char database[], |
||||
bool *matches_p, bool *error_p,
|
||||
enum Userauth *userauth_p, char usermap_name[] ) { |
||||
/*---------------------------------------------------------------------------
|
||||
Process the non-comment record in the config file that is next on the file. |
||||
See if it applies to a connection to a host with IP address "ip_addr" |
||||
to a database named "database[]". If so, return *matches_p true |
||||
and *userauth_p and usermap_name[] as the values from the entry. |
||||
If not, return matches_p false. If the record has a syntax error, |
||||
return *error_p true, after issuing a message to stderr. If no error, |
||||
leave *error_p as it was. |
||||
---------------------------------------------------------------------------*/ |
||||
char buf[MAX_TOKEN]; /* A token from the record */ |
||||
|
||||
/* Read the record type field */ |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf[0] == '\0') *matches_p = false; |
||||
else { |
||||
/* if this isn't a "host" record, it can't match. */ |
||||
if (strcmp(buf, "host") != 0) { |
||||
*matches_p = false; |
||||
read_through_eol(file); |
||||
} else { |
||||
/* It's a "host" record. Read the database name field. */ |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf[0] == '\0') *matches_p = false; |
||||
else { |
||||
/* If this record isn't for our database, ignore it. */ |
||||
if (strcmp(buf, database) != 0 && strcmp(buf, "all") != 0) { |
||||
*matches_p = false; |
||||
read_through_eol(file); |
||||
} else { |
||||
/* Read the IP address field */ |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf[0] == '\0') *matches_p = false; |
||||
else { |
||||
int valid; /* Field is valid dotted decimal */ |
||||
/* Remember the IP address field and go get mask field */ |
||||
struct in_addr file_ip_addr; /* IP address field value */ |
||||
|
||||
valid = inet_aton(buf, &file_ip_addr); |
||||
if (!valid) { |
||||
*matches_p = false; |
||||
read_through_eol(file); |
||||
} else { |
||||
/* Read the mask field */ |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf[0] == '\0') *matches_p = false; |
||||
else { |
||||
struct in_addr mask; |
||||
/* Got mask. Now see if this record is for our host. */ |
||||
valid = inet_aton(buf, &mask); |
||||
if (!valid) { |
||||
*matches_p = false; |
||||
read_through_eol(file); |
||||
} else { |
||||
if (((file_ip_addr.s_addr ^ ip_addr.s_addr) & mask.s_addr) |
||||
!= 0x0000) { |
||||
*matches_p = false; |
||||
read_through_eol(file); |
||||
} else { |
||||
/* This is the record we're looking for. Read
|
||||
the rest of the info from it. |
||||
*/ |
||||
read_hba_entry2(file, userauth_p, usermap_name, |
||||
error_p); |
||||
*matches_p = true; |
||||
if (*error_p) { |
||||
sprintf(PQerrormsg, |
||||
"process_hba_record: invalid syntax in " |
||||
"hba config file " |
||||
"for host record for IP address %s\n", |
||||
inet_ntoa(file_ip_addr)); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
process_open_config_file(FILE *file,
|
||||
const struct in_addr ip_addr, const char database[], |
||||
bool *host_ok_p, enum Userauth *userauth_p,
|
||||
char usermap_name[] ) { |
||||
/*---------------------------------------------------------------------------
|
||||
This function does the same thing as find_hba_entry, only with |
||||
the config file already open on stream descriptor "file". |
||||
----------------------------------------------------------------------------*/ |
||||
bool found_entry;
|
||||
/* We've processed a record that applies to our connection */ |
||||
bool error;
|
||||
/* Said record has invalid syntax. */ |
||||
bool eof; /* We've reached the end of the file we're reading */ |
||||
|
||||
found_entry = false; /* initial value */ |
||||
error = false; /* initial value */ |
||||
eof = false; /* initial value */
|
||||
while (!eof && !found_entry && !error) { |
||||
/* Process a line from the config file */ |
||||
|
||||
int c; /* a character read from the file */ |
||||
|
||||
c = getc(file); ungetc(c, file); |
||||
if (c == EOF) eof = true; |
||||
else { |
||||
if (c == '#') read_through_eol(file); |
||||
else { |
||||
process_hba_record(file, ip_addr, database,
|
||||
&found_entry, &error, userauth_p, usermap_name); |
||||
} |
||||
}
|
||||
} |
||||
if (found_entry) { |
||||
if (error) *host_ok_p = false; |
||||
else *host_ok_p = true; |
||||
} else *host_ok_p = false; |
||||
}
|
||||
|
||||
|
||||
|
||||
static void |
||||
find_hba_entry(const char DataDir[], const struct in_addr ip_addr,
|
||||
const char database[], |
||||
bool *host_ok_p, enum Userauth *userauth_p,
|
||||
char usermap_name[] ) { |
||||
/*--------------------------------------------------------------------------
|
||||
Read the config file and find an entry that allows connection from |
||||
host "ip_addr" to database "database". If not found, return
|
||||
*host_ok_p == false. If found, return *userauth_p and *usermap_name |
||||
representing the contents of that entry. |
||||
|
||||
When a record has invalid syntax, we either ignore it or reject the |
||||
connection (depending on where it's invalid). No message or anything. |
||||
We need to fix that some day. |
||||
|
||||
If we don't find or can't access the config file, we issue an error |
||||
message and deny the connection. |
||||
|
||||
If we find a file by the old name of the config file (pg_hba), we issue |
||||
an error message because it probably needs to be converted. He didn't |
||||
follow directions and just installed his old hba file in the new database |
||||
system. |
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
int rc; |
||||
struct stat statbuf; |
||||
|
||||
FILE *file; /* The config file we have to read */ |
||||
|
||||
char *old_conf_file;
|
||||
/* The name of old config file that better not exist. */ |
||||
|
||||
/* Fail if config file by old name exists. */ |
||||
|
||||
|
||||
/* put together the full pathname to the old config file */ |
||||
old_conf_file = (char *) malloc((strlen(DataDir) + |
||||
strlen(OLD_CONF_FILE)+2)*sizeof(char)); |
||||
sprintf(old_conf_file, "%s/%s", DataDir, OLD_CONF_FILE); |
||||
|
||||
rc = stat(old_conf_file, &statbuf); |
||||
if (rc == 0) {
|
||||
/* Old config file exists. Tell this guy he needs to upgrade. */ |
||||
sprintf(PQerrormsg, |
||||
"A file exists by the name used for host-based authentication " |
||||
"in prior releases of Postgres (%s). The name and format of " |
||||
"the configuration file have changed, so this file should be " |
||||
"converted.\n", |
||||
old_conf_file); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
} else { |
||||
char *conf_file; /* The name of the config file we have to read */ |
||||
|
||||
/* put together the full pathname to the config file */ |
||||
conf_file = (char *) malloc((strlen(DataDir) + |
||||
strlen(CONF_FILE)+2)*sizeof(char)); |
||||
sprintf(conf_file, "%s/%s", DataDir, CONF_FILE); |
||||
|
||||
file = fopen(conf_file, "r"); |
||||
if (file == 0) {
|
||||
/* The open of the config file failed. */ |
||||
|
||||
const int open_errno = errno; |
||||
|
||||
*host_ok_p = false; |
||||
|
||||
sprintf(PQerrormsg, |
||||
"find_hba_entry: Host-based authentication config file " |
||||
"does not exist or permissions are not setup correctly! " |
||||
"Unable to open file \"%s\".\n",
|
||||
conf_file); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
} else { |
||||
process_open_config_file(file, ip_addr, database, host_ok_p, userauth_p, |
||||
usermap_name); |
||||
fclose(file); |
||||
} |
||||
free(conf_file); |
||||
} |
||||
free(old_conf_file); |
||||
return; |
||||
} |
||||
|
||||
|
||||
static void |
||||
interpret_ident_response(char ident_response[],
|
||||
bool *error_p, char ident_username[]) { |
||||
/*----------------------------------------------------------------------------
|
||||
Parse the string "ident_response[]" as a response from a query to an Ident |
||||
server. If it's a normal response indicating a username, return
|
||||
*error_p == false and the username as ident_username[]. If it's anything |
||||
else, return *error_p == true and ident_username[] undefined. |
||||
----------------------------------------------------------------------------*/ |
||||
char *cursor; /* Cursor into ident_response[] */ |
||||
|
||||
cursor = &ident_response[0]; |
||||
|
||||
/* Ident's response, in the telnet tradition, should end in crlf (\r\n). */ |
||||
if (strlen(ident_response) < 2) *error_p = true; |
||||
else if (ident_response[strlen(ident_response)-2] != '\r') *error_p = true; |
||||
else { |
||||
while (*cursor != ':' && *cursor != '\r') cursor++; /* skip port field */ |
||||
|
||||
if (*cursor != ':') *error_p = true; |
||||
else { |
||||
/* We're positioned to colon before response type field */ |
||||
char response_type[80]; |
||||
int i; /* Index into response_type[] */ |
||||
cursor++; /* Go over colon */ |
||||
while (isblank(*cursor)) cursor++; /* skip blanks */ |
||||
i = 0; |
||||
while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor)
|
||||
&& i < sizeof(response_type)-1)
|
||||
response_type[i++] = *cursor++; |
||||
response_type[i] = '\0'; |
||||
while (isblank(*cursor)) cursor++; /* skip blanks */ |
||||
if (strcmp(response_type, "USERID") != 0) |
||||
*error_p = true; |
||||
else { |
||||
/* It's a USERID response. Good. "cursor" should be pointing to
|
||||
the colon that precedes the operating system type. |
||||
*/ |
||||
if (*cursor != ':') *error_p = true; |
||||
else { |
||||
cursor++; /* Go over colon */ |
||||
/* Skip over operating system field. */ |
||||
while (*cursor != ':' && *cursor != '\r') cursor++; |
||||
if (*cursor != ':') *error_p = true; |
||||
else { |
||||
int i; /* Index into ident_username[] */ |
||||
cursor ++; /* Go over colon */ |
||||
while (isblank(*cursor)) cursor++; /* skip blanks */ |
||||
/* Rest of line is username. Copy it over. */ |
||||
i = 0; |
||||
while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
|
||||
ident_username[i++] = *cursor++; |
||||
ident_username[i] = '\0'; |
||||
*error_p = false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, |
||||
const ushort remote_port, const ushort local_port, |
||||
bool *ident_failed, char ident_username[]) { |
||||
/*--------------------------------------------------------------------------
|
||||
Talk to the ident server on host "remote_ip_addr" and find out who |
||||
owns the tcp connection from his port "remote_port" to port |
||||
"local_port_addr" on host "local_ip_addr". Return the username the |
||||
ident server gives as "ident_username[]". |
||||
|
||||
IP addresses and port numbers are in network byte order. |
||||
|
||||
But iff we're unable to get the information from ident, return |
||||
*ident_failed == true (and ident_username[] undefined). |
||||
----------------------------------------------------------------------------*/ |
||||
|
||||
int sock_fd; |
||||
/* File descriptor for socket on which we talk to Ident */ |
||||
|
||||
int rc; /* Return code from a locally called function */ |
||||
|
||||
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); |
||||
if (sock_fd == -1) { |
||||
sprintf(PQerrormsg, |
||||
"Failed to create socket on which to talk to Ident server. " |
||||
"socket() returned errno = %s (%d)\n", |
||||
strerror(errno), errno); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
} else { |
||||
struct sockaddr_in ident_server; |
||||
/* Socket address of Ident server on the system from which client
|
||||
is attempting to connect to us. |
||||
*/ |
||||
ident_server.sin_family = AF_INET; |
||||
ident_server.sin_port = htons(IDENT_PORT); |
||||
ident_server.sin_addr = remote_ip_addr; |
||||
rc = connect(sock_fd,
|
||||
(struct sockaddr *) &ident_server, sizeof(ident_server)); |
||||
if (rc != 0) { |
||||
sprintf(PQerrormsg, |
||||
"Unable to connect to Ident server on the host which is " |
||||
"trying to connect to Postgres " |
||||
"(IP address %s, Port %d). " |
||||
"errno = %s (%d)\n", |
||||
inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
*ident_failed = true; |
||||
} else { |
||||
char ident_query[80]; |
||||
/* The query we send to the Ident server */ |
||||
sprintf(ident_query, "%d,%d\n",
|
||||
ntohs(remote_port), ntohs(local_port)); |
||||
rc = send(sock_fd, ident_query, strlen(ident_query), 0); |
||||
if (rc < 0) { |
||||
sprintf(PQerrormsg, |
||||
"Unable to send query to Ident server on the host which is " |
||||
"trying to connect to Postgres (Host %s, Port %s)," |
||||
"even though we successfully connected to it. " |
||||
"errno = %s (%d)\n", |
||||
inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
*ident_failed = true; |
||||
} else { |
||||
char ident_response[80+IDENT_USERNAME_MAX]; |
||||
rc = recv(sock_fd, ident_response, sizeof(ident_response), 0); |
||||
if (rc < 0) { |
||||
sprintf(PQerrormsg, |
||||
"Unable to receive response from Ident server " |
||||
"on the host which is " |
||||
"trying to connect to Postgres (Host %s, Port %s)," |
||||
"even though we successfully sent our query to it. " |
||||
"errno = %s (%d)\n", |
||||
inet_ntoa(remote_ip_addr), IDENT_PORT,
|
||||
strerror(errno), errno); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
*ident_failed = true; |
||||
} else { |
||||
bool error; /* response from Ident is garbage. */ |
||||
interpret_ident_response(ident_response, &error, ident_username); |
||||
*ident_failed = error; |
||||
} |
||||
} |
||||
close(sock_fd); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
parse_map_record(FILE *file,
|
||||
char file_map[], char file_pguser[], char file_iuser[]) { |
||||
/*---------------------------------------------------------------------------
|
||||
Take the noncomment line which is next on file "file" and interpret |
||||
it as a line in a usermap file. Specifically, return the first |
||||
3 tokens as file_map, file_iuser, and file_pguser, respectively. If |
||||
there are fewer than 3 tokens, return null strings for the missing |
||||
ones. |
||||
|
||||
---------------------------------------------------------------------------*/ |
||||
char buf[MAX_TOKEN]; |
||||
/* A token read from the file */ |
||||
|
||||
/* Set defaults in case fields not in file */ |
||||
file_map[0] = '\0'; |
||||
file_pguser[0] = '\0'; |
||||
file_iuser[0] = '\0'; |
||||
|
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf != '\0') { |
||||
strcpy(file_map, buf); |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf != '\0') { |
||||
strcpy(file_iuser, buf); |
||||
next_token(file, buf, sizeof(buf)); |
||||
if (buf != '\0') { |
||||
strcpy(file_pguser, buf); |
||||
read_through_eol(file); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
verify_against_open_usermap(FILE *file, |
||||
const char pguser[],
|
||||
const char ident_username[],
|
||||
const char usermap_name[], |
||||
bool *checks_out_p) { |
||||
/*--------------------------------------------------------------------------
|
||||
This function does the same thing as verify_against_usermap, |
||||
only with the config file already open on stream descriptor "file". |
||||
---------------------------------------------------------------------------*/ |
||||
bool match; /* We found a matching entry in the map file */ |
||||
bool eof; /* We've reached the end of the file we're reading */ |
||||
|
||||
match = false; /* initial value */ |
||||
eof = false; /* initial value */
|
||||
while (!eof && !match) { |
||||
/* Process a line from the map file */ |
||||
|
||||
int c; /* a character read from the file */ |
||||
|
||||
c = getc(file); ungetc(c, file); |
||||
if (c == EOF) eof = true; |
||||
else { |
||||
if (c == '#') read_through_eol(file); |
||||
else { |
||||
/* The following are fields read from a record of the file */ |
||||
char file_map[MAX_TOKEN+1]; |
||||
char file_pguser[MAX_TOKEN+1]; |
||||
char file_iuser[MAX_TOKEN+1]; |
||||
|
||||
parse_map_record(file, file_map, file_pguser, file_iuser); |
||||
if (strcmp(file_map, usermap_name) == 0 && |
||||
strcmp(file_pguser, pguser) == 0 && |
||||
strcmp(file_iuser, ident_username) == 0) |
||||
match = true; |
||||
} |
||||
}
|
||||
} |
||||
*checks_out_p = match; |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
verify_against_usermap(const char DataDir[], |
||||
const char pguser[],
|
||||
const char ident_username[],
|
||||
const char usermap_name[], |
||||
bool *checks_out_p) { |
||||
/*--------------------------------------------------------------------------
|
||||
See if the user with ident username "ident_username" is allowed to act |
||||
as Postgres user "pguser" according to usermap "usermap_name". Look |
||||
it up in the usermap file. |
||||
|
||||
Special case: For usermap "sameuser", don't look in the usermap |
||||
file. That's an implied map where "pguser" must be identical to |
||||
"ident_username" in order to be authorized. |
||||
|
||||
Iff authorized, return *checks_out_p == true. |
||||
|
||||
--------------------------------------------------------------------------*/ |
||||
|
||||
if (usermap_name[0] == '\0') { |
||||
*checks_out_p = false; |
||||
sprintf(PQerrormsg, |
||||
"verify_against_usermap: hba configuration file does not " |
||||
"have the usermap field filled in in the entry that pertains " |
||||
"to this connection. That field is essential for Ident-based " |
||||
"authentication.\n"); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
} else if (strcmp(usermap_name, "sameuser") == 0) { |
||||
if (strcmp(ident_username, pguser) == 0) *checks_out_p = true; |
||||
else *checks_out_p = false; |
||||
} else { |
||||
FILE *file; /* The map file we have to read */ |
||||
|
||||
char *map_file; /* The name of the map file we have to read */ |
||||
|
||||
/* put together the full pathname to the map file */ |
||||
map_file = (char *) malloc((strlen(DataDir) + |
||||
strlen(MAP_FILE)+2)*sizeof(char)); |
||||
sprintf(map_file, "%s/%s", DataDir, MAP_FILE); |
||||
|
||||
file = fopen(map_file, "r"); |
||||
if (file == 0) {
|
||||
/* The open of the map file failed. */ |
||||
|
||||
const int open_errno = errno; |
||||
|
||||
*checks_out_p = false; |
||||
|
||||
sprintf(PQerrormsg, |
||||
"verify_against_usermap: usermap file for Ident-based " |
||||
"authentication " |
||||
"does not exist or permissions are not setup correctly! " |
||||
"Unable to open file \"%s\".\n",
|
||||
map_file); |
||||
fputs(PQerrormsg, stderr); |
||||
pqdebug("%s", PQerrormsg); |
||||
} else { |
||||
verify_against_open_usermap(file,
|
||||
pguser, ident_username, usermap_name, |
||||
checks_out_p); |
||||
fclose(file); |
||||
} |
||||
free(map_file); |
||||
|
||||
|
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
static void |
||||
authident(const char DataDir[],
|
||||
const Port port, const char postgres_username[],
|
||||
const char usermap_name[], |
||||
bool *authentic_p) { |
||||
/*---------------------------------------------------------------------------
|
||||
Talk to the ident server on the remote host and find out who owns the
|
||||
connection described by "port". Then look in the usermap file under |
||||
the usermap usermap_name[] and see if that user is equivalent to
|
||||
Postgres user user[]. |
||||
|
||||
Return *authentic_p true iff yes. |
||||
---------------------------------------------------------------------------*/ |
||||
bool ident_failed; |
||||
/* We were unable to get ident to give us a username */ |
||||
char ident_username[IDENT_USERNAME_MAX+1]; |
||||
/* The username returned by ident */ |
||||
|
||||
ident(port.raddr.sin_addr, port.laddr.sin_addr,
|
||||
port.raddr.sin_port, port.laddr.sin_port, |
||||
&ident_failed, ident_username); |
||||
|
||||
if (ident_failed) *authentic_p = false; |
||||
else { |
||||
bool checks_out; |
||||
verify_against_usermap(DataDir,
|
||||
postgres_username, ident_username, usermap_name, |
||||
&checks_out); |
||||
if (checks_out) *authentic_p = true; |
||||
else *authentic_p = false; |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
extern int |
||||
hba_recvauth(const Port *port, const char database[], const char user[], |
||||
const char DataDir[]) { |
||||
/*---------------------------------------------------------------------------
|
||||
Determine if the TCP connection described by "port" is with someone |
||||
allowed to act as user "user" and access database "database". Return |
||||
STATUS_OK if yes; STATUS_ERROR if not. |
||||
----------------------------------------------------------------------------*/
|
||||
bool host_ok;
|
||||
/* There's an entry for this database and remote host in the pg_hba file */ |
||||
char usermap_name[USERMAP_NAME_SIZE+1]; |
||||
/* The name of the map pg_hba specifies for this connection (or special
|
||||
value "SAMEUSER") |
||||
*/ |
||||
enum Userauth userauth; |
||||
/* The type of user authentication pg_hba specifies for this connection */ |
||||
int retvalue; |
||||
/* Our eventual return value */ |
||||
|
||||
|
||||
find_hba_entry(DataDir, port->raddr.sin_addr, database,
|
||||
&host_ok, &userauth, usermap_name); |
||||
|
||||
if (!host_ok) retvalue = STATUS_ERROR; |
||||
else { |
||||
switch (userauth) { |
||||
case Trust:
|
||||
retvalue = STATUS_OK; |
||||
break; |
||||
case Ident: { |
||||
/* Here's where we need to call up ident and authenticate the user */ |
||||
|
||||
bool authentic; /* He is who he says he is. */ |
||||
|
||||
authident(DataDir, *port, user, usermap_name, &authentic); |
||||
|
||||
if (authentic) retvalue = STATUS_OK; |
||||
else retvalue = STATUS_ERROR; |
||||
} |
||||
break; |
||||
default: |
||||
Assert(false); |
||||
} |
||||
} |
||||
return(retvalue); |
||||
} |
||||
|
||||
|
||||
/*----------------------------------------------------------------
|
||||
* This version of hba was written by Bryan Henderson |
||||
* in September 1996 for Release 6.0. It changed the format of the
|
||||
* hba file and added ident function. |
||||
* |
||||
* Here are some notes about the original host based authentication |
||||
* the preceded this one.
|
||||
* |
||||
* based on the securelib package originally written by William |
||||
* LeFebvre, EECS Department, Northwestern University |
||||
* (phil@eecs.nwu.edu) - orginal configuration file code handling |
||||
* by Sam Horrocks (sam@ics.uci.edu) |
||||
* |
||||
* modified and adapted for use with Postgres95 by Paul Fisher |
||||
* (pnfisher@unity.ncsu.edu) |
||||
* |
||||
-----------------------------------------------------------------*/ |
||||
|
||||
@ -0,0 +1,100 @@ |
||||
# |
||||
# Example Postgres95 host access control file. |
||||
# |
||||
# |
||||
# This file controls what hosts are allowed to connect to what databases |
||||
# and specifies some options on how users on a particular host are identified. |
||||
# |
||||
# Each line (terminated by a newline character) is a record. A record cannot |
||||
# be continued across two lines. |
||||
# |
||||
# There are 3 kinds of records: |
||||
# |
||||
# 1) comment: Starts with #. |
||||
# |
||||
# 2) empty: Contains nothing excepting spaces and tabs. |
||||
# |
||||
# 3) content: anything else. |
||||
# |
||||
# Unless specified otherwise, "record" from here on means a content |
||||
# record. |
||||
# |
||||
# A record consists of tokens separated by spaces or tabs. Spaces and |
||||
# tabs at the beginning and end of a record are ignored as are extra |
||||
# spaces and tabs between two tokens. |
||||
# |
||||
# The first token in a record is the record type. The interpretation of the |
||||
# rest of the record depends on the record type. |
||||
# |
||||
# Record type "host" |
||||
# ------------------ |
||||
# |
||||
# This record identifies a set of hosts that are permitted to connect to |
||||
# databases. No hosts are permitted to connect except as specified by a |
||||
# "host" record. |
||||
# |
||||
# Format: |
||||
# |
||||
# host DBNAME IP_ADDRESS ADDRESS_MASK USERAUTH [MAP] |
||||
# |
||||
# DBNAME is the name of a Postgres database, or "all" to indicate all |
||||
# databases. |
||||
# |
||||
# IP_ADDRESS and ADDRESS_MASK are a standard dotted decimal IP address and |
||||
# mask to identify a set of hosts. These hosts are allowed to connect to |
||||
# Database DBNAME. |
||||
# |
||||
# USERAUTH is a keyword indicating the method used to authenticate the |
||||
# user, i.e. to determine that the principal is authorized to connect |
||||
# under the Postgres username he supplies in his connection parameters. |
||||
# |
||||
# ident: Authentication is done by the ident server on the remote |
||||
# host, via the ident (RFC 1413) protocol. |
||||
# |
||||
# trust: No authentication is done. Trust that the user has the |
||||
# authority to user whatever username he says he does. |
||||
# Before Postgres Version 6, all authentication was this way. |
||||
# |
||||
# MAP is the name of a map that matches an authenticated principal with |
||||
# a Postgres username. If USERNAME is "trust", this value is ignored and |
||||
# may be absent. |
||||
# |
||||
# In the case of USERAUTH=ident, this is a map name to be found in the |
||||
# pg_ident.conf file. That table maps from ident usernames to Postgres |
||||
# usernames. The special map name "sameuser" indicates an implied map |
||||
# (not found in pg_ident.conf) that maps every ident username to the identical |
||||
# Postgres username. |
||||
|
||||
# |
||||
# For backwards compatibility, Postgres also accepts pre-Version 2 records, |
||||
# which look like: |
||||
# |
||||
# all 127.0.0.1 0.0.0.0 |
||||
# |
||||
# |
||||
|
||||
# TYPE DATABASE IP_ADDRESS MASK USERAUTH MAP |
||||
|
||||
host all 127.0.0.1 255.255.255.255 trust |
||||
|
||||
# The above allows any user on the local system to connect to any database |
||||
# under any username. |
||||
|
||||
host template1 192.168.0.0 255.255.255.0 ident sameuser |
||||
|
||||
# The above allows any user from any host with IP address 192.168.0.x to |
||||
# connect to database template1 as the same username that ident on that host |
||||
# identifies him as (typically his Unix username). |
||||
|
||||
#host all 0.0.0.0 0.0.0.0 trust |
||||
|
||||
# The above would allow anyone anywhere to connect to any database under |
||||
# any username. |
||||
|
||||
#host all 192.168.0.0 255.255.255.0 ident omicron |
||||
# |
||||
# The above would allow users from 192.168.0.x hosts to connect to any |
||||
# database, but if e.g. Ident says the user is "bryanh" and he requests to |
||||
# connect as Postgres user "guest1", the connection is only allowed if |
||||
# there is an entry for map "omicron" in pg_ident.conf that says "bryanh" is |
||||
# allowed to connect as "guest1". |
||||
@ -1,13 +0,0 @@ |
||||
# |
||||
# Example config file for Postgres95 host based access |
||||
# |
||||
# Lines starting with "all" apply to all databases. Otherwise the first |
||||
# column has to match the name of the database being connected to. Up to |
||||
# ten config lines can apply to each database. Mask specifies bits that |
||||
# aren't counted. After those bits are taken out, the connection address |
||||
# must match the address in the middle column. |
||||
# |
||||
# <name> <address> <mask> |
||||
# |
||||
all 127.0.0.1 0.0.0.0 |
||||
|
||||
@ -0,0 +1,25 @@ |
||||
# This is the pg_ident.conf file, which is used with Postgres ident-based |
||||
# authentication (a subtype of host-based authentication). |
||||
|
||||
# This is a table of ident usernames (typically Unix usernames) and |
||||
# their corresponding Postgres usernames. For example, user "bryanh" on |
||||
# some particular remote system may equate to Postgres user "guest1". |
||||
|
||||
# This file contains multiple maps. Each has a name. The pg_hba.conf |
||||
# file determines what connections relate to this file and for those that |
||||
# do, which map to use. |
||||
|
||||
# Each record consists of 3 tokens: |
||||
# |
||||
# 1) map name |
||||
# 2) ident username |
||||
# 3) Postgres username |
||||
|
||||
# Note that it is possible for one user to map to multiple Postgres usernames. |
||||
# A user always has to specify when he connects what Postgres username he is |
||||
# using. This file is only used to validate that selection. |
||||
|
||||
testmap robert bob |
||||
testmap lucy lucy |
||||
|
||||
|
||||
Loading…
Reference in new issue