auth: local realms configuration
authorMichael Meffie <mmeffie@sinenomine.net>
Tue, 28 Feb 2012 13:50:33 +0000 (08:50 -0500)
committerDerrick Brashear <shadow@dementix.org>
Mon, 16 Apr 2012 17:31:04 +0000 (10:31 -0700)
Add krb.conf and krb.excl support to the auth cell configuration
library.  Provide a function to determine if the user is local to the
cell.  Provide a function to set the local realms during application
initialization.  These changes are intended to replace the functions
afs_krb_get_lrealm and afs_is_foreign_ticket_name.

Change-Id: Iba57e9ffc2c958f3a4565a9352ce172189276ce9
Reviewed-on: http://gerrit.openafs.org/5744
Reviewed-by: Derrick Brashear <shadow@dementix.org>
Tested-by: BuildBot <buildbot@rampaginggeek.com>

18 files changed:
acinclude.m4
src/auth/Makefile.in
src/auth/NTMakefile
src/auth/cellconfig.c
src/auth/cellconfig.p.h
src/auth/internal.h
src/auth/realms.c [new file with mode: 0644]
src/libafsauthent/Makefile.in
src/libafsauthent/NTMakefile
src/libafsauthent/afsauthent.def
src/libuafs/Makefile.common.in
src/shlibafsauthent/Makefile.in
src/shlibafsauthent/afsauthent.def
tests/TESTS
tests/auth/.gitignore
tests/auth/Makefile.in
tests/auth/realms-t.c [new file with mode: 0644]
tests/common/config.c

index d2ce60f..602e934 100644 (file)
@@ -1432,6 +1432,7 @@ AC_CHECK_FUNCS([ \
        strerror \
        sysconf \
        sysctl \
+       tdestroy \
        timegm \
 ])
 
index f4fb044..8f2cf1c 100644 (file)
@@ -11,9 +11,9 @@ include @TOP_OBJDIR@/src/config/Makefile.lwp
 
 
 OBJS= cellconfig.o keys.o ktc.o userok.o writeconfig.o authcon.o \
-    acfg_errors.o ktc_errors.o token.xdr.o token.o
+    acfg_errors.o ktc_errors.o token.xdr.o token.o realms.o
 KOBJS= cellconfig.o keys.o ktc.krb.o userok.o writeconfig.o authcon.o \
-    acfg_errors.o ktc_errors.o token.xdr.o token.o
+    acfg_errors.o ktc_errors.o token.xdr.o token.o realms.o
 
 LIBS=libauth.a \
       ${TOP_LIBDIR}/librxkad.a \
@@ -70,6 +70,7 @@ userok.o: userok.c ${INCLS}
 cellconfig.o: cellconfig.c ${INCLS}
 copyauth.o: copyauth.c ${INCLS} AFS_component_version_number.o
 setkey.o: setkey.c ${INCLS} AFS_component_version_number.o
+realms.o: realms.c ${INCLS}
 
 CFLAGS_ktc.krb.o = -DAFS_KERBEROS_ENV
 ktc.krb.o: ktc.c ${INCLS} ${TOP_INCDIR}/afs/vice.h
index 0cd3015..feb3040 100644 (file)
@@ -41,6 +41,7 @@ AFSAUTH_LIBOBJS =\
        $(OUT)\userok.obj \
        $(OUT)\writeconfig.obj \
        $(OUT)\authcon.obj \
+       $(OUT)\realms.obj \
        $(OUT)\acfg_errors.obj \
        $(OUT)\ktc_errors.obj \
        $(OUT)\ktc_nt.obj \
@@ -67,6 +68,7 @@ AFSAUTH_KRB_LIBOBJS =\
        $(OUT)\userok.obj \
        $(OUT)\writeconfig.obj \
        $(OUT)\authcon.obj \
+       $(OUT)\realms.obj \
        $(OUT)\acfg_errors.obj \
        $(OUT)\ktc_errors.obj \
        $(OUT)\ktc_nt.obj \
index e4490e8..a5b2d3d 100644 (file)
@@ -823,6 +823,10 @@ afsconf_OpenInternal(struct afsconf_dir *adir, char *cell,
 
     /* now read the fs keys, if possible */
     code = _afsconf_LoadKeys(adir);
+    if (code) {
+        return code;
+    }
+    code = _afsconf_LoadRealms(adir);
 
     return code;
 }
@@ -1559,6 +1563,7 @@ afsconf_CloseInternal(struct afsconf_dir *adir)
     }
 
     _afsconf_FreeAllKeys(adir);
+    _afsconf_FreeRealms(adir);
 
     /* reinit */
     memset(adir, 0, sizeof(struct afsconf_dir));
index 94b72b9..5751968 100644 (file)
@@ -97,6 +97,8 @@ struct afsconf_dir {
     afs_int32 timeCheck;       /* time of last check for update */
     struct afsconf_aliasentry *alias_entries;  /* cell aliases */
     afsconf_secflags securityFlags;
+    struct afsconf_realms *local_realms;        /* local realms */
+    struct afsconf_realms *exclusions;          /* excluded principals */
 };
 
 extern afs_int32 afsconf_FindService(const char *aname);
@@ -252,6 +254,12 @@ extern int afsconf_SuperIdentity(struct afsconf_dir *, struct rx_call *,
                                 struct rx_identity **);
 extern int afsconf_IsSuperIdentity(struct afsconf_dir *, struct rx_identity *);
 
+/* realms.c */
+extern int afsconf_SetLocalRealm(const char *realm);
+extern int afsconf_IsLocalRealmMatch(struct afsconf_dir *dir, afs_int32 * local,
+                               const char *name, const char *instance,
+                               const char *cell);
+
 /* some well-known ports and their names; new additions to table in cellconfig.c, too */
 #define        AFSCONF_FILESERVICE             "afs"
 #define        AFSCONF_FILEPORT                7000
