2 * Copyright 2012, Sine Nomine Associates and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
13 #include <opr/queue.h>
15 #include <afs/pthread_glock.h>
16 #include <afs/afsutil.h>
19 #include "cellconfig.h"
22 #define MAXLINESIZE 2047
24 /* Can be set during initialization, overriding the krb.conf file. */
25 static struct opr_queue *lrealms = NULL;
28 * Realm and exclusion list entries.
30 struct afsconf_realm_entry {
31 struct opr_queue link; /**< linked list header */
32 char *value; /**< local realm or principal */
36 * Realm and exclusion lists.
38 struct afsconf_realms {
39 struct opr_queue list; /**< list of afsconf_realm_entry */
40 int time_read; /**< time when read from file */
41 void *tree; /**< for lookup */
42 int (*compare) (const void *, const void *); /**< compare entries */
46 compare_realms(const void *a, const void *b)
48 return strcasecmp((char *)a, (char *)b);
52 compare_principals(const void *a, const void *b)
54 return strcmp((char *)a, (char *)b);
58 * Format the k4-style principal string.
60 * @param[out] pvname output buffer, must be freed by caller
61 * @param[in] name user name, required
62 * @param[in] inst user instance, optional
63 * @param[in] cell cell name, optional
67 * @retval EINVAL invalid arguments
68 * @retval E2BIG insufficient output buffer space
73 create_name(char **pvname, const char *name,
74 const char *inst, const char *cell)
78 if (!name || !*name) {
83 code = asprintf(pvname, "%s.%s@%s", name, inst, cell);
85 code = asprintf(pvname, "%s@%s", name, cell);
89 code = asprintf(pvname, "%s.%s", name, inst);
91 code = asprintf(pvname, "%s", name);
94 return (code < 0 ? ENOMEM : 0);
98 * Parse whitespace delimited values
100 * @param[in] buffer input string
101 * @param[out] result output string
102 * @param[in] size size of result buffer
104 * @return pointer to the next value
109 parse_str(char *buffer, char *result, int size)
116 while (*buffer && isspace(*buffer))
118 while (*buffer && !isspace(*buffer)) {
120 *result++ = *buffer++;
133 * Add a new list element.
135 * Add the name element if not already present in the list.
136 * The names are case insensitive.
138 * @param[inout] list list of name elements
139 * @param[in] name name to add
143 * @retval ENOMEM unable to allocate new entry
148 add_entry(struct opr_queue *list, const char *name)
150 struct afsconf_realm_entry *entry;
152 entry = malloc(sizeof(struct afsconf_realm_entry));
156 entry->value = strdup(name);
157 opr_queue_Append(list, (struct opr_queue *)entry);
162 * Free all entries in a list.
164 * @param[in] list list of entries
171 free_realm_entries(struct opr_queue *list)
173 struct afsconf_realm_entry *entry;
175 while (!opr_queue_IsEmpty(list)) {
176 entry = opr_queue_First(list, struct afsconf_realm_entry, link);
177 opr_queue_Remove(&entry->link);
187 * Placeholder for tdestroy.
190 free_tree_node(void *nodep)
197 * Delete all the entries from the search tree.
199 * @param[in] list list of entries
206 destroy_tree(struct afsconf_realms *entries)
210 tdestroy(entries->tree, free_tree_node);
212 struct opr_queue *cursor;
213 struct afsconf_realm_entry *entry;
215 for (opr_queue_Scan(&entries->list, cursor)) {
216 entry = opr_queue_Entry(cursor, struct afsconf_realm_entry, link);
217 tdelete(entry->value, &entries->tree, entries->compare);
220 entries->tree = NULL;
225 * Build a search tree from the list of entries.
227 * @param[in] list list of entries
234 build_tree(struct afsconf_realms *entries)
236 struct opr_queue *cursor;
237 struct afsconf_realm_entry *entry;
239 for (opr_queue_Scan(&entries->list, cursor)) {
240 entry = opr_queue_Entry(cursor, struct afsconf_realm_entry, link);
241 tsearch(entry->value, &entries->tree, entries->compare);
246 * Read the list of local realms from a config file.
248 * @param[inout] dir config dir object
255 read_local_realms(struct afsconf_realms *entries, const char *path)
258 char realm[AFS_REALM_SZ];
259 struct opr_queue temp;
260 char *filename = NULL;
262 FILE *cnffile = NULL;
263 char *linebuf = NULL;
266 opr_queue_Init(&temp);
267 code = asprintf(&filename, "%s/%s", path, AFSDIR_KCONF_FILE);
272 code = stat(filename, &tstat);
274 code = (errno == ENOENT ? 0 : errno); /* this file is optional */
277 if (tstat.st_mtime == entries->time_read) {
281 entries->time_read = tstat.st_mtime;
282 if ((cnffile = fopen(filename, "r")) == NULL) {
283 code = (errno == ENOENT ? 0 : errno); /* this file is optional */
286 linebuf = malloc(sizeof(char) * (MAXLINESIZE + 1));
291 if (fgets(linebuf, MAXLINESIZE, cnffile) == NULL) {
295 linebuf[MAXLINESIZE] = '\0';
296 for (p = linebuf; *p;) {
297 p = parse_str(p, realm, AFS_REALM_SZ);
299 code = add_entry(&temp, realm);
305 destroy_tree(entries);
306 opr_queue_Swap(&temp, &entries->list);
310 free_realm_entries(&temp);
324 * Read the list of local exclusions from a config file.
326 * @param[inout] dir config dir object
333 read_local_exclusions(struct afsconf_realms *entries, const char *path)
336 char *linebuf = NULL;
337 char *filename = NULL;
339 FILE *cnffile = NULL;
340 struct opr_queue temp;
343 opr_queue_Init(&temp);
344 code = asprintf(&filename, "%s/%s", path, AFSDIR_KRB_EXCL_FILE);
349 code = stat(filename, &tstat);
351 code = (errno == ENOENT ? 0 : errno); /* this file is optional */
354 if (tstat.st_mtime == entries->time_read) {
358 if ((cnffile = fopen(filename, "r")) == NULL) {
359 code = (errno != ENOENT ? errno : 0); /* this file is optional */
362 linebuf = malloc(sizeof(char) * (MAXLINESIZE + 1));
368 if (fgets(linebuf, MAXLINESIZE, cnffile) == NULL) {
371 linebuf[MAXLINESIZE] = '\0';
372 parse_str(linebuf, name, sizeof(name));
374 code = add_entry(&temp, name);
380 destroy_tree(entries);
381 opr_queue_Swap(&temp, &entries->list);
384 free_realm_entries(&temp);
399 * Free the local realms and exclusions lists.
401 * @param[in] dir afsconf dir object
408 _afsconf_FreeRealms(struct afsconf_dir *dir)
411 if (dir->local_realms) {
412 destroy_tree(dir->local_realms);
413 free_realm_entries(&dir->local_realms->list);
414 dir->local_realms = NULL;
416 if (dir->exclusions) {
417 destroy_tree(dir->exclusions);
418 free_realm_entries(&dir->exclusions->list);
419 dir->exclusions = NULL;
425 * Load the local realms and exclusions lists.
427 * @param[in] dir afsconf dir object
434 _afsconf_LoadRealms(struct afsconf_dir *dir)
437 struct afsconf_realms *local_realms = NULL;
438 struct afsconf_realms *exclusions = NULL;
440 /* Create and load the list of local realms. */
441 local_realms = calloc(1, sizeof(struct afsconf_realms));
446 opr_queue_Init(&local_realms->list);
447 local_realms->compare = compare_realms;
450 code = read_local_realms(local_realms, dir->name);
455 struct opr_queue *cursor;
456 struct afsconf_realm_entry *entry;
457 for (opr_queue_Scan(lrealms, cursor)) {
458 entry = opr_queue_Entry(cursor, struct afsconf_realm_entry, link);
459 code = add_entry(&local_realms->list, entry->value);
464 build_tree(local_realms);
467 /* Create and load the list of excluded principals. */
468 exclusions = calloc(1, sizeof(struct afsconf_realms));
473 opr_queue_Init(&exclusions->list);
474 exclusions->compare = compare_principals;
475 code = read_local_exclusions(exclusions, dir->name);
480 dir->local_realms = local_realms;
481 dir->exclusions = exclusions;
486 destroy_tree(local_realms);
487 free_realm_entries(&local_realms->list);
490 destroy_tree(dir->exclusions);
491 free_realm_entries(&exclusions->list);
497 * Set a local realm, instead of retrieving the local realms from the
498 * configuration file krb.conf (if it exists). Maybe called multiple
499 * times during application initialization to set one or more local
504 * @retval ENOMEM unable to allocate new entry
507 afsconf_SetLocalRealm(const char *realm)
513 lrealms = malloc(sizeof(struct opr_queue));
518 opr_queue_Init(lrealms);
520 code = add_entry(lrealms, realm);
527 * Determine if a principal is local to this cell.
529 * @param[in] dir afsconf dir object
530 * @param[out] plocal set to 1 if user is local, 0 if foreign
531 * @param[in] name user name
532 * @param[in] inst user instance
533 * @param[in] cell user cell name
537 * @retval ENOMEM unable to allocate memory
538 * @retval EINVAL invalid argument
541 afsconf_IsLocalRealmMatch(struct afsconf_dir *dir, afs_int32 * plocal,
542 const char *name, const char *inst,
546 char *localcell = NULL;
548 struct afsconf_realms *local_realms = NULL;
549 struct afsconf_realms *exclusions = NULL;
554 if (!cell || !*cell) {
560 code = _afsconf_GetLocalCell(dir, &localcell, 1);
564 /* Does the cell match the local cell name? */
565 if (strcasecmp(localcell, cell) == 0) {
566 *plocal = 1; /* cell matches the local cell name. */
570 /* Does the cell match one of the local_realms? */
571 local_realms = dir->local_realms;
572 if (!tfind(cell, &local_realms->tree, local_realms->compare)) {
573 *plocal = 0; /* Cell name not found in local realms. */
577 /* Local realm matches, make sure the principal is not in the
578 * exclusion list, if one. */
579 exclusions = dir->exclusions;
580 if (!exclusions->tree) {
581 *plocal = 1; /* Matches one of the local realms; no exclusions */
585 /* Create a full principal name for the exclusion check. */
586 code = create_name(&tvname, name, inst, cell);
588 if (tfind(tvname, &exclusions->tree, exclusions->compare)) {
589 *plocal = 0; /* name found in the exclusion list */
591 *plocal = 1; /* not in the exclusion list */