summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2010-08-24 21:52:32 +0000
committerGreg Hudson <ghudson@mit.edu>2010-08-24 21:52:32 +0000
commitc187732f09e477d9a611ad18bfa1739befb86074 (patch)
tree297982ef42c8a291f686a5bbe380b3ee2603ab2e
parent63a020538575070aeb66faf948467b877139384e (diff)
downloadkrb5-c187732f09e477d9a611ad18bfa1739befb86074.tar.gz
krb5-c187732f09e477d9a611ad18bfa1739befb86074.tar.xz
krb5-c187732f09e477d9a611ad18bfa1739befb86074.zip
add profile include support
Add support for "include" and "includedir" directives in profile files. See http://k5wiki.kerberos.org/wiki/Projects/Profile_Includes for more details. ticket: 6761 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24253 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--doc/krb5conf.texinfo14
-rw-r--r--src/config-files/krb5.conf.M10
-rw-r--r--src/util/profile/prof_err.et8
-rw-r--r--src/util/profile/prof_parse.c105
-rw-r--r--src/util/profile/prof_test157
5 files changed, 180 insertions, 14 deletions
diff --git a/doc/krb5conf.texinfo b/doc/krb5conf.texinfo
index 09825524f5..21f5396532 100644
--- a/doc/krb5conf.texinfo
+++ b/doc/krb5conf.texinfo
@@ -40,6 +40,20 @@ foo = baz
then the second value of foo (baz) would never be read.
+The @code{krb5.conf} file can include other files using either of the
+following directives at the beginning of a line:
+
+@smallexample
+include @var{FILENAME}
+includedir @var{DIRNAME}
+@end smallexample
+
+@var{FILENAME} or @var{DIRNAME} should be an absolute path. The named
+file or directory must exist and be readable. Including a directory
+includes all files within the directory whose names consist solely of
+alphanumeric characters, dashes, or underscores. Included configuration
+fragments should begin with a section header.
+
The @code{krb5.conf} file may contain any or all of the following
sections:
diff --git a/src/config-files/krb5.conf.M b/src/config-files/krb5.conf.M
index 5ecfd426c7..40db552d3e 100644
--- a/src/config-files/krb5.conf.M
+++ b/src/config-files/krb5.conf.M
@@ -60,6 +60,16 @@ multiple values. Here is an example of the INI-style format used by
.sp
.PP
+.I krb5.conf
+can include other files using the directives "include FILENAME" or
+"includedir DIRNAME", which must occur at the beginning of a line.
+FILENAME or DIRNAME should be an absolute path. The named file or
+directory must exist and be readable. Including a directory includes
+all files within the directory whose names consist solely of
+alphanumeric characters, dashes, or underscores. Included profile
+fragments should begin with a section header.
+
+.PP
The following sections are currently used in the
.I krb5.conf
file:
diff --git a/src/util/profile/prof_err.et b/src/util/profile/prof_err.et
index af7801ee0a..2384127af9 100644
--- a/src/util/profile/prof_err.et
+++ b/src/util/profile/prof_err.et
@@ -60,7 +60,13 @@ error_code PROF_EXISTS, "Section already exists"
error_code PROF_BAD_BOOLEAN, "Invalid boolean value"
error_code PROF_BAD_INTEGER, "Invalid integer value"
+#
+# new error codes added at end to avoid changing values
+#
error_code PROF_MAGIC_FILE_DATA, "Bad magic value in profile_file_data_t"
-
+error_code PROF_FAIL_INCLUDE_FILE,
+ "Included profile file could not be read"
+error_code PROF_FAIL_INCLUDE_DIR,
+ "Included profile directory could not be read"
end
diff --git a/src/util/profile/prof_parse.c b/src/util/profile/prof_parse.c
index 413c7dfbb0..1ed4484430 100644
--- a/src/util/profile/prof_parse.c
+++ b/src/util/profile/prof_parse.c
@@ -1,6 +1,7 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "prof_int.h"
+#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_STDLIB_H
@@ -8,6 +9,7 @@
#endif
#include <errno.h>
#include <ctype.h>
+#include <dirent.h>
#define SECTION_SEP_CHAR '/'
@@ -22,6 +24,8 @@ struct parse_state {
struct profile_node *current_section;
};
+static errcode_t parse_file(FILE *f, struct parse_state *state);
+
static char *skip_over_blanks(char *cp)
{
while (*cp && isspace((int) (*cp)))
@@ -33,7 +37,7 @@ static void strip_line(char *line)
{
char *p = line + strlen(line);
while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
- *p-- = 0;
+ *--p = 0;
}
static void parse_quoted_string(char *str)
@@ -201,10 +205,76 @@ static errcode_t parse_std_line(char *line, struct parse_state *state)
return 0;
}
+/* Parse lines from filename as if they were part of the profile file. */
+static errcode_t parse_include_file(char *filename, struct parse_state *state)
+{
+ FILE *fp;
+ errcode_t retval = 0;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL)
+ return PROF_FAIL_INCLUDE_FILE;
+ retval = parse_file(fp, state);
+ fclose(fp);
+ return retval;
+}
+
+/* Return non-zero if filename contains only alphanumeric characters and
+ * underscores. */
+static int valid_name(const char *filename)
+{
+ const char *p;
+
+ for (p = filename; *p != '\0'; p++) {
+ if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_')
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Parse lines from files in dirname as if they were part of the profile file.
+ * Only files with names consisting entirely of alphanumeric chracters and
+ * underscores are parsed, in order to avoid parsing editor backup files,
+ * .rpmsave files, and the like.
+ */
+static errcode_t parse_include_dir(char *dirname, struct parse_state *state)
+{
+ DIR *dir;
+ char *pathname;
+ errcode_t retval;
+ struct dirent *ent;
+
+ dir = opendir(dirname);
+ if (dir == NULL)
+ return PROF_FAIL_INCLUDE_DIR;
+ while ((ent = readdir(dir)) != NULL) {
+ if (!valid_name(ent->d_name))
+ continue;
+ if (asprintf(&pathname, "%s/%s", dirname, ent->d_name) < 0)
+ return ENOMEM;
+ retval = parse_include_file(pathname, state);
+ free(pathname);
+ if (retval)
+ return retval;
+ }
+ return 0;
+}
+
static errcode_t parse_line(char *line, struct parse_state *state)
{
char *cp;
+ if (strncmp(line, "include", 7) == 0 && isspace(line[7])) {
+ cp = skip_over_blanks(line + 7);
+ strip_line(cp);
+ return parse_include_file(cp, state);
+ }
+ if (strncmp(line, "includedir", 10) == 0 && isspace(line[10])) {
+ cp = skip_over_blanks(line + 10);
+ strip_line(cp);
+ return parse_include_dir(cp, state);
+ }
switch (state->state) {
case STATE_INIT_COMMENT:
if (line[0] != '[')
@@ -221,29 +291,22 @@ static errcode_t parse_line(char *line, struct parse_state *state)
return 0;
}
-errcode_t profile_parse_file(FILE *f, struct profile_node **root)
+static errcode_t parse_file(FILE *f, struct parse_state *state)
{
#define BUF_SIZE 2048
char *bptr;
errcode_t retval;
- struct parse_state state;
bptr = malloc (BUF_SIZE);
if (!bptr)
return ENOMEM;
- retval = parse_init_state(&state);
- if (retval) {
- free (bptr);
- return retval;
- }
while (!feof(f)) {
if (fgets(bptr, BUF_SIZE, f) == NULL)
break;
#ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
- retval = parse_line(bptr, &state);
+ retval = parse_line(bptr, state);
if (retval) {
- profile_free_node(state.root_section);
free (bptr);
return retval;
}
@@ -286,9 +349,8 @@ errcode_t profile_parse_file(FILE *f, struct profile_node **root)
/* parse_line modifies contents of p */
newp = p + strlen (p) + 1;
- retval = parse_line (p, &state);
+ retval = parse_line (p, state);
if (retval) {
- profile_free_node(state.root_section);
free (bptr);
return retval;
}
@@ -298,12 +360,29 @@ errcode_t profile_parse_file(FILE *f, struct profile_node **root)
}
#endif
}
- *root = state.root_section;
free (bptr);
return 0;
}
+errcode_t profile_parse_file(FILE *f, struct profile_node **root)
+{
+ struct parse_state state;
+ errcode_t retval;
+
+ *root = NULL;
+ retval = parse_init_state(&state);
+ if (retval)
+ return retval;
+ retval = parse_file(f, &state);
+ if (retval) {
+ profile_free_node(state.root_section);
+ return retval;
+ }
+ *root = state.root_section;
+ return 0;
+}
+
/*
* Return TRUE if the string begins or ends with whitespace
*/
diff --git a/src/util/profile/prof_test1 b/src/util/profile/prof_test1
index bd4901272d..dc0867123d 100644
--- a/src/util/profile/prof_test1
+++ b/src/util/profile/prof_test1
@@ -147,8 +147,65 @@ proc test3 {} {
puts "OK: test3: Clearing relation and adding one entry yields correct count."
}
+# Exercise the include and includedir directives.
+proc test4 {} {
+ global wd verbose
+
+ # Test expected error message when including nonexistent file.
+ catch [file delete $wd/testinc.ini]
+ exec echo "include does-not-exist" >$wd/testinc.ini
+ catch { profile_init_path $wd/testinc.ini } err
+ if $verbose { puts "Got error message $err" }
+ if { $err ne "Included profile file could not be read" } {
+ puts stderr "Error: test4: Did not get expected error when including nonexistent file."
+ exit 1
+ }
+
+ # Test expected error message when including nonexistent directory.
+ catch [file delete $wd/testinc.ini]
+ exec echo "includedir does-not-exist" >$wd/testinc.ini
+ catch { profile_init_path $wd/testinc.ini } err
+ if $verbose { puts "Got error message $err" }
+ if { $err ne "Included profile directory could not be read" } {
+ puts stderr "Error: test4: Did not get expected error when including nonexistent directory."
+ exit 1
+ }
+
+ # Test including a file.
+ catch [file delete $wd/testinc.ini]
+ exec echo "include $wd/test2.ini" >$wd/testinc.ini
+ set p [profile_init_path $wd/testinc.ini]
+ set x [profile_get_values $p {{test section 1} bar}]
+ if $verbose { puts "Read $x from included profile" }
+ if { [lindex $x 0] ne "foo" } {
+ puts stderr "Error: test4: Did not get expected result from included profile."
+ exit 1
+ }
+ profile_release $p
+
+ # Test including a directory. (Put two copies of test2.ini inside
+ # it and check that we get two values for one of the variables.)
+ catch [file delete -force $wd/test_include_dir]
+ exec mkdir $wd/test_include_dir
+ exec cp $wd/test2.ini $wd/test_include_dir/a
+ exec cp $wd/test2.ini $wd/test_include_dir/b
+ catch [file delete $wd/testinc.ini]
+ exec echo "includedir $wd/test_include_dir" >$wd/testinc.ini
+ set p [profile_init_path $wd/testinc.ini]
+ set x [profile_get_values $p {{test section 1} bar}]
+ if $verbose { puts "Read $x from included directory" }
+ if { $x ne "foo foo" } {
+ puts stderr, "Error: test4: Did not get expected result from included directory."
+ exit 1
+ }
+ profile_release $p
+
+ puts "OK: test4: include and includedir directives"
+}
+
test1
test2
test3
+test4
exit 0