index 9a351fe..957fa34 100644 (file)
@@ -6,3 +6,5 @@ extern int _afsconf_LoadKeys(struct afsconf_dir *adir);
 extern void _afsconf_InitKeys(struct afsconf_dir *adir);
 extern void _afsconf_FreeAllKeys(struct afsconf_dir *adir);
 extern int _afsconf_GetLocalCell(struct afsconf_dir *adir, char **pname, int check);
+extern int _afsconf_LoadRealms(struct afsconf_dir *dir);
+extern void _afsconf_FreeRealms(struct afsconf_dir *dir);
diff --git a/src/auth/realms.c b/src/auth/realms.c
new file mode 100644 (file)
index 0000000..8236153
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * Copyright 2012, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+#include <roken.h>
+#include <opr/queue.h>
+#include <afs/stds.h>
+#include <afs/pthread_glock.h>
+#include <afs/afsutil.h>
+#include <ctype.h>
+#include <search.h>
+#include "cellconfig.h"
+#include "internal.h"
+
+#define MAXLINESIZE 2047
+
+/* Can be set during initialization, overriding the krb.conf file. */
+static struct opr_queue *lrealms = NULL;
+
+/**
+ * Realm and exclusion list entries.
+ */
+struct afsconf_realm_entry {
+    struct opr_queue link;  /**< linked list header */
+    char *value;           /**< local realm or principal */
+};
+
+/**
+ * Realm and exclusion lists.
+ */
+struct afsconf_realms {
+    struct opr_queue list;  /**< list of afsconf_realm_entry */
+    int time_read;         /**< time when read from file */
+    void *tree;                    /**< for lookup */
+    int (*compare) (const void *, const void *); /**< compare entries */
+};
+
+static int
+compare_realms(const void *a, const void *b)
+{
+    return strcasecmp((char *)a, (char *)b);
+}
+
+static int
+compare_principals(const void *a, const void *b)
+{
+    return strcmp((char *)a, (char *)b);
+}
+
+/**
+ * Format the k4-style principal string.
+ *
+ * @param[out] pvname output buffer, must be freed by caller
+ * @param[in]  name   user name, required
+ * @param[in]  inst   user instance, optional
+ * @param[in]  cell   cell name, optional
+ *
+ * @return status
+ *   @retval 0 success
+ *   @retval EINVAL invalid arguments
+ *   @retval E2BIG insufficient output buffer space
+ *
+ * @internal
+ */
+static int
+create_name(char **pvname, const char *name,
+           const char *inst, const char *cell)
+{
+    int code = 0;
+
+    if (!name || !*name) {
+       return EINVAL;
+    }
+    if (cell && *cell) {
+       if (inst && *inst) {
+           code = asprintf(pvname, "%s.%s@%s", name, inst, cell);
+       } else {
+           code = asprintf(pvname, "%s@%s", name, cell);
+       }
+    } else {
+       if (inst && *inst) {
+           code = asprintf(pvname, "%s.%s", name, inst);
+       } else {
+           code = asprintf(pvname, "%s", name);
+       }
+    }
+    return (code < 0 ? ENOMEM : 0);
+}
+
+/**
+ * Parse whitespace delimited values
+ *
+ * @param[in]    buffer    input string
+ * @param[out]   result    output string
+ * @param[in]    size      size of result buffer
+ *
+ * @return pointer to the next value
+ *
+ * @internal
+ */
+static char *
+parse_str(char *buffer, char *result, int size)
+{
+    int n = 0;
+
+    if (!buffer)
+       goto cleanup;
+
+    while (*buffer && isspace(*buffer))
+       buffer++;
+    while (*buffer && !isspace(*buffer)) {
+       if (n < size - 1) {
+           *result++ = *buffer++;
+           n++;
+       } else {
+           buffer++;
+       }
+    }
+
+  cleanup:
+    *result = '\0';
+    return buffer;
+}
+
+/**
+ * Add a new list element.
+ *
+ * Add the name element if not already present in the list.
+ * The names are case insensitive.
+ *
+ * @param[inout] list  list of name elements
+ * @param[in]    name  name to add
+ *
+ * @return status
+ *   @retval 0 success
+ *   @retval ENOMEM unable to allocate new entry
+ *
+ * @internal
+ */
+static int
+add_entry(struct opr_queue *list, const char *name)
+{
+    struct afsconf_realm_entry *entry;
+
+    entry = malloc(sizeof(struct afsconf_realm_entry));
+    if (!entry) {
+       return ENOMEM;
+    }
+    entry->value = strdup(name);
+    opr_queue_Append(list, (struct opr_queue *)entry);
+    return 0;
+}
+
+/**
+ * Free all entries in a list.
+ *
+ * @param[in] list  list of entries
+ *
+ * @return none
+ *
+ * @internal
+ */
+static void
+free_realm_entries(struct opr_queue *list)
+{
+    struct afsconf_realm_entry *entry;
+
+    while (!opr_queue_IsEmpty(list)) {
+       entry = opr_queue_First(list, struct afsconf_realm_entry, link);
+       opr_queue_Remove(&entry->link);
+       if (entry->value) {
+           free(entry->value);
+       }
+       free(entry);
+    }
+}
+
+#if HAVE_TDESTROY
+/**
+ * Placeholder for tdestroy.
+ */
+static void
+free_tree_node(void *nodep)
+{
+    return;                    /* empty */
+}
+#endif
+
+/**
+ * Delete all the entries from the search tree.
+ *
+ * @param[in] list  list of entries
+ *
+ * @return none
+ *
+ * @internal
+ */
+/*static*/ void
+destroy_tree(struct afsconf_realms *entries)
+{
+    if (entries->tree) {
+#if HAVE_TDESTROY
+       tdestroy(entries->tree, free_tree_node);
+#else
+       struct opr_queue *cursor;
+       struct afsconf_realm_entry *entry;
+
+       for (opr_queue_Scan(&entries->list, cursor)) {
+           entry = opr_queue_Entry(cursor, struct afsconf_realm_entry, link);
+           tdelete(entry->value, &entries->tree, entries->compare);
+       }
+#endif
+       entries->tree = NULL;
+    }
+}
+
+/**
+ * Build a search tree from the list of entries.
+ *
+ * @param[in] list  list of entries
+ *
+ * @return none
+ *
+ * @internal
+ */
+static void
+build_tree(struct afsconf_realms *entries)
+{
+    struct opr_queue *cursor;
+    struct afsconf_realm_entry *entry;
+
+    for (opr_queue_Scan(&entries->list, cursor)) {
+       entry = opr_queue_Entry(cursor, struct afsconf_realm_entry, link);
+       tsearch(entry->value, &entries->tree, entries->compare);
+    }
+}
+
+/**
+ * Read the list of local realms from a config file.
+ *
+ * @param[inout]  dir   config dir object
+ *
+ * @return status
+ *
+ * @internal
+ */
+static int
+read_local_realms(struct afsconf_realms *entries, const char *path)
+{
+    int code = 0;
+    char realm[AFS_REALM_SZ];
+    struct opr_queue temp;
+    char *filename = NULL;
+    struct stat tstat;
+    FILE *cnffile = NULL;
+    char *linebuf = NULL;
+    char *p;
+
+    opr_queue_Init(&temp);
+    code = asprintf(&filename, "%s/%s", path, AFSDIR_KCONF_FILE);
+    if (code < 0) {
+       code = ENOMEM;
+       goto done;
+    }
+    code = stat(filename, &tstat);
+    if (code < 0) {
+       code = (errno == ENOENT ? 0 : errno);   /* this file is optional */
+       goto done;
+    }
+    if (tstat.st_mtime == entries->time_read) {
+       code = 0;
+       goto done;
+    }
+    entries->time_read = tstat.st_mtime;
+    if ((cnffile = fopen(filename, "r")) == NULL) {
+       code = (errno == ENOENT ? 0 : errno);   /* this file is optional */
+       goto done;
+    }
+    linebuf = malloc(sizeof(char) * (MAXLINESIZE + 1));
+    if (!linebuf) {
+       code = ENOMEM;
+       goto done;
+    }
+    if (fgets(linebuf, MAXLINESIZE, cnffile) == NULL) {
+       code = errno;
+       goto done;
+    }
+    linebuf[MAXLINESIZE] = '\0';
+    for (p = linebuf; *p;) {
+       p = parse_str(p, realm, AFS_REALM_SZ);
+       if (*realm) {
+           code = add_entry(&temp, realm);
+           if (code) {
+               goto done;
+           }
+       }
+    }
+    destroy_tree(entries);
+    opr_queue_Swap(&temp, &entries->list);
+    build_tree(entries);
+
+  done:
+    free_realm_entries(&temp);
+    if (filename) {
+       free(filename);
+    }
+    if (linebuf) {
+       free(linebuf);
+    }
+    if (cnffile) {
+       fclose(cnffile);
+    }
+    return code;
+}
+
+/**
+ * Read the list of local exclusions from a config file.
+ *
+ * @param[inout]  dir   config dir object
+ *
+ * @return status
+ *
+ * @internal
+ */
+static int
+read_local_exclusions(struct afsconf_realms *entries, const char *path)
+{
+    int code = 0;
+    char *linebuf = NULL;
+    char *filename = NULL;
+    char name[256];
+    FILE *cnffile = NULL;
+    struct opr_queue temp;
+    struct stat tstat;
+
+    opr_queue_Init(&temp);
+    code = asprintf(&filename, "%s/%s", path, AFSDIR_KRB_EXCL_FILE);
+    if (code < 0) {
+       code = ENOMEM;
+       goto done;
+    }
+    code = stat(filename, &tstat);
+    if (code < 0) {
+       code = (errno == ENOENT ? 0 : errno);   /* this file is optional */
+       goto done;
+    }
+    if (tstat.st_mtime == entries->time_read) {
+       code = 0;
+       goto done;
+    }
+    if ((cnffile = fopen(filename, "r")) == NULL) {
+       code = (errno != ENOENT ? errno : 0);   /* this file is optional */
+       goto done;
+    }
+    linebuf = malloc(sizeof(char) * (MAXLINESIZE + 1));
+    if (!linebuf) {
+       code = ENOMEM;
+       goto done;
+    }
+    for (;;) {
+       if (fgets(linebuf, MAXLINESIZE, cnffile) == NULL) {
+           break;
+       }
+       linebuf[MAXLINESIZE] = '\0';
+       parse_str(linebuf, name, sizeof(name));
+       if (*name) {
+           code = add_entry(&temp, name);
+           if (code) {
+               goto done;
+           }
+       }
+    }
+    destroy_tree(entries);
+    opr_queue_Swap(&temp, &entries->list);
+    build_tree(entries);
+  done:
+    free_realm_entries(&temp);
+    if (filename) {
+       free(filename);
+    }
+    if (linebuf) {
+       free(linebuf);
+    }
+    if (cnffile) {
+       fclose(cnffile);
+    }
+    return code;
+}
+
+
+/**
+ * Free the local realms and exclusions lists.
+ *
+ * @param[in] dir afsconf dir object
+ *
+ * @return none
+ *
+ * @internal
+ */
+void
+_afsconf_FreeRealms(struct afsconf_dir *dir)
+{
+    if (dir) {
+       if (dir->local_realms) {
+           destroy_tree(dir->local_realms);
+           free_realm_entries(&dir->local_realms->list);
+           dir->local_realms = NULL;
+       }
+       if (dir->exclusions) {
+           destroy_tree(dir->exclusions);
+           free_realm_entries(&dir->exclusions->list);
+           dir->exclusions = NULL;
+       }
+    }
+}
+
+/**
+ * Load the local realms and exclusions lists.
+ *
+ * @param[in] dir afsconf dir object
+ *
+ * @return none
+ *
+ * @internal
+ */
+int
+_afsconf_LoadRealms(struct afsconf_dir *dir)
+{
+    int code = 0;
+    struct afsconf_realms *local_realms = NULL;
+    struct afsconf_realms *exclusions = NULL;
+
+    /* Create and load the list of local realms. */
+    local_realms = malloc(sizeof(struct afsconf_realms));
+    if (!local_realms) {
+       code = ENOMEM;
+       goto cleanup;
+    }
+    memset(local_realms, 0, sizeof(struct afsconf_realms));
+    opr_queue_Init(&local_realms->list);
+    local_realms->compare = compare_realms;
+
+    if (!lrealms) {
+       code = read_local_realms(local_realms, dir->name);
+       if (code) {
+           goto cleanup;
+       }
+    } else {
+       struct opr_queue *cursor;
+       struct afsconf_realm_entry *entry;
+       for (opr_queue_Scan(lrealms, cursor)) {
+           entry = opr_queue_Entry(cursor, struct afsconf_realm_entry, link);
+           code = add_entry(&local_realms->list, entry->value);
+           if (code) {
+               goto cleanup;
+           }
+       }
+       build_tree(local_realms);
+    }
+
+    /* Create and load the list of excluded principals. */
+    exclusions = malloc(sizeof(struct afsconf_realms));
+    if (!exclusions) {
+       code = ENOMEM;
+       goto cleanup;
+    }
+    memset(exclusions, 0, sizeof(struct afsconf_realms));
+    opr_queue_Init(&exclusions->list);
+    exclusions->compare = compare_principals;
+    code = read_local_exclusions(exclusions, dir->name);
+    if (code) {
+       goto cleanup;
+    }
+
+    dir->local_realms = local_realms;
+    dir->exclusions = exclusions;
+    return 0;
+
+  cleanup:
+    if (local_realms) {
+       destroy_tree(local_realms);
+       free_realm_entries(&local_realms->list);
+    }
+    if (exclusions) {
+       destroy_tree(dir->exclusions);
+       free_realm_entries(&exclusions->list);
+    }
+    return code;
+}
+
+/**
+ * Set a local realm, instead of retrieving the local realms from the
+ * configuration file krb.conf (if it exists).  Maybe called multiple
+ * times during application initialization to set one or more local
+ * realms.
+ *
+ * @return status
+ *   @retval 0 success
+ *   @retval ENOMEM unable to allocate new entry
+ */
+int
+afsconf_SetLocalRealm(const char *realm)
+{
+    int code = 0;
+
+    LOCK_GLOBAL_MUTEX;
+    if (!lrealms) {
+       lrealms = malloc(sizeof(struct opr_queue));
+       if (!lrealms) {
+           code = ENOMEM;
+           goto done;
+       }
+       opr_queue_Init(lrealms);
+    }
+    code = add_entry(lrealms, realm);
+  done:
+    UNLOCK_GLOBAL_MUTEX;
+    return code;
+}
+
+/**
+ * Determine if a principal is local to this cell.
+ *
+ * @param[in]  dir     afsconf dir object
+ * @param[out] plocal  set to 1 if user is local, 0 if foreign
+ * @param[in]  name    user name
+ * @param[in]  inst    user instance
+ * @param[in]  cell    user cell name
+ *
+ * @returns status
+ *   @retval 0 success
+ *   @retval ENOMEM unable to allocate memory
+ *   @retval EINVAL invalid argument
+ */
+int
+afsconf_IsLocalRealmMatch(struct afsconf_dir *dir, afs_int32 * plocal,
+                         const char *name, const char *inst,
+                         const char *cell)
+{
+    int code = 0;
+    char *localcell = NULL;
+    char *tvname = NULL;
+    struct afsconf_realms *local_realms = NULL;
+    struct afsconf_realms *exclusions = NULL;
+
+    if (!name)
+       return EINVAL;
+
+    if (!cell || !*cell) {
+       *plocal = 1;
+       return code;
+    }
+
+    LOCK_GLOBAL_MUTEX;
+    code = _afsconf_GetLocalCell(dir, &localcell, 1);
+    if (code)
+       goto done;
+
+    /* Does the cell match the local cell name? */
+    if (strcasecmp(localcell, cell) == 0) {
+       *plocal = 1;            /* cell matches the local cell name. */
+       goto done;
+    }
+
+    /* Does the cell match one of the local_realms? */
+    local_realms = dir->local_realms;
+    if (!tfind(cell, &local_realms->tree, local_realms->compare)) {
+       *plocal = 0;            /* Cell name not found in local realms. */
+       goto done;
+    }
+
+    /* Local realm matches, make sure the principal is not in the
+     * exclusion list, if one. */
+    exclusions = dir->exclusions;
+    if (!exclusions->tree) {
+       *plocal = 1;            /* Matches one of the local realms; no exclusions */
+       goto done;
+    }
+
+    /* Create a full principal name for the exclusion check. */
+    code = create_name(&tvname, name, inst, cell);
+    if (!code) {
+       if (tfind(tvname, &exclusions->tree, exclusions->compare)) {
+           *plocal = 0;        /* name found in the exclusion list */
+       } else {
+           *plocal = 1;        /* not in the exclusion list */
+       }
+    }
+    if (tvname)
+       free(tvname);
+
+  done:
+    UNLOCK_GLOBAL_MUTEX;
+
+    return code;
+}
index dfe0c67..79af716 100644 (file)
@@ -35,7 +35,8 @@ AUTHOBJS = \
        ktc_errors.o \
        acfg_errors.o \
        token.o \
