Load pg_hba.conf and pg_ident.conf on startup and SIGHUP into List of
authorBruce Momjian <bruce@momjian.us>
Mon, 30 Jul 2001 14:50:24 +0000 (14:50 +0000)
committerBruce Momjian <bruce@momjian.us>
Mon, 30 Jul 2001 14:50:24 +0000 (14:50 +0000)
Lists, and use that for user validation.

Bruce Momjian

src/backend/libpq/hba.c
src/backend/postmaster/postmaster.c
src/backend/tcop/postgres.c
src/include/libpq/hba.h

index 7c847ae354c01d828155d9c31e7e90d5252d0a6d..91241376184e6f5e8f6b06bd06ac7f94c98c943c 100644 (file)
@@ -5,7 +5,7 @@
  *   wherein you authenticate a user by seeing what IP address the system
  *   says he comes from and possibly using ident).
  *
- * $Id: hba.c,v 1.55 2001/02/10 02:31:26 tgl Exp $
+ * $Id: hba.c,v 1.56 2001/07/30 14:50:21 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "nodes/pg_list.h"
 #include "storage/fd.h"
 
 
 #define IDENT_USERNAME_MAX 512
  /* Max size of username ident server can return */
 
+static List *hba_lines = NULL; /* A list of lists: entry for every line,
+                                * list of tokens on each line.
+                                */
+
+static List *ident_lines = NULL;/* A list of lists: entry for every line,
+                                * list of tokens on each line.
+                                */
 
 /* Some standard C libraries, including GNU, have an isblank() function.
    Others, including Solaris, do not.  So we have our own.
 static bool
 isblank(const char c)
 {
-   return c == ' ' || c == 9 /* tab */ ;
+   return c == ' ' || c == 0x09;/* tab */
 }
 
 
+/*
+ *  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.
+ */
 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)));
+   while (isblank(c = getc(fp)))
+       ;
 
    if (c != '\n')
    {
-
        /*
         * build a token in buf of next characters up to EOF, eol, or
         * blank.
@@ -72,160 +80,214 @@ next_token(FILE *fp, char *buf, const int bufsz)
            if (buf < eb)
                *buf++ = c;
            c = getc(fp);
-
-           /*
-            * Put back the char right after the token (putting back EOF
-            * is ok)
-            */
        }
+       /*
+        * Put back the char right after the token (putting back EOF
+        * is ok)
+        */
        ungetc(c, fp);
    }
    *buf = '\0';
 }
 
 
-
 static void
-read_through_eol(FILE *file)
+read_to_eol(FILE *file)
 {
    int         c;
 
-   do
-       c = getc(file);
-   while (c != '\n' && c != EOF);
+   while ((c = getc(file)) != '\n' && c != EOF)
+       ;
 }
 
 
-
+/*
+ *  Process the file line by line and create a list of list of tokens.
+ */
 static void
