*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <netinet/in.h>
#include <arpa/inet.h>
-#include "libpq/auth.h"
-#include "libpq/libpq.h"
-#include "libpq/pqcomm.h"
-#include "libpq/libpq-be.h"
+#include <libpq/auth.h>
+#include <libpq/libpq.h>
+#include <libpq/libpq-be.h>
+#include <libpq/hba.h>
/*----------------------------------------------------------------
* common definitions for generic fe/be routines
* allowed. Unauthenticated connections are disallowed unless there
* 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[] = {
-#ifdef KRB4
+ { "unauth", STARTUP_UNAUTH_MSG, UNAUTH_ALLOWED },
+ { "hba", STARTUP_HBA_MSG, 1 },
{ "krb4", STARTUP_KRB4_MSG, 1 },
- { "kerberos", STARTUP_KRB4_MSG, 1 },
-#endif /* KRB4 */
-#ifdef KRB5
{ "krb5", STARTUP_KRB5_MSG, 1 },
- { "kerberos", STARTUP_KRB5_MSG, 1 },
-#endif /* KRB5 */
- { UNAUTHNAME, STARTUP_MSG,
-#if defined(KRB4) || defined(KRB5)
- 0
-#else /* !(KRB4 || KRB5) */
- 1
-#endif /* !(KRB4 || KRB5) */
- }
+#if defined(KRB5)
+ { "kerberos", STARTUP_KRB5_MSG, 1 }
+#else
+ { "kerberos", STARTUP_KRB4_MSG, 1 }
+#endif
};
static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
#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
*----------------------------------------------------------------
#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 */
+
#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
*----------------------------------------------------------------
#else /* !FRONTEND */
/*
- * pg_krb4_recvauth -- server routine to receive authentication information
+ * pg_krb5_recvauth -- server routine to receive authentication information
* from the client
*
* We still need to compare the username obtained from the client's setup
#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
-hba_recvauth(struct sockaddr_in *addr, PacketBuf *pbuf, StartupInfo *sp)
-{
- u_long ip_addr;
- static struct conf_line conf[MAX_LINES];
- static int nconf;
- int i;
-
- char buf[MAX_TOKEN];
- 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)
+pg_krb5_recvauth(int sock,
+ struct sockaddr_in *laddr,
+ struct sockaddr_in *raddr,
+ char *username)
{
- 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';
+ (void) sprintf(PQerrormsg,
+ "pg_krb5_recvauth: Kerberos not implemented on this "
+ "server.\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
- /* 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);
+return(STATUS_ERROR);
}
+#endif /* KRB5 */
/*
* be_recvauth -- server demux routine for incoming authentication information
*/
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) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: no user name passed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: no user name passed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
}
if (!port) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: no port structure passed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: no port structure passed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
}
switch (msgtype) {
-#ifdef KRB4
case STARTUP_KRB4_MSG:
- if (!be_getauthsvc(msgtype)) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: krb4 authentication disallowed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
- }
+ if (!be_getauthsvc(msgtype)) {
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: krb4 authentication disallowed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
+ }
if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr,
- username) != STATUS_OK) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: krb4 authentication failed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
- }
+ username) != STATUS_OK) {
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: krb4 authentication failed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
+ }
break;
-#endif
-#ifdef KRB5
case STARTUP_KRB5_MSG:
- if (!be_getauthsvc(msgtype)) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: krb5 authentication disallowed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
- }
- if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr,
- username) != STATUS_OK) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: krb5 authentication failed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
- }
- break;
-#endif
- case STARTUP_MSG:
- if (!be_getauthsvc(msgtype)) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: unauthenticated connections disallowed failed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
- }
- break;
+ if (!be_getauthsvc(msgtype)) {
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: krb5 authentication disallowed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
+ }
+ if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr,
+ username) != STATUS_OK) {
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: krb5 authentication failed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
+ }
+ break;
+ case STARTUP_UNAUTH_MSG:
+ if (!be_getauthsvc(msgtype)) {
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: "
+ "unauthenticated connections disallowed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
+ }
+ break;
case STARTUP_HBA_MSG:
- if (hba_recvauth(&port->raddr, &port->buf, sp) != STATUS_OK) {
- (void) sprintf(PQerrormsg,
- "be_recvauth: host-based authentication failed\n");
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
- }
- break;
+ if (hba_recvauth(port, sp->database, sp->user, DataDir) != STATUS_OK) {
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: host-based authentication failed\n");
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
+ }
+ break;
default:
- (void) sprintf(PQerrormsg,
- "be_recvauth: unrecognized message type: %d\n",
- msgtype);
- fputs(PQerrormsg, stderr);
- pqdebug("%s", PQerrormsg);
- return(STATUS_ERROR);
+ (void) sprintf(PQerrormsg,
+ "be_recvauth: unrecognized message type: %d\n",
+ msgtype);
+ fputs(PQerrormsg, stderr);
+ pqdebug("%s", PQerrormsg);
+ return(STATUS_ERROR);
}
return(STATUS_OK);
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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)
+ *
+ -----------------------------------------------------------------*/
+