From 370f90970dc3a3c009e3147468629187ada0a614 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 27 Dec 2004 19:19:24 +0000
Subject: [PATCH] Cause pg_hba.conf file inclusion (@file stuff) to behave as
 documented, that is, files are sought in the same directory as the
 referencing file. Also allow absolute paths in @file constructs.  Improve
 documentation to actually say what is allowed in an included file.

---
 doc/src/sgml/client-auth.sgml |  18 ++--
 src/backend/libpq/hba.c       | 158 ++++++++++++++++++----------------
 2 files changed, 95 insertions(+), 81 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 126d3a810df..edcefa6903e 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.69 2004/12/26 23:06:56 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.70 2004/12/27 19:19:23 tgl Exp $
 -->
 
 <chapter id="client-authentication">
@@ -175,8 +175,7 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
        a specific <productname>PostgreSQL</productname> database.
        Multiple database names can be supplied by separating them with
        commas.  A file containing database names can be specified by
-       preceding the file name with <literal>@</>. The file must be in
-       the same directory as <filename>pg_hba.conf</>.
+       preceding the file name with <literal>@</>.
       </para>
      </listitem>
     </varlistentry>
@@ -192,8 +191,7 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
        can be supplied by separating them with commas. Group names can
        be specified by preceding the group name with <literal>+</>. A
        file containing user names can be specified by preceding the
-       file name with <literal>@</>. The file must be in the same
-       directory as <filename>pg_hba.conf</>.
+       file name with <literal>@</>.
       </para>
      </listitem>
     </varlistentry>
@@ -393,6 +391,16 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
    </variablelist>
   </para>
 
+  <para>
+   Files included by <literal>@</> constructs are read as lists of names,
+   which can be separated by either whitespace or commas.  Comments are
+   introduced by <literal>#</literal>, just as in
+   <filename>pg_hba.conf</filename>, and nested <literal>@</> constructs are
+   allowed.  Unless the file name following <literal>@</> is an absolute
+   path, it is taken to be relative to the directory containing the
+   referencing file.
+  </para>
+
   <para>
    Since the <filename>pg_hba.conf</filename> records are examined
    sequentially for each connection attempt, the order of the records is
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 40ff28059a4..190f582550a 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.134 2004/11/17 19:54:24 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.135 2004/12/27 19:19:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,8 +82,10 @@ static List **group_sorted = NULL;		/* sorted group list, for
 static int	user_length;
 static int	group_length;
 
-static void tokenize_file(FILE *file, List **lines, List **line_nums);
-static char *tokenize_inc_file(const char *inc_filename);
+static void tokenize_file(const char *filename, FILE *file,
+						  List **lines, List **line_nums);
+static char *tokenize_inc_file(const char *outer_filename,
+							   const char *inc_filename);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -212,7 +214,7 @@ next_token(FILE *fp, char *buf, int bufsz)
  * we have reached EOL.
  */
 static char *
-next_token_expand(FILE *file)
+next_token_expand(const char *filename, FILE *file)
 {
 	char		buf[MAX_TOKEN];
 	char	   *comma_str = pstrdup("");
@@ -236,7 +238,7 @@ next_token_expand(FILE *file)
 
 		/* Is this referencing a file? */
 		if (buf[0] == '@')
-			incbuf = tokenize_inc_file(buf + 1);
+			incbuf = tokenize_inc_file(filename, buf + 1);
 		else
 			incbuf = pstrdup(buf);
 
@@ -301,23 +303,34 @@ free_lines(List **lines, List **line_nums)
 
 
 static char *
-tokenize_inc_file(const char *inc_filename)
+tokenize_inc_file(const char *outer_filename,
+				  const char *inc_filename)
 {
 	char	   *inc_fullname;
 	FILE	   *inc_file;
 	List	   *inc_lines;
 	List	   *inc_line_nums;
 	ListCell   *line;
-	char	   *comma_str = pstrdup("");
+	char	   *comma_str;
 
-	inc_fullname = (char *) palloc(strlen(DataDir) + 1 +
-								   strlen(inc_filename) + 1);
-	strcpy(inc_fullname, DataDir);
-	strcat(inc_fullname, "/");
-	strcat(inc_fullname, inc_filename);
+	if (is_absolute_path(inc_filename))
+	{
+		/* absolute path is taken as-is */
+		inc_fullname = pstrdup(inc_filename);
+	}
+	else
+	{
+		/* relative path is relative to dir of calling file */
+		inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
+									   strlen(inc_filename) + 1);
+		strcpy(inc_fullname, outer_filename);
+		get_parent_directory(inc_fullname);
+		join_path_components(inc_fullname, inc_fullname, inc_filename);
+		canonicalize_path(inc_fullname);
+	}
 
 	inc_file = AllocateFile(inc_fullname, "r");
-	if (!inc_file)
+	if (inc_file == NULL)
 	{
 		ereport(LOG,
 				(errcode_for_file_access(),
@@ -325,16 +338,18 @@ tokenize_inc_file(const char *inc_filename)
 						inc_filename, inc_fullname)));
 		pfree(inc_fullname);
 
-		/* return empty string, it matches nothing */
-		return comma_str;
+		/* return single space, it matches nothing */
+		return pstrdup(" ");
 	}
-	pfree(inc_fullname);
 
 	/* There is possible recursion here if the file contains @ */
-	tokenize_file(inc_file, &inc_lines, &inc_line_nums);
+	tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
+
 	FreeFile(inc_file);