-read_hba_entry2(FILE *file, UserAuth *userauth_p, char *auth_arg,
-               bool *error_p)
+tokenize_file(FILE *file, List **lines)
 {
-/*--------------------------------------------------------------------------
-  Read from file FILE the rest of a host record, after the mask field,
-  and return the interpretation of it as *userauth_p, auth_arg, and
-  *error_p.
----------------------------------------------------------------------------*/
    char        buf[MAX_TOKEN];
+   List        *next_line = NIL;
+   bool        comment_found = false;
 
-   /* Get authentication type token. */
-   next_token(file, buf, sizeof(buf));
-
-   if (strcmp(buf, "trust") == 0)
-       *userauth_p = uaTrust;
-   else if (strcmp(buf, "ident") == 0)
-       *userauth_p = uaIdent;
-   else if (strcmp(buf, "password") == 0)
-       *userauth_p = uaPassword;
-   else if (strcmp(buf, "krb4") == 0)
-       *userauth_p = uaKrb4;
-   else if (strcmp(buf, "krb5") == 0)
-       *userauth_p = uaKrb5;
-   else if (strcmp(buf, "reject") == 0)
-       *userauth_p = uaReject;
-   else if (strcmp(buf, "crypt") == 0)
-       *userauth_p = uaCrypt;
-   else
+   while (1)
    {
-       *error_p = true;
+       next_token(file, buf, sizeof(buf));
+       if (feof(file))
+           break;
 
+       /* trim off comment, even if inside a token */
+       if (strstr(buf,"#") != NULL)
+       {
+           *strstr(buf,"#") = '\0';
+           comment_found = true;
+       }
+
+       /* add token to list */
        if (buf[0] != '\0')
-           read_through_eol(file);
+       {
+           if (next_line == NIL)
+           {
+               /* make a new line List */
+               next_line = lcons(pstrdup(buf), NIL);
+               *lines = lappend(*lines, next_line);
+           }
+           else
+               /* append token to line */
+               next_line = lappend(next_line, pstrdup(buf));
+       }
+       else
+           /* force a new List line */
+           next_line = NIL;
+
+       if (comment_found)
+       {
+           /* Skip the rest of the line */
+           read_to_eol(file);
+           next_line = NIL;
+           comment_found = false;
+       }
+   }
+}
+
+
+/*
+ * Free memory used by lines/tokens
+ */
+static void free_lines(List **lines)
+{
+   if (*lines)
+   {
+       List *line, *token;
+
+       foreach(line, *lines)
+       {
+           foreach(token,lfirst(line))
+               pfree(lfirst(token));
+           freeList(lfirst(line));
+       }
+       freeList(*lines);
+       *lines = NULL;
+   }
+}
+
+
+/*
+ *  Read from file FILE the rest of a host record, after the mask field,
+ *  and return the interpretation of it as *userauth_p, auth_arg, and
+ *  *error_p.
+ */
+static void
+parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg,
+               bool *error_p)
+{
+   char        *token = NULL;
+
+   if (!line)
+       *error_p = true;
+   else
+   {
+       /* Get authentication type token. */
+       token = lfirst(line);
+       if (strcmp(token, "trust") == 0)
+           *userauth_p = uaTrust;
+       else if (strcmp(token, "ident") == 0)
+           *userauth_p = uaIdent;
+       else if (strcmp(token, "password") == 0)
+           *userauth_p = uaPassword;
+       else if (strcmp(token, "krb4") == 0)
+           *userauth_p = uaKrb4;
+       else if (strcmp(token, "krb5") == 0)
+           *userauth_p = uaKrb5;
+       else if (strcmp(token, "reject") == 0)
+           *userauth_p = uaReject;
+       else if (strcmp(token, "crypt") == 0)
+           *userauth_p = uaCrypt;
+       else
+           *error_p = true;
    }
 
    if (!*error_p)
    {
        /* Get the authentication argument token, if any */
-       next_token(file, buf, sizeof(buf));
-       if (buf[0] == '\0')
+       line = lnext(line);
+       if (!line)
            auth_arg[0] = '\0';
        else
        {
-           StrNCpy(auth_arg, buf, MAX_AUTH_ARG - 1);
-           next_token(file, buf, sizeof(buf));
-           if (buf[0] != '\0')
-           {
+           StrNCpy(auth_arg, token, MAX_AUTH_ARG - 1);
+           /* If there is more on the line, it is an error */
+           if (lnext(line))
                *error_p = true;
-               read_through_eol(file);
-           }
        }
    }
 }
 
 
-
+/*
+ *  Process the non-comment lines in the config file.
+ *
+ *  See if it applies to a connection to a host with IP address "*raddr"
+ *  to a database named "*database".   If so, return *found_p true
+ *  and *userauth_p and *auth_arg as the values from the entry.
+ *  If not, leave *found_p as it was.  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.
+ */
 static void
-process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p)
+parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
 {
-/*---------------------------------------------------------------------------
-  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 "*raddr"
-  to a database named "*database". If so, return *matches_p true
-  and *userauth_p and *auth_arg as the values from the entry.
-  If not, leave *matches_p as it was.  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        db[MAX_TOKEN],
-               buf[MAX_TOKEN];
-
-   /* Read the record type field. */
-
-   next_token(file, buf, sizeof(buf));
-
-   if (buf[0] == '\0')
-       return;
+   char        *db;
+   char        *token;
 
+   Assert(line != NIL);
+   token = lfirst(line);
    /* Check the record type. */
-
-   if (strcmp(buf, "local") == 0)
+   if (strcmp(token, "local") == 0)
    {
        /* Get the database. */
-
-       next_token(file, db, sizeof(db));
-
-       if (db[0] == '\0')
-           goto syntax;
-
+       line = lnext(line);
+       if (!line)
+           goto hba_syntax;
+       db = lfirst(line);
+
+       line = lnext(line);
+       if (!line)
+           goto hba_syntax;
        /* Read the rest of the line. */
-
-       read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
+       parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
+       if (*error_p)
+           goto hba_syntax;
 
        /*
         * For now, disallow methods that need AF_INET sockets to work.
         */
-
        if (!*error_p &&
            (port->auth_method == uaIdent ||
             port->auth_method == uaKrb4 ||
             port->auth_method == uaKrb5))
-           *error_p = true;
-
-       if (*error_p)
-           goto syntax;
+           goto hba_syntax;
 
        /*
         * If this record isn't for our database, or this is the wrong
         * sort of connection, ignore it.
         */
-
        if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 &&
             (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) ||
            port->raddr.sa.sa_family != AF_UNIX)
            return;
    }
