]> granicus.if.org Git - postgresql/commitdiff
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