summaryrefslogtreecommitdiffstats
path: root/src/plugins_exts/nacm.c
blob: 5ab8daa8e4a53f9b4be2cf612214ea6a4dc46183 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/**
 * @file nacm.c
 * @author Radek Krejci <rkrejci@cesnet.cz>
 * @author Michal Vasko <mvasko@cesnet.cz>
 * @brief libyang extension plugin - NACM (RFC 6536)
 *
 * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "compat.h"
#include "libyang.h"
#include "plugins_exts.h"

struct nacm_dfs_arg {
    struct lysc_ext_instance *ext;
    struct lysc_node *parent;
};

/**
 * @brief DFS callback implementation for inheriting the NACM extension.
 */
static LY_ERR
nacm_inherit_clb(struct lysc_node *node, void *data, ly_bool *dfs_continue)
{
    LY_ERR ret;
    struct nacm_dfs_arg *arg = data;
    struct lysc_ext_instance *inherited;
    LY_ARRAY_COUNT_TYPE u;

    /* ignore the parent from which we inherit and input/output nodes */
    if ((node != arg->parent) && !(node->nodetype & (LYS_INPUT | LYS_OUTPUT))) {
        /* check that the node does not have its own NACM extension instance */
        LY_ARRAY_FOR(node->exts, u) {
            if (node->exts[u].def == arg->ext->def) {
                /* the child already have its own NACM flag, so skip the subtree */
                *dfs_continue = 1;
                return LY_SUCCESS;
            }
        }

        /* duplicate this one to inherit it to the child */
        LY_ARRAY_NEW_GOTO(node->module->ctx, node->exts, inherited, ret, emem);

        inherited->def = arg->ext->def;
        inherited->parent = node;
        inherited->parent_stmt = lyplg_ext_nodetype2stmt(node->nodetype);
        if (arg->ext->argument) {
            if ((ret = lydict_insert(node->module->ctx, arg->ext->argument, 0, &inherited->argument))) {
                return ret;
            }
        }
        /* copy the pointer to the static variables */
        inherited->compiled = arg->ext->compiled;
    }

    return LY_SUCCESS;

emem:
    lyplg_ext_compile_log(NULL, arg->ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s()).", __func__);
    return ret;
}

/**
 * @brief Parse NACM extension instances.
 *
 * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
 */
static LY_ERR
nacm_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
{
    struct lysp_node *parent = NULL;
    LY_ARRAY_COUNT_TYPE u;

    /* check that the extension is instantiated at an allowed place - data node */
    if (!(ext->parent_stmt & LY_STMT_NODE_MASK)) {
        lyplg_ext_parse_log(pctx, ext, LY_LLWRN, 0, "Extension %s is allowed only in a data nodes, but it is placed in "
                "\"%s\" statement.", ext->name, lyplg_ext_stmt2str(ext->parent_stmt));
        return LY_ENOT;
    }

    parent = ext->parent;
    if (!(parent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE | LYS_ANYDATA |
            LYS_CASE | LYS_RPC | LYS_ACTION | LYS_NOTIF)) || (!strcmp(strchr(ext->name, ':') + 1, "default-deny-write") &&
            (parent->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)))) {
        /* note LYS_AUGMENT and LYS_USES is not in the list since they are not present in the compiled tree. Instead, libyang
         * passes all their extensions to their children nodes */
        lyplg_ext_parse_log(pctx, ext, LY_LLWRN, 0, "Extension %s is not allowed in %s statement.", ext->name,
                lys_nodetype2str(parent->nodetype));
        return LY_ENOT;
    }

    /* check for duplication */
    LY_ARRAY_FOR(parent->exts, u) {
        if ((&parent->exts[u] != ext) && parent->exts[u].record && (parent->exts[u].record->plugin.id == ext->record->plugin.id)) {
            /* duplication of a NACM extension on a single node
             * We check for all NACM plugins since we want to catch even the situation that there is default-deny-all
             * AND default-deny-write */
            if (parent->exts[u].name == ext->name) {
                lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension %s is instantiated multiple times.", ext->name);
            } else {
                lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID,
                        "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.");
            }
            return LY_EVALID;
        }
    }

    return LY_SUCCESS;
}