-   else if (strcmp(buf, "host") == 0 || strcmp(buf, "hostssl") == 0)
+   else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
    {
-       struct in_addr file_ip_addr,
-                   mask;
-       bool        discard = 0;/* Discard this entry */
+       struct in_addr file_ip_addr, mask;
 
 #ifdef USE_SSL
        /* If SSL, then check that we are on SSL */
-       if (strcmp(buf, "hostssl") == 0)
+       if (strcmp(token, "hostssl") == 0)
        {
            if (!port->ssl)
-               discard = 1;
+               return;
 
            /* Placeholder to require specific SSL level, perhaps? */
            /* Or a client certificate */
@@ -234,67 +296,50 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p)
        }
 #else
        /* If not SSL, we don't support this */
-       if (strcmp(buf, "hostssl") == 0)
-           goto syntax;
+       if (strcmp(token, "hostssl") == 0)
+           goto hba_syntax;
 #endif
 
        /* Get the database. */
-
-       next_token(file, db, sizeof(db));
-
-       if (db[0] == '\0')
-           goto syntax;
+       line = lnext(line);
+       if (!line)
+           goto hba_syntax;
+       db = lfirst(line);
 
        /* Read the IP address field. */
-
-       next_token(file, buf, sizeof(buf));
-
-       if (buf[0] == '\0')
-           goto syntax;
+       line = lnext(line);
+       if (!line)
+           goto hba_syntax;
+       token = lfirst(line);
 
        /* Remember the IP address field and go get mask field. */
-
-       if (!inet_aton(buf, &file_ip_addr))
-       {
-           read_through_eol(file);
-           goto syntax;
-       }
+       if (!inet_aton(token, &file_ip_addr))
+           goto hba_syntax;
 
        /* Read the mask field. */
+       line = lnext(line);
+       if (!line)
+           goto hba_syntax;
+       token = lfirst(line);
 
-       next_token(file, buf, sizeof(buf));
-
-       if (buf[0] == '\0')
-           goto syntax;
-
-       if (!inet_aton(buf, &mask))
-       {
-           read_through_eol(file);
-           goto syntax;
-       }
+       if (!inet_aton(token, &mask))
+           goto hba_syntax;
 
        /*
         * This is the record we're looking for.  Read the rest of the
         * info from it.
         */
-
-       read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
-
+       line = lnext(line);
+       if (!line)
+           goto hba_syntax;
+       parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
        if (*error_p)
-           goto syntax;
-
-       /*
-        * If told to discard earlier. Moved down here so we don't get
-        * "out of sync" with the file.
-        */
-       if (discard)
-           return;
+           goto hba_syntax;
 
        /*
         * If this record isn't for our database, or this is the wrong
         * sort of connection, ignore it.
         */
-
        if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 &&
             (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) ||
            port->raddr.sa.sa_family != AF_INET ||
@@ -302,98 +347,75 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p)
            return;
    }
    else
-   {
-       read_through_eol(file);
-       goto syntax;
-   }
-
-   *matches_p = true;
+       goto hba_syntax;
 
+   /* Success */
+   *found_p = true;
    return;
 
-syntax:
+hba_syntax:
    snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-            "process_hba_record: invalid syntax in pg_hba.conf file\n");
+            "parse_hba: invalid syntax in pg_hba.conf file\n");
    fputs(PQerrormsg, stderr);
    pqdebug("%s", PQerrormsg);
 
    *error_p = true;
+   return;
 }
 
 
