# HG changeset patch # User Sebastien Decugis # Date 1264414049 -32400 # Node ID 645ff1487c23015f34c733d8a587685bd1fd03f7 # Parent 3577f20319c16bd9384c2e42968a800c8178584f Draft for ACL white-list extension diff -r 3577f20319c1 -r 645ff1487c23 doc/acl_wl.conf.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/acl_wl.conf.sample Mon Jan 25 19:07:29 2010 +0900 @@ -0,0 +1,18 @@ +# Configuration file for the peer whitelist extension. +# +# This extension is meant to allow connection from remote peers, without actively +# maintaining this connection ourselves (as it would be the case by declaring the +# peer in a ConnectPeer directive). +# The format of this file is very simple. It contains a list of peer names +# separated by spaces or newlines. +# +# The peer name must be a fqdn. We allow also a special "*" character as the +# first label of the fqdn, to allow all fqdn with the same domain name. +# Example: *.example.net will allow host1.example.net and host2.example.net +# +# At the beginning of a line, the following flags are allowed (case sensitive) -- either or both can appear: +# ALLOW_OLD_TLS : we accept unprotected CER/CEA exchange with Inband-Security-Id = TLS +# ALLOW_IPSEC : we accept implicitly protected connection with with peer (Inband-Security-Id = IPSec) +# It is specified for example as: +# ALLOW_IPSEC vpn.example.net vpn2.example.net *.vpn.example.net + diff -r 3577f20319c1 -r 645ff1487c23 extensions/CMakeLists.txt --- a/extensions/CMakeLists.txt Wed Jan 20 18:52:07 2010 +0900 +++ b/extensions/CMakeLists.txt Mon Jan 25 19:07:29 2010 +0900 @@ -49,10 +49,10 @@ #### # Peers security extensions -# OPTION(BUILD_PS_DEFAULT "Build ps_default? (white-list of peers)" ON) -# IF (BUILD_PS_DEFAULT) -# # -- enable once API is updated: SUBDIRS(ps_default) -# ENDIF (BUILD_PS_DEFAULT) +OPTION(BUILD_ACL_WL "Build acl_wl? (White-list of remote connecting peers)" ON) + IF (BUILD_ACL_WL) + SUBDIRS(acl_wl) + ENDIF (BUILD_ACL_WL) #### diff -r 3577f20319c1 -r 645ff1487c23 extensions/acl_wl/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/acl_wl/CMakeLists.txt Mon Jan 25 19:07:29 2010 +0900 @@ -0,0 +1,20 @@ +# The dict_nasreq extension +PROJECT("Access Control / White List" C) + +# Parser files +BISON_FILE(aw_conf.y) +FLEX_FILE(aw_conf.l) +SET_SOURCE_FILES_PROPERTIES(lex.aw_conf.c aw_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}") + +# List of source files +SET( APP_TEST_SRC + acl_wl.h + acl_wl.c + aw_tree.c + lex.aw_conf.c + aw_conf.tab.c + aw_conf.tab.h +) + +# Compile as a module +FD_ADD_EXTENSION(acl_wl ${APP_TEST_SRC}) diff -r 3577f20319c1 -r 645ff1487c23 extensions/acl_wl/acl_wl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/acl_wl/acl_wl.c Mon Jan 25 19:07:29 2010 +0900 @@ -0,0 +1,104 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* + * Whitelist extension for freeDiameter. + */ + +#include "acl_wl.h" + +/* The validator function */ +static int aw_validate(struct peer_info * info, int * auth, int (**cb2)(struct peer_info *)) +{ + int res; + + TRACE_ENTRY("%p %p %p", info, auth, cb2); + + CHECK_PARAMS(info && auth && cb2); + + /* We don't use the second callback */ + *cb2 = NULL; + + /* Default to unknown result */ + *auth = 0; + + /* Now search the peer in our tree */ + CHECK_FCT( aw_tree_lookup(info->pi_diamid, &res) ); + if (res < 0) { + /* The peer is not whitelisted */ + return 0; + } + + /* We found the peer in the tree, now check the status */ + + /* First, if TLS is already in place, just accept */ + if (info->runtime.pir_cert_list) { + *auth = 1; + return 0; + } + + /* Now, if we did not specify any flag, reject */ + + +} + +/* entry point */ +static int aw_entry(char * conffile) +{ + TRACE_ENTRY("%p", conffile); + + CHECK_PARAMS(conffile); + + /* Parse configuration file */ + CHECK_FCT( aw_conf_handle(conffile) ); + + TRACE_DEBUG(INFO, "Extension ACL_wl initialized with configuration: '%s'", conffile); + aw_tree_dump(); + + /* Register the validator function */ + + return 0; +} + +/* Unload */ +void fd_ext_fini(void) +{ + /* Unregister the validator function */ + + /* Destroy the tree */ + +} + +EXTENSION_ENTRY("acl_wl", aw_entry); diff -r 3577f20319c1 -r 645ff1487c23 extensions/acl_wl/acl_wl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/acl_wl/acl_wl.h Mon Jan 25 19:07:29 2010 +0900 @@ -0,0 +1,60 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Header file for the acl_wl extension. + * + * This extension provides a simple mechanism to allow connections from remote peers + * without actively maintaining a connection to these peers. + * + * See the acl_wl.conf.sample file for the format of the configuration file. + */ + +#include + +/* Parse the configuration file */ +int aw_conf_handle(char * conffile); + +/* Add to the tree (name is \0 terminated) */ +int aw_tree_add(char * name, int flags); + +/* Search in the tree. On return, *result = -1: not found; >=0: found with PI_SEC_* flags */ +int aw_tree_lookup(char * name, int * result); + +/* Cleanup the tree */ +void aw_tree_destroy(void); + +/* For debug */ +void aw_tree_dump(void); + diff -r 3577f20319c1 -r 645ff1487c23 extensions/acl_wl/aw_conf.l --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/acl_wl/aw_conf.l Mon Jan 25 19:07:29 2010 +0900 @@ -0,0 +1,95 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Lex extension's configuration parser. + * + * The configuration file contains a default priority, and a list of peers with optional overwite priority. + * -- see the app_test.conf.sample file for more detail. + */ + +%{ +#include "acl_wl.h" +/* Include yacc tokens definitions */ +#include "aw_conf.tab.h" + +/* Update the column information */ +#define YY_USER_ACTION { \ + yylloc->first_column = yylloc->last_column + 1; \ + yylloc->last_column = yylloc->first_column + yyleng - 1; \ +} + +/* Avoid warning with newer flex */ +#define YY_NO_INPUT + +static int curflag = 0; + +%} + +%option bison-bridge bison-locations +%option noyywrap +%option nounput + +%% + + /* Update the line count, reset flag value */ +\n { + yylloc->first_line++; + yylloc->last_line++; + yylloc->last_column=0; + curflag = 0; + } + + /* Eat all spaces but not new lines */ +([[:space:]]{-}[\n])+ ; + /* Eat all comments */ +#.*$ ; + + /* Match the two allowed flags directly in LEX */ +ALLOW_IPSEC { + curflag |= PI_SEC_NONE; + } + +ALLOW_OLD_TLS { + curflag |= PI_SEC_TLS_OLD; + } + + /* Any other string is considered a fqdn or partial fqdn with a star. The star can only be the first label. */ +(\*|[[:alnum:]][[:alnum:]-]*)(\.[[:alnum:]][[:alnum:]-]*)+ { + /* We matched a valid label, let's directly save it into the tree. The function will issue the appropriate warnings. */ + CHECK_FCT_DO( aw_tree_add(yytext, curflag), return LEX_ERROR); + return FQDN; + } + +%% diff -r 3577f20319c1 -r 645ff1487c23 extensions/acl_wl/aw_conf.y --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/acl_wl/aw_conf.y Mon Jan 25 19:07:29 2010 +0900 @@ -0,0 +1,130 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Yacc extension's configuration parser. + * See doc/app_test.conf.sample for configuration file format + */ + +/* For development only : */ +%debug +%error-verbose + +/* The parser receives the configuration file filename as parameter */ +%parse-param {char * conffile} + +/* Keep track of location */ +%locations +%pure-parser + +%{ +#include "acl_wl.h" +#include "aw_conf.tab.h" /* bison is not smart enough to define the YYLTYPE before including this code, so... */ + +#include +#include + +/* Forward declaration */ +int yyparse(char * conffile); + +static int fqdn_added = 0; + +/* Parse the configuration file */ +int aw_conf_handle(char * conffile) +{ + extern FILE * aw_confin; + int ret; + + TRACE_ENTRY("%p", conffile); + + TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile); + + aw_confin = fopen(conffile, "r"); + if (aw_confin == NULL) { + ret = errno; + fd_log_debug("Unable to open extension configuration file %s for reading: %s\n", conffile, strerror(ret)); + TRACE_DEBUG (INFO, "Error occurred, message logged -- configuration file."); + return ret; + } + + ret = yyparse(conffile); + + fclose(aw_confin); + + if (ret != 0) { + TRACE_DEBUG (INFO, "Unable to parse the configuration file."); + return EINVAL; + } else { + TRACE_DEBUG(FULL, "Read %d FQDN entries successfully.", fqdn_added); + } + + return 0; +} + +/* The Lex parser prototype */ +int aw_conflex(YYSTYPE *lvalp, YYLTYPE *llocp); + +/* Function to report the errors */ +void yyerror (YYLTYPE *ploc, char * conffile, char const *s) +{ + TRACE_DEBUG(INFO, "Error in configuration parsing"); + + if (ploc->first_line != ploc->last_line) + fd_log_debug("%s:%d.%d-%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); + else if (ploc->first_column != ploc->last_column) + fd_log_debug("%s:%d.%d-%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); + else + fd_log_debug("%s:%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, s); +} + +%} + +/* In case of error in the lexical analysis */ +%token LEX_ERROR + +/* Key words */ +%token FQDN + + +/* -------------------------------------- */ +%% + + /* The grammar definition */ +conffile: /* empty grammar is OK */ + | conffile FQDN + { + fqdn_added++; + } + ; + diff -r 3577f20319c1 -r 645ff1487c23 extensions/acl_wl/aw_tree.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/acl_wl/aw_tree.c Mon Jan 25 19:07:29 2010 +0900 @@ -0,0 +1,412 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +#include "acl_wl.h" + +/* The configuration simply contains the allowed fqdn and/or domains (*.example.net) + * It is represented similarly to the DNS tree: + * (root)--___ + * / \ \ + * tld1 tld2 (tld3...) + * / | + * label1 label2 + * / \ + * lbl21 lbl22 + * / \ + * lbl211 * + * + * This tree would whitelist: + * - label1.tld1 + * - lbl211.lbl21.label2.tld2 + * - *.lbl22.label2.tld2 + * + * The functions to add and search the tree are in aw_tree.c. + * + */ + +/* Maximum depth of the tree. We set a static size to avoid dynamic allocations. We report an error if this is not sufficient. */ +#define AW_TREE_MAXDEPTH 10 + +/* An element of the tree */ +struct tree_item { + struct fd_list chain; /* Link to elements at the same level. Ordered alphabetically. */ + struct fd_list children; /* Sentinel for the subtree. */ + char * str; /* the \0 terminated label, or NULL if it is a generic container ("*") */ + int flags; /* PI_SEC_* flags */ + int leaf; /* true if this item can be a leaf of the tree */ +}; + +/* The root of the tree */ +static struct fd_list tree_root = FD_LIST_INITIALIZER(tree_root); + +/* Note: we don't need to lock, since we add only when parsing the conf, and then read only */ + + +/* The parsed name */ +struct split_name { + struct { + char * str; /* start of this label */ + size_t len; /* length of this label. It does not include the final "." or "\0" */ + } label[AW_TREE_MAXDEPTH]; + int last_lbl; /* idx of last label defined */ +}; + +/* The following function explodes a name into a split_name structure */ +static int parse_name(char * name, struct split_name * result) +{ + int i, l, prev_offset; + char * c; + + TRACE_ENTRY("%p %p", name, result); + + /* First, initialize the result array */ + memset(result, 0, sizeof(struct split_name)); + result->label[0].str = name; + l = 0; prev_offset = 0; + + for (i=0; name[i] != '\0'; i++) { + if (name[i]=='.') { + l++; + CHECK_PARAMS( l < AW_TREE_MAXDEPTH ); + + /* The previous label is complete, write its size */ + result->label[l - 1].len = i - prev_offset; + prev_offset = i + 1; + + /* Write the start of the new label */ + result->label[l].str = name + i + 1; + } + } + + /* Finally, write the size of the last label */ + result->label[l].len = i - prev_offset; + + result->last_lbl = l; + +#if 0 + fd_log_debug("Parsed name %s as:\n", name); + for (i=0; i<=l; i++) + fd_log_debug(" str[%d] len: %d, v:%.*s\n", i, result->label[i].len, result->label[i].len, result->label[i].str); +#endif /* 0 */ + return 0; +} + +/* Create a new tree_item structure */ +static struct tree_item * new_ti(char * str, size_t len, int flags, int leaf) +{ + struct tree_item * ti; + char * s = NULL; + + TRACE_ENTRY("%p %d %x", str, len, flags); + + if (str) { + CHECK_MALLOC_DO(s = malloc(len + 1), return NULL); + memcpy(s, str, len); + s[len] = '\0'; + } + + CHECK_MALLOC_DO( ti = malloc(sizeof(struct tree_item)), {free(s); return NULL; } ); + memset(ti, 0, sizeof(struct tree_item)); + + fd_list_init(&ti->chain, ti); + fd_list_init(&ti->children, ti); + ti->str = s; + ti->flags = flags; + ti->leaf = leaf; + + return ti; +} + +/* Recursively delete a subtree */ +static void delete_tree(struct fd_list * senti) +{ + while (!FD_IS_LIST_EMPTY(senti)) { + struct tree_item * ti = (struct tree_item *)(senti->next); + + /* Delete recursively its children first */ + delete_tree(&ti->children); + + /* Now, unlink from the sentinel list */ + fd_list_unlink(&ti->chain); + + /* destroy this tree item */ + free(ti->str); + free(ti); + } +} + +/* Top-level destroy function */ +void aw_tree_destroy(void) +{ + delete_tree(&tree_root); +} + +/* Display the content of a subtree */ +static void tree_dump(struct fd_list * sub, int indent) +{ + struct fd_list * li; + for (li = sub->next; li != sub; li = li->next) { + struct tree_item * ti = (struct tree_item *)li; + fd_log_debug("%*s%s", indent * 2, "", ti->str?:"*"); + if (ti->leaf) + fd_log_debug(" (flag:%x)", ti->flags); + fd_log_debug("\n"); + tree_dump(&ti->children, indent + 1); + } +} + +/* Top-level function */ +void aw_tree_dump(void) +{ + fd_log_debug("[acl_wl] tree dump:\n(root)\n"); + tree_dump(&tree_root, 1); + fd_log_debug("[acl_wl] end of dump\n"); +} + +/* Function to add a new entry in the tree */ +int aw_tree_add(char * name, int flags) +{ + struct split_name sn; + struct tree_item * ti; + struct fd_list * li, *senti; + int lbl, found; + + TRACE_ENTRY("%p %x", name, flags); + CHECK_PARAMS(name && *name); + + CHECK_FCT_DO( parse_name(name, &sn), + { + fd_log_debug("The name '%s' contains too many labels, try a generic (*) or recompile with bigger AW_TREE_MAXDEPTH value (cur: %d)\n", name, AW_TREE_MAXDEPTH); + return EINVAL; + } ); + + senti = &tree_root; + + for (lbl = sn.last_lbl; lbl > 0; lbl--) { + /* Check if the list is empty, we can directly create the new entry */ + if (FD_IS_LIST_EMPTY(senti)) { + CHECK_MALLOC( ti = new_ti(sn.label[lbl].str, sn.label[lbl].len, 0, 0 /* flags are only set in the terminals */) ); + /* Insert this label in the sentinel sublist */ + fd_list_insert_after(senti, &ti->chain); + /* Update the sentinel */ + senti = &ti->children; + /* loop to the next label */ + continue; + } + + /* Check if we have a '*' element already that overlapses */ + ti = (struct tree_item *)(senti->next); + if (ti->str == NULL) { + fd_log_debug("[acl_wl] Warning: entry '%s' is superseeded by a generic entry at level %d, ignoring.\n", name, lbl); + return 0; + } + + /* Search this label in the ordered list */ + found = 0; + for (li = senti->next; li != senti; li=li->next) { + int cmp, len; + ti = (struct tree_item *)li; + + cmp = strncasecmp(ti->str, sn.label[lbl].str, sn.label[lbl].len); + if (cmp > 0) + break; /* the new label must be inserted before li */ + if (cmp < 0) + continue; + + /* Check the lengths */ + len = strlen(ti->str); + if (len > sn.label[lbl].len) + break; /* the new label must be inserted before li */ + if (len < sn.label[lbl].len) + continue; + + /* We already had this label */ + found = 1; + senti = &ti->children; + break; + } + + if (found) + continue; + + /* Otherwise, we have to create a new ti, and add it before li */ + CHECK_MALLOC( ti = new_ti(sn.label[lbl].str, sn.label[lbl].len, 0, 0 /* flags are only set in the terminals */) ); + /* Insert this label in the sentinel sublist */ + fd_list_insert_before(li, &ti->chain); + /* Update the sentinel */ + senti = &ti->children; + } + + ti = NULL; + li = senti; + + /* At this point, senti points to the list where we are supposed to insert our last label. */ + if (sn.label[0].str[0] == '*') { + if (!FD_IS_LIST_EMPTY(senti)) { + fd_log_debug("[acl_wl] Warning: entry '%s' overwrites previous more detailed entries, these are deleted.\n", name); + delete_tree(senti); + } + + /* Create the new entry */ + CHECK_MALLOC( ti = new_ti(NULL, 0, flags, 1) ); + } else { + if (!FD_IS_LIST_EMPTY(senti)) { + /* Check we don't have a '*' entry already */ + ti = (struct tree_item *)(senti->next); + if (ti->str == NULL) { + fd_log_debug("[acl_wl] Warning: entry '%s' is superseeded by a generic entry at level 0, ignoring.\n", name); + return 0; + } + + /* Search the place for the new label */ + for (li = senti->next; li != senti; li=li->next) { + int cmp, len; + ti = (struct tree_item *)li; + + cmp = strncasecmp(ti->str, sn.label[0].str, sn.label[0].len); + if (cmp > 0) + break; /* the new label must be inserted before li */ + if (cmp < 0) + continue; + + /* Check the lengths */ + len = strlen(ti->str); + if (len > sn.label[0].len) + break; /* the new label must be inserted before li */ + if (len < sn.label[0].len) + continue; + + /* We already had this label */ + if (ti->leaf) { + fd_log_debug("[acl_wl] Warning: entry '%s' is duplicated, merging the flags.\n", name); + ti->flags |= flags; + return 0; + } else { + /* Just mark this entry as a valid leaf also */ + ti->leaf = 1; + ti->flags = flags; + return 0; + } + } + } + + /* Create the new entry */ + CHECK_MALLOC( ti = new_ti(sn.label[0].str, sn.label[0].len, flags, 1) ); + } + + /* The new label is "ti", it is inserted before "li" */ + fd_list_insert_before(li, &ti->chain); + + /* Done! */ + return 0; +} + +/* Search in the tree. On return, *result = -1: not found; >=0: found with PI_SEC_* flags */ +int aw_tree_lookup(char * name, int * result) +{ + struct split_name sn; + int lbl, found; + struct tree_item * ti; + struct fd_list * senti, *li; + + TRACE_ENTRY("%p %p", name, result); + CHECK_PARAMS(name && result); + + /* Initialize */ + *result = -1; + + /* Parse the name into labels */ + CHECK_FCT_DO( parse_name(name, &sn), + { + TRACE_DEBUG(INFO, "Too many labels in this name, it cannot be found in the tree, skipping."); + return 0; + } ); + + senti = &tree_root; + + for (lbl = sn.last_lbl; lbl >= 0; lbl--) { + /* Check if the list is empty, we can directly return */ + if (FD_IS_LIST_EMPTY(senti)) { + /* The item is not found */ + return 0; + } + + /* Check if we have a '*' element */ + ti = (struct tree_item *)(senti->next); + if (ti->str == NULL) { + TRACE_DEBUG(FULL, "[acl_wl] %s matched at level %d with a generic entry.", name, lbl); + *result = ti->flags; + return 0; + } + + /* Search this label in the ordered list */ + found = 0; + for (li = senti->next; li != senti; li=li->next) { + int cmp, len; + ti = (struct tree_item *)li; + + cmp = strncasecmp(ti->str, sn.label[lbl].str, sn.label[lbl].len); + if (cmp > 0) + return 0; /* the label was not found */ + if (cmp < 0) + continue; + + /* Check the lengths */ + len = strlen(ti->str); + if (len > sn.label[lbl].len) + return 0; /* the label was not found */ + if (len < sn.label[lbl].len) + continue; + + /* We found the label */ + found = 1; + senti = &ti->children; + break; + } + + if (!found) + return 0; /* label not found */ + + /* otherwise, continue, sentinel has been updated */ + } + + /* At the end, ti points to the correct leaf */ + if (!ti->leaf) + return 0; + + TRACE_DEBUG(FULL, "[acl_wl] %s matched exactly.", name); + *result = ti->flags; + return 0; +}