-       token.xdr.o
+       token.xdr.o \
+       realms.o
 
 KAUTHOBJS = \
        kauth.xdr.o \
@@ -228,6 +229,9 @@ rxkad_errs.o: ../rxkad/rxkad_errs.c
 ptclient.o: ${PTSERVER}/ptclient.c
        ${AFS_CCRULE} -I../ptserver ${PTSERVER}/ptclient.c
 
+realms.o: ${AUTH}/realms.c
+       ${AFS_CCRULE} -I../auth ${AUTH}/realms.c
+
 ptuser.o: ${PTSERVER}/ptuser.c
        ${AFS_CCRULE} -I../ptserver ${PTSERVER}/ptuser.c
 
index 977f8ed..11bd6d9 100644 (file)
@@ -39,6 +39,7 @@ AUTHOBJS = \
        $(OUT)\userok.obj \
        $(OUT)\writeconfig.obj \
        $(OUT)\authcon.obj \
+       $(OUT)\realms.obj \
        $(OUT)\ktc_errors.obj \
        $(OUT)\ktc_nt.obj \
        $(OUT)\keys.obj \
index c39f588..02243fe 100644 (file)
@@ -152,3 +152,5 @@ EXPORTS
         afsconf_GetExtendedCellInfo                     @151
         afsconf_UpToDate                                @152
        afsconf_SetSecurityFlags                        @153