/**
 * @brief Compile NACM extension instances.
 *
 * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
 */
static LY_ERR
nacm_compile(struct lysc_ctx *UNUSED(cctx), const struct lysp_ext_instance *UNUSED(extp), struct lysc_ext_instance *ext)
{
    struct nacm_dfs_arg dfs_arg;

    static const uint8_t nacm_deny_all = 1;
    static const uint8_t nacm_deny_write = 2;

    /* store the NACM flag */
    if (!strcmp(ext->def->name, "default-deny-write")) {
        ext->compiled = (void *)&nacm_deny_write;
    } else if (!strcmp(ext->def->name, "default-deny-all")) {
        ext->compiled = (void *)&nacm_deny_all;
    } else {
        return LY_EINT;
    }

    /* inherit the extension instance to all the children nodes */
    dfs_arg.ext = ext;
    dfs_arg.parent = ext->parent;
    return lysc_tree_dfs_full(ext->parent, nacm_inherit_clb, &dfs_arg);
}

/**
 * @brief Plugin descriptions for the NACM's default-deny-write and default-deny-all extensions
 *
 * Note that external plugins are supposed to use:
 *
 *   LYPLG_EXTENSIONS = {
 */
const struct lyplg_ext_record plugins_nacm[] = {
    {
        .module = "ietf-netconf-acm",
        .revision = "2012-02-22",
        .name = "default-deny-write",

        .plugin.id = "ly2 NACM v1",
        .plugin.parse = nacm_parse,
        .plugin.compile = nacm_compile,
        .plugin.printer_info = NULL,
        .plugin.printer_ctree = NULL,
        .plugin.printer_ptree = NULL,
        .plugin.node = NULL,
        .plugin.snode = NULL,
        .plugin.validate = NULL,
        .plugin.pfree = NULL,
        .plugin.cfree = NULL
    }, {
        .module = "ietf-netconf-acm",
        .revision = "2018-02-14",
        .name = "default-deny-write",

        .plugin.id = "ly2 NACM v1",
        .plugin.parse = nacm_parse,
        .plugin.compile = nacm_compile,
        .plugin.printer_info = NULL,
        .plugin.printer_ctree = NULL,
        .plugin.printer_ptree = NULL,
        .plugin.node = NULL,
        .plugin.snode = NULL,
        .plugin.validate = NULL,
        .plugin.pfree = NULL,
        .plugin.cfree = NULL
    }, {
        .module = "ietf-netconf-acm",
        .revision = "2012-02-22",
        .name = "default-deny-all",

        .plugin.id = "ly2 NACM v1",
        .plugin.parse = nacm_parse,
        .plugin.compile = nacm_compile,
        .plugin.printer_info = NULL,
        .plugin.printer_ctree = NULL,
        .plugin.printer_ptree = NULL,
        .plugin.node = NULL,
        .plugin.snode = NULL,
        .plugin.validate = NULL,
        .plugin.pfree = NULL,
        .plugin.cfree = NULL
    }, {
        .module = "ietf-netconf-acm",
        .revision = "2018-02-14",
        .name = "default-deny-all",

        .plugin.id = "ly2 NACM v1",
        .plugin.parse = nacm_parse,
        .plugin.compile = nacm_compile,
        .plugin.printer_info = NULL,
        .plugin.printer_ctree = NULL,
        .plugin.printer_ptree = NULL,
        .plugin.node = NULL,
        .plugin.snode = NULL,
        .plugin.validate = NULL,
        .plugin.pfree = NULL,
        .plugin.cfree = NULL
    },
    {0} /* terminating zeroed item */
};