+	pfree(inc_fullname);
 
 	/* Create comma-separated string from List */
+	comma_str = pstrdup("");
 	foreach(line, inc_lines)
 	{
 		List	   *token_list = (List *) lfirst(line);
@@ -357,6 +372,13 @@ tokenize_inc_file(const char *inc_filename)
 
 	free_lines(&inc_lines, &inc_line_nums);
 
+	/* if file is empty, return single space rather than empty string */
+	if (strlen(comma_str) == 0)
+	{
+		pfree(comma_str);
+		return pstrdup(" ");
+	}
+
 	return comma_str;
 }
 
@@ -365,9 +387,12 @@ tokenize_inc_file(const char *inc_filename)
  * Tokenize the given file, storing the resulting data into two lists:
  * a list of sublists, each sublist containing the tokens in a line of
  * the file, and a list of line numbers.
+ *
+ * filename must be the absolute path to the target file.
  */
 static void
-tokenize_file(FILE *file, List **lines, List **line_nums)
+tokenize_file(const char *filename, FILE *file,
+			  List **lines, List **line_nums)
 {
 	List	   *current_line = NIL;
 	int			line_number = 1;
@@ -377,7 +402,7 @@ tokenize_file(FILE *file, List **lines, List **line_nums)
 
 	while (!feof(file))
 	{
-		buf = next_token_expand(file);
+		buf = next_token_expand(filename, file);
 
 		/* add token to list, unless we are at EOL or comment start */
 		if (buf[0])
@@ -893,61 +918,13 @@ check_hba(hbaPort *port)
 }
 
 
-
-/*
- * Open the group file if possible (return NULL if not)
- */
-static FILE *
-group_openfile(void)
-{
-	char	   *filename;
-	FILE	   *groupfile;
-
-	filename = group_getfilename();
-	groupfile = AllocateFile(filename, "r");
-
-	if (groupfile == NULL && errno != ENOENT)
-		ereport(LOG,
-				(errcode_for_file_access(),
-				 errmsg("could not open file \"%s\": %m", filename)));
-
-	pfree(filename);
-
-	return groupfile;
-}
-
-
-
-/*
- * Open the password file if possible (return NULL if not)
- */
-static FILE *
-user_openfile(void)
-{
-	char	   *filename;
-	FILE	   *pwdfile;
-
-	filename = user_getfilename();
-	pwdfile = AllocateFile(filename, "r");
-
-	if (pwdfile == NULL && errno != ENOENT)
-		ereport(LOG,
-				(errcode_for_file_access(),
-				 errmsg("could not open file \"%s\": %m", filename)));
-
-	pfree(filename);
-
-	return pwdfile;
-}
-
-
-
 /*
  *	 Load group/user name mapping file
  */
 void
 load_group(void)
 {
+	char	   *filename;
 	FILE	   *group_file;
 
 	/* Discard any old data */
@@ -958,11 +935,25 @@ load_group(void)
 	group_sorted = NULL;
 	group_length = 0;
 
-	group_file = group_openfile();
-	if (!group_file)
+	/* Read in the file contents */
+	filename = group_getfilename();
+	group_file = AllocateFile(filename, "r");
+
+	if (group_file == NULL)
+	{
+		/* no complaint if not there */
+		if (errno != ENOENT)
+			ereport(LOG,
+					(errcode_for_file_access(),
+					 errmsg("could not open file \"%s\": %m", filename)));
+		pfree(filename);
 		return;
-	tokenize_file(group_file, &group_lines, &group_line_nums);
+	}
+
+	tokenize_file(filename, group_file, &group_lines, &group_line_nums);
+
 	FreeFile(group_file);
+	pfree(filename);
 
 	/* create sorted lines for binary searching */
 	group_length = list_length(group_lines);
@@ -990,6 +981,7 @@ load_group(void)
 void
 load_user(void)
 {
+	char	   *filename;
 	FILE	   *user_file;
 
 	/* Discard any old data */
@@ -1000,11 +992,25 @@ load_user(void)
 	user_sorted = NULL;
 	user_length = 0;
 
-	user_file = user_openfile();
-	if (!user_file)
+	/* Read in the file contents */
+	filename = user_getfilename();
+	user_file = AllocateFile(filename, "r");
+
+	if (user_file == NULL)
+	{
+		/* no complaint if not there */
+		if (errno != ENOENT)
+			ereport(LOG,
+					(errcode_for_file_access(),
+					 errmsg("could not open file \"%s\": %m", filename)));
+		pfree(filename);
 		return;
-	tokenize_file(user_file, &user_lines, &user_line_nums);
+	}
+
+	tokenize_file(filename, user_file, &user_lines, &user_line_nums);
+
 	FreeFile(user_file);
+	pfree(filename);
 
 	/* create sorted lines for binary searching */
 	user_length = list_length(user_lines);
@@ -1045,7 +1051,7 @@ load_hba(void)
 				 errmsg("could not open configuration file \"%s\": %m",
 						HbaFileName)));
 
-	tokenize_file(file, &hba_lines, &hba_line_nums);
+	tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums);
 	FreeFile(file);
 }
 
@@ -1189,7 +1195,7 @@ load_ident(void)
 	}
 	else
 	{
-		tokenize_file(file, &ident_lines, &ident_line_nums);
+		tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums);
 		FreeFile(file);
 	}
 }
-- 
GitLab