+       afsconf_SetLocalRealm                           @154
+       afsconf_IsLocalRealmMatch                       @155
index 7962468..2439c7a 100644 (file)
@@ -228,6 +228,7 @@ UAFSOBJ = \
        $(UOBJ)/authcon.o \
        $(UOBJ)/cellconfig.o \
        $(UOBJ)/keys.o \
+       $(UOBJ)/realms.o \
        $(UOBJ)/client.o \
        $(UOBJ)/acfg_errors.o \
        $(UOBJ)/kaaux.o \
@@ -376,6 +377,7 @@ PICUAFSOBJ = \
        $(PICOBJ)/authcon.o \
        $(PICOBJ)/cellconfig.o \
        $(PICOBJ)/keys.o \
+       $(PICOBJ)/realms.o \
        $(PICOBJ)/client.o \
        $(PICOBJ)/acfg_errors.o \
        $(PICOBJ)/kaaux.o \
@@ -525,6 +527,7 @@ AFSWEBOBJ = \
        $(WEBOBJ)/cellconfig.o \
        $(WEBOBJ)/client.o \
        $(WEBOBJ)/keys.o \
+       $(WEBOBJ)/realms.o \
        $(WEBOBJ)/acfg_errors.o \
        $(WEBOBJ)/kaaux.o \
        $(WEBOBJ)/kalocalcell.o \
