From f8b759b63ff938e2829fefd71059202c44e7ca13 Mon Sep 17 00:00:00 2001 From: Michael Meffie Date: Tue, 28 Feb 2012 08:50:33 -0500 Subject: [PATCH] auth: local realms configuration 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 Tested-by: BuildBot --- acinclude.m4 | 1 + src/auth/Makefile.in | 5 +- src/auth/NTMakefile | 2 + src/auth/cellconfig.c | 5 + src/auth/cellconfig.p.h | 8 + src/auth/internal.h | 2 + src/auth/realms.c | 603 +++++++++++++++++++++++++++++++++++++ src/libafsauthent/Makefile.in | 6 +- src/libafsauthent/NTMakefile | 1 + src/libafsauthent/afsauthent.def | 2 + src/libuafs/Makefile.common.in | 13 + src/shlibafsauthent/Makefile.in | 6 +- src/shlibafsauthent/afsauthent.def | 3 + tests/TESTS | 1 + tests/auth/.gitignore | 1 + tests/auth/Makefile.in | 5 +- tests/auth/realms-t.c | 371 +++++++++++++++++++++++ tests/common/config.c | 2 + 18 files changed, 1032 insertions(+), 5 deletions(-) create mode 100644 src/auth/realms.c create mode 100644 tests/auth/realms-t.c diff --git a/acinclude.m4 b/acinclude.m4 index d2ce60f..602e934 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1432,6 +1432,7 @@ AC_CHECK_FUNCS([ \ strerror \ sysconf \ sysctl \ + tdestroy \ timegm \ ]) diff --git a/src/auth/Makefile.in b/src/auth/Makefile.in index f4fb044..8f2cf1c 100644 --- a/src/auth/Makefile.in +++ b/src/auth/Makefile.in @@ -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 diff --git a/src/auth/NTMakefile b/src/auth/NTMakefile index 0cd3015..feb3040 100644 --- a/src/auth/NTMakefile +++ b/src/auth/NTMakefile @@ -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 \ diff --git a/src/auth/cellconfig.c b/src/auth/cellconfig.c index e4490e8..a5b2d3d 100644 --- a/src/auth/cellconfig.c +++ b/src/auth/cellconfig.c @@ -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)); diff --git a/src/auth/cellconfig.p.h b/src/auth/cellconfig.p.h index 94b72b9..5751968 100644 --- a/src/auth/cellconfig.p.h +++ b/src/auth/cellconfig.p.h @@ -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 diff --git a/src/auth/internal.h b/src/auth/internal.h index 9a351fe..957fa34 100644 --- a/src/auth/internal.h +++ b/src/auth/internal.h @@ -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 index 0000000..8236153 --- /dev/null +++ b/src/auth/realms.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/src/libafsauthent/Makefile.in b/src/libafsauthent/Makefile.in index dfe0c67..79af716 100644 --- a/src/libafsauthent/Makefile.in +++ b/src/libafsauthent/Makefile.in @@ -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 diff --git a/src/libafsauthent/NTMakefile b/src/libafsauthent/NTMakefile index 977f8ed..11bd6d9 100644 --- a/src/libafsauthent/NTMakefile +++ b/src/libafsauthent/NTMakefile @@ -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 \ diff --git a/src/libafsauthent/afsauthent.def b/src/libafsauthent/afsauthent.def index c39f588..02243fe 100644 --- a/src/libafsauthent/afsauthent.def +++ b/src/libafsauthent/afsauthent.def @@ -152,3 +152,5 @@ EXPORTS afsconf_GetExtendedCellInfo @151 afsconf_UpToDate @152 afsconf_SetSecurityFlags @153 + afsconf_SetLocalRealm @154 + afsconf_IsLocalRealmMatch @155 diff --git a/src/libuafs/Makefile.common.in b/src/libuafs/Makefile.common.in index 7962468..2439c7a 100644 --- a/src/libuafs/Makefile.common.in +++ b/src/libuafs/Makefile.common.in @@ -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 diff --git a/src/shlibafsauthent/Makefile.in b/src/shlibafsauthent/Makefile.in index e430b85..2212a79 100644 --- a/src/shlibafsauthent/Makefile.in +++ b/src/shlibafsauthent/Makefile.in @@ -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 diff --git a/src/shlibafsauthent/afsauthent.def b/src/shlibafsauthent/afsauthent.def index 6a43206..dc96468 100644 --- a/src/shlibafsauthent/afsauthent.def +++ b/src/shlibafsauthent/afsauthent.def @@ -87,3 +87,6 @@ EXPORTS rx_Finalize @85 pr_End @86 pioctl_utf8 @87 + + afsconf_SetLocalRealm @88 + afsconf_IsLocalRealmMatch @89 diff --git a/tests/TESTS b/tests/TESTS index f9cb17e..48ade15 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -3,6 +3,7 @@ util/exec-alt auth/keys auth/superuser auth/authcon +auth/realms cmd/command opr/jhash opr/queues diff --git a/tests/auth/.gitignore b/tests/auth/.gitignore index 3e2a46f..19d9319 100644 --- a/tests/auth/.gitignore +++ b/tests/auth/.gitignore @@ -3,3 +3,4 @@ /superuser-t /test.h /writekeyfile +/realms-t diff --git a/tests/auth/Makefile.in b/tests/auth/Makefile.in index d373687..d22c3d4 100644 --- a/tests/auth/Makefile.in +++ b/tests/auth/Makefile.in @@ -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 index 0000000..6ab9b46 --- /dev/null +++ b/tests/auth/realms-t.c @@ -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 +#include + +#include + +#include +#include +#include + +#include +#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; +} diff --git a/tests/common/config.c b/tests/common/config.c index 8f143b4..aced66b 100644 --- a/tests/common/config.c +++ b/tests/common/config.c @@ -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); } -- 1.9.4