-
-static void
-process_open_config_file(FILE *file, hbaPort *port, bool *hba_ok_p)
+/*
+ *  Process the hba file line by line.
+ */
+static bool
+check_hba(hbaPort *port)
 {
-/*---------------------------------------------------------------------------
-  This function does the same thing as find_hba_entry, only with
-  the config file already open on stream descriptor "file".
-----------------------------------------------------------------------------*/
-   bool        found_entry = false;    /* found an applicable entry? */
-   bool        error = false;  /* found an erroneous entry? */
-   bool        eof = false;    /* end of hba file */
-
-   while (!eof && !found_entry && !error)
-   {
-       /* Process a line from the config file */
-       int         c = getc(file);
+   List    *line;
+   bool    found_entry = false;
+   bool    error = false;
 
-       if (c == EOF)
-           eof = true;
-       else
-       {
-           ungetc(c, file);
-           if (c == '#')
-               read_through_eol(file);
-           else
-               process_hba_record(file, port, &found_entry, &error);
-       }
+   foreach (line, hba_lines)
+   {
+       parse_hba(lfirst(line), port, &found_entry, &error);
+       if (found_entry || error)
+           break;
    }
 
    if (!error)
    {
        /* If no matching entry was found, synthesize 'reject' entry. */
-
        if (!found_entry)
            port->auth_method = uaReject;
-
-       *hba_ok_p = true;
+       return true;
    }
+   else
+       return false;
 }
 
 
-
-static void
-find_hba_entry(hbaPort *port, bool *hba_ok_p)
-{
 /*
- * Read the config file and find an entry that allows connection from
- * host "raddr", user "user", to database "database".  If found,
- * return *hba_ok_p = true and *userauth_p and *auth_arg representing
- * the contents of that entry. If there is no matching entry, we
- * set *hba_ok_p = true, *userauth_p = uaReject.
- *
- * If the config file is unreadable or contains invalid syntax, we
- * issue a diagnostic message to stderr (ie, the postmaster log file)
- * and return without changing *hba_ok_p.
- *
+ * Read the config file and create a List of Lists of tokens in the file.
  * 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.
  */
+static void
+load_hba()
+{
 
    int         fd,
                bufsize;
    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 */
+   if (hba_lines)
+       free_lines(&hba_lines);
+   /*
+    *  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.
+    */
    bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char);
    old_conf_file = (char *) palloc(bufsize);
    snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE);
@@ -403,11 +425,10 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p)
        /* Old config file exists.  Tell this guy he needs to upgrade. */
        close(fd);
        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-         "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);
+           "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);
    }
@@ -425,16 +446,15 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p)
        if (file == NULL)
        {
            /* The open of the config file failed.  */
-
            snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-                    "find_hba_entry: Unable to open authentication config file \"%s\": %s\n",
+                    "load_hba: Unable to open authentication config file \"%s\": %s\n",
                     conf_file, strerror(errno));
            fputs(PQerrormsg, stderr);
            pqdebug("%s", PQerrormsg);
        }
        else
        {
-           process_open_config_file(file, port, hba_ok_p);
+           tokenize_file(file, &hba_lines);
            FreeFile(file);
        }
        pfree(conf_file);
@@ -443,35 +463,175 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p)
 }
 
 
+/*
+ *  Take the line and compare it to the needed map, pg_user and ident_user.
+ */
 static void