@@ -667,6 +670,7 @@ AFSWEBOBJKRB = \
        $(WEBOBJ)/cellconfig.o \
        $(WEBOBJ)/client.o \
        $(WEBOBJ)/keys.o \
+       $(WEBOBJ)/realms.o \
        $(WEBOBJ)/acfg_errors.o \
        $(WEBOBJ)/kaaux.o \
        $(WEBOBJ)/kalocalcell.o \
@@ -812,6 +816,7 @@ JUAFSOBJ = \
        $(JUAFS)/authcon.o \
        $(JUAFS)/cellconfig.o \
        $(JUAFS)/keys.o \
+       $(JUAFS)/realms.o \
        $(JUAFS)/client.o \
        $(JUAFS)/acfg_errors.o \
        $(JUAFS)/kaaux.o \
@@ -1105,6 +1110,8 @@ $(UOBJ)/keys.o: $(TOP_SRCDIR)/auth/keys.c
        $(CRULE1)
 $(UOBJ)/casestrcpy.o: $(TOP_SRCDIR)/opr/casestrcpy.c
        $(CRULE1)
+$(UOBJ)/realms.o: $(TOP_SRCDIR)/auth/realms.c
+       $(CRULE1)
 $(UOBJ)/dirpath.o: $(TOP_SRCDIR)/util/dirpath.c
        $(CRULE1)
 $(UOBJ)/fileutil.o: $(TOP_SRCDIR)/util/fileutil.c
@@ -1408,6 +1415,8 @@ $(PICOBJ)/keys.o: $(TOP_SRCDIR)/auth/keys.c
        $(CRULEPIC)
 $(PICOBJ)/casestrcpy.o: $(TOP_SRCDIR)/opr/casestrcpy.c
        $(CRULEPIC)
+$(PICOBJ)/realms.o: $(TOP_SRCDIR)/auth/realms.c
+       $(CRULEPIC)
 $(PICOBJ)/dirpath.o: $(TOP_SRCDIR)/util/dirpath.c
        $(CRULEPIC)
 $(PICOBJ)/fileutil.o: $(TOP_SRCDIR)/util/fileutil.c
@@ -1720,6 +1729,8 @@ $(WEBOBJ)/keys.o: $(TOP_SRCDIR)/auth/keys.c
        $(CRULE2)
 $(WEBOBJ)/casestrcpy.o: $(TOP_SRCDIR)/opr/casestrcpy.c
        $(CRULE1)
+$(WEBOBJ)/realms.o: $(TOP_SRCDIR)/auth/realms.c
+       $(CRULE2)
 $(WEBOBJ)/dirpath.o: $(TOP_SRCDIR)/util/dirpath.c
        $(CRULE1)
 $(WEBOBJ)/fileutil.o: $(TOP_SRCDIR)/util/fileutil.c
