1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6 Copyright (C) 2017-2019 Olof Hagsand
7 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8
9 This file is part of CLIXON.
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22
23 Alternatively, the contents of this file may be used under the terms of
24 the GNU General Public License Version 3 or later (the "GPL"),
25 in which case the provisions of the GPL are applicable instead
26 of those above. If you wish to allow use of your version of this file only
27 under the terms of the GPL, and not to allow others to
28 use your version of this file under the terms of Apache License version 2,
29 indicate your decision by deleting the provisions above and replace them with
30 the notice and other provisions required by the GPL. If you do not delete
31 the provisions above, a recipient may use your version of this file under
32 the terms of any one of the Apache License version 2 or the GPL.
33
34 ***** END LICENSE BLOCK *****
35
36 *
37 * XML code
38 *
39 * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
40 */
41 #ifdef HAVE_CONFIG_H
42 #include "clixon_config.h" /* generated by config & autoconf */
43 #endif
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include <ctype.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <fcntl.h>
53 #include <assert.h>
54 #include <arpa/inet.h>
55 #include <sys/param.h>
56 #include <netinet/in.h>
57
58 /* cligen */
59 #include <cligen/cligen.h>
60
61 /* clicon */
62
63 #include "clixon_string.h"
64 #include "clixon_queue.h"
65 #include "clixon_hash.h"
66 #include "clixon_handle.h"
67 #include "clixon_string.h"
68 #include "clixon_err.h"
69 #include "clixon_log.h"
70 #include "clixon_yang.h"
71 #include "clixon_xml.h"
72 #include "clixon_netconf_lib.h"
73 #include "clixon_options.h"
74 #include "clixon_xml_nsctx.h"
75 #include "clixon_xpath_ctx.h"
76 #include "clixon_xpath.h"
77 #include "clixon_yang_module.h"
78 #include "clixon_yang_type.h"
79 #include "clixon_xml_map.h"
80 #include "clixon_validate.h"
81
82 /*! Validate xml node of type leafref, ensure the value is one of that path's reference
83 * @param[in] xt XML leaf node of type leafref
84 * @param[in] ys Yang spec of leaf
85 * @param[in] ytype Yang type statement belonging to the XML node
86 * @param[out] xret Error XML tree. Free with xml_free after use
87 * @retval 1 Validation OK
88 * @retval 0 Validation failed
89 * @retval -1 Error
90 * From rfc7950 Sec 9.9.2
91 * The "path" XPath expression is evaluated in the following context,
92 * in addition to the definition in Section 6.4.1:
93 * o If the "path" statement is defined within a typedef, the context
94 * node is the leaf or leaf-list node in the data tree that
95 * references the typedef. (ie ys)
96 * o Otherwise, the context node is the node in the data tree for which
97 * the "path" statement is defined. (ie yc)
98 */
99 static int
validate_leafref(cxobj * xt,yang_stmt * ys,yang_stmt * ytype,cxobj ** xret)100 validate_leafref(cxobj *xt,
101 yang_stmt *ys,
102 yang_stmt *ytype,
103 cxobj **xret)
104 {
105 int retval = -1;
106 yang_stmt *ypath;
107 yang_stmt *yp;
108 cxobj **xvec = NULL;
109 cxobj *x;
110 int i;
111 size_t xlen = 0;
112 char *leafrefbody;
113 char *leafbody;
114 cvec *nsc = NULL;
115 cbuf *cberr = NULL;
116 char *path;
117
118 if ((leafrefbody = xml_body(xt)) == NULL)
119 goto ok;
120 if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
121 if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0)
122 goto done;
123 goto fail;
124 }
125 /* See comment^: If path is defined in typedef or not */
126 if ((yp = yang_parent_get(ytype)) != NULL &&
127 yang_keyword_get(yp) == Y_TYPEDEF){
128 if (xml_nsctx_yang(ys, &nsc) < 0)
129 goto done;
130 }
131 else
132 if (xml_nsctx_yang(ytype, &nsc) < 0)
133 goto done;
134 path = yang_argument_get(ypath);
135 if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, path) < 0)
136 goto done;
137 for (i = 0; i < xlen; i++) {
138 x = xvec[i];
139 if ((leafbody = xml_body(x)) == NULL)
140 continue;
141 if (strcmp(leafbody, leafrefbody) == 0)
142 break;
143 }
144 if (i==xlen){
145 if ((cberr = cbuf_new()) == NULL){
146 clicon_err(OE_UNIX, errno, "cbuf_new");
147 goto done;
148 }
149 cprintf(cberr, "Leafref validation failed: No leaf %s matching path %s", leafrefbody, path);
150 if (netconf_bad_element_xml(xret, "application", leafrefbody, cbuf_get(cberr)) < 0)
151 goto done;
152 goto fail;
153 }
154 ok:
155 retval = 1;
156 done:
157 if (cberr)
158 cbuf_free(cberr);
159 if (nsc)
160 xml_nsctx_free(nsc);
161 if (xvec)
162 free(xvec);
163 return retval;
164 fail:
165 retval = 0;
166 goto done;
167 }
168
169 /*! Validate xml node of type identityref, ensure value is a defined identity
170 * Check if a given node has value derived from base identity. This is
171 * a run-time check necessary when validating eg netconf.
172 * Valid values for an identityref are any identities derived from all
173 * the identityref's base identities.
174 * Example:
175 * b0 --> b1 --> b2 (b1 & b2 are derived)
176 * identityref b2
177 * base b0;
178 * This function does: derived_from(b2, b0);
179 * @param[in] xt XML leaf node of type identityref
180 * @param[in] ys Yang spec of leaf
181 * @param[in] ytype Yang type field of type identityref
182 * @param[out] xret Error XML tree. Free with xml_free after use
183 * @retval 1 Validation OK
184 * @retval 0 Validation failed
185 * @retval -1 Error
186 * @see ys_populate_identity where the derived types are set
187 * @see yang_augment_node
188 * @see RFC7950 Sec 9.10.2:
189 * @see xp_function_derived_from similar code other context
190 */
191 static int
validate_identityref(cxobj * xt,yang_stmt * ys,yang_stmt * ytype,cxobj ** xret)192 validate_identityref(cxobj *xt,
193 yang_stmt *ys,
194 yang_stmt *ytype,
195 cxobj **xret)
196 {
197 int retval = -1;
198 char *node = NULL;
199 char *idref = NULL;
200 yang_stmt *ybaseref; /* This is the type's base reference */
201 yang_stmt *ybaseid;
202 char *prefix = NULL;
203 char *id = NULL;
204 cbuf *cberr = NULL;
205 cbuf *cb = NULL;
206 cvec *idrefvec; /* Derived identityref list: (module:id)**/
207 yang_stmt *ymod;
208
209 if ((cb = cbuf_new()) == NULL){
210 clicon_err(OE_UNIX, errno, "cbuf_new");
211 goto done;
212 }
213 if ((cberr = cbuf_new()) == NULL){
214 clicon_err(OE_UNIX, errno, "cbuf_new");
215 goto done;
216 }
217 /* Get idref value. Then see if this value is derived from ytype.
218 */
219 if ((node = xml_body(xt)) == NULL){ /* It may not be empty */
220 if (netconf_bad_element_xml(xret, "application", xml_name(xt), "Identityref should not be empty") < 0)
221 goto done;
222 goto fail;
223 }
224 if (nodeid_split(node, &prefix, &id) < 0)
225 goto done;
226 /* This is the type's base reference */
227 if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){
228 if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
229 goto done;
230 goto fail;
231 }
232 /* This is the actual base identity */
233 if ((ybaseid = yang_find_identity(ybaseref, yang_argument_get(ybaseref))) == NULL){
234 if (netconf_missing_element_xml(xret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
235 goto done;
236 goto fail;
237 }
238
239 /* idref from prefix:id to module:id */
240 if (prefix == NULL)
241 ymod = ys_module(ys);
242 else{ /* from prefix to name */
243 #if 1 /* IDENTITYREF_KLUDGE */
244 ymod = yang_find_module_by_prefix_yspec(ys_spec(ys), prefix);
245 #endif
246 }
247 if (ymod == NULL){
248 cprintf(cberr, "Identityref validation failed, %s not derived from %s",
249 node, yang_argument_get(ybaseid));
250 if (xret && netconf_operation_failed_xml(xret, "application", cbuf_get(cberr)) < 0)
251 goto done;
252 goto fail;
253 }
254 cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
255 idref = cbuf_get(cb);
256 /* Here check if node is in the derived node list of the base identity
257 * The derived node list is a cvec computed XXX
258 */
259 idrefvec = yang_cvec_get(ybaseid);
260 if (cvec_find(idrefvec, idref) == NULL){
261 cprintf(cberr, "Identityref validation failed, %s not derived from %s",
262 node, yang_argument_get(ybaseid));
263 if (netconf_operation_failed_xml(xret, "application", cbuf_get(cberr)) < 0)
264 goto done;
265 goto fail;
266 }
267 retval = 1;
268 done:
269 if (cberr)
270 cbuf_free(cberr);
271 if (cb)
272 cbuf_free(cb);
273 if (id)
274 free(id);
275 if (prefix)
276 free(prefix);
277 return retval;
278 fail:
279 retval = 0;
280 goto done;
281 }
282
283 /*! Validate an RPC node
284 * @param[in] h Clicon handle
285 * @param[in] xrpc XML node to be validated
286 * @param[out] xret Error XML tree. Free with xml_free after use
287 * @retval 1 Validation OK
288 * @retval 0 Validation failed
289 * @retval -1 Error
290 * rfc7950
291 * 7.14.2
292 * If a leaf in the input tree has a "mandatory" statement with the
293 * value "true", the leaf MUST be present in an RPC invocation.
294 *
295 * If a leaf in the input tree has a default value, the server MUST use
296 * this value in the same cases as those described in Section 7.6.1. In
297 * these cases, the server MUST operationally behave as if the leaf was
298 * present in the RPC invocation with the default value as its value.
299 *
300 * If a leaf-list in the input tree has one or more default values, the
301 * server MUST use these values in the same cases as those described in
302 * Section 7.7.2. In these cases, the server MUST operationally behave
303 * as if the leaf-list was present in the RPC invocation with the
304 * default values as its values.
305 *
306 * Since the input tree is not part of any datastore, all "config"
307 * statements for nodes in the input tree are ignored.
308 *
309 * If any node has a "when" statement that would evaluate to "false",
310 * then this node MUST NOT be present in the input tree.
311 *
312 * 7.14.4
313 * Input parameters are encoded as child XML elements to the rpc node's
314 * XML element, in the same order as they are defined within the "input"
315 * statement.
316 *
317 * If the RPC operation invocation succeeded and no output parameters
318 * are returned, the <rpc-reply> contains a single <ok/> element defined
319 * in [RFC6241]. If output parameters are returned, they are encoded as
320 * child elements to the <rpc-reply> element defined in [RFC6241], in
321 * the same order as they are defined within the "output" statement.
322 * @see xml_yang_validate_all
323 * @note Should need a variant accepting cxobj **xret
324 */
325 int
xml_yang_validate_rpc(clicon_handle h,cxobj * xrpc,cxobj ** xret)326 xml_yang_validate_rpc(clicon_handle h,
327 cxobj *xrpc,
328 cxobj **xret)
329 {
330 int retval = -1;
331 yang_stmt *yn=NULL; /* rpc name */
332 cxobj *xn; /* rpc name */
333
334 if (strcmp(xml_name(xrpc), "rpc")){
335 clicon_err(OE_XML, EINVAL, "Expected RPC");
336 goto done;
337 }
338 xn = NULL;
339 /* xn is name of rpc, ie <rcp><xn/></rpc> */
340 while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
341 if ((yn = xml_spec(xn)) == NULL){
342 if (netconf_unknown_element_xml(xret, "application", xml_name(xn), NULL) < 0)
343 goto done;
344 goto fail;
345 }
346 if ((retval = xml_yang_validate_all(h, xn, xret)) < 1)
347 goto done; /* error or validation fail */
348 if ((retval = xml_yang_validate_add(h, xn, xret)) < 1)
349 goto done; /* error or validation fail */
350 if (xml_default_recurse(xn, 0) < 0)
351 goto done;
352 }
353 // ok: /* pass validation */
354 retval = 1;
355 done:
356 return retval;
357 fail:
358 retval = 0;
359 goto done;
360 }
361
362 /*! Check if an xml node is a part of a choice and have >1 siblings
363 * @param[in] xt XML node to be validated
364 * @param[in] yt xt:s yang statement
365 * @param[out] xret Error XML tree. Free with xml_free after use
366 * @retval 1 Validation OK
367 * @retval 0 Validation failed (cbret set)
368 * @retval -1 Error
369 * Check if xt is part of valid choice
370 */
371 static int
check_choice(cxobj * xt,yang_stmt * yt,cxobj ** xret)372 check_choice(cxobj *xt,
373 yang_stmt *yt,
374 cxobj **xret)
375 {
376 int retval = -1;
377 yang_stmt *y;
378 yang_stmt *ytp; /* yt:s parent */
379 yang_stmt *ytcase = NULL; /* yt:s parent case if any */
380 yang_stmt *ytchoice = NULL;
381 yang_stmt *yp;
382 cxobj *x;
383 cxobj *xp;
384
385 if ((ytp = yang_parent_get(yt)) == NULL)
386 goto ok;
387 /* Return OK if xt is not choice */
388 switch (yang_keyword_get(ytp)){
389 case Y_CASE:
390 ytcase = ytp;
391 ytchoice = yang_parent_get(ytp);
392 break;
393 case Y_CHOICE:
394 ytchoice = ytp;
395 break;
396 default:
397 goto ok; /* Not choice */
398 break;
399 }
400 if ((xp = xml_parent(xt)) == NULL)
401 goto ok;
402 x = NULL; /* Find a child with same yang spec */
403 while ((x = xml_child_each(xp, x, CX_ELMNT)) != NULL) {
404 if (x == xt)
405 continue;
406 y = xml_spec(x);
407 if (y == yt) /* eg same list */
408 continue;
409 yp = yang_parent_get(y);
410 switch (yang_keyword_get(yp)){
411 case Y_CASE:
412 if (yang_parent_get(yp) != ytchoice) /* Not same choice (not releveant) */
413 continue;
414 if (yp == ytcase) /* same choice but different case */
415 continue;
416 break;
417 case Y_CHOICE:
418 if (yp != ytcase) /* Not same choice (not relevant) */
419 continue;
420 break;
421 default:
422 continue; /* not choice */
423 break;
424 }
425 if (netconf_bad_element_xml(xret, "application", xml_name(x), "Element in choice statement already exists") < 0)
426 goto done;
427 goto fail;
428 } /* while */
429
430 ok:
431 retval = 1;
432 done:
433 return retval;
434 fail:
435 retval = 0;
436 goto done;
437 }
438
439 /*! Check that an XML tree of type list has valid keys
440 * @param[in] xt XML tree
441 * @param[in] yt Yang spec of xt of type LIST which is a config true node.
442 * @param[out] xret Error XML tree. Free with xml_free after use
443 * @retval 1 Validation OK
444 * @retval 0 Validation failed (xret set)
445 * @retval -1 Error
446 * This ensures that there are keys in XML corresponding to the Yang
447 * This function is broken out from generic validation since it needs specially checked in incoming
448 * edit-config
449 * XXX: It does not check double (more than one) key
450 * @see xml_yang_validate_list_key_only
451 */
452 static int
check_list_key(cxobj * xt,yang_stmt * yt,cxobj ** xret)453 check_list_key(cxobj *xt,
454 yang_stmt *yt,
455 cxobj **xret)
456
457 {
458 int retval = -1;
459 yang_stmt *yc;
460 cvec *cvk = NULL; /* vector of index keys */
461 cg_var *cvi;
462 char *keyname;
463
464 if (yt == NULL || !yang_config(yt) || yang_keyword_get(yt) != Y_LIST){
465 clicon_err(OE_YANG, EINVAL, "yt is not a config true list node");
466 goto done;
467 }
468 yc = NULL;
469 while ((yc = yn_each(yt, yc)) != NULL) {
470 if (yang_keyword_get(yc) != Y_KEY)
471 continue;
472 /* Check if a list does not have mandatory key leafs */
473 cvk = yang_cvec_get(yt); /* Use Y_LIST cache, see ys_populate_list() */
474 cvi = NULL;
475 while ((cvi = cvec_each(cvk, cvi)) != NULL) {
476 keyname = cv_string_get(cvi);
477 if (xml_find_type(xt, NULL, keyname, CX_ELMNT) == NULL){
478 if (netconf_missing_element_xml(xret, "application", keyname, "Mandatory key") < 0)
479 goto done;
480 goto fail;
481 }
482 }
483 }
484 retval = 1;
485 done:
486 return retval;
487 fail:
488 retval = 0;
489 goto done;
490 }
491
492 /*! Check if an xml node lacks mandatory children
493 * @param[in] xt XML node to be validated
494 * @param[in] yt xt:s yang statement
495 * @param[out] xret Error XML tree. Free with xml_free after use
496 * @retval 1 Validation OK
497 * @retval 0 Validation failed (cbret set)
498 * @retval -1 Error
499 */
500 static int
check_mandatory(cxobj * xt,yang_stmt * yt,cxobj ** xret)501 check_mandatory(cxobj *xt,
502 yang_stmt *yt,
503 cxobj **xret)
504
505 {
506 int retval = -1;
507 cxobj *x;
508 yang_stmt *y;
509 yang_stmt *yc;
510 yang_stmt *yp;
511 cbuf *cb = NULL;
512 int ret;
513
514 if (yt == NULL || !yang_config(yt)){
515 clicon_err(OE_YANG, EINVAL, "yt is not config true");
516 goto done;
517 }
518 if (yang_keyword_get(yt) == Y_LIST){
519 if ((ret = check_list_key(xt, yt, xret)) < 0)
520 goto done;
521 if (ret == 0)
522 goto fail;
523 }
524 yc = NULL;
525 while ((yc = yn_each(yt, yc)) != NULL) {
526 if (!yang_mandatory(yc))
527 continue;
528 switch (yang_keyword_get(yc)){
529 case Y_CONTAINER:
530 case Y_ANYDATA:
531 case Y_ANYXML:
532 case Y_LEAF:
533 if (yang_config(yc)==0)
534 break;
535 /* Find a child with the mandatory yang */
536 x = NULL;
537 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
538 if ((y = xml_spec(x)) != NULL
539 && y==yc)
540 break; /* got it */
541 }
542 if (x == NULL){
543 if ((cb = cbuf_new()) == NULL){
544 clicon_err(OE_UNIX, errno, "cbuf_new");
545 goto done;
546 }
547 cprintf(cb, "Mandatory variable of %s in module %s", xml_name(xt), yang_argument_get(ys_module(yc)));
548 if (netconf_missing_element_xml(xret, "application", yang_argument_get(yc), cbuf_get(cb)) < 0)
549 goto done;
550 goto fail;
551 }
552 break;
553 case Y_CHOICE: /* More complex because of choice/case structure */
554 x = NULL;
555 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
556 if ((y = xml_spec(x)) != NULL &&
557 (yp = yang_choice(y)) != NULL &&
558 yp == yc){
559 break; /* leave loop with x set */
560 }
561 }
562 if (x == NULL){
563 /* @see RFC7950: 15.6 Error Message for Data That Violates
564 * a Mandatory "choice" Statement */
565 if (netconf_data_missing_xml(xret, yang_argument_get(yc), NULL) < 0)
566 goto done;
567 goto fail;
568 }
569 break;
570 default:
571 break;
572 } /* switch */
573 }
574 retval = 1;
575 done:
576 if (cb)
577 cbuf_free(cb);
578 return retval;
579 fail:
580 retval = 0;
581 goto done;
582 }
583
584 /*! New element last in list, check if already exists if sp return -1
585 * @param[in] vec Vector of existing entries (new is last)
586 * @param[in] i1 The new entry is placed at vec[i1]
587 * @param[in] vlen Lenght of entry
588 * @param[in] sorted Sorted by system, ie sorted by key, otherwise no assumption
589 * @retval 0 OK, entry is unique
590 * @retval -1 Duplicate detected
591 * @note This is currently quadratic complexity. It could be improved by inserting new element sorted and binary search.
592 */
593 static int
check_insert_duplicate(char ** vec,int i1,int vlen,int sorted)594 check_insert_duplicate(char **vec,
595 int i1,
596 int vlen,
597 int sorted)
598 {
599 int i;
600 int v;
601 char *b;
602
603 if (sorted){
604 /* Just go look at previous element to see if it is duplicate (sorted by system) */
605 if (i1 == 0)
606 return 0;
607 i = i1-1;
608 for (v=0; v<vlen; v++){
609 b = vec[i*vlen+v];
610 if (b == NULL || strcmp(b, vec[i1*vlen+v]))
611 return 0;
612 }
613 /* here we have passed thru all keys of previous element and they are all equal */
614 return -1;
615 }
616 else{
617 for (i=0; i<i1; i++){
618 for (v=0; v<vlen; v++){
619 b = vec[i*vlen+v];
620 if (b == NULL || strcmp(b, vec[i1*vlen+v]))
621 break;
622 }
623 if (v==vlen) /* duplicate */
624 break;
625 }
626 return i==i1?0:-1;
627 }
628 }
629
630 /*! Given a list with unique constraint, detect duplicates
631 * @param[in] x The first element in the list (on return the last)
632 * @param[in] xt The parent of x
633 * @param[in] y Its yang spec (Y_LIST)
634 * @param[in] yu A yang unique spec (Y_UNIQUE) for unique keyword or (Y_LIST) for list keys
635 * @param[out] xret Error XML tree. Free with xml_free after use
636 * @retval 1 Validation OK
637 * @retval 0 Validation failed (cbret set)
638 * @retval -1 Error
639 * @note It would be possible to cache the vector built below
640 */
641 static int
check_unique_list(cxobj * x,cxobj * xt,yang_stmt * y,yang_stmt * yu,cxobj ** xret)642 check_unique_list(cxobj *x,
643 cxobj *xt,
644 yang_stmt *y,
645 yang_stmt *yu,
646 cxobj **xret)
647
648 {
649 int retval = -1;
650 cvec *cvk; /* unique vector */
651 cg_var *cvi; /* unique node name */
652 cxobj *xi;
653 char **vec = NULL; /* 2xmatrix */
654 int vlen;
655 int i;
656 int v;
657 char *bi;
658 int sorted;
659
660 /* If list and is sorted by system, then it is assumed elements are in key-order which is optimized
661 * Other cases are "unique" constraint or list sorted by user which is quadratic in complexity
662 * This second case COULD be optimized if binary insert is made on the vec vector.
663 */
664 sorted = (yang_keyword_get(yu) == Y_LIST &&
665 yang_find(y, Y_ORDERED_BY, "user") == NULL);
666 cvk = yang_cvec_get(yu);
667 vlen = cvec_len(cvk); /* nr of unique elements to check */
668 if ((vec = calloc(vlen*xml_child_nr(xt), sizeof(char*))) == NULL){
669 clicon_err(OE_UNIX, errno, "calloc");
670 goto done;
671 }
672 /* A vector is built with key-values, for each iteration check "backward" in the vector
673 * for duplicates
674 */
675 i = 0; /* x element index */
676 do {
677 cvi = NULL;
678 v = 0; /* index in each tuple */
679 while ((cvi = cvec_each(cvk, cvi)) != NULL){
680 /* RFC7950: Sec 7.8.3.1: entries that do not have value for all
681 * referenced leafs are not taken into account */
682 if ((xi = xml_find(x, cv_string_get(cvi))) ==NULL)
683 break;
684 if ((bi = xml_body(xi)) == NULL)
685 break;
686 vec[i*vlen + v++] = bi;
687 }
688 if (cvi==NULL){
689 /* Last element (i) is newly inserted, see if it is already there */
690 if (check_insert_duplicate(vec, i, vlen, sorted) < 0){
691 if (netconf_data_not_unique_xml(xret, x, cvk) < 0)
692 goto done;
693 goto fail;
694 }
695 }
696 x = xml_child_each(xt, x, CX_ELMNT);
697 i++;
698 } while (x && y == xml_spec(x)); /* stop if list ends, others may follow */
699 /* It would be possible to cache vec here as an optimization */
700 retval = 1;
701 done:
702 if (vec)
703 free(vec);
704 return retval;
705 fail:
706 retval = 0;
707 goto done;
708 }
709
710 /*! Given a list, check if any min/max-elemants constraints apply
711 * @param[in] xp Parent of the xml list there are too few/many
712 * @param[in] y Yang spec of the failing list
713 * @param[in] nr Number of elements (like x) in the list
714 * @param[out] xret Error XML tree. Free with xml_free after use
715 * @retval 1 Validation OK
716 * @retval 0 Validation failed (cbret set)
717 * @retval -1 Error
718 * @see RFC7950 7.7.5
719 */
720 static int
check_min_max(cxobj * xp,yang_stmt * y,int nr,cxobj ** xret)721 check_min_max(cxobj *xp,
722 yang_stmt *y,
723 int nr,
724 cxobj **xret)
725 {
726 int retval = -1;
727 yang_stmt *ymin; /* yang min */
728 yang_stmt *ymax; /* yang max */
729 cg_var *cv;
730
731 if ((ymin = yang_find(y, Y_MIN_ELEMENTS, NULL)) != NULL){
732 cv = yang_cv_get(ymin);
733 if (nr < cv_uint32_get(cv)){
734 if (netconf_minmax_elements_xml(xret, xp, yang_argument_get(y), 0) < 0)
735 goto done;
736 goto fail;
737 }
738 }
739 if ((ymax = yang_find(y, Y_MAX_ELEMENTS, NULL)) != NULL){
740 cv = yang_cv_get(ymax);
741 if (cv_uint32_get(cv) > 0 && /* 0 means unbounded */
742 nr > cv_uint32_get(cv)){
743 if (netconf_minmax_elements_xml(xret, xp, yang_argument_get(y), 1) < 0)
744 goto done;
745 goto fail;
746 }
747 }
748 retval = 1;
749 done:
750 return retval;
751 fail:
752 retval = 0;
753 goto done;
754 }
755
756 /*! Detect unique constraint for duplicates from parent node and minmax
757 * @param[in] xt XML parent (may have lists w unique constraints as child)
758 * @param[out] xret Error XML tree. Free with xml_free after use
759 * @retval 1 Validation OK
760 * @retval 0 Validation failed (xret set)
761 * @retval -1 Error
762 * Assume xt:s children are sorted and yang populated.
763 * The function does two different things of the children of an XML node:
764 * (1) Check min/max element constraints
765 * (2) Check unique constraints
766 *
767 * The routine uses a node traversing mechanism as the following example, where
768 * two lists [x1,..] and [x2,..] are embedded:
769 * xt: {a, b, [x1, x1, x1], d, e, f, [x2, x2, x2], g}
770 * The function does this using a single iteration and uses the fact that the
771 * xml symbols share yang symbols: ie [x1..] has yang y1 and d has yd.
772 *
773 * Unique constraints:
774 * Lists are identified, then check_unique_list is called on each list.
775 * Example, x has an associated yang list node with list of unique constraints
776 * y-list->y-unique - "a"
777 * xt->x -> ab
778 * x -> bc
779 * x -> ab
780 *
781 * Min-max constraints:
782 * Find upper and lower bound of existing lists and report violations
783 * Somewhat tricky to find violation of min-elements of empty
784 * lists, but this is done by a "gap-detection" mechanism, which detects
785 * gaps in the xml nodes given the ancestor Yang structure.
786 * But no gap analysis is done if the yang spec of the top-level xml is unknown
787 * Example:
788 * Yang structure: y1, y2, y3,
789 * XML structure: [x1, x1], [x3, x3] where [x2] list is missing
790 * @note min-element constraints on empty lists are not detected on top-level.
791 * Or more specifically, if no yang spec if associated with the top-level
792 * XML node. This may not be a large problem since it would mean empty configs
793 * are not allowed.
794 */
795 static int
check_list_unique_minmax(cxobj * xt,cxobj ** xret)796 check_list_unique_minmax(cxobj *xt,
797 cxobj **xret)
798 {
799 int retval = -1;
800 cxobj *x = NULL;
801 yang_stmt *y;
802 yang_stmt *yt;
803 yang_stmt *yprev = NULL; /* previous in list */
804 yang_stmt *ye = NULL; /* yang each list to catch emtpy */
805 yang_stmt *ych; /* y:s parent node (if choice that ye can compare to) */
806 yang_stmt *yu; /* yang unique */
807 int ret;
808 int nr=0; /* Nr of list elements for min/max check */
809 enum rfc_6020 keyw;
810
811 /* RFC 7950 7.7.5: regarding min-max elements check
812 * The behavior of the constraint depends on the type of the
813 * leaf-list's or list's closest ancestor node in the schema tree
814 * that is not a non-presence container (see Section 7.5.1):
815 * o If no such ancestor exists in the schema tree, the constraint
816 * is enforced.
817 * o Otherwise, if this ancestor is a case node, the constraint is
818 * enforced if any other node from the case exists.
819 * o Otherwise, it is enforced if the ancestor node exists.
820 */
821 yt = xml_spec(xt); /* If yt == NULL, then no gap-analysis is done */
822 /* Traverse all elemenents */
823 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
824 if ((y = xml_spec(x)) == NULL)
825 continue;
826 if ((ych = yang_choice(y)) == NULL)
827 ych = y;
828 keyw = yang_keyword_get(y);
829 if (keyw != Y_LIST && keyw != Y_LEAF_LIST){
830 if (yprev != NULL && y == yprev && yang_choice(y)==NULL){
831 /* Only lists and leaf-lists are allowed to be many
832 * This checks duplicate container and leafs
833 */
834 if (netconf_minmax_elements_xml(xret, xt, xml_name(x), 1) < 0)
835 goto done;
836 goto fail;
837 }
838 yprev = y; /* Restart min/max count */
839 continue;
840 }
841 /* Here only (leaf)lists */
842 if (yprev != NULL){ /* There exists a previous (leaf)list */
843 if (y == yprev){ /* If same yang as previous x, then skip (eg same list) */
844 nr++;
845 continue;
846 }
847 else {
848 /* Check if the list length violates min/max */
849 if ((ret = check_min_max(xt, yprev, nr, xret)) < 0)
850 goto done;
851 if (ret == 0)
852 goto fail;
853 }
854 }
855 /* Here is only new list / leaf-list */
856 yprev = y; /* Restart min/max count */
857 nr = 1;
858 /* Gap analysis: Check if there is any empty list between y and yprev
859 * Note, does not detect empty choice list (too complicated)
860 */
861 if (yt != NULL && ych != ye){
862 /* Skip analysis if Yang spec is unknown OR
863 * if we are still iterating the same Y_CASE w multiple lists
864 */
865 ye = yn_each(yt, ye);
866 if (ye && ych != ye)
867 do {
868 if (yang_config(ye) == 1 &&
869 (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST)){
870 /* Check if the list length violates min/max */
871 if ((ret = check_min_max(xt, ye, 0, xret)) < 0)
872 goto done;
873 if (ret == 0)
874 goto fail;
875 }
876 ye = yn_each(yt, ye);
877 } while(ye != NULL && /* to avoid livelock (shouldnt happen) */
878 ye != ych);
879 }
880 if (keyw != Y_LIST)
881 continue;
882 /* Here new (first element) of lists only
883 * First check unique keys
884 */
885 if ((ret = check_unique_list(x, xt, y, y, xret)) < 0)
886 goto done;
887 if (ret == 0)
888 goto fail;
889 /* Check if there is a unique constraint on the list
890 */
891 yu = NULL;
892 while ((yu = yn_each(y, yu)) != NULL) {
893 if (yang_keyword_get(yu) != Y_UNIQUE)
894 continue;
895 /* Here is a list w unique constraints identified by:
896 * its first element x, its yang spec y, its parent xt, and
897 * a unique yang spec yu,
898 */
899 if ((ret = check_unique_list(x, xt, y, yu, xret)) < 0)
900 goto done;
901 if (ret == 0)
902 goto fail;
903 }
904 } /* while x */
905
906 /* yprev if set, is a list that has been traversed
907 * This check is made in the loop as well - this is for the last list
908 */
909 if (yprev){
910 /* Check if the list length violates min/max */
911 if ((ret = check_min_max(xt, yprev, nr, xret)) < 0)
912 goto done;
913 if (ret == 0)
914 goto fail;
915 }
916 /* Check if there is any empty list between after last non-empty list
917 * Note, does not detect empty lists within choice/case (too complicated)
918 */
919 if ((ye = yn_each(yt, ye)) != NULL)
920 do {
921 if (yang_config(ye) == 1 &&
922 (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST)){
923 /* Check if the list length violates min/max */
924 if ((ret = check_min_max(xt, ye, 0, xret)) < 0)
925 goto done;
926 if (ret == 0)
927 goto fail;
928 }
929 } while((ye = yn_each(yt, ye)) != NULL);
930 retval = 1;
931 done:
932 return retval;
933 fail:
934 retval = 0;
935 goto done;
936 }
937
938 /*! Validate a single XML node with yang specification for added entry
939 * 1. Check if mandatory leafs present as subs.
940 * 2. Check leaf values, eg int ranges and string regexps.
941 * @param[in] xt XML node to be validated
942 * @param[out] xret Error XML tree. Free with xml_free after use
943 * @retval 1 Validation OK
944 * @retval 0 Validation failed (cbret set)
945 * @retval -1 Error
946 * @code
947 * cxobj *x;
948 * cbuf *xret = NULL;
949 * if ((ret = xml_yang_validate_add(h, x, &xret)) < 0)
950 * err;
951 * if (ret == 0)
952 * fail;
953 * @endcode
954 * @see xml_yang_validate_all
955 * @see xml_yang_validate_rpc
956 * @note Should need a variant accepting cxobj **xret
957 */
958 int
xml_yang_validate_add(clicon_handle h,cxobj * xt,cxobj ** xret)959 xml_yang_validate_add(clicon_handle h,
960 cxobj *xt,
961 cxobj **xret)
962 {
963 int retval = -1;
964 cg_var *cv = NULL;
965 char *reason = NULL;
966 yang_stmt *yt; /* yang spec of xt going in */
967 char *body;
968 int ret;
969 cxobj *x;
970 enum cv_type cvtype;
971
972 /* if not given by argument (overide) use default link
973 and !Node has a config sub-statement and it is false */
974 if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
975 if ((ret = check_choice(xt, yt, xret)) < 0)
976 goto done;
977 if (ret == 0)
978 goto fail;
979 if ((ret = check_mandatory(xt, yt, xret)) < 0)
980 goto done;
981 if (ret == 0)
982 goto fail;
983 /* Check leaf values */
984 switch (yang_keyword_get(yt)){
985 case Y_LEAF:
986 /* fall thru */
987 case Y_LEAF_LIST:
988 /* validate value against ranges, etc */
989 if ((cv = cv_dup(yang_cv_get(yt))) == NULL){
990 clicon_err(OE_UNIX, errno, "cv_dup");
991 goto done;
992 }
993 /* In the union case, value is parsed as generic REST type,
994 * needs to be reparsed when concrete type is selected
995 */
996 if ((body = xml_body(xt)) == NULL){
997 /* We do not allow ints to be empty. Otherwise NULL strings
998 * are considered as "" */
999 cvtype = cv_type_get(cv);
1000 if (cv_isint(cvtype) || cvtype == CGV_BOOL || cvtype == CGV_DEC64){
1001 if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), "Invalid NULL value") < 0)
1002 goto done;
1003 goto fail;
1004 }
1005 }
1006 else{
1007 if (cv_parse1(body, cv, &reason) != 1){
1008 if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
1009 goto done;
1010 goto fail;
1011 }
1012 }
1013 if ((ret = ys_cv_validate(h, cv, yt, &reason)) < 0)
1014 goto done;
1015 if (ret == 0){
1016 if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
1017 goto done;
1018 goto fail;
1019 }
1020 break;
1021 default:
1022 break;
1023 }
1024 }
1025 x = NULL;
1026 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
1027 if ((ret = xml_yang_validate_add(h, x, xret)) < 0)
1028 goto done;
1029 if (ret == 0)
1030 goto fail;
1031 }
1032 retval = 1;
1033 done:
1034 if (cv)
1035 cv_free(cv);
1036 if (reason)
1037 free(reason);
1038 return retval;
1039 fail:
1040 retval = 0;
1041 goto done;
1042 }
1043
1044 /*! Some checks done only at edit_config, eg keys in lists
1045 * @param[in] xt XML tree
1046 * @param[out] xret Error XML tree. Free with xml_free after use
1047 */
1048 int
xml_yang_validate_list_key_only(cxobj * xt,cxobj ** xret)1049 xml_yang_validate_list_key_only(cxobj *xt,
1050 cxobj **xret)
1051 {
1052 int retval = -1;
1053 yang_stmt *yt; /* yang spec of xt going in */
1054 int ret;
1055 cxobj *x;
1056
1057 /* if not given by argument (overide) use default link
1058 and !Node has a config sub-statement and it is false */
1059 if ((yt = xml_spec(xt)) != NULL &&
1060 yang_config(yt) != 0 &&
1061 yang_keyword_get(yt) == Y_LIST){
1062 if ((ret = check_list_key(xt, yt, xret)) < 0)
1063 goto done;
1064 if (ret == 0)
1065 goto fail;
1066 }
1067 x = NULL;
1068 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
1069 if ((ret = xml_yang_validate_list_key_only(x, xret)) < 0)
1070 goto done;
1071 if (ret == 0)
1072 goto fail;
1073 }
1074 retval = 1;
1075 done:
1076 return retval;
1077 fail:
1078 retval = 0;
1079 goto done;
1080 }
1081
1082 /*! Validate a single XML node with yang specification for all (not only added) entries
1083 * 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
1084 * @param[in] xt XML node to be validated
1085 * @param[out] xret Error XML tree (if retval=0). Free with xml_free after use
1086 * @retval 1 Validation OK
1087 * @retval 0 Validation failed (cbret set)
1088 * @retval -1 Error
1089 * @code
1090 * cxobj *x;
1091 * cbuf *xret = NULL;
1092 * if ((ret = xml_yang_validate_all(h, x, &xret)) < 0)
1093 * err;
1094 * if (ret == 0)
1095 * fail;
1096 * xml_free(xret);
1097 * @endcode
1098 * @see xml_yang_validate_add
1099 * @see xml_yang_validate_rpc
1100 * @note Should need a variant accepting cxobj **xret
1101 */
1102 int
xml_yang_validate_all(clicon_handle h,cxobj * xt,cxobj ** xret)1103 xml_yang_validate_all(clicon_handle h,
1104 cxobj *xt,
1105 cxobj **xret)
1106 {
1107 int retval = -1;
1108 yang_stmt *ys; /* yang node */
1109 yang_stmt *yc; /* yang child */
1110 yang_stmt *ye; /* yang must error-message */
1111 char *xpath;
1112 int nr;
1113 int ret;
1114 cxobj *x;
1115 cxobj *xp;
1116 char *ns = NULL;
1117 cbuf *cb = NULL;
1118 cvec *nsc = NULL;
1119
1120 /* if not given by argument (overide) use default link
1121 and !Node has a config sub-statement and it is false */
1122 ys=xml_spec(xt);
1123 if (ys==NULL){
1124 if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1) {
1125 clicon_log(LOG_WARNING,
1126 "%s: %d: No YANG spec for %s, validation skipped",
1127 __FUNCTION__, __LINE__, xml_name(xt));
1128 goto ok;
1129 }
1130 if ((cb = cbuf_new()) == NULL){
1131 clicon_err(OE_UNIX, errno, "cbuf_new");
1132 goto done;
1133 }
1134 cprintf(cb, "Failed to find YANG spec of XML node: %s", xml_name(xt));
1135 if ((xp = xml_parent(xt)) != NULL)
1136 cprintf(cb, " with parent: %s", xml_name(xp));
1137 if (xml2ns(xt, xml_prefix(xt), &ns) < 0)
1138 goto done;
1139 if (ns)
1140 cprintf(cb, " in namespace: %s", ns);
1141 if (netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0)
1142 goto done;
1143 goto fail;
1144 }
1145 if (yang_config(ys) != 0){
1146 /* Node-specific validation */
1147 switch (yang_keyword_get(ys)){
1148 case Y_ANYXML:
1149 case Y_ANYDATA:
1150 goto ok;
1151 break;
1152 case Y_LEAF:
1153 /* fall thru */
1154 case Y_LEAF_LIST:
1155 /* Special case if leaf is leafref, then first check against
1156 current xml tree
1157 */
1158 /* Get base type yc */
1159 if (yang_type_get(ys, NULL, &yc, NULL, NULL, NULL, NULL, NULL) < 0)
1160 goto done;
1161 if (strcmp(yang_argument_get(yc), "leafref") == 0){
1162 if ((ret = validate_leafref(xt, ys, yc, xret)) < 0)
1163 goto done;
1164 if (ret == 0)
1165 goto fail;
1166 }
1167 else if (strcmp(yang_argument_get(yc), "identityref") == 0){
1168 if ((ret = validate_identityref(xt, ys, yc, xret)) < 0)
1169 goto done;
1170 if (ret == 0)
1171 goto fail;
1172 }
1173 break;
1174 default:
1175 break;
1176 }
1177 /* must sub-node RFC 7950 Sec 7.5.3. Can be several.
1178 * XXX. use yang path instead? */
1179 yc = NULL;
1180 while ((yc = yn_each(ys, yc)) != NULL) {
1181 if (yang_keyword_get(yc) != Y_MUST)
1182 continue;
1183 xpath = yang_argument_get(yc); /* "must" has xpath argument */
1184 if (xml_nsctx_yang(yc, &nsc) < 0)
1185 goto done;
1186 if ((nr = xpath_vec_bool(xt, nsc, "%s", xpath)) < 0)
1187 goto done;
1188 if (!nr){
1189 ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
1190 if (netconf_operation_failed_xml(xret, "application",
1191 ye?yang_argument_get(ye):"must xpath validation failed") < 0)
1192 goto done;
1193 goto fail;
1194 }
1195 if (nsc){
1196 xml_nsctx_free(nsc);
1197 nsc = NULL;
1198 }
1199 }
1200 /* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
1201 if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
1202 xpath = yang_argument_get(yc); /* "when" has xpath argument */
1203 /* WHEN xpath needs namespace context */
1204 if (xml_nsctx_yang(ys, &nsc) < 0)
1205 goto done;
1206 if ((nr = xpath_vec_bool(xt, nsc, "%s", xpath)) < 0)
1207 goto done;
1208 if (nsc){
1209 xml_nsctx_free(nsc);
1210 nsc = NULL;
1211 }
1212 if (nr == 0){
1213 if ((cb = cbuf_new()) == NULL){
1214 clicon_err(OE_UNIX, errno, "cbuf_new");
1215 goto done;
1216 }
1217 cprintf(cb, "Failed WHEN condition of %s in module %s",
1218 xml_name(xt),
1219 yang_argument_get(ys_module(ys)));
1220 if (netconf_operation_failed_xml(xret, "application",
1221 cbuf_get(cb)) < 0)
1222 goto done;
1223 goto fail;
1224 }
1225 }
1226 /* Augmented when using special struct. */
1227 if ((xpath = yang_when_xpath_get(ys)) != NULL){
1228 if ((nr = xpath_vec_bool(xml_parent(xt), yang_when_nsc_get(ys),
1229 "%s", xpath)) < 0)
1230 goto done;
1231 if (nr == 0){
1232 if ((cb = cbuf_new()) == NULL){
1233 clicon_err(OE_UNIX, errno, "cbuf_new");
1234 goto done;
1235 }
1236 cprintf(cb, "Failed augmented WHEN condition %s of node %s in module %s",
1237 xpath,
1238 xml_name(xt),
1239 yang_argument_get(ys_module(ys)));
1240 if (netconf_operation_failed_xml(xret, "application",
1241 cbuf_get(cb)) < 0)
1242 goto done;
1243 goto fail;
1244 }
1245 }
1246 }
1247 x = NULL;
1248 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
1249 if ((ret = xml_yang_validate_all(h, x, xret)) < 0)
1250 goto done;
1251 if (ret == 0)
1252 goto fail;
1253 }
1254 /* Check unique and min-max after choice test for example*/
1255 if (yang_config(ys) != 0){
1256 /* Checks if next level contains any unique list constraints */
1257 if ((ret = check_list_unique_minmax(xt, xret)) < 0)
1258 goto done;
1259 if (ret == 0)
1260 goto fail;
1261 }
1262 ok:
1263 retval = 1;
1264 done:
1265 if (cb)
1266 cbuf_free(cb);
1267 if (nsc)
1268 xml_nsctx_free(nsc);
1269 return retval;
1270 fail:
1271 retval = 0;
1272 goto done;
1273 }
1274 /*! Translate a single xml node to a cligen variable vector. Note not recursive
1275 * @param[out] xret Error XML tree (if ret == 0). Free with xml_free after use
1276 * @retval 1 Validation OK
1277 * @retval 0 Validation failed (xret set)
1278 * @retval -1 Error
1279 */
1280 int
xml_yang_validate_all_top(clicon_handle h,cxobj * xt,cxobj ** xret)1281 xml_yang_validate_all_top(clicon_handle h,
1282 cxobj *xt,
1283 cxobj **xret)
1284 {
1285 int ret;
1286 cxobj *x;
1287
1288 x = NULL;
1289 while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
1290 if ((ret = xml_yang_validate_all(h, x, xret)) < 1)
1291 return ret;
1292 }
1293 if ((ret = check_list_unique_minmax(xt, xret)) < 1)
1294 return ret;
1295 return 1;
1296 }
1297