-interpret_ident_response(char *ident_response,
-                        bool *error_p, char *ident_username)
+parse_ident_usermap(List *line, const char *usermap_name, const char *pg_user,
+                const char *ident_user, bool *found_p, bool *error_p)
+{
+   char        *token;
+   char        *file_map;
+   char        *file_pguser;
+   char        *file_ident_user;
+
+   *error_p = false;
+   *found_p = false;
+
+   /* A token read from the file */
+   Assert(line != NIL);
+   token = lfirst(line);
+   file_map = token;
+
+   line = lnext(line);
+   if (!line)
+       goto ident_syntax;
+   token = lfirst(line);
+   if (token[0] != '\0')
+   {
+       file_ident_user = token;
+       line = lnext(line);
+       if (!line)
+           goto ident_syntax;
+       token = lfirst(line);
+       if (token[0] != '\0')
+       {
+           file_pguser = token;
+           if (strcmp(file_map, usermap_name) == 0 &&
+               strcmp(file_pguser, pg_user) == 0 &&
+               strcmp(file_ident_user, ident_user) == 0)
+               *found_p = true;
+       }
+   }
+
+   return;
+
+ident_syntax:
+   snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+            "parse_ident_usermap: invalid syntax in pg_ident.conf file\n");
+   fputs(PQerrormsg, stderr);
+   pqdebug("%s", PQerrormsg);
+   *error_p = true;
+   return;
+}
+
+
+/*
+ *  Process the ident usermap file line by line.
+ */
+static bool
+check_ident_usermap(const char *usermap_name,
+                   const char *pg_user,
+                   const char *ident_user)
+{
+   List    *line;
+   bool    found_entry = false, error = false;
+
+   if (usermap_name[0] == '\0')
+   {
+       snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+           "load_ident_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);
+       found_entry = false;
+   }
+   else if (strcmp(usermap_name, "sameuser") == 0)
+   {
+       if (strcmp(pg_user, ident_user) == 0)
+           found_entry = true;
+       else
+           found_entry = false;
+   }
+   else
+   {
+       foreach(line, ident_lines)
+       {
+           parse_ident_usermap(lfirst(line), usermap_name, pg_user,
+                               ident_user, &found_entry, &error);
+           if (found_entry || error)
+               break;
+       }
+   }
+   return found_entry;
+}
+
+
+/*
+ *  See if the user with ident username "ident_user" 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_user" in order to be authorized.
+ *
+ *  Iff authorized, return *checks_out_p == true.
+ */
+static void
+load_ident()
 {
-/*----------------------------------------------------------------------------
-  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 */
+   FILE       *file;       /* The map file we have to read */
+   char       *map_file;   /* The name of the map file we have to
+                            * read */
+   int         bufsize;
 
-   cursor = &ident_response[0];
+   if (ident_lines)
+       free_lines(&ident_lines);
+
+   /* put together the full pathname to the map file */
+   bufsize = (strlen(DataDir) + strlen(USERMAP_FILE) + 2) * sizeof(char);
+   map_file = (char *) palloc(bufsize);
+   snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);
+
+   file = AllocateFile(map_file, PG_BINARY_R);
+   if (file == NULL)
+   {
+       /* The open of the map file failed.  */
+       snprintf(PQerrormsg, PQERRORMSG_LENGTH,
+           "load_ident_usermap: Unable to open usermap file \"%s\": %s\n",
+           map_file, strerror(errno));
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+   }
+   else
+   {
+       tokenize_file(file, &ident_lines);
+       FreeFile(file);
+   }
+   pfree(map_file);
+}
+
+
+/*
+ *  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_user.  If it's anything
+ *  else, return *error_p == true and *ident_user undefined.
+ */
+static bool
+interpret_ident_response(char *ident_response,
+                        char *ident_user)
+{
+   char       *cursor = ident_response;/* Cursor into *ident_response */
 
    /*
     * Ident's response, in the telnet tradition, should end in crlf
     * (\r\n).
     */
    if (strlen(ident_response) < 2)
-       *error_p = true;
+       return false;
    else if (ident_response[strlen(ident_response) - 2] != '\r')
-       *error_p = true;
+       return false;
    else
    {
        while (*cursor != ':' && *cursor != '\r')
            cursor++;           /* skip port field */
 
        if (*cursor != ':')
-           *error_p = true;
+           return false;
        else
        {
            /* We're positioned to colon before response type field */
@@ -482,24 +642,23 @@ interpret_ident_response(char *ident_response,
            while (isblank(*cursor))
                cursor++;       /* skip blanks */
            i = 0;
-           while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor)
-                  && i < (int) (sizeof(response_type) - 1))
+           while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) &&
+                  i < (int) (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;
+               return false;
            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;
+                   return false;
                else
                {
                    cursor++;   /* Go over colon */
@@ -507,10 +666,10 @@ interpret_ident_response(char *ident_response,
                    while (*cursor != ':' && *cursor != '\r')
                        cursor++;
                    if (*cursor != ':')
-                       *error_p = true;
+                       return false;
                    else
                    {
-                       int         i;  /* Index into *ident_username */
+                       int         i;  /* Index into *ident_user */
 
                        cursor++;       /* Go over colon */
                        while (isblank(*cursor))
@@ -518,9 +677,9 @@ interpret_ident_response(char *ident_response,
                        /* 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;
+                           ident_user[i++] = *cursor++;
+                       ident_user[i] = '\0';
+                       return true;
                    }
                }
            }
@@ -529,39 +688,39 @@ interpret_ident_response(char *ident_response,
 }
 
 
+/*
+ *  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_user".
 
-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).
-----------------------------------------------------------------------------*/
-
+ *  IP addresses and port numbers are in network byte order.
 
+ *  But iff we're unable to get the information from ident, return
+ *  false.
+ */
+static int
+ident(const struct in_addr remote_ip_addr,
+     const struct in_addr local_ip_addr,
+     const ushort remote_port,
+     const ushort local_port,
+     char *ident_user)
+{
    int         sock_fd,        /* File descriptor for socket on which we
                                 * talk to Ident */
                rc;             /* Return code from a locally called
                                 * function */
+   bool ident_return;
 
    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (sock_fd == -1)
    {
        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-            "Failed to create socket on which to talk to Ident server. "
-                "socket() returned errno = %s (%d)\n",
-                strerror(errno), errno);
+           "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);
+       ident_return = false;
    }
    else
    {
@@ -595,13 +754,13 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
        {
            snprintf(PQerrormsg, PQERRORMSG_LENGTH,
                "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);
+               "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;
+           ident_return = false;
        }
        else
        {
@@ -614,260 +773,106 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
            if (rc < 0)
            {
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-                        "Unable to send query to Ident server on the host which is "
-                     "trying to connect to Postgres (Host %s, Port %d),"
-                        "even though we successfully connected to it.  "
-                        "errno = %s (%d)\n",
-                        inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
+                   "Unable to send query to Ident server on the host which is "
+                   "trying to connect to Postgres (Host %s, Port %d),"
+                   "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;
+               ident_return = false;
            }
            else
            {
                char        ident_response[80 + IDENT_USERNAME_MAX];
 
-               rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
+               rc = recv(sock_fd, ident_response,
+                         sizeof(ident_response) - 1, 0);
                if (rc < 0)
                {
                    snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-                         "Unable to receive response from Ident server "
-                            "on the host which is "
-                     "trying to connect to Postgres (Host %s, Port %d),"
-                   "even though we successfully sent our query to it.  "
-                            "errno = %s (%d)\n",
-                            inet_ntoa(remote_ip_addr), IDENT_PORT,
-                            strerror(errno), errno);
+                       "Unable to receive response from Ident server "
+                       "on the host which is "
+                       "trying to connect to Postgres (Host %s, Port %d),"
+                       "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;
+                   ident_return = false;
                }
                else
                {
-                   bool        error;  /* response from Ident is garbage. */
-
                    ident_response[rc] = '\0';
-                   interpret_ident_response(ident_response, &error, ident_username);
-                   *ident_failed = error;
+                   ident_return = interpret_ident_response(ident_response,
+                                                           ident_user);
                }
            }
            close(sock_fd);
        }
    }
+   return ident_return;
 }
 
 
-
-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] != '\0')
-   {
-       strcpy(file_map, buf);
-       next_token(file, buf, sizeof(buf));
-       if (buf[0] != '\0')
-       {
-           strcpy(file_iuser, buf);
-           next_token(file, buf, sizeof(buf));
-           if (buf[0] != '\0')
-           {
-               strcpy(file_pguser, buf);
-               read_through_eol(file);
-               return;
-           }
-       }
-       snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-                "Incomplete line in pg_ident: %s", file_map);
-       fputs(PQerrormsg, stderr);
-       pqdebug("%s", PQerrormsg);
-   }
-}
-
-
-
-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 *pguser,
-                      const char *ident_username,
-                      const char *usermap_name,
-                      bool *checks_out_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 *auth_arg and see if that user is equivalent to
+ *  Postgres user *user.
+ *
+ *  Return STATUS_OK if yes.
+ */
+int
+authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr,
+         const char *pg_user, const char *auth_arg)
 {
-/*--------------------------------------------------------------------------
-  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.
+   /* We were unable to get ident to give us a username */
+   char        ident_user[IDENT_USERNAME_MAX + 1];
 
---------------------------------------------------------------------------*/
+   /* The username returned by ident */
+   if (!ident(raddr->sin_addr, laddr->sin_addr,
+         raddr->sin_port, laddr->sin_port, ident_user))
+       return STATUS_ERROR;
 
-   if (usermap_name[0] == '\0')
-   {
-       *checks_out_p = false;
-       snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-              "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;
-   }
+   if (check_ident_usermap(auth_arg, pg_user, ident_user))
+       return STATUS_OK;
    else
-   {
-       FILE       *file;       /* The map file we have to read */
-       char       *map_file;   /* The name of the map file we have to
-                                * read */
-       int         bufsize;
-
-       /* put together the full pathname to the map file */
-       bufsize = (strlen(DataDir) + strlen(USERMAP_FILE) + 2) * sizeof(char);
-       map_file = (char *) palloc(bufsize);
-       snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);
-
-       file = AllocateFile(map_file, PG_BINARY_R);
-       if (file == NULL)
-       {
-           /* The open of the map file failed.  */
-
-           snprintf(PQerrormsg, PQERRORMSG_LENGTH,
-                    "verify_against_usermap: Unable to open usermap file \"%s\": %s\n",
-                    map_file, strerror(errno));
-           fputs(PQerrormsg, stderr);
-           pqdebug("%s", PQerrormsg);
-
-           *checks_out_p = false;
-       }
-       else
-       {
-           verify_against_open_usermap(file,
-                                   pguser, ident_username, usermap_name,
-                                       checks_out_p);
-           FreeFile(file);
-       }
-       pfree(map_file);
-
-
-   }
+       return STATUS_ERROR;
 }
 
 