@@ -2018,6 +2029,8 @@ $(JUAFS)/keys.o: $(TOP_SRCDIR)/auth/keys.c
        $(CRULE1)
 $(JUAFS)/casestrcpy.o: $(TOP_SRCDIR)/opr/casestrcpy.c
        $(CRULE1)
+$(JUAFS)/realms.o: $(TOP_SRCDIR)/auth/realms.c
+       $(CRULE1)
 $(JUAFS)/dirpath.o: $(TOP_SRCDIR)/util/dirpath.c
        $(CRULE1)
 $(JUAFS)/fileutil.o: $(TOP_SRCDIR)/util/fileutil.c
index e430b85..2212a79 100644 (file)
@@ -42,7 +42,8 @@ AUTHOBJS = \
        ktc_errors.o \
        acfg_errors.o \
        token.xdr.o \
-       token.o
+       token.o \
+       realms.o
 
 KAUTHOBJS = \
        kauth.xdr.o \
@@ -191,6 +192,9 @@ ktc.o: ${AUTH}/ktc.c
 keys.o: ${AUTH}/keys.c
        ${AFS_CCRULE} -I../auth ${AUTH}/keys.c
 
+realms.o: ${AUTH}/realms.c
+       ${AFS_CCRULE} -I../auth ${AUTH}/realms.c
+
 token.o: ${AUTH}/token.c
        ${AFS_CCRULE} -I../auth ${AUTH}/token.c
 
index 6a43206..dc96468 100644 (file)
@@ -87,3 +87,6 @@ EXPORTS
        rx_Finalize                                     @85
        pr_End                                          @86
        pioctl_utf8                                     @87
+
+       afsconf_SetLocalRealm                           @88
+       afsconf_IsLocalRealmMatch                       @89
index f9cb17e..48ade15 100644 (file)
@@ -3,6 +3,7 @@ util/exec-alt
 auth/keys
 auth/superuser
 auth/authcon
+auth/realms
 cmd/command
 opr/jhash
 opr/queues
index 3e2a46f..19d9319 100644 (file)
@@ -3,3 +3,4 @@
 /superuser-t
 /test.h
 /writekeyfile
+/realms-t
index d373687..d22c3d4 100644 (file)
@@ -4,7 +4,7 @@ abs_top_builddir=@abs_top_builddir@
 include @TOP_OBJDIR@/src/config/Makefile.config
 include @TOP_OBJDIR@/src/config/Makefile.pthread
 
-TESTS = authcon-t superuser-t keys-t
+TESTS = authcon-t superuser-t keys-t realms-t
 
 MODULE_CFLAGS=-I$(srcdir)/.. -I$(srcdir)/../common/
 
@@ -30,6 +30,9 @@ superuser-t: superuser-t.o ../common/config.o test.cs.o test.ss.o test.xdr.o
 keys-t: keys-t.o ../common/config.o
        $(AFS_LDRULE) keys-t.o ../common/config.o $(MODULE_LIBS)
 
+realms-t: realms-t.o ../common/config.o
+       $(AFS_LDRULE) realms-t.o ../common/config.o $(MODULE_LIBS)
+
 writekeyfile: writekeyfile.o
        $(AFS_LDRULE) writekeyfile.o $(MODULE_LIBS)
 
