New host-based authentication with ident
This commit is contained in:
parent
57026d6009
commit
4b5c977782
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.2 1996/08/14 04:51:02 scrappy Exp $
|
* $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.3 1996/10/12 07:47:08 bryanh Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -58,10 +58,10 @@
|
|||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include "libpq/auth.h"
|
#include <libpq/auth.h>
|
||||||
#include "libpq/libpq.h"
|
#include <libpq/libpq.h>
|
||||||
#include "libpq/pqcomm.h"
|
#include <libpq/libpq-be.h>
|
||||||
#include "libpq/libpq-be.h"
|
#include <libpq/hba.h>
|
||||||
|
|
||||||
/*----------------------------------------------------------------
|
/*----------------------------------------------------------------
|
||||||
* common definitions for generic fe/be routines
|
* common definitions for generic fe/be routines
|
||||||
@ -86,27 +86,37 @@ struct authsvc {
|
|||||||
* allowed. Unauthenticated connections are disallowed unless there
|
* allowed. Unauthenticated connections are disallowed unless there
|
||||||
* isn't any authentication system.
|
* isn't any authentication system.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if defined(HBA)
|
||||||
|
static int useHostBasedAuth = 1;
|
||||||
|
#else
|
||||||
|
static int useHostBasedAuth = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(KRB4) || defined(KRB5) || defined(HBA)
|
||||||
|
#define UNAUTH_ALLOWED 0
|
||||||
|
#else
|
||||||
|
#define UNAUTH_ALLOWED 1
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct authsvc authsvcs[] = {
|
static struct authsvc authsvcs[] = {
|
||||||
#ifdef KRB4
|
{ "unauth", STARTUP_UNAUTH_MSG, UNAUTH_ALLOWED },
|
||||||
|
{ "hba", STARTUP_HBA_MSG, 1 },
|
||||||
{ "krb4", STARTUP_KRB4_MSG, 1 },
|
{ "krb4", STARTUP_KRB4_MSG, 1 },
|
||||||
{ "kerberos", STARTUP_KRB4_MSG, 1 },
|
|
||||||
#endif /* KRB4 */
|
|
||||||
#ifdef KRB5
|
|
||||||
{ "krb5", STARTUP_KRB5_MSG, 1 },
|
{ "krb5", STARTUP_KRB5_MSG, 1 },
|
||||||
{ "kerberos", STARTUP_KRB5_MSG, 1 },
|
#if defined(KRB5)
|
||||||
#endif /* KRB5 */
|
{ "kerberos", STARTUP_KRB5_MSG, 1 }
|
||||||
{ UNAUTHNAME, STARTUP_MSG,
|
#else
|
||||||
#if defined(KRB4) || defined(KRB5)
|
{ "kerberos", STARTUP_KRB4_MSG, 1 }
|
||||||
0
|
#endif
|
||||||
#else /* !(KRB4 || KRB5) */
|
|
||||||
1
|
|
||||||
#endif /* !(KRB4 || KRB5) */
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
|
static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
|
||||||
|
|
||||||
#ifdef KRB4
|
#ifdef KRB4
|
||||||
|
/* This has to be ifdef'd out because krb.h does exist. This needs
|
||||||
|
to be fixed.
|
||||||
|
*/
|
||||||
/*----------------------------------------------------------------
|
/*----------------------------------------------------------------
|
||||||
* MIT Kerberos authentication system - protocol version 4
|
* MIT Kerberos authentication system - protocol version 4
|
||||||
*----------------------------------------------------------------
|
*----------------------------------------------------------------
|
||||||
@ -184,9 +194,28 @@ pg_krb4_recvauth(int sock,
|
|||||||
|
|
||||||
#endif /* !FRONTEND */
|
#endif /* !FRONTEND */
|
||||||
|
|
||||||
|
#else
|
||||||
|
static int
|
||||||
|
pg_krb4_recvauth(int sock,
|
||||||
|
struct sockaddr_in *laddr,
|
||||||
|
struct sockaddr_in *raddr,
|
||||||
|
char *username)
|
||||||
|
{
|
||||||
|
(void) sprintf(PQerrormsg,
|
||||||
|
"pg_krb4_recvauth: Kerberos not implemented on this "
|
||||||
|
"server.\n");
|
||||||
|
fputs(PQerrormsg, stderr);
|
||||||
|
pqdebug("%s", PQerrormsg);
|
||||||
|
|
||||||
|
return(STATUS_ERROR);
|
||||||
|
}
|
||||||
#endif /* KRB4 */
|
#endif /* KRB4 */
|
||||||
|
|
||||||
|
|
||||||
#ifdef KRB5
|
#ifdef KRB5
|
||||||
|
/* This needs to be ifdef'd out because krb5.h doesn't exist. This needs
|
||||||
|
to be fixed.
|
||||||
|
*/
|
||||||
/*----------------------------------------------------------------
|
/*----------------------------------------------------------------
|
||||||
* MIT Kerberos authentication system - protocol version 5
|
* MIT Kerberos authentication system - protocol version 5
|
||||||
*----------------------------------------------------------------
|
*----------------------------------------------------------------
|
||||||
@ -222,7 +251,7 @@ pg_an_to_ln(char *aname)
|
|||||||
#else /* !FRONTEND */
|
#else /* !FRONTEND */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pg_krb4_recvauth -- server routine to receive authentication information
|
* pg_krb5_recvauth -- server routine to receive authentication information
|
||||||
* from the client
|
* from the client
|
||||||
*
|
*
|
||||||
* We still need to compare the username obtained from the client's setup
|
* We still need to compare the username obtained from the client's setup
|
||||||
@ -348,266 +377,118 @@ pg_krb5_recvauth(int sock,
|
|||||||
|
|
||||||
#endif /* !FRONTEND */
|
#endif /* !FRONTEND */
|
||||||
|
|
||||||
#endif /* KRB5 */
|
|
||||||
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------
|
|
||||||
* host based authentication
|
|
||||||
*----------------------------------------------------------------
|
|
||||||
* 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)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define CONF_FILE "pg_hba" /* Name of the config file */
|
|
||||||
|
|
||||||
#define MAX_LINES 255 /* Maximum number of config lines *
|
|
||||||
* that can apply to one database */
|
|
||||||
|
|
||||||
#define ALL_NAME "all" /* Name used in config file for *
|
|
||||||
* lines that apply to all databases */
|
|
||||||
|
|
||||||
#define MAX_TOKEN 80 /* Maximum size of one token in the *
|
|
||||||
* configuration file */
|
|
||||||
|
|
||||||
struct conf_line { /* Info about config file line */
|
|
||||||
u_long adr, mask;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int next_token(FILE *, char *, int);
|
|
||||||
|
|
||||||
/* hba_recvauth */
|
|
||||||
/* check for host-based authentication */
|
|
||||||
/*
|
|
||||||
* hba_recvauth - check the sockaddr_in "addr" to see if it corresponds
|
|
||||||
* to an acceptable host for the database that's being
|
|
||||||
* connected to. Return STATUS_OK if acceptable,
|
|
||||||
* otherwise return STATUS_ERROR.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
#else
|
||||||
static int
|
static int
|
||||||
hba_recvauth(struct sockaddr_in *addr, PacketBuf *pbuf, StartupInfo *sp)
|
pg_krb5_recvauth(int sock,
|
||||||
|
struct sockaddr_in *laddr,
|
||||||
|
struct sockaddr_in *raddr,
|
||||||
|
char *username)
|
||||||
{
|
{
|
||||||
u_long ip_addr;
|
(void) sprintf(PQerrormsg,
|
||||||
static struct conf_line conf[MAX_LINES];
|
"pg_krb5_recvauth: Kerberos not implemented on this "
|
||||||
static int nconf;
|
"server.\n");
|
||||||
int i;
|
fputs(PQerrormsg, stderr);
|
||||||
|
pqdebug("%s", PQerrormsg);
|
||||||
|
|
||||||
char buf[MAX_TOKEN];
|
return(STATUS_ERROR);
|
||||||
FILE *file;
|
|
||||||
|
|
||||||
char *conf_file;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Open the config file. */
|
|
||||||
file = fopen(conf_file, "r");
|
|
||||||
if (file)
|
|
||||||
{
|
|
||||||
free(conf_file);
|
|
||||||
nconf = 0;
|
|
||||||
|
|
||||||
/* Grab the "name" */
|
|
||||||
while ((i = next_token(file, buf, sizeof(buf))) != EOF)
|
|
||||||
{
|
|
||||||
/* If only token on the line, ignore */
|
|
||||||
if (i == '\n') continue;
|
|
||||||
|
|
||||||
/* Comment -- read until end of line then next line */
|
|
||||||
if (buf[0] == '#')
|
|
||||||
{
|
|
||||||
while (next_token(file, buf, sizeof(buf)) == 0) ;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check to make sure this says "all" or that it matches
|
|
||||||
* the database name.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (strcmp(buf, ALL_NAME) == 0 || (strcmp(buf, sp->database) == 0))
|
|
||||||
{
|
|
||||||
/* Get next token, if last on line, ignore */
|
|
||||||
if (next_token(file, buf, sizeof(buf)) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Got address */
|
|
||||||
conf[nconf].adr = inet_addr(buf);
|
|
||||||
|
|
||||||
/* Get next token (mask) */
|
|
||||||
i = next_token(file, buf, sizeof(buf));
|
|
||||||
|
|
||||||
/* Only ignore if we got no text at all */
|
|
||||||
if (i != EOF)
|
|
||||||
{
|
|
||||||
/* Add to list, quit if array is full */
|
|
||||||
conf[nconf++].mask = inet_addr(buf);
|
|
||||||
if (nconf == MAX_LINES) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If not at end-of-line, keep reading til we are */
|
|
||||||
while (i == 0)
|
|
||||||
i = next_token(file, buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ (void) sprintf(PQerrormsg,
|
|
||||||
"hba_recvauth: 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);
|
|
||||||
free(conf_file);
|
|
||||||
return(STATUS_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Config lines now in memory so start checking address */
|
|
||||||
/* grab just the address */
|
|
||||||
ip_addr = addr->sin_addr.s_addr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Go through the conf array, turn off the bits given by the mask
|
|
||||||
* and then compare the result with the address. A match means
|
|
||||||
* that this address is ok.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < nconf; ++i)
|
|
||||||
if ((ip_addr & ~conf[i].mask) == conf[i].adr) return(STATUS_OK);
|
|
||||||
|
|
||||||
/* no match, so we can't approve the address */
|
|
||||||
return(STATUS_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Grab one token out of fp. Defined as the next string of non-whitespace
|
|
||||||
* in the file. After we get the token, continue reading until EOF, end of
|
|
||||||
* line or the next token. If it's the last token on the line, return '\n'
|
|
||||||
* for the value. If we get EOF before reading a token, return EOF. In all
|
|
||||||
* other cases return 0.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
next_token(FILE *fp, char *buf, int bufsz)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
char *eb = buf+(bufsz-1);
|
|
||||||
|
|
||||||
/* Discard inital whitespace */
|
|
||||||
while (isspace(c = getc(fp))) ;
|
|
||||||
|
|
||||||
/* EOF seen before any token so return EOF */
|
|
||||||
if (c == EOF) return -1;
|
|
||||||
|
|
||||||
/* Form a token in buf */
|
|
||||||
do {
|
|
||||||
if (buf < eb) *buf++ = c;
|
|
||||||
c = getc(fp);
|
|
||||||
} while (!isspace(c) && c != EOF);
|
|
||||||
*buf = '\0';
|
|
||||||
|
|
||||||
/* Discard trailing tabs and spaces */
|
|
||||||
while (c == ' ' || c == '\t') c = getc(fp);
|
|
||||||
|
|
||||||
/* Put back the char that was non-whitespace (putting back EOF is ok) */
|
|
||||||
(void) ungetc(c, fp);
|
|
||||||
|
|
||||||
/* If we ended with a newline, return that, otherwise return 0 */
|
|
||||||
return (c == '\n' ? '\n' : 0);
|
|
||||||
}
|
}
|
||||||
|
#endif /* KRB5 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* be_recvauth -- server demux routine for incoming authentication information
|
* be_recvauth -- server demux routine for incoming authentication information
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
be_recvauth(MsgType msgtype, Port *port, char *username, StartupInfo* sp)
|
be_recvauth(MsgType msgtype_arg, Port *port, char *username, StartupInfo* sp)
|
||||||
{
|
{
|
||||||
|
MsgType msgtype;
|
||||||
|
|
||||||
|
/* A message type of STARTUP_MSG (which once upon a time was the only
|
||||||
|
startup message type) means user wants us to choose. "unauth" is
|
||||||
|
what used to be the only choice, but installation may choose "hba"
|
||||||
|
instead.
|
||||||
|
*/
|
||||||
|
if (msgtype_arg == STARTUP_MSG && useHostBasedAuth)
|
||||||
|
msgtype = STARTUP_HBA_MSG;
|
||||||
|
else
|
||||||
|
msgtype = STARTUP_UNAUTH_MSG;
|
||||||
|
|
||||||
if (!username) {
|
if (!username) {
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: no user name passed\n");
|
"be_recvauth: no user name passed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
if (!port) {
|
if (!port) {
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: no port structure passed\n");
|
"be_recvauth: no port structure passed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (msgtype) {
|
switch (msgtype) {
|
||||||
#ifdef KRB4
|
|
||||||
case STARTUP_KRB4_MSG:
|
case STARTUP_KRB4_MSG:
|
||||||
if (!be_getauthsvc(msgtype)) {
|
if (!be_getauthsvc(msgtype)) {
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: krb4 authentication disallowed\n");
|
"be_recvauth: krb4 authentication disallowed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr,
|
if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr,
|
||||||
username) != STATUS_OK) {
|
username) != STATUS_OK) {
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: krb4 authentication failed\n");
|
"be_recvauth: krb4 authentication failed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
#ifdef KRB5
|
|
||||||
case STARTUP_KRB5_MSG:
|
case STARTUP_KRB5_MSG:
|
||||||
if (!be_getauthsvc(msgtype)) {
|
if (!be_getauthsvc(msgtype)) {
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: krb5 authentication disallowed\n");
|
"be_recvauth: krb5 authentication disallowed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr,
|
if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr,
|
||||||
username) != STATUS_OK) {
|
username) != STATUS_OK) {
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: krb5 authentication failed\n");
|
"be_recvauth: krb5 authentication failed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
case STARTUP_UNAUTH_MSG:
|
||||||
case STARTUP_MSG:
|
if (!be_getauthsvc(msgtype)) {
|
||||||
if (!be_getauthsvc(msgtype)) {
|
(void) sprintf(PQerrormsg,
|
||||||
(void) sprintf(PQerrormsg,
|
"be_recvauth: "
|
||||||
"be_recvauth: unauthenticated connections disallowed failed\n");
|
"unauthenticated connections disallowed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STARTUP_HBA_MSG:
|
case STARTUP_HBA_MSG:
|
||||||
if (hba_recvauth(&port->raddr, &port->buf, sp) != STATUS_OK) {
|
if (hba_recvauth(port, sp->database, sp->user, DataDir) != STATUS_OK) {
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: host-based authentication failed\n");
|
"be_recvauth: host-based authentication failed\n");
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
(void) sprintf(PQerrormsg,
|
(void) sprintf(PQerrormsg,
|
||||||
"be_recvauth: unrecognized message type: %d\n",
|
"be_recvauth: unrecognized message type: %d\n",
|
||||||
msgtype);
|
msgtype);
|
||||||
fputs(PQerrormsg, stderr);
|
fputs(PQerrormsg, stderr);
|
||||||
pqdebug("%s", PQerrormsg);
|
pqdebug("%s", PQerrormsg);
|
||||||
return(STATUS_ERROR);
|
return(STATUS_ERROR);
|
||||||
}
|
}
|
||||||
return(STATUS_OK);
|
return(STATUS_OK);
|
||||||
}
|
}
|
||||||
|
768
src/backend/libpq/hba.c
Normal file
768
src/backend/libpq/hba.c
Normal file
@ -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)
|
||||||
|
*
|
||||||
|
-----------------------------------------------------------------*/
|
||||||
|
|
100
src/backend/libpq/pg_hba.conf.sample
Normal file
100
src/backend/libpq/pg_hba.conf.sample
Normal file
@ -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
|
|
||||||
|
|
25
src/backend/libpq/pg_ident.conf.sample
Normal file
25
src/backend/libpq/pg_ident.conf.sample
Normal file
@ -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…
x
Reference in New Issue
Block a user