-
+/*
+ *  Determine what authentication method should be used when accessing database
+ *  "database" from frontend "raddr", user "user".  Return the method,
+ *  an optional argument, and STATUS_OK.
+ *  Note that STATUS_ERROR indicates a problem with the hba config file.
+ *  If the file is OK but does not contain any entry matching the request,
+ *  we return STATUS_OK and method = uaReject.
+ */
 int
-authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
-         const char *postgres_username,
-         const char *auth_arg)
+hba_getauthmethod(hbaPort *port)
 {
-/*---------------------------------------------------------------------------
-  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 *auth_arg and see if that user is equivalent to
-  Postgres user *user.
-
-  Return STATUS_OK if yes.
----------------------------------------------------------------------------*/
-   bool        checks_out;
-   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(raddr->sin_addr, laddr->sin_addr,
-         raddr->sin_port, laddr->sin_port,
-         &ident_failed, ident_username);
-
-   if (ident_failed)
+   if (check_hba(port))
+       return STATUS_OK;
+   else
        return STATUS_ERROR;
+}
 
-   verify_against_usermap(postgres_username, ident_username, auth_arg,
-                          &checks_out);
-
-   return checks_out ? STATUS_OK : STATUS_ERROR;
+/*
+ * Clear tokenized file contents and force reload on next use.
+ */
+void load_hba_and_ident(void)
+{
+   load_hba();
+   load_ident();
 }
 
 