diff --git a/tests/auth/realms-t.c b/tests/auth/realms-t.c
new file mode 100644 (file)
index 0000000..6ab9b46
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2010, Sine Nomine Associates and others.
+ * All Rights Reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+#include <roken.h>
+
+#include <rx/rx.h>
+#include <rx/rxkad.h>
+#include <afs/cellconfig.h>
+
+#include <tap/basic.h>
+#include "common.h"
+
+extern int _afsconf_Touch(struct afsconf_dir *adir);
+
+int verbose = 0;
+
+#define LOCAL 1
+#define FOREIGN 0
+struct testcase {
+    char *name;
+    char *inst;
+    char *cell;
+    int expectedLocal;
+};
+
+/*
+ * test set 0
+ * cell: example.org
+ */
+struct testcase testset0[] = {
+    {"jdoe", NULL, NULL, LOCAL},
+    {"jdoe", NULL, "example.org", LOCAL},
+    {"jdoe", NULL, "EXAMPLE.ORG", LOCAL},
+    {"jdoe", NULL, NULL, LOCAL},
+    {"jdoe", NULL, "my.realm.org", FOREIGN},
+    {"jdoe", NULL, "MY.REALM.ORG", FOREIGN},
+    {"jdoe", NULL, "MY.OTHER.REALM.ORG", FOREIGN},
+    {"jdoe", "admin", NULL, LOCAL},
+    {"jdoe", "admin", "my.realm.org", FOREIGN},
+    {"jdoe", "admin", "MY.REALM.ORG", FOREIGN},
+    {"jdoe", "admin", "your.realm.org", FOREIGN},
+    {"jdoe", "admin", "YOUR.REALM.ORG", FOREIGN},
+    {"admin", NULL, "example.org", LOCAL},
+    {"admin", NULL, "my.realm.org", FOREIGN},
+    {"admin", NULL, "MY.REALM.ORG", FOREIGN},
+    {"admin", NULL, "MY.OTHER.REALM.ORG", FOREIGN},
+    {NULL},
+};
+
+/*
+ * test set 1
+ * cell: example.org
+ * local realms: MY.REALM.ORG, MY.OTHER.REALM.ORG
+ */
+struct testcase testset1[] = {
+    {"jdoe", NULL, NULL, LOCAL},
+    {"jdoe", NULL, "example.org", LOCAL},
+    {"jdoe", NULL, "EXAMPLE.ORG", LOCAL},
+    {"jdoe", NULL, NULL, LOCAL},
+    {"jdoe", NULL, "my.realm.org", LOCAL},
+    {"jdoe", NULL, "MY.REALM.ORG", LOCAL},
+    {"jdoe", NULL, "MY.OTHER.REALM.ORG", LOCAL},
+    {"jdoe", NULL, "SOME.REALM.ORG", FOREIGN},
+    {"jdoe", "admin", NULL, LOCAL},
+    {"jdoe", "admin", "my.realm.org", LOCAL},
+    {"jdoe", "admin", "MY.REALM.ORG", LOCAL},
+    {"jdoe", "admin", "MY.OTHER.REALM.ORG", LOCAL},
+    {"jdoe", "admin", "your.realm.org", FOREIGN},
+    {"jdoe", "admin", "YOUR.REALM.ORG", FOREIGN},
+    {"admin", NULL, "example.org", LOCAL},
+    {"admin", NULL, "my.realm.org", LOCAL},
+    {"admin", NULL, "MY.REALM.ORG", LOCAL},
+    {"admin", NULL, "MY.OTHER.REALM.ORG", LOCAL},
+    {NULL},
+};
+
+/*
+ * test set 2
+ * cell: example.org
+ * local realms: MY.REALM.ORG, MY.OTHER.REALM.ORG
+ * exclude: admin@MY.REALM.ORG
+ */
+struct testcase testset2[] = {
+    {"jdoe", NULL, NULL, LOCAL},
+    {"jdoe", NULL, "example.org", LOCAL},
+    {"jdoe", NULL, "EXAMPLE.ORG", LOCAL},
+    {"jdoe", NULL, NULL, LOCAL},
+    {"jdoe", NULL, "my.realm.org", LOCAL},
+    {"jdoe", NULL, "MY.REALM.ORG", LOCAL},
+    {"jdoe", NULL, "MY.OTHER.REALM.ORG", LOCAL},
+    {"jdoe", "admin", NULL, LOCAL},
+    {"jdoe", "admin", "my.realm.org", LOCAL},
+    {"jdoe", "admin", "MY.REALM.ORG", LOCAL},
+    {"jdoe", "admin", "MY.OTHER.REALM.ORG", LOCAL},
+    {"jdoe", "admin", "your.realm.org", FOREIGN},
+    {"jdoe", "admin", "YOUR.REALM.ORG", FOREIGN},
+    {"admin", NULL, "example.org", LOCAL},
+    {"admin", NULL, "my.realm.org", LOCAL},
+    {"admin", NULL, "MY.REALM.ORG", FOREIGN},
+    {"admin", NULL, "MY.OTHER.REALM.ORG", LOCAL},
+    {NULL},
+};
+
+struct testcase* testset[] = { testset0, testset1, testset2 };
+
+char *
+make_string(int len)
+{
+    char *s = malloc(len + 1);
+    if (!s) {
+       fprintf(stderr, "Failed to allocate string buffer.\n");
+       exit(1);
+    }
+    memset(s, 'x', len);
+    s[len] = '\0';
+    return s;
+}
+
+void
+run_tests(struct afsconf_dir *dir, int setnum, char *setname)
+{
+    struct testcase *t;
+    int code;
+
+    for (t = testset[setnum]; t->name; t++) {
+        afs_int32 local = -1;
+
+       code = afsconf_IsLocalRealmMatch(dir, &local, t->name, t->inst, t->cell);
+       ok(code == 0, "%s: test case %s/%s/%s",
+           setname,
+          t->name ? t->name : "(null)",
+           t->inst ? t->inst : "(null)",
+          t->cell ? t->cell : "(null)");
+        if (code==0) {
+          ok(local == t->expectedLocal, "... expected %d, got %d", t->expectedLocal, local);
+        }
+    }
+}
+
+void
+run_edge_tests(struct afsconf_dir *dir)
+{
+    afs_int32 local = -1;
+    int code = 0;
+    char *name = "jdoe";
+    char *inst = "";
+    char *cell = "";
+
+    /* null argument checks */
+    code = afsconf_IsLocalRealmMatch(dir, &local, NULL, inst, cell);
+    ok(code == EINVAL, "null name: code=%d", code);
+
+    code = afsconf_IsLocalRealmMatch(dir, &local, name, NULL, cell);
+    ok(code == 0, "null inst: code=%d", code);
+
+    code = afsconf_IsLocalRealmMatch(dir, &local, name, inst, NULL);
+    ok(code == 0, "null cell: code=%d", code);
+
+    /* large ticket test */
+    name = make_string(64);
+    inst = make_string(64);
+    cell = make_string(64);
+    code = afsconf_IsLocalRealmMatch(dir, &local, name, inst, cell);
+    ok(code == 0, "name size 64: code=%d", code);
+    free(name);
+    free(inst);
+    free(cell);
+
+    name = make_string(255);
+    inst = NULL;
+    cell = "my.realm.org";
+    code = afsconf_IsLocalRealmMatch(dir, &local, name, inst, cell);
+    ok(code == 0, "name size 255: code=%d", code);
+    free(name);
+}
+
+void
+write_krb_conf(char *dirname, char *data)
+{
+    char *filename = NULL;
+    FILE *fp;
+
+    asnprintf(&filename, 256, "%s/%s", dirname, "krb.conf");
+    if ((fp = fopen(filename, "w")) == NULL) {
+       fprintf(stderr, "Unable to create test file %s\n", filename);
+       exit(1);
+    }
+    if (verbose) {
+       diag("writing to %s: %s", filename, data);
+    }
+    fprintf(fp, "%s\n", data);
+    fclose(fp);
+    free(filename);
+}
+
+void
+write_krb_excl(char *dirname)
+{
+    char *filename = NULL;
+    FILE *fp;
+    char *data;
+
+    asnprintf(&filename, 256, "%s/%s", dirname, "krb.excl");
+    if ((fp = fopen(filename, "w")) == NULL) {
+       fprintf(stderr, "Unable to create test file %s\n", filename);
+       exit(1);
+    }
+    data = "admin@MY.REALM.ORG";
+    if (verbose) {
+       diag("writing to %s: %s", filename, data);
+    }
+    fprintf(fp, "%s\n", data);
+    data = "admin@EXAMPLE.ORG";
+    if (verbose) {
+       diag("writing to %s: %s", filename, data);
+    }
+    fprintf(fp, "%s\n", data);
+    fclose(fp);
+    free(filename);
+}
+
+void
+update_csdb(char *dirname)
+{
+    char *filename = NULL;
+    FILE *fp;
+
+    asnprintf(&filename, 256, "%s/%s", dirname, "CellServDB");
+    if ((fp = fopen(filename, "a")) == NULL) {
+       fprintf(stderr, "Unable to create test file %s\n", filename);
+       exit(1);
+    }
+    fprintf(fp, "10.0.0.1 #bogus.example.org\n");
+    fclose(fp);
+    free(filename);
+}
+
+void
+test_edges(void)
+{
+    struct afsconf_dir *dir;
+    char *dirname;
+
+    /* run edge case tests */
+    dirname = afstest_BuildTestConfig();
+    dir = afsconf_Open(dirname);
+    if (dir == NULL) {
+       fprintf(stderr, "Unable to configure directory.\n");
+       exit(1);
+    }
+    run_edge_tests(dir);
+    afstest_UnlinkTestConfig(dirname);
+}
+
+void
+test_no_config_files(void)
+{
+    struct afsconf_dir *dir;
+    char *dirname;
+
+    /* run tests without config files */
+    dirname = afstest_BuildTestConfig();
+    dir = afsconf_Open(dirname);
+    if (dir == NULL) {
+       fprintf(stderr, "Unable to configure directory.\n");
+       exit(1);
+    }
+    run_tests(dir, 0, "no config");
+    afstest_UnlinkTestConfig(dirname);
+}
+
+void
+test_with_config_files(void)
+{
+    struct afsconf_dir *dir;
+    char *dirname;
+
+    /* run tests with config files */
+    dirname = afstest_BuildTestConfig();
+    write_krb_conf(dirname, "MY.REALM.ORG MY.OTHER.REALM.ORG");
+    write_krb_excl(dirname);
+    dir = afsconf_Open(dirname);
+    if (dir == NULL) {
+       fprintf(stderr, "Unable to configure directory.\n");
+       exit(1);
+    }
+    run_tests(dir, 2, "config");
+    afstest_UnlinkTestConfig(dirname);
+}
+
+void
+test_set_local_realms(void)
+{
+    struct afsconf_dir *dir;
+    char *dirname;
+
+    /* Simulate command line -realm option; overrides config file, if one.
+     * Multiple realms can be added. */
+    ok(afsconf_SetLocalRealm("MY.REALM.ORG") == 0, "set local realm MY.REALM.ORG");
+    ok(afsconf_SetLocalRealm("MY.OTHER.REALM.ORG") == 0, "set local realm MY.OTHER.REALM.ORG");
+
+    /* run tests without config files */
+    dirname = afstest_BuildTestConfig();
+    dir = afsconf_Open(dirname);
+    if (dir == NULL) {
+       fprintf(stderr, "Unable to configure directory.\n");
+       exit(1);
+    }
+    write_krb_conf(dirname, "SOME.REALM.ORG");
+    run_tests(dir, 1, "set realm test");
+    afstest_UnlinkTestConfig(dirname);
+}
+
+void
+test_update_config_files(void)
+{
+    int code;
+    struct afsconf_dir *dir;
+    char *dirname;
+    afs_int32 local = -1;
+
+    dirname = afstest_BuildTestConfig();
+    write_krb_conf(dirname, "SOME.REALM.ORG");
+    dir = afsconf_Open(dirname);
+    if (dir == NULL) {
+       fprintf(stderr, "Unable to configure directory.\n");
+       exit(1);
+    }
+
+    code = afsconf_IsLocalRealmMatch(dir, &local, "jdoe", NULL, "SOME.REALM.ORG");
+    ok(code == 0 && local == 1, "before update: jdoe@SOME.REALM.ORG");
+
+    code = afsconf_IsLocalRealmMatch(dir, &local, "jdoe", NULL, "MY.REALM.ORG");
+    ok(code == 0 && local == 0, "before update: admin@MY.REALM.ORG");
+
+    write_krb_conf(dirname, "MY.REALM.ORG MY.OTHER.REALM.ORG");
+    write_krb_excl(dirname);
+    update_csdb(dirname);
+    _afsconf_Touch(dir);       /* forces reopen */
+
+    code = afsconf_IsLocalRealmMatch(dir, &local, "jdoe", NULL, "MY.REALM.ORG");
+    ok(code == 0 && local == 1, "after update: jdoe@MY.REALM.ORG");
+
+    code = afsconf_IsLocalRealmMatch(dir, &local, "admin", NULL, "MY.REALM.ORG");
+    ok(code == 0 && local == 0, "after update: admin@MY.REALM.ORG");
+
+    afstest_UnlinkTestConfig(dirname);
+}
+
+int
+main(int argc, char **argv)
+{
+    plan(113);
+
+    test_edges();
+    test_no_config_files();
+    test_with_config_files();
+    test_update_config_files();
+    test_set_local_realms(); /* must be the last test */
+
+    return 0;
+}
index 8f143b4..aced66b 100644 (file)
@@ -124,6 +124,8 @@ afstest_UnlinkTestConfig(char *dir)
     unlinkConfigFile(dir, "CellServDB");
     unlinkConfigFile(dir, "ThisCell");
     unlinkConfigFile(dir, "UserList");
+    unlinkConfigFile(dir, "krb.conf");
+    unlinkConfigFile(dir, "krb.excl");
     rmdir(dir);
 }