From 5cabc11bfb91a47a66c35d56b7fa81eb6d93ac87 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 7 Jun 2018 21:34:52 +0200 Subject: [PATCH] * Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests --- CHANGELOG.md | 1 + apps/backend/backend_client.c | 6 ++--- apps/backend/backend_main.c | 21 +++++++-------- apps/backend/backend_plugin.c | 2 ++ apps/cli/cli_main.c | 38 +++++++++++++++++---------- apps/netconf/netconf_main.c | 11 ++++++-- example/example_backend.c | 16 ++++++------ example/example_cli.c | 9 ++++++- include/clixon_custom.h | 8 ++++++ lib/clixon/clixon_xml.h | 2 -- lib/src/clixon_proto_client.c | 3 +++ lib/src/clixon_xml_map.c | 2 +- test/test_auth.sh | 2 +- test/test_auth_ext.sh | 48 ++++++++++++++++++++++++++++++++--- test/test_identity.sh | 11 ++++++-- 15 files changed, 132 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c73fdc88..e2ee0eae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * 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 -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests * Added a generated CLI show command that works on the generated parse tree with auto completion. * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); * The example contains a more elaborate example. diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 469f7652..d721b471 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -803,7 +803,7 @@ from_client_debug(clicon_handle h, return retval; } -/*! Match nacm access operations according to RFC8321 3.4.4. +/*! Match nacm access operations according to RFC8341 3.4.4. * Incoming RPC Message Validation Step 7 (c) * The rule's "access-operations" leaf has the "exec" bit set or * has the special value "*". @@ -832,7 +832,7 @@ nacm_match_access(char *access_operations, * @retval 0 Matching rule AND Not access and cbret set * @retval 1 Matchung rule AND Access * @retval 2 No matching rule Goto step 10 - * From RFC8321 3.4.4. Incoming RPC Message Validation + * From RFC8341 3.4.4. Incoming RPC Message Validation +---------+-----------------+---------------------+-----------------+ | Method | Resource class | NETCONF operation | Access | | | | | operation | @@ -911,7 +911,7 @@ nacm_match_rule(clicon_handle h, * @retval -1 Error * @retval 0 Not access and cbret set * @retval 1 Access - * From RFC8321 3.4.4. Incoming RPC Message Validation + * From RFC8341 3.4.4. Incoming RPC Message Validation */ static int nacm_access(clicon_handle h, diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 9babb24e..db331b7e 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -117,7 +117,8 @@ backend_sig_term(int arg) /*! usage */ static void -usage(char *argv0, clicon_handle h) +usage(clicon_handle h, + char *argv0) { char *plgdir = clicon_backend_dir(h); char *confsock = clicon_sock(h); @@ -581,11 +582,11 @@ main(int argc, break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &debug) != 1) - usage(argv[0], h); + usage(h, argv[0]); break; case 'f': /* config file */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; } @@ -602,7 +603,7 @@ main(int argc, /* Find and read configfile */ if (clicon_options_main(h) < 0){ if (help) - usage(argv[0], h); + usage(h, argv[0]); return -1; } /* External NACM file? */ @@ -621,12 +622,12 @@ main(int argc, break; /* see above */ case 'd': /* Plugin directory */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_BACKEND_DIR", optarg); break; case 'b': /* XMLDB database directory */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_XMLDB_DIR", optarg); break; case 'F' : /* foreground */ @@ -640,7 +641,7 @@ main(int argc, break; case 'u': /* config unix domain path / ip address */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_SOCK", optarg); break; case 'P': /* pidfile */ @@ -650,7 +651,7 @@ main(int argc, clicon_option_str_set(h, "CLICON_STARTUP_MODE", optarg); if (clicon_startup_mode(h) < 0){ fprintf(stderr, "Invalid startup mode: %s\n", optarg); - usage(argv[0], h); + usage(h, argv[0]); } break; case 'c': /* Load application config */ @@ -668,7 +669,7 @@ main(int argc, break; } default: - usage(argv[0], h); + usage(h, argv[0]); break; } @@ -677,7 +678,7 @@ main(int argc, /* Defer: Wait to the last minute to print help message */ if (help) - usage(argv[0], h); + usage(h, argv[0]); /* Check pid-file, if zap kil the old daemon, else return here */ if ((pidfile = clicon_backend_pidfile(h)) == NULL){ diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index e69c21fa..4d7e9eaa 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -157,6 +157,8 @@ clixon_plugin_statedata(clicon_handle h, if (reason){ while ((xc = xml_child_i(*xtop, 0)) != NULL) xml_purge(xc); + clicon_log(LOG_NOTICE, "%s: Plugin '%s' state callback failed", + __FUNCTION__, cp->cp_name); if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0) goto done; goto ok; diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 4e5b2048..685546f7 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -71,7 +71,7 @@ #include "cli_handle.h" /* Command line options to be passed to getopt(3) */ -#define CLI_OPTS "hD:f:xl:F:1u:d:m:qpGLy:c:" +#define CLI_OPTS "hD:f:xl:F:1u:d:m:qpGLy:c:U:" /*! terminate cli application */ static int @@ -194,7 +194,8 @@ dump_configfile_xml_fn(FILE *fout, } static void -usage(char *argv0, clicon_handle h) +usage(clicon_handle h, + char *argv0) { char *confsock = clicon_sock(h); char *plgdir = clicon_cli_dir(h); @@ -217,7 +218,8 @@ usage(char *argv0, clicon_handle h) "\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n" "\t-l \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n" "\t-y \tOverride yang spec file (dont include .yang suffix)\n" - "\t-c \tSpecify cli spec file.\n", + "\t-c \tSpecify cli spec file.\n" + "\t-U \tOver-ride unix user with a pseudo user for NACM.\n", argv0, confsock ? confsock : "none", plgdir ? plgdir : "none" @@ -256,7 +258,9 @@ main(int argc, char **argv) /* Initiate CLICON handle */ if ((h = cli_handle_init()) == NULL) goto done; - /* Set username to clicon handle. Use in all communication to backend */ + /* Set username to clicon handle. Use in all communication to backend + * Note, can be overridden by -U + */ if ((pw = getpwuid(getuid())) == NULL){ clicon_err(OE_UNIX, errno, "getpwuid"); goto done; @@ -283,11 +287,11 @@ main(int argc, char **argv) break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &debug) != 1) - usage(argv[0], h); + usage(h, argv[0]); break; case 'f': /* config file */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; case 'x': /* dump config file as xml (migration from .conf file)*/ @@ -305,9 +309,9 @@ main(int argc, char **argv) logdst = CLICON_LOG_STDOUT; break; default: - usage(argv[0], h); + usage(h, argv[0]); } - break; + break; } /* * Logs, error and debug to stderr or syslog, set debug level @@ -327,7 +331,7 @@ main(int argc, char **argv) /* Find and read configfile */ if (clicon_options_main(h) < 0){ if (help) - usage(argv[0], h); + usage(h, argv[0]); return -1; } @@ -352,17 +356,17 @@ main(int argc, char **argv) break; case 'u': /* config unix domain path/ ip host */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_SOCK", optarg); break; case 'd': /* Plugin directory: overrides configfile */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CLI_DIR", optarg); break; case 'm': /* CLI syntax mode */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CLI_MODE", optarg); break; case 'q' : /* Quiet mode */ @@ -385,8 +389,14 @@ main(int argc, char **argv) clicon_option_str_set(h, "CLICON_CLISPEC_FILE", optarg); break; } + case 'U': /* Clixon 'pseudo' user */ + if (!strlen(optarg)) + usage(h, argv[0]); + if (clicon_username_set(h, optarg) < 0) + goto done; + break; default: - usage(argv[0], h); + usage(h, argv[0]); break; } } @@ -395,7 +405,7 @@ main(int argc, char **argv) /* Defer: Wait to the last minute to print help message */ if (help) - usage(argv[0], h); + usage(h, argv[0]); /* Setup signal handlers */ cli_signal_init(h); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 20fd8cdf..708b49f5 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -71,7 +71,7 @@ #include "netconf_rpc.h" /* Command line options to be passed to getopt(3) */ -#define NETCONF_OPTS "hDqf:d:Sy:" +#define NETCONF_OPTS "hDqf:d:Sy:U:" /*! Process incoming packet * @param[in] h Clicon handle @@ -292,7 +292,8 @@ usage(clicon_handle h, "\t-f \tConfiguration file (mandatory)\n" "\t-d \tSpecify netconf plugin directory dir (default: %s)\n" "\t-S\t\tLog on syslog\n" - "\t-y \tOverride yang spec file (dont include .yang suffix)\n", + "\t-y \tOverride yang spec file (dont include .yang suffix)\n" + "\t-U \tOver-ride unix user with a pseudo user for NACM.\n", argv0, clicon_netconf_dir(h) ); @@ -379,6 +380,12 @@ main(int argc, clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg); break; } + case 'U': /* Clixon 'pseudo' user */ + if (!strlen(optarg)) + usage(h, argv[0]); + if (clicon_username_set(h, optarg) < 0) + goto done; + break; default: usage(h, argv[0]); break; diff --git a/example/example_backend.c b/example/example_backend.c index a3dfe832..4db69f71 100644 --- a/example/example_backend.c +++ b/example/example_backend.c @@ -180,7 +180,7 @@ empty(clicon_handle h, /* Clicon handle */ * Real code would poll state */ int -plugin_statedata(clicon_handle h, +example_statedata(clicon_handle h, char *xpath, cxobj *xstate) { @@ -214,8 +214,8 @@ plugin_statedata(clicon_handle h, * @note This assumes example yang with interfaces/interface */ int -plugin_reset(clicon_handle h, - const char *db) +example_reset(clicon_handle h, + const char *db) { int retval = -1; cxobj *xt = NULL; @@ -250,7 +250,7 @@ plugin_reset(clicon_handle h, * can be processed with the standard getopt(3). */ int -plugin_start(clicon_handle h, +example_start(clicon_handle h, int argc, char **argv) { @@ -261,11 +261,11 @@ clixon_plugin_api *clixon_plugin_init(clicon_handle h); static clixon_plugin_api api = { "example", /* name */ - clixon_plugin_init, /* init */ - plugin_start, /* start */ + clixon_plugin_init, /* init - must be called clixon_plugin_init */ + example_start, /* start */ NULL, /* exit */ - .ca_reset=plugin_reset, /* reset */ - .ca_statedata=plugin_statedata, /* statedata */ + .ca_reset=example_reset, /* reset */ + .ca_statedata=example_statedata, /* statedata */ .ca_trans_begin=NULL, /* trans begin */ .ca_trans_validate=transaction_validate,/* trans validate */ .ca_trans_complete=NULL, /* trans complete */ diff --git a/example/example_cli.c b/example/example_cli.c index fef13c35..ed5ebb20 100644 --- a/example/example_cli.c +++ b/example/example_cli.c @@ -90,17 +90,24 @@ fib_route_rpc(clicon_handle h, cxobj *xtop = NULL; cxobj *xrpc; cxobj *xret = NULL; + cxobj *xerr; /* User supplied variable in CLI command */ instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */ /* Create XML for fib-route netconf RPC */ - if (xml_parse_va(&xtop, NULL, "%s", cv_string_get(instance)) < 0) + if (xml_parse_va(&xtop, NULL, "%s", + clicon_username_get(h), + cv_string_get(instance)) < 0) goto done; /* Skip top-level */ xrpc = xml_child_i(xtop, 0); /* Send to backend */ if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0) goto done; + if ((xerr = xpath_first(xret, "/rpc-error")) != NULL){ + clicon_rpc_generate_error("Get configuration", xerr); + goto done; + } /* Print result */ xml_print(stdout, xml_child_i(xret, 0)); retval = 0; diff --git a/include/clixon_custom.h b/include/clixon_custom.h index 92b12204..d95ce399 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -47,3 +47,11 @@ int strverscmp (__const char *__s1, __const char *__s2); * This was obsoleted in 3.7 */ #undef COMPAT_CLIV + +/* Set if you want to assert that all rpc messages have set username + */ +#undef RPC_USERNAME_ASSERT + +/* Full xmlns validation check is made only if XML has associated YANG spec +*/ +#define XMLNS_YANG_ONLY 1 diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 34b0265a..2199629b 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -80,8 +80,6 @@ typedef int (xml_applyfn_t)(cxobj *x, void *arg); #define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */ #define XML_FLAG_NONE 0x10 /* Node is added as NONE */ -/* Full xmlns validation check is made only if XML has associated YANG spec */ -#define XMLNS_YANG_ONLY 1 /* Sort and binary search of XML children * Experimental diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 7c1bec04..7e255208 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -91,6 +91,9 @@ clicon_rpc_msg(clicon_handle h, cxobj *xret = NULL; yang_spec *yspec; +#ifdef RPC_USERNAME_ASSERT + assert(strstr(msg->op_body, "username")!=NULL); /* XXX */ +#endif clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body); if ((sock = clicon_sock(h)) == NULL){ clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set"); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 6960d6a7..cac926d9 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1779,7 +1779,7 @@ xml_merge1(cxobj *x0, * @param[in] x0 Base xml tree (can be NULL in add scenarios) * @param[in] x1 xml tree which modifies base * @param[in] yspec Yang spec - * @param[out] reason If retval=0 a malloced string. Needs to be freed by caller + * @param[out] reason If retval=0, reason is set. Malloced. Needs to be freed by caller * @retval 0 OK. If reason is set, Yang error * @retval -1 Error * @note both x0 and x1 need to be top-level trees diff --git a/test/test_auth.sh b/test/test_auth.sh index 447fb4bf..a99e0242 100755 --- a/test/test_auth.sh +++ b/test/test_auth.sh @@ -1,6 +1,6 @@ #!/bin/bash # Authentication and authorization and IETF NACM -# See RFC 8321 A.2 +# See RFC 8341 A.2 # But replaced ietf-netconf-monitoring with * APPNAME=example diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh index 373fca1f..a6162fe1 100755 --- a/test/test_auth_ext.sh +++ b/test/test_auth_ext.sh @@ -1,7 +1,7 @@ #!/bin/bash # Authentication and authorization and IETF NACM # External NACM file -# See RFC 8321 A.2 +# See RFC 8341 A.2 # But replaced ietf-netconf-monitoring with * APPNAME=example @@ -16,9 +16,11 @@ nacmfile=$dir/nacmfile cat < $cfg $cfg - /usr/local/share/$APPNAME/yang + /usr/local/share/clixon $fyang /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/backend + example_backend.so$ /usr/local/lib/$APPNAME/restconf /usr/local/lib/$APPNAME/cli $APPNAME @@ -36,6 +38,12 @@ EOF cat < $fyang module $APPNAME{ prefix ex; + import ietf-interfaces { + prefix if; + } + import iana-if-type { + prefix ianaift; + } container authentication { description "Example code for enabling www basic auth and some example users"; @@ -61,6 +69,21 @@ module $APPNAME{ type int32; description "something to edit"; } + container interfaces-state { + config false; + list interface{ + key "name"; + leaf name{ + type string; + } + leaf type{ + type string; + } + leaf if-index { + type int32; + } + } + } } EOF @@ -75,7 +98,6 @@ cat < $nacmfile admin admin adm1 - olof limited @@ -167,7 +189,7 @@ new "restconf DELETE whole datastore" expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" "" new2 "auth get" -expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": null} +expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} ' new "Set x to 0" @@ -203,6 +225,24 @@ expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf new2 "guest edit nacm" expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' +new "cli show conf as admin" +expectfn "$clixon_cli -1 -U adm1 -f $cfg -y $fyang show conf" 0 "^x 1;$" + +new "cli show conf as limited" +expectfn "$clixon_cli -1 -U wilma -f $cfg -y $fyang show conf" 0 "^x 1;$" + +new "cli show conf as guest" +expectfn "$clixon_cli -1 -U guest -f $cfg -y $fyang show conf" 255 "CLI command error" + +new "cli rpc as admin" +expectfn "$clixon_cli -1 -U adm1 -f $cfg -y $fyang rpc ipv4" 0 "2.3.4.5" + +new "cli rpc as limited" +expectfn "$clixon_cli -1 -U wilma -f $cfg -y $fyang rpc ipv4" 0 "access-denied" + +new "cli rpc as guest" +expectfn "$clixon_cli -1 -U guest -f $cfg -y $fyang rpc ipv4" 0 "access-denied" + new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf diff --git a/test/test_identity.sh b/test/test_identity.sh index b828fc1f..2d01fa01 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -143,12 +143,19 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" -#new "Set crypto to x:des3" -#expecteof "$clixon_netconf -qf $cfg -y $fyang" "x:des3]]>]]>" "^]]>]]>$" +new "Set crypto to des:des3 using xmlns" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "des:des3]]>]]>" "^]]>]]>$" new "netconf validate" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +# XXX this is not supported +#new "Set crypto to x:des3 using xmlns" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" "x:des3]]>]]>" "^]]>]]>$" + +#new "netconf validate" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + new "Set crypto to foo:bar" expecteof "$clixon_netconf -qf $cfg -y $fyang" "foo:bar]]>]]>" "^]]>]]>$"