+/* Character set stuff.  Not sure it really belongs in this file. */
+
 #ifdef CYR_RECODE
 #define CHARSET_FILE "charset.conf"
 #define MAX_CHARSETS   10
@@ -882,8 +887,9 @@ struct CharsetItem
    char        Table[MAX_TOKEN];
 };
 
+
 static bool
-InRange(char *buf, int host)
+CharSetInRange(char *buf, int host)
 {
    int         valid,
                i,
@@ -989,7 +995,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
        else
        {
            if (c == '#')
-               read_through_eol(file);
+               read_to_eol(file);
            else
            {
                /* Read the key */
@@ -1009,7 +1015,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
                            next_token(file, buf, sizeof(buf));
                            if (buf[0] != '\0')
                            {
-                               if (InRange(buf, host))
+                               if (CharSetInRange(buf, host))
                                {
                                    /* Read the charset */
                                    next_token(file, buf, sizeof(buf));
@@ -1050,7 +1056,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
                            }
                            break;
                    }
-                   read_through_eol(file);
+                   read_to_eol(file);
                }
            }
        }
@@ -1066,23 +1072,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
        pfree((struct CharsetItem *) ChArray[i]);
    }
 }
-
 #endif
 
-int
-hba_getauthmethod(hbaPort *port)
-{
-/*---------------------------------------------------------------------------
-  Determine what authentication method should be used when accessing database
-  "database" from frontend "raddr", user "user".  Return the method,
-  an optional argument, and STATUS_OK.
-  Note that STATUS_ERROR indicates a problem with the hba config file.
-  If the file is OK but does not contain any entry matching the request,
-  we return STATUS_OK and method = uaReject.
-----------------------------------------------------------------------------*/
-   bool        hba_ok = false;
-
-   find_hba_entry(port, &hba_ok);
-
-   return hba_ok ? STATUS_OK : STATUS_ERROR;
-}
+
+
index 3e92f16720e76c8077b1863d130350a1711fa678..c1cd24c7d60919bc73c51a8d052ac3ab9fe1105d 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.231 2001/07/03 16:52:12 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.232 2001/07/30 14:50:22 momjian Exp $
  *
  * NOTES
  *
@@ -809,6 +809,8 @@ ServerLoop(void)
 
    nSockets = initMasks(&readmask, &writemask);
 
+   load_hba_and_ident();
+
    for (;;)
    {
        Port       *port;
@@ -874,6 +876,7 @@ ServerLoop(void)
        if (got_SIGHUP)
        {
            got_SIGHUP = false;
+           load_hba_and_ident();
            ProcessConfigFile(PGC_SIGHUP);
        }
 
@@ -993,7 +996,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 
    buf = palloc(len);
    pq_getbytes(buf, len);
-   
+
    packet = buf;
 
    /*
@@ -1479,7 +1482,7 @@ reaper(SIGNAL_ARGS)
 #endif
        /*
         * Check if this child was the statistics collector. If
-        * so, start a new one. 
+        * so, start a new one.
         */
        if (pgstat_ispgstat(pid))
        {
@@ -1987,19 +1990,8 @@ DoBackend(Port *port)
        av[ac++] = "-o";
        av[ac++] = ttybuf;
    }
-
    av[ac] = (char *) NULL;
 
-   /*
-    * Release postmaster's working memory context so that backend can
-    * recycle the space.  Note this does not trash *MyProcPort, because
-    * ConnCreate() allocated that space with malloc() ... else we'd need
-    * to copy the Port data here.
-    */
-   MemoryContextSwitchTo(TopMemoryContext);
-   MemoryContextDelete(PostmasterContext);
-   PostmasterContext = NULL;
-
    /*
     * Debug: print arguments being passed to backend
     */
index acf89356e80781874e3eaf85d829440d3687a8ef..0de677a94421cde2de44e7616ef5a753891dbeea 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.227 2001/06/29 16:05:56 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.228 2001/07/30 14:50:24 momjian Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -1120,7 +1120,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
    unsigned short remote_port;
 
    char       *potential_DataDir = NULL;
-   
+
    /*
     * Catch standard options before doing much else.  This even works on
     * systems without getopt_long.
@@ -1144,16 +1144,27 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
     *
     * If we are running under the postmaster, this is done already.
     */
-   if (!IsUnderPostmaster)
+   if (IsUnderPostmaster)
+   {
+       MemoryContextSwitchTo(TopMemoryContext);
+       ClientAuthentication(MyProcPort); /* might not return */
+       /*
+        * Release postmaster's working memory context so that backend can
+        * recycle the space.  Note this does not trash *MyProcPort, because
+        * ConnCreate() allocated that space with malloc() ... else we'd need
+        * to copy the Port data here.  We delete it here because the
+        * authorization file tokens are stored in this context.
+        */
+       MemoryContextDelete(PostmasterContext);
+       PostmasterContext = NULL;
+   }
+   else
    {
        SetProcessingMode(InitProcessing);
        EnableExceptionHandling(true);
        MemoryContextInit();
    }
 
-   if (IsUnderPostmaster)
-       ClientAuthentication(MyProcPort); /* might not return */
-
    /*
     * Set default values for command-line options.
     */
@@ -1714,7 +1725,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
    if (!IsUnderPostmaster)
    {
        puts("\nPOSTGRES backend interactive interface ");
-       puts("$Revision: 1.227 $ $Date: 2001/06/29 16:05:56 $\n");
+       puts("$Revision: 1.228 $ $Date: 2001/07/30 14:50:24 $\n");
    }
 
    /*
index 9afd9f841732c2a0cc001033b05eea55e53593bd..f6493373e23536079f57ce312bf0a7b6c4e23612 100644 (file)
@@ -4,7 +4,7 @@
  *   Interface to hba.c
  *
  *
- * $Id: hba.h,v 1.19 2001/03/22 04:00:47 momjian Exp $
+ * $Id: hba.h,v 1.20 2001/07/30 14:50:24 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,8 +40,9 @@ typedef enum UserAuth
 
 typedef struct Port hbaPort;
 
-int            hba_getauthmethod(hbaPort *port);
+int    hba_getauthmethod(hbaPort *port);
 int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
          const char *postgres_username, const char *auth_arg);
+void load_hba_and_ident(void);
 
 #endif