From 849e46191efa6d404546c46d595dfbe9515a161e Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 5 Jun 2018 09:42:11 +0200 Subject: [PATCH] * Added xmlns validation * for eg --- CHANGELOG.md | 2 + lib/clixon/clixon_xml.h | 2 +- lib/src/clixon_plugin.c | 2 +- lib/src/clixon_xml.c | 77 +++++++++++++++++++++++++++++--- lib/src/clixon_xml_parse.y | 91 ++++++++++++++++++++------------------ test/test_cli.sh | 2 +- 6 files changed, 124 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9faaded1..f10731fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: +* Added xmlns validation + * for eg * Added yang identityref runtime validation * Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. * Replace functions as follows in CLI SPEC files: diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 6a0513ec..f22d30f2 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -69,7 +69,7 @@ typedef struct xml cxobj; /* struct defined in clicon_xml.c */ * @retval 1 Abort, dont continue with others * @retval 2 Locally, just abort this subtree, continue with others */ -typedef int (xml_applyfn_t)(cxobj *yn, void *arg); +typedef int (xml_applyfn_t)(cxobj *x, void *arg); /* * xml_flag() flags: diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index a0f2a4d3..2e1ffdfb 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -430,7 +430,7 @@ rpc_callback_register(clicon_handle h, clicon_err(OE_DB, errno, "malloc: %s", strerror(errno)); goto done; } - memset (rc, 0, sizeof (*rc)); + memset(rc, 0, sizeof(*rc)); rc->rc_callback = cb; rc->rc_arg = arg; rc->rc_tag = strdup(tag); /* XXX strdup memleak */ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index c23d8d37..edc11643 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -109,7 +109,7 @@ struct xml{ enum cxobj_type x_type; /* type of node: element, attribute, body */ char *x_value; /* attribute and body nodes have values */ int _x_vector_i; /* internal use: xml_child_each */ - int x_flags; /* Flags according to XML_FLAG_* above */ + int x_flags; /* Flags according to XML_FLAG_* */ yang_stmt *x_spec; /* Pointer to specification, eg yang, by reference, dont free */ cg_var *x_cv; /* If body this contains the typed value */ @@ -181,7 +181,7 @@ xml_namespace(cxobj *xn) return xn->x_namespace; } -/*! Set name of xnode, name is copied +/*! Set name space of xnode, namespace is copied * @param[in] xn xml node * @param[in] namespace new namespace, null-terminated string, copied by function * @retval -1 on error with clicon-err set @@ -204,6 +204,71 @@ xml_namespace_set(cxobj *xn, return 0; } +/*! See if xmlns:= exists, if so return + * + * @param[in] xn XML node + * @param[in] nsn Namespace name + * @retval URI return associated URI if found + * @retval NULL No namespace name binding found for nsn + */ +static char * +xmlns_check(cxobj *xn, + char *nsn) +{ + cxobj *x = NULL; + char *xns; + + while ((x = xml_child_each(xn, x, -1)) != NULL) + if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 && + strcmp(xml_name(x), nsn) == 0) + return xml_value(x); + return NULL; +} + +/*! Check namespace of xml node by searhing recursively among ancestors + * @param[in] xn xml node + * @param[in] namespace check validity of namespace + * @retval 0 Found / validated or no yang spec + * @retval -1 Not found + * @note This function is grossly inefficient + */ +static int +xml_namespace_check(cxobj *xn, + void *arg) +{ + cxobj *xp = NULL; + char *nsn; + char *n; + yang_stmt *ys = xml_spec(xn); + + /* No namespace name - comply */ + if ((nsn = xml_namespace(xn)) == NULL) + return 0; + /* Check if NSN defined in same node */ + if (xmlns_check(xn, nsn) != NULL) + return 0; + /* Check if NSN defined in some ancestor */ + while ((xp = xml_parent(xn)) != NULL) { + if (xmlns_check(xp, nsn) != NULL) + return 0; + xn = xp; + } + if (ys == NULL) + return 0; /* If no yang spec */ + else{ + /* Check if my namespace */ + if ((n = yang_find_myprefix(ys)) != NULL && strcmp(nsn,n)==0) + return 0; + /* Check if any imported module */ + if (yang_find_module_by_prefix(ys, nsn) != NULL) + return 0; + } + /* Not found, error */ + clicon_err(OE_XML, ENOENT, "Namespace name %s in %s:%s not found", + nsn, nsn, xml_name(xn)); + return -1; +} + /*! Get parent of xnode * @param[in] xn xml node * @retval parent xml node @@ -869,7 +934,6 @@ xml_find_value(cxobj *xt, * Explaining picture: * xt --> x --> bx (x_type=CX_BODY) * x_name=name return x_value - */ char * xml_find_body(cxobj *xt, @@ -1247,6 +1311,9 @@ _xml_parse(const char *str, goto done; if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */ goto done; + /* Verify namespaces after parsing */ + if (xml_apply0(xt, CX_ELMNT, xml_namespace_check, NULL) < 0) + goto done; /* Sort the complete tree after parsing */ if (yspec){ if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) @@ -1658,7 +1725,6 @@ xml_apply0(cxobj *xn, return retval; } - /*! Apply a function call recursively on all ancestors * Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for * each object found. The function is called with the xml node and an @@ -1861,7 +1927,6 @@ xml_operation2str(enum operation_type op) } } - /* * Turn this on to get a xml parse and pretty print test program * Usage: xpath @@ -1892,7 +1957,7 @@ main(int argc, char **argv) return 0; } if (xml_parse_file(0, "", NULL, &xt) < 0){ - fprintf(stderr, "parsing 2\n"); + fprintf(stderr, "xml parse error %s\n", clicon_err_reason); return -1; } xc = NULL; diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y index ab9d5193..8a80ff8d 100644 --- a/lib/src/clixon_xml_parse.y +++ b/lib/src/clixon_xml_parse.y @@ -43,12 +43,11 @@ %token NAME CHARDATA %token VER ENC -%token BSLASH ESLASH +%token BSLASH ESLASH %token BTEXT ETEXT %token BCOMMENT ECOMMENT - -%type attvalue attqname +%type attvalue %lex-param {void *_ya} /* Add this argument to parse() and lex() function */ %parse-param {void *_ya} @@ -117,7 +116,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya, char *ver) { if(strcmp(ver, "1.0")){ - clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0\n", ver); + clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0", ver); free(ver); return -1; } @@ -125,15 +124,41 @@ xml_parse_version(struct xml_parse_yacc_arg *ya, return 0; } -/*! Parse Qualified name +/*! Parse Qualified name --> Unprefixed name * @param[in] ya XML parser yacc handler struct * @param[in] prefix Prefix, namespace, or NULL * @param[in] localpart Name */ static int -xml_parse_qname(struct xml_parse_yacc_arg *ya, - char *prefix, - char *name) +xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya, + char *name) +{ + int retval = -1; + cxobj *x; + yang_stmt *y = NULL; /* yang node */ + cxobj *xp; /* xml parent */ + + xp = ya->ya_xparent; + if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0) + goto done; + if ((x = xml_new(name, xp, y)) == NULL) + goto done; + ya->ya_xelement = x; + retval = 0; + done: + free(name); + return retval; +} + +/*! Parse Qualified name -> PrefixedName + * @param[in] ya XML parser yacc handler struct + * @param[in] prefix Prefix, namespace, or NULL + * @param[in] localpart Name + */ +static int +xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya, + char *prefix, + char *name) { int retval = -1; cxobj *x; @@ -197,7 +222,7 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, goto done; } if (xml_namespace(x)!=NULL){ - clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s\n", + clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s", xml_namespace(x), xml_name(x), name); goto done; } @@ -235,7 +260,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya, cxobj *xc; if (strcmp(xml_name(x), name)){ - clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n", + clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", xml_namespace(x), xml_name(x), namespace, @@ -244,7 +269,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya, } if (xml_namespace(x)==NULL || strcmp(xml_namespace(x), namespace)){ - clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n", + clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", xml_namespace(x), xml_name(x), namespace, @@ -276,44 +301,29 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya, static int xml_parse_attr(struct xml_parse_yacc_arg *ya, - char *qname, + char *prefix, + char *name, char *attval) { int retval = -1; cxobj *xa; - if ((xa = xml_new(qname, ya->ya_xelement, NULL)) == NULL) + if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL) goto done; xml_type_set(xa, CX_ATTR); + if (prefix && xml_namespace_set(xa, prefix) < 0) + goto done; if (xml_value_set(xa, attval) < 0) goto done; retval = 0; done: - free(qname); + free(name); + if (prefix) + free(prefix); free(attval); return retval; } -/*! Parse Attribue Qualified name, Just transform prefix:name into a new string - * - */ -static char* -xml_merge_attqname(struct xml_parse_yacc_arg *ya, - char *prefix, - char *name) -{ - char *str; - int len = strlen(prefix)+strlen(name)+2; - - if ((str=malloc(len)) == NULL) - return NULL; - snprintf(str, len, "%s:%s", prefix, name); - free(prefix); - free(name); - return str; -} - - %} %% @@ -344,9 +354,9 @@ element : '<' qname attrs element1 { clicon_debug(3, "element -> < qname attrs element1"); } ; -qname : NAME { if (xml_parse_qname(_YA, NULL, $1) < 0) YYABORT; +qname : NAME { if (xml_parse_unprefixed_name(_YA, $1) < 0) YYABORT; clicon_debug(3, "qname -> NAME %s", $1);} - | NAME ':' NAME { if (xml_parse_qname(_YA, $1, $3) < 0) YYABORT; + | NAME ':' NAME { if (xml_parse_prefixed_name(_YA, $1, $3) < 0) YYABORT; clicon_debug(3, "qname -> NAME : NAME");} ; @@ -385,15 +395,10 @@ attrs : attrs attr | ; -attr : attqname '=' attvalue { if (xml_parse_attr(_YA, $1, $3) < 0) YYABORT; } +attr : NAME '=' attvalue { if (xml_parse_attr(_YA, NULL, $1, $3) < 0) YYABORT; } + | NAME ':' NAME '=' attvalue { if (xml_parse_attr(_YA, $1, $3, $5) < 0) YYABORT; } ; -attqname : NAME {$$ = $1;} - | NAME ':' NAME - { if (($$ = xml_merge_attqname(_YA, $1, $3)) == NULL) YYABORT; } - ; - - attvalue : '\"' CHARDATA '\"' { $$=$2; /* $2 must be consumed */} | '\"' '\"' { $$=strdup(""); /* $2 must be consumed */} ; diff --git a/test/test_cli.sh b/test/test_cli.sh index ee524339..8b3857ff 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -67,7 +67,7 @@ new "cli configure using encoded chars name <&" expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type ianaift:ethernetCsmacd" 0 "" new "cli failed validate" -expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "Missing mandatory variable" +expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Missing mandatory variable" new "cli configure more" expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$"