diff --git a/CHANGELOG.md b/CHANGELOG.md index e2ab6c86..7e3837ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,10 @@ * [Roadmap](ROADMAP.md) (Uncommitted and unprioritized) ### Major New features -* More complete Yang parser + +* NACM extension (RFC8341) + * Move NACM files from backend to lib src dir +* Yang code upgrade (RFC7950) * YANG parser cardinality checked (https://github.com/clicon/clixon/issues/48) * See https://github.com/clicon/clixon/issues/84 * Support of submodule, include and belongs-to. diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index fedad77a..7ff71e72 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -935,272 +935,6 @@ from_client_debug(clicon_handle h, return retval; } -/*! Match nacm access operations according to RFC8341 3.4.4. - * Incoming RPC Message Validation Step 7 (c) - * The rule's "access-operations" leaf has the "exec" bit set or - * has the special value "*". - * @retval 0 No match - * @retval 1 Match - * XXX access_operations is bit-fields - */ -static int -nacm_match_access(char *access_operations, - char *mode) -{ - if (access_operations==NULL) - return 0; - if (strcmp(access_operations,"*")==0) - return 1; - if (strstr(access_operations, mode)!=NULL) - return 1; - return 0; -} - -/*! Match nacm single rule. Either match with access or deny. Or not match. - * @param[in] h Clicon handle - * @param[in] name rpc name - * @param[in] xrule NACM rule XML tree - * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0. - * @retval -1 Error - * @retval 0 Matching rule AND Not access and cbret set - * @retval 1 Matchung rule AND Access - * @retval 2 No matching rule Goto step 10 - * From RFC8341 3.4.4. Incoming RPC Message Validation - +---------+-----------------+---------------------+-----------------+ - | Method | Resource class | NETCONF operation | Access | - | | | | operation | - +---------+-----------------+---------------------+-----------------+ - | OPTIONS | all | none | none | - | HEAD | all | , | read | - | GET | all | , | read | - | POST | datastore, data | | create | - | POST | operation | specified operation | execute | - | PUT | data | | create, update | - | PUT | datastore | | update | - | PATCH | data, datastore | | update | - | DELETE | data | | delete | - - 7.(cont) A rule matches if all of the following criteria are met: - * The rule's "module-name" leaf is "*" or equals the name of - the YANG module where the protocol operation is defined. - - * Either (1) the rule does not have a "rule-type" defined or - (2) the "rule-type" is "protocol-operation" and the - "rpc-name" is "*" or equals the name of the requested - protocol operation. - - * The rule's "access-operations" leaf has the "exec" bit set or - has the special value "*". - */ -static int -nacm_match_rule(clicon_handle h, - char *name, - cxobj *xrule, - cbuf *cbret) -{ - int retval = -1; - // cxobj *x; - char *module_name; - char *rpc_name; - char *access_operations; - char *action; - - module_name = xml_find_body(xrule, "module-name"); - rpc_name = xml_find_body(xrule, "rpc-name"); - /* XXX access_operations can be a set of bits */ - access_operations = xml_find_body(xrule, "access-operations"); - action = xml_find_body(xrule, "action"); - clicon_debug(1, "%s: %s %s %s %s", __FUNCTION__, - module_name, rpc_name, access_operations, action); - if (module_name && strcmp(module_name,"*")==0){ - if (nacm_match_access(access_operations, "exec")){ - if (rpc_name==NULL || - strcmp(rpc_name, "*")==0 || strcmp(rpc_name, name)==0){ - /* Here is a matching rule */ - if (action && strcmp(action, "permit")==0){ - retval = 1; - goto done; - } - else{ - if (netconf_access_denied(cbret, "protocol", "access denied") < 0) - goto done; - retval = 0; - goto done; - } - } - } - } - retval = 2; /* no matching rule */ - done: - return retval; - -} - -/*! Make nacm access control - * @param[in] h Clicon handle - * @param[in] mode NACMmode, internal or external - * @param[in] name rpc name - * @param[in] username - * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0. - * @retval -1 Error - * @retval 0 Not access and cbret set - * @retval 1 Access - * From RFC8341 3.4.4. Incoming RPC Message Validation - */ -static int -nacm_access(clicon_handle h, - char *mode, - char *name, - char *username, - cbuf *cbret) -{ - int retval = -1; - cxobj *xtop = NULL; - cxobj *xacm; - cxobj *x; - cxobj *xrlist; - cxobj *xrule; - char *enabled = NULL; - cxobj **gvec = NULL; /* groups */ - size_t glen; - cxobj **rlistvec = NULL; /* rule-list */ - size_t rlistlen; - cxobj **rvec = NULL; /* rules */ - size_t rlen; - int i, j; - char *exec_default = NULL; - int ret; - - clicon_debug(1, "%s", __FUNCTION__); - /* 0. If nacm-mode is external, get NACM defintion from separet tree, - otherwise get it from internal configuration */ - if (strcmp(mode, "external")==0){ - if ((xtop = backend_nacm_list_get(h)) == NULL){ - clicon_err(OE_XML, 0, "No nacm external tree"); - goto done; - } - } - else if (strcmp(mode, "internal")==0){ - if (xmldb_get(h, "running", "nacm", 0, &xtop) < 0) - goto done; - } - else{ - clicon_err(OE_UNIX, 0, "Invalid NACM mode: %s", mode); - goto done; - } - - /* 1. If the "enable-nacm" leaf is set to "false", then the protocol - operation is permitted. (or config does not exist) */ - - if ((xacm = xpath_first(xtop, "nacm")) == NULL) - goto permit; - exec_default = xml_find_body(xacm, "exec-default"); - if ((x = xpath_first(xacm, "enable-nacm")) == NULL) - goto permit; - enabled = xml_body(x); - if (strcmp(enabled, "true") != 0) - goto permit; - - /* 2. If the requesting session is identified as a recovery session, - then the protocol operation is permitted. NYI */ - - /* 3. If the requested operation is the NETCONF - protocol operation, then the protocol operation is permitted. - */ - if (strcmp(name, "close-session") == 0) - goto permit; - /* 4. Check all the "group" entries to see if any of them contain a - "user-name" entry that equals the username for the session - making the request. (If the "enable-external-groups" leaf is - "true", add to these groups the set of groups provided by the - transport layer.) */ - if (username == NULL) - goto step10; - /* User's group */ - if (xpath_vec(xacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0) - goto done; - /* 5. If no groups are found, continue with step 10. */ - if (glen == 0) - goto step10; - /* 6. Process all rule-list entries, in the order they appear in the - configuration. If a rule-list's "group" leaf-list does not - match any of the user's groups, proceed to the next rule-list - entry. */ - if (xpath_vec(xacm, "rule-list", &rlistvec, &rlistlen) < 0) - goto done; - for (i=0; i or , then the protocol operation - is denied. */ - if (strcmp(name, "kill-session")==0 || strcmp(name, "delete-config")==0){ - if (netconf_access_denied(cbret, "protocol", "default deny") < 0) - goto done; - goto deny; - } - /* 12. If the "exec-default" leaf is set to "permit", then permit the - protocol operation; otherwise, deny the request. */ - if (exec_default ==NULL || strcmp(exec_default, "permit")==0) - goto permit; - if (netconf_access_denied(cbret, "protocol", "default deny") < 0) - goto done; - goto deny; - permit: - retval = 1; - done: - clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval); - if (strcmp(mode, "internal")==0 && xtop) - xml_free(xtop); - if (gvec) - free(gvec); - if (rlistvec) - free(rlistvec); - if (rvec) - free(rvec); - return retval; - deny: /* Here, cbret must contain a netconf error msg */ - assert(cbuf_len(cbret)); - retval = 0; - goto done; -} - /*! An internal clicon message has arrived from a client. Receive and dispatch. * @param[in] h Clicon handle * @param[in] s Socket where message arrived. read from this. diff --git a/apps/backend/backend_handle.h b/apps/backend/backend_handle.h index 676cc50c..8eb3e073 100644 --- a/apps/backend/backend_handle.h +++ b/apps/backend/backend_handle.h @@ -52,8 +52,4 @@ struct client_entry *backend_client_list(clicon_handle h); int backend_client_delete(clicon_handle h, struct client_entry *ce); -int backend_nacm_list_set(clicon_handle h, cxobj *xnacm); - -cxobj * backend_nacm_list_get(clicon_handle h); - #endif /* _BACKEND_HANDLE_H_ */ diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 1359e984..ec4a99d7 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -91,6 +91,8 @@ backend_terminate(clicon_handle h) yspec_free(yspec); if ((yspec = clicon_config_yang(h)) != NULL) yspec_free(yspec); + if ((x = clicon_nacm_ext(h)) != NULL) + xml_free(x); if ((x = clicon_conf_xml(h)) != NULL) xml_free(x); stream_publish_exit(); @@ -274,7 +276,7 @@ nacm_load_external(clicon_handle h) clicon_err(OE_XML, 0, "No xml tree in %s", filename); goto done; } - if (backend_nacm_list_set(h, xt) < 0) + if (clicon_nacm_ext_set(h, xt) < 0) goto done; retval = 0; done: diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index 8ed13d97..f4707de4 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -91,7 +91,6 @@ struct backend_handle { /* ------ end of common handle ------ */ struct client_entry *bh_ce_list; /* The client list */ int bh_ce_nr; /* Number of clients, just increment */ - cxobj *bh_nacm; /* NACM external struct */ }; /*! Creates and returns a clicon config handle for other CLICON API calls @@ -109,7 +108,6 @@ backend_handle_init(void) int backend_handle_exit(clicon_handle h) { - struct backend_handle *bh = handle(h); struct client_entry *ce; /* only delete client structs, not close sockets, etc, see backend_client_rm WHY NOT? */ @@ -120,8 +118,6 @@ backend_handle_exit(clicon_handle h) } backend_client_delete(h, ce); } - if (bh->bh_nacm) - xml_free(bh->bh_nacm); clicon_handle_exit(h); /* frees h and options (and streams) */ return 0; } @@ -188,22 +184,3 @@ backend_client_delete(clicon_handle h, return 0; } -int -backend_nacm_list_set(clicon_handle h, - cxobj *xnacm) -{ - struct backend_handle *bh = handle(h); - - if (bh->bh_nacm) - xml_free(bh->bh_nacm); - bh->bh_nacm = xnacm; - return 0; -} - -cxobj * -backend_nacm_list_get(clicon_handle h) -{ - struct backend_handle *bh = handle(h); - - return bh->bh_nacm; -} diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index c454835f..2fc6c50d 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -87,6 +87,7 @@ #include #include #include +#include /* * Global variables generated by Makefile diff --git a/lib/clixon/clixon_nacm.h b/lib/clixon/clixon_nacm.h new file mode 100644 index 00000000..34e9a939 --- /dev/null +++ b/lib/clixon/clixon_nacm.h @@ -0,0 +1,44 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * XML sort and earch functions when used with YANG + */ +#ifndef _CLIXON_NACM_H +#define _CLIXON_NACM_H + +/* + * Prototypes + */ +int nacm_access(clicon_handle h, char *mode, char *name, char *username, cbuf *cbret); + +#endif /* _CLIXON_NACM_H */ diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index 1f4069fc..b5cb55c3 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -171,6 +171,9 @@ int clicon_quiet_mode_set(clicon_handle h, int val); yang_spec * clicon_dbspec_yang(clicon_handle h); int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys); +cxobj * clicon_nacm_ext(clicon_handle h); +int clicon_nacm_ext_set(clicon_handle h, cxobj *xn); + #if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */ yang_spec * clicon_config_yang(clicon_handle h); int clicon_config_yang_set(clicon_handle h, struct yang_spec *ys); diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index eefa969f..ce0c0587 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -74,7 +74,7 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \ clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_proto.c clixon_proto_client.c \ clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \ - clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c + clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c clixon_nacm.c YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ lex.clixon_yang_parse.o clixon_yang_parse.tab.o \ diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c new file mode 100644 index 00000000..1ab18411 --- /dev/null +++ b/lib/src/clixon_nacm.c @@ -0,0 +1,334 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * NACM code according to RFC8341 Network Configuration Access Control Model + */ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clixon */ +#include "clixon_queue.h" +#include "clixon_hash.h" +#include "clixon_err.h" +#include "clixon_log.h" +#include "clixon_string.h" +#include "clixon_handle.h" +#include "clixon_yang.h" +#include "clixon_xml.h" +#include "clixon_options.h" +#include "clixon_netconf_lib.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" +#include "clixon_xml_db.h" +#include "clixon_nacm.h" + +/*! Match nacm access operations according to RFC8341 3.4.4. + * Incoming RPC Message Validation Step 7 (c) + * The rule's "access-operations" leaf has the "exec" bit set or + * has the special value "*". + * @retval 0 No match + * @retval 1 Match + * @note access_operations is bit-fields + */ +static int +nacm_match_access(char *access_operations, + char *mode) +{ + if (access_operations==NULL) + return 0; + if (strcmp(access_operations,"*")==0) + return 1; + if (strstr(access_operations, mode)!=NULL) + return 1; + return 0; +} + +/*! Match nacm single rule. Either match with access or deny. Or not match. + * @param[in] h Clicon handle + * @param[in] name rpc name + * @param[in] xrule NACM rule XML tree + * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0. + * @retval -1 Error + * @retval 0 Matching rule AND Not access and cbret set + * @retval 1 Matchung rule AND Access + * @retval 2 No matching rule Goto step 10 + * From RFC8341 3.4.4. Incoming RPC Message Validation + +---------+-----------------+---------------------+-----------------+ + | Method | Resource class | NETCONF operation | Access | + | | | | operation | + +---------+-----------------+---------------------+-----------------+ + | OPTIONS | all | none | none | + | HEAD | all | , | read | + | GET | all | , | read | + | POST | datastore, data | | create | + | POST | operation | specified operation | execute | + | PUT | data | | create, update | + | PUT | datastore | | update | + | PATCH | data, datastore | | update | + | DELETE | data | | delete | + + 7.(cont) A rule matches if all of the following criteria are met: + * The rule's "module-name" leaf is "*" or equals the name of + the YANG module where the protocol operation is defined. + + * Either (1) the rule does not have a "rule-type" defined or + (2) the "rule-type" is "protocol-operation" and the + "rpc-name" is "*" or equals the name of the requested + protocol operation. + + * The rule's "access-operations" leaf has the "exec" bit set or + has the special value "*". + */ +static int +nacm_match_rule(clicon_handle h, + char *name, + cxobj *xrule, + cbuf *cbret) +{ + int retval = -1; + // cxobj *x; + char *module_name; + char *rpc_name; + char *access_operations; + char *action; + + module_name = xml_find_body(xrule, "module-name"); + rpc_name = xml_find_body(xrule, "rpc-name"); + /* XXX access_operations can be a set of bits */ + access_operations = xml_find_body(xrule, "access-operations"); + action = xml_find_body(xrule, "action"); + clicon_debug(1, "%s: %s %s %s %s", __FUNCTION__, + module_name, rpc_name, access_operations, action); + if (module_name && strcmp(module_name,"*")==0){ + if (nacm_match_access(access_operations, "exec")){ + if (rpc_name==NULL || + strcmp(rpc_name, "*")==0 || strcmp(rpc_name, name)==0){ + /* Here is a matching rule */ + if (action && strcmp(action, "permit")==0){ + retval = 1; + goto done; + } + else{ + if (netconf_access_denied(cbret, "protocol", "access denied") < 0) + goto done; + retval = 0; + goto done; + } + } + } + } + retval = 2; /* no matching rule */ + done: + return retval; + +} + +/*! Make nacm access control + * @param[in] h Clicon handle + * @param[in] mode NACMmode, internal or external + * @param[in] name rpc name + * @param[in] username + * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0. + * @retval -1 Error + * @retval 0 Not access and cbret set + * @retval 1 Access + * @see RFC8341 3.4.4. Incoming RPC Message Validation + */ +int +nacm_access(clicon_handle h, + char *mode, + char *name, + char *username, + cbuf *cbret) +{ + int retval = -1; + cxobj *xtop = NULL; + cxobj *xacm; + cxobj *x; + cxobj *xrlist; + cxobj *xrule; + char *enabled = NULL; + cxobj **gvec = NULL; /* groups */ + size_t glen; + cxobj **rlistvec = NULL; /* rule-list */ + size_t rlistlen; + cxobj **rvec = NULL; /* rules */ + size_t rlen; + int i, j; + char *exec_default = NULL; + int ret; + + clicon_debug(1, "%s", __FUNCTION__); + /* 0. If nacm-mode is external, get NACM defintion from separet tree, + otherwise get it from internal configuration */ + if (strcmp(mode, "external")==0){ + if ((xtop = clicon_nacm_ext(h)) == NULL){ + clicon_err(OE_XML, 0, "No nacm external tree"); + goto done; + } + } + else if (strcmp(mode, "internal")==0){ + if (xmldb_get(h, "running", "nacm", 0, &xtop) < 0) + goto done; + } + else{ + clicon_err(OE_UNIX, 0, "Invalid NACM mode: %s", mode); + goto done; + } + + /* 1. If the "enable-nacm" leaf is set to "false", then the protocol + operation is permitted. (or config does not exist) */ + if ((xacm = xpath_first(xtop, "nacm")) == NULL) + goto permit; + exec_default = xml_find_body(xacm, "exec-default"); + if ((x = xpath_first(xacm, "enable-nacm")) == NULL) + goto permit; + enabled = xml_body(x); + if (strcmp(enabled, "true") != 0) + goto permit; + + /* 2. If the requesting session is identified as a recovery session, + then the protocol operation is permitted. NYI */ + + /* 3. If the requested operation is the NETCONF + protocol operation, then the protocol operation is permitted. + */ + if (strcmp(name, "close-session") == 0) + goto permit; + /* 4. Check all the "group" entries to see if any of them contain a + "user-name" entry that equals the username for the session + making the request. (If the "enable-external-groups" leaf is + "true", add to these groups the set of groups provided by the + transport layer.) */ + if (username == NULL) + goto step10; + /* User's group */ + if (xpath_vec(xacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0) + goto done; + /* 5. If no groups are found, continue with step 10. */ + if (glen == 0) + goto step10; + /* 6. Process all rule-list entries, in the order they appear in the + configuration. If a rule-list's "group" leaf-list does not + match any of the user's groups, proceed to the next rule-list + entry. */ + if (xpath_vec(xacm, "rule-list", &rlistvec, &rlistlen) < 0) + goto done; + for (i=0; i or , then the protocol operation + is denied. */ + if (strcmp(name, "kill-session")==0 || strcmp(name, "delete-config")==0){ + if (netconf_access_denied(cbret, "protocol", "default deny") < 0) + goto done; + goto deny; + } + /* 12. If the "exec-default" leaf is set to "permit", then permit the + protocol operation; otherwise, deny the request. */ + if (exec_default ==NULL || strcmp(exec_default, "permit")==0) + goto permit; + if (netconf_access_denied(cbret, "protocol", "default deny") < 0) + goto done; + goto deny; + permit: + retval = 1; + done: + clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval); + if (strcmp(mode, "internal")==0 && xtop) + xml_free(xtop); + if (gvec) + free(gvec); + if (rlistvec) + free(rlistvec); + if (rvec) + free(rvec); + return retval; + deny: /* Here, cbret must contain a netconf error msg */ + assert(cbuf_len(cbret)); + retval = 0; + goto done; +} diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 9c737b18..74e5a645 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -33,8 +33,6 @@ * * CLICON options - * See clicon_tutorial appendix and clicon.conf.cpp.cpp on documentation of - * options */ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ @@ -607,6 +605,48 @@ clicon_dbspec_yang_set(clicon_handle h, return 0; } +/*! Get NACM (rfc 8341) XML parse tree if external not in std xml config + * @param[in] h Clicon handle + * @retval xn XML NACM tree, or NULL + * @note only used if config option CLICON_NACM_MODE is external + * @see clicon_nacm_ext_set + */ +cxobj * +clicon_nacm_ext(clicon_handle h) +{ + clicon_hash_t *cdat = clicon_data(h); + size_t len; + void *p; + + if ((p = hash_value(cdat, "nacm_xml", &len)) != NULL) + return *(cxobj **)p; + return NULL; +} + +/*! Set NACM (rfc 8341) external XML parse tree, free old if any + * @param[in] h Clicon handle + * @param[in] xn XML Nacm tree + * @note only used if config option CLICON_NACM_MODE is external + * @see clicon_nacm_ext + */ +int +clicon_nacm_ext_set(clicon_handle h, + cxobj *xn) +{ + clicon_hash_t *cdat = clicon_data(h); + cxobj *xo; + + if ((xo = clicon_nacm_ext(h)) != NULL) + xml_free(xo); + /* It is the pointer to xn that should be copied by hash, + so we send a ptr to the ptr to indicate what to copy. + */ + if (hash_add(cdat, "nacm_xml", &xn, sizeof(xn)) == NULL) + return -1; + return 0; +} + + #if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */ /*! Get YANG specification for clixon config * Must use hash functions directly since they are not strings.