/src/libyang/src/plugins_exts/nacm.c
Line | Count | Source |
1 | | /** |
2 | | * @file nacm.c |
3 | | * @author Radek Krejci <rkrejci@cesnet.cz> |
4 | | * @brief libyang extension plugin - NACM (RFC 6536) |
5 | | * |
6 | | * Copyright (c) 2019 CESNET, z.s.p.o. |
7 | | * |
8 | | * This source code is licensed under BSD 3-Clause License (the "License"). |
9 | | * You may not use this file except in compliance with the License. |
10 | | * You may obtain a copy of the License at |
11 | | * |
12 | | * https://opensource.org/licenses/BSD-3-Clause |
13 | | */ |
14 | | |
15 | | #include <stdint.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | |
19 | | #include "libyang.h" |
20 | | #include "plugins_exts.h" |
21 | | |
22 | | struct nacm_dfs_arg { |
23 | | struct lysc_ext_instance *c_ext; |
24 | | struct lysc_node *parent; |
25 | | }; |
26 | | |
27 | | /** |
28 | | * @brief DFS callback implementation for inheriting the NACM extension. |
29 | | */ |
30 | | static LY_ERR |
31 | | nacm_inherit_clb(struct lysc_node *node, void *data, ly_bool *dfs_continue) |
32 | 0 | { |
33 | 0 | LY_ERR ret; |
34 | 0 | struct nacm_dfs_arg *arg = data; |
35 | 0 | struct lysc_ext_instance *inherited; |
36 | 0 | LY_ARRAY_COUNT_TYPE u; |
37 | | |
38 | | /* ignore the parent from which we inherit and input/output nodes */ |
39 | 0 | if ((node != arg->parent) && !(node->nodetype & (LYS_INPUT | LYS_OUTPUT))) { |
40 | | /* check that the node does not have its own NACM extension instance */ |
41 | 0 | LY_ARRAY_FOR(node->exts, u) { |
42 | 0 | if (node->exts[u].def == arg->c_ext->def) { |
43 | | /* the child already have its own NACM flag, so skip the subtree */ |
44 | 0 | *dfs_continue = 1; |
45 | 0 | return LY_SUCCESS; |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | /* duplicate this one to inherit it to the child */ |
50 | 0 | LY_ARRAY_NEW_GOTO(node->module->ctx, node->exts, inherited, ret, emem); |
51 | |
|
52 | 0 | inherited->def = lysc_ext_dup(arg->c_ext->def); |
53 | 0 | inherited->parent = node; |
54 | 0 | inherited->parent_stmt = lys_nodetype2stmt(node->nodetype); |
55 | 0 | if (arg->c_ext->argument) { |
56 | 0 | LY_ERR ret; |
57 | |
|
58 | 0 | if ((ret = lydict_insert(node->module->ctx, arg->c_ext->argument, strlen(arg->c_ext->argument), |
59 | 0 | &inherited->argument))) { |
60 | 0 | return ret; |
61 | 0 | } |
62 | 0 | } |
63 | | /* TODO duplicate extension instances */ |
64 | 0 | inherited->data = arg->c_ext->data; |
65 | 0 | } |
66 | | |
67 | 0 | return LY_SUCCESS; |
68 | | |
69 | 0 | emem: |
70 | 0 | lyplg_ext_log(arg->c_ext, LY_LLERR, LY_EMEM, NULL, "Memory allocation failed (%s()).", __func__); |
71 | 0 | return ret; |
72 | 0 | } |
73 | | |
74 | | /** |
75 | | * @brief Compile NAMC's extension instances. |
76 | | * |
77 | | * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile. |
78 | | */ |
79 | | static LY_ERR |
80 | | nacm_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext) |
81 | 0 | { |
82 | 0 | LY_ERR ret; |
83 | 0 | struct lysc_node *parent = NULL; |
84 | 0 | LY_ARRAY_COUNT_TYPE u; |
85 | 0 | struct nacm_dfs_arg dfs_arg; |
86 | |
|
87 | 0 | static const uint8_t nacm_deny_all = 1; |
88 | 0 | static const uint8_t nacm_deny_write = 2; |
89 | | |
90 | | /* store the NACM flag */ |
91 | 0 | if (!strcmp(c_ext->def->name, "default-deny-write")) { |
92 | 0 | c_ext->data = (void *)&nacm_deny_write; |
93 | 0 | } else if (!strcmp(c_ext->def->name, "default-deny-all")) { |
94 | 0 | c_ext->data = (void *)&nacm_deny_all; |
95 | 0 | } else { |
96 | 0 | return LY_EINT; |
97 | 0 | } |
98 | | |
99 | | /* check that the extension is instantiated at an allowed place - data node */ |
100 | 0 | if (!LY_STMT_IS_NODE(c_ext->parent_stmt)) { |
101 | 0 | lyplg_ext_log(c_ext, LY_LLWRN, 0, lysc_ctx_get_path(cctx), |
102 | 0 | "Extension %s is allowed only in a data nodes, but it is placed in \"%s\" statement.", |
103 | 0 | p_ext->name, ly_stmt2str(c_ext->parent_stmt)); |
104 | 0 | return LY_ENOT; |
105 | 0 | } else { |
106 | 0 | parent = (struct lysc_node *)c_ext->parent; |
107 | 0 | if (!(parent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE | LYS_ANYDATA | |
108 | 0 | LYS_CASE | LYS_RPC | LYS_ACTION | LYS_NOTIF))) { |
109 | | /* note LYS_AUGMENT and LYS_USES is not in the list since they are not present in the compiled tree. Instead, libyang |
110 | | * passes all their extensions to their children nodes */ |
111 | 0 | invalid_parent: |
112 | 0 | lyplg_ext_log(c_ext, LY_LLWRN, 0, lysc_ctx_get_path(cctx), |
113 | 0 | "Extension %s is not allowed in %s statement.", p_ext->name, lys_nodetype2str(parent->nodetype)); |
114 | 0 | return LY_ENOT; |
115 | 0 | } |
116 | 0 | if ((c_ext->data == (void *)&nacm_deny_write) && (parent->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) { |
117 | 0 | goto invalid_parent; |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | | /* check for duplication */ |
122 | 0 | LY_ARRAY_FOR(parent->exts, u) { |
123 | 0 | if ((&parent->exts[u] != c_ext) && (parent->exts[u].def->plugin->compile == c_ext->def->plugin->compile)) { |
124 | | /* duplication of a NACM extension on a single node |
125 | | * We check for all NACM plugins since we want to catch even the situation that there is default-deny-all |
126 | | * AND default-deny-write */ |
127 | 0 | if (parent->exts[u].def == c_ext->def) { |
128 | 0 | lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx), |
129 | 0 | "Extension %s is instantiated multiple times.", p_ext->name); |
130 | 0 | } else { |
131 | 0 | lyplg_ext_log(c_ext, LY_LLERR, LY_EVALID, lysc_ctx_get_path(cctx), |
132 | 0 | "Extension nacm:default-deny-write is mixed with nacm:default-deny-all."); |
133 | 0 | } |
134 | 0 | return LY_EVALID; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | /* inherit the extension instance to all the children nodes */ |
139 | 0 | dfs_arg.c_ext = c_ext; |
140 | 0 | dfs_arg.parent = parent; |
141 | 0 | ret = lysc_tree_dfs_full(parent, nacm_inherit_clb, &dfs_arg); |
142 | |
|
143 | 0 | return ret; |
144 | 0 | } |
145 | | |
146 | | /** |
147 | | * @brief Plugin descriptions for the NACM's default-deny-write and default-deny-all extensions |
148 | | * |
149 | | * Note that external plugins are supposed to use: |
150 | | * |
151 | | * LYPLG_EXTENSIONS = { |
152 | | */ |
153 | | const struct lyplg_ext_record plugins_nacm[] = { |
154 | | { |
155 | | .module = "ietf-netconf-acm", |
156 | | .revision = "2012-02-22", |
157 | | .name = "default-deny-write", |
158 | | |
159 | | .plugin.id = "libyang 2 - NACM, version 1", |
160 | | .plugin.compile = &nacm_compile, |
161 | | .plugin.validate = NULL, |
162 | | .plugin.sprinter = NULL, |
163 | | .plugin.free = NULL |
164 | | }, { |
165 | | .module = "ietf-netconf-acm", |
166 | | .revision = "2018-02-14", |
167 | | .name = "default-deny-write", |
168 | | |
169 | | .plugin.id = "libyang 2 - NACM, version 1", |
170 | | .plugin.compile = &nacm_compile, |
171 | | .plugin.validate = NULL, |
172 | | .plugin.sprinter = NULL, |
173 | | .plugin.free = NULL |
174 | | }, { |
175 | | .module = "ietf-netconf-acm", |
176 | | .revision = "2012-02-22", |
177 | | .name = "default-deny-all", |
178 | | |
179 | | .plugin.id = "libyang 2 - NACM, version 1", |
180 | | .plugin.compile = &nacm_compile, |
181 | | .plugin.validate = NULL, |
182 | | .plugin.sprinter = NULL, |
183 | | .plugin.free = NULL |
184 | | }, { |
185 | | .module = "ietf-netconf-acm", |
186 | | .revision = "2018-02-14", |
187 | | .name = "default-deny-all", |
188 | | |
189 | | .plugin.id = "libyang 2 - NACM, version 1", |
190 | | .plugin.compile = &nacm_compile, |
191 | | .plugin.validate = NULL, |
192 | | .plugin.sprinter = NULL, |
193 | | .plugin.free = NULL |
194 | | }, |
195 | | {0} /* terminating zeroed item */ |
196 | | }; |