1 /**
2  * @file nacm.c
3  * @author Radek Krejci <rkrejci@cesnet.cz>
4  * @brief libyang extension plugin - NACM (RFC 6536)
5  *
6  * Copyright (c) 2016-2017 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 #ifdef __GNUC__
16 #  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
17 #else
18 #  define UNUSED(x) UNUSED_ ## x
19 #endif
20 
21 #include <stdlib.h>
22 
23 #include "../extensions.h"
24 
25 /**
26  * @brief Storage for ID used to check plugin API version compatibility.
27  */
28 LYEXT_VERSION_CHECK
29 
30 /**
31  * @brief Callback to check that the NACM extension can be instantiated inside the provided node
32  *
33  * @param[in] parent The parent of the instantiated extension.
34  * @param[in] parent_type The type of the structure provided as \p parent.
35  * @param[in] substmt_type libyang does not store all the extension instances in the structures where they are
36  *                         instantiated in the module. In some cases (see #LYEXT_SUBSTMT) they are stored in parent
37  *                         structure and marked with flag to know in which substatement of the parent the extension
38  *                         was originally instantiated.
39  * @return 0 - ok
40  *         1 - error
41  */
nacm_position(const void * parent,LYEXT_PAR parent_type,LYEXT_SUBSTMT UNUSED (substmt_type))42 int nacm_position(const void *parent, LYEXT_PAR parent_type, LYEXT_SUBSTMT UNUSED(substmt_type))
43 {
44     if (parent_type != LYEXT_PAR_NODE) {
45         return 1;
46     }
47 
48     if (((struct lys_node*)parent)->nodetype &
49             (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE | LYS_ANYDATA | LYS_AUGMENT | LYS_CASE |
50              LYS_USES | LYS_RPC | LYS_ACTION | LYS_NOTIF )) {
51         return 0;
52     } else {
53         return 1;
54     }
55 }
56 
57 /**
58  * @brief Callback to decide whether the extension will be inherited into the provided schema node. The extension
59  * instance is always from some of the node's parents.
60  *
61  * @param[in] ext Extension instance to be inherited.
62  * @param[in] node Schema node where the node is supposed to be inherited.
63  * @return 0 - yes
64  *         1 - no (do not process the node's children)
65  *         2 - no, but continue with children
66  */
nacm_inherit(struct lys_ext_instance * UNUSED (ext),struct lys_node * node)67 int nacm_inherit(struct lys_ext_instance *UNUSED(ext), struct lys_node *node)
68 {
69     /* libyang already checks if there is explicit instance of the extension already present,
70      * in such a case the extension is never inherited and we don't need to check it here */
71 
72     /* inherit into all the schema nodes that can be instantiated in data trees */
73     if (node->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_ACTION | LYS_NOTIF )) {
74         return 0;
75     } else {
76         return 2;
77     }
78 }
79 
80 /**
81  * @brief Callback to check that the extension instance is correct - have
82  * the valid argument, cardinality, etc.
83  *
84  * In NACM case, we are checking only the cardinality.
85  *
86  * @param[in] ext Extension instance to be checked.
87  * @return 0 - ok
88  *         1 - error
89  */
90 int
nacm_cardinality(struct lys_ext_instance * ext)91 nacm_cardinality(struct lys_ext_instance *ext)
92 {
93     struct lys_ext_instance **extlist;
94     uint8_t extsize, i, c;
95     char *path;
96 
97     if (ext->flags & LYEXT_OPT_PLUGIN1) {
98         /* already checked */
99         ext->flags &= ~LYEXT_OPT_PLUGIN1;
100         return 0;
101     }
102 
103     extlist = ((struct lys_node *)ext->parent)->ext;
104     extsize = ((struct lys_node *)ext->parent)->ext_size;
105 
106     for (i = c = 0; i < extsize; i++) {
107         if (extlist[i]->def == ext->def) {
108             /* note, that it is not necessary to check also ext->insubstmt since
109              * nacm_position() ensures that NACM's extension instances are placed only
110              * in schema nodes */
111             if (extlist[i] != ext) {
112                 /* do not mark the instance being checked */
113                 extlist[i]->flags |= LYEXT_OPT_PLUGIN1;
114             }
115             c++;
116         }
117     }
118 
119     if (c > 1) {
120         path = lys_path((struct lys_node *)(ext->parent), LYS_PATH_FIRST_PREFIX);
121         LYEXT_LOG(ext->module->ctx, LY_LLERR, "NACM", "Extension nacm:%s can appear only once, but %d instances found in %s.",
122                   ext->def->name, c, path);
123         free(path);
124         return 1;
125     } else {
126         return 0;
127     }
128 }
129 
130 /**
131  * @brief Plugin for the NACM's default-deny-write extension
132  */
133 struct lyext_plugin nacm_deny_write = {
134     .type = LYEXT_FLAG,
135     .flags = LYEXT_OPT_INHERIT,
136     .check_position = &nacm_position,
137     .check_result = &nacm_cardinality,
138     .check_inherit = &nacm_inherit
139 };
140 
141 /**
142  * @brief Plugin for the NACM's default-deny-all extension
143  */
144 struct lyext_plugin nacm_deny_all = {
145     .type = LYEXT_FLAG,
146     .flags = LYEXT_OPT_INHERIT,
147     .check_position = &nacm_position,
148     .check_result = &nacm_cardinality,
149     .check_inherit = &nacm_inherit
150 };
151 
152 /**
153  * @brief list of all extension plugins implemented here
154  *
155  * MANDATORY object for all libyang extension plugins, the name must match the <name>.so
156  */
157 struct lyext_plugin_list nacm[] = {
158     {"ietf-netconf-acm", "2012-02-22", "default-deny-write", &nacm_deny_write},
159     {"ietf-netconf-acm", "2012-02-22", "default-deny-all", &nacm_deny_all},
160     {NULL, NULL, NULL, NULL} /* terminating item */
161 };
162