* Fixed: [namespace prefix nc is not supported](https://github.com/clicon/clixon/issues/143)

* Added more sanity checks on incoming top-level rpc and hello messages, including verifying top-level namespace
This commit is contained in:
Olof hagsand 2020-10-17 18:04:46 +02:00
parent 46bee9e358
commit a77cd27abb
8 changed files with 280 additions and 110 deletions

View file

@ -1594,6 +1594,9 @@ from_client_msg(clicon_handle h,
cxobj *xret = NULL;
uint32_t id;
enum nacm_credentials_t creds;
char *rpcname;
char *rpcprefix;
char *namespace;
clicon_debug(1, "%s", __FUNCTION__);
yspec = clicon_dbspec_yang(h);
@ -1615,18 +1618,47 @@ from_client_msg(clicon_handle h,
goto done;
goto reply;
}
if ((x = xpath_first(xt, NULL, "/rpc")) == NULL){
if ((x = xpath_first(xt, NULL, "/hello")) != NULL){
if ((ret = from_client_hello(h, x, ce, cbret)) <0)
goto done;
goto reply;
}
else{
if (netconf_malformed_message(cbret, "rpc keyword expected")< 0)
/* Check for empty frame (no mesaages), return empty message, not clear from RFC what to do */
if (xml_child_nr_type(xt, CX_ELMNT) == 0){
if (netconf_malformed_message(cbret, "Empty message in netconf rpc frame")< 0)
goto done;
goto reply;
}
/* Check for multi-messages in frame */
if (xml_child_nr_type(xt, CX_ELMNT) != 1){
if (netconf_malformed_message(cbret, "More than one message in netconf rpc frame")< 0)
goto done;
goto reply;
}
if ((x = xml_child_i_type(xt, 0, CX_ELMNT)) == NULL){ /* Shouldnt happen */
clicon_err(OE_XML, EFAULT, "No xml req (shouldnt happen)");
goto done;
}
rpcname = xml_name(x);
rpcprefix = xml_prefix(x);
if (0) { /* XXX notyet (4.8) restconf seems not to produce right namespace */
if (xml2ns(x, rpcprefix, &namespace) < 0)
goto done;
/* Only accept resolved NETCONF base namespace */
if (namespace == NULL || strcmp(namespace, NETCONF_BASE_NAMESPACE) != 0){
if (netconf_unknown_namespace(cbret, "protocol", rpcprefix, "No appropriate namespace associated with prefix")< 0)
goto done;
goto reply;
}
}
if (strcmp(rpcname, "rpc") == 0){
; /* continue below */
}
else if (strcmp(rpcname, "hello") == 0){
if ((ret = from_client_hello(h, x, ce, cbret)) <0)
goto done;
goto reply;
}
else{
if (netconf_unknown_element(cbret, "protocol", rpcname, "Unrecognized netconf operation")< 0)
goto done;
goto reply;
}
ce->ce_id = id;
if ((ret = xml_yang_validate_rpc(h, x, &xret)) < 0)
goto done;

View file

@ -79,7 +79,8 @@
static int ignore_packet_errors = 1;
static int
netconf_hello(cxobj *xn)
netconf_hello(clicon_handle h,
cxobj *xn)
{
#ifdef nyi
cxobj *x;
@ -93,125 +94,230 @@ netconf_hello(cxobj *xn)
return 0;
}
/*! Process incoming Netconf RPC netconf message
* @param[in] h Clicon handle
* @param[in] xreq XML tree containing netconf RPC message
* @param[in] yspec YANG spec
* @retval 0 OK
* @retval -1 Error
*/
int
netconf_hello_dispatch(cxobj *xn)
netconf_rpc_message(clicon_handle h,
cxobj *xrpc,
yang_stmt *yspec)
{
cxobj *xp;
int retval = -1;
int retval = -1;
cxobj *xret = NULL; /* Return (out) */
int ret;
cbuf *cbret = NULL;
cxobj *xc;
cxobj *xa;
cxobj *xa2;
if ((xp = xpath_first(xn, NULL, "//hello")) != NULL)
retval = netconf_hello(xp);
if ((ret = xml_bind_yang_rpc(xrpc, yspec, &xret)) < 0)
goto done;
if (ret > 0 &&
(ret = xml_yang_validate_rpc(h, xrpc, &xret)) < 0)
goto done;
if (ret == 0){
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
}
clicon_xml2cbuf(cbret, xret, 0, 0, -1);
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
if (netconf_rpc_dispatch(h, xrpc, &xret) < 0){
goto done;
}
else{ /* there is a return message in xret */
if (xret == NULL){
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
}
if (netconf_operation_failed(cbret, "rpc", "Internal error: no xml return")< 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error");
goto done;
}
if ((xc = xml_child_i(xret, 0))!=NULL){
xa=NULL;
/* Copy message-id attribute from incoming to reply.
* RFC 6241:
* If additional attributes are present in an <rpc> element, a NETCONF
* peer MUST return them unmodified in the <rpc-reply> element. This
* includes any "xmlns" attributes.
*/
while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
/* If attribute already exists, dont copy it */
if (xml_find_type(xc, NULL, xml_name(xa), CX_ATTR) != NULL)
continue;
if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
if (xml_addsub(xc, xa2) < 0)
goto done;
}
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
}
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0, -1);
if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
goto done;
}
}
}
ok:
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
if (xret)
xml_free(xret);
return retval;
}
/*! Process incoming packet
* @param[in] h Clicon handle
* @param[in] cb Packet buffer
/*! Process incoming a single netconf message parsed as XML
* Identify what netconf message it is
* @param[in] h Clicon handle
* @param[in] xreq XML tree containing netconf
* @param[in] yspec YANG spec
* @retval 0 OK
* @retval -1 Error
*/
static int
netconf_input_packet(clicon_handle h,
cbuf *cb)
netconf_input_packet(clicon_handle h,
cxobj *xreq,
yang_stmt *yspec)
{
int retval = -1;
char *str;
char *str0;
cxobj *xreq = NULL; /* Request (in) */
int isrpc = 0; /* either hello or rpc */
cbuf *cbret = NULL;
char *rpcname;
char *rpcprefix;
char *namespace = NULL;
clicon_debug(1, "%s", __FUNCTION__);
rpcname = xml_name(xreq);
rpcprefix = xml_prefix(xreq);
if (xml2ns(xreq, rpcprefix, &namespace) < 0)
goto done;
/* Only accept resolved NETCONF base namespace */
if (namespace == NULL || strcmp(namespace, NETCONF_BASE_NAMESPACE) != 0){
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
}
if (netconf_unknown_namespace(cbret, "protocol", rpcprefix, "No appropriate namespace associated with prefix")< 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error");
}
if (strcmp(rpcname, "rpc") == 0){
if (netconf_rpc_message(h, xreq, yspec) < 0)
goto done;
}
else if (strcmp(rpcname, "hello") == 0){
if (netconf_hello(h, xreq) < 0)
goto done;
}
else{
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
}
if (netconf_unknown_element(cbret, "protocol", rpcname, "Unrecognized netconf operation")< 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error");
}
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
return retval;
}
/*! Process incoming frame, ie a char message framed by ]]>]]>
* Parse string to xml, check only one netconf message within a frame
* @param[in] h Clicon handle
* @param[in] cb Packet buffer
* @retval 0 OK
* @retval -1 Fatal error
*/
static int
netconf_input_frame(clicon_handle h,
cbuf *cb)
{
int retval = -1;
char *str = NULL;
cxobj *xtop = NULL; /* Request (in) */
cxobj *xreq = NULL;
cxobj *xret = NULL; /* Return (out) */
cxobj *xrpc;
cxobj *xc;
cbuf *cbret = NULL;
yang_stmt *yspec;
int ret;
cxobj *xa;
cxobj *xa2;
clicon_debug(1, "%s", __FUNCTION__);
clicon_debug(2, "%s: \"%s\"", __FUNCTION__, cbuf_get(cb));
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
yspec = clicon_dbspec_yang(h);
if ((str = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
yspec = clicon_dbspec_yang(h);
if ((str0 = strdup(cbuf_get(cb))) == NULL){
clicon_log(LOG_ERR, "%s: strdup: %s", __FUNCTION__, strerror(errno));
return -1;
}
str = str0;
/* Parse incoming XML message */
if (clixon_xml_parse_string(str, YB_MODULE, yspec, &xreq, NULL) < 0){
free(str0);
if ((ret = clixon_xml_parse_string(str, YB_RPC, yspec, &xtop, &xret)) < 0){
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (netconf_operation_failed(cbret, "rpc", clicon_err_reason)< 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error"); /* XXX */
goto ok;
}
if (ret == 0){
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
}
clicon_xml2cbuf(cbret, xret, 0, 0, -1);
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
/* Check for empty frame (no mesaages), return empty message, not clear from RFC what to do */
if (xml_child_nr_type(xtop, CX_ELMNT) == 0){
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
/* Check for multi-messages in frame */
if (xml_child_nr_type(xtop, CX_ELMNT) != 1){
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (netconf_malformed_message(cbret, "More than one message in netconf rpc frame")< 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
if ((xreq = xml_child_i_type(xtop, 0, CX_ELMNT)) == NULL){ /* Shouldnt happen */
clicon_err(OE_XML, EFAULT, "No xml req (shouldnt happen)");
goto done;
}
free(str0);
if ((xrpc=xpath_first(xreq, NULL, "//rpc")) != NULL){
isrpc++;
if ((ret = xml_bind_yang_rpc(xrpc, yspec, &xret)) < 0)
goto done;
if (ret > 0 &&
(ret = xml_yang_validate_rpc(h, xrpc, &xret)) < 0)
goto done;
if (ret == 0){
clicon_xml2cbuf(cbret, xret, 0, 0, -1);
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
}
else
if (xpath_first(xreq, NULL, "//hello") != NULL)
;
else{
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropped");
goto done;
}
if (!isrpc){ /* hello */
if (netconf_hello_dispatch(xreq) < 0)
goto done;
}
else /* rpc */
if (netconf_rpc_dispatch(h, xrpc, &xret) < 0){
goto done;
}
else{ /* there is a return message in xret */
if (xret == NULL){
if (netconf_operation_failed(cbret, "rpc", "Internal error: no xml return")< 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error");
goto done;
}
if ((xc = xml_child_i(xret, 0))!=NULL){
xa=NULL;
/* Copy message-id attribute from incoming to reply.
* RFC 6241:
* If additional attributes are present in an <rpc> element, a NETCONF
* peer MUST return them unmodified in the <rpc-reply> element. This
* includes any "xmlns" attributes.
*/
while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
/* If attribute already exists, dont copy it */
if (xml_find_type(xc, NULL, xml_name(xa), CX_ATTR) != NULL)
continue;
if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
if (xml_addsub(xc, xa2) < 0)
goto done;
}
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0, -1);
if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
goto done;
}
}
}
if (netconf_input_packet(h, xreq, yspec) < 0)
goto done;
ok:
retval = 0;
done:
if (xreq)
xml_free(xreq);
done:
if (str)
free(str);
if (xtop)
xml_free(xtop);
if (xret)
xml_free(xret);
if (cbret)
@ -269,7 +375,7 @@ netconf_input_cb(int s,
/* OK, we have an xml string from a client */
/* Remove trailer */
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
if (netconf_input_packet(h, cb) < 0 &&
if (netconf_input_frame(h, cb) < 0 &&
!ignore_packet_errors) // default is to ignore errors
goto done;
if (cc_closed)