From d57a6cf53cdda4a3e5da122eb34de5ce069676f0 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 24 Apr 2018 17:43:19 +0200 Subject: [PATCH] * plugin_start() callbacks added for restconf * Hard-wired users for authentication example --- CHANGELOG.md | 1 + apps/restconf/restconf_main.c | 12 +++++- example/example.yang | 21 ---------- example/example_restconf.c | 75 ++++++++++++++++++++++------------- lib/clixon/clixon_plugin.h | 4 +- lib/src/clixon_plugin.c | 12 +++++- test/test_auth.sh | 41 ++----------------- test/test_auth_ext.sh | 52 +++++++----------------- 8 files changed, 92 insertions(+), 126 deletions(-) mode change 100644 => 100755 test/test_auth_ext.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d3d88b2..73006c3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ clixon_plugin_api *clixon_plugin_init(clicon_handle h) ### Minor changes: +* plugin_start() callbacks added for restconf * Authentication * Example extended with http basic authentication for restconf * Documentation in FAQ.md diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 0bc98bda..7ffc9260 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -503,6 +503,7 @@ main(int argc, { int retval = -1; int sock; + char *argv0 = argv[0]; FCGX_Request request; FCGX_Request *r = &request; char c; @@ -511,7 +512,8 @@ main(int argc, clicon_handle h; char *yangspec=NULL; char *dir; - + char *tmp; + /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_SYSLOG); /* Create handle */ @@ -575,6 +577,14 @@ main(int argc, if (yang_spec_main(h) == NULL) goto done; + /* Call start function in all plugins before we go interactive + Pass all args after the standard options to plugin_start + */ + tmp = *(argv-1); + *(argv-1) = argv0; + clixon_plugin_start(h, argc+1, argv-1); + *(argv-1) = tmp; + if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){ clicon_err(OE_CFG, errno, "No CLICON_RESTCONF_PATH in clixon configure file"); goto done; diff --git a/example/example.yang b/example/example.yang index 08799eae..f9159228 100644 --- a/example/example.yang +++ b/example/example.yang @@ -11,27 +11,6 @@ module example { } description "Example code that includes ietf-ip and ietf-routing"; - container authentication { - description "Example code for enabling www basic auth and some example - users"; - leaf basic_auth{ - description "Basic user / password authentication as in HTTP basic auth"; - type boolean; - default false; - } - list auth { - description "user / password entries. Valid if basic_auth=true"; - key user; - leaf user{ - description "User name"; - type string; - } - leaf password{ - description "Password"; - type string; - } - } - } rpc client-rpc { description "Example local client-side RPC that is processed by the the netconf/restconf and not sent to the backend. diff --git a/example/example_restconf.c b/example/example_restconf.c index bace08fd..fd579954 100644 --- a/example/example_restconf.c +++ b/example/example_restconf.c @@ -53,6 +53,11 @@ static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; +/* Use http basic auth. Set by starting restonf with: + clixon_restconf ... -- -a +*/ +static int basic_auth = 0; + /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. @@ -181,22 +186,21 @@ b64_decode(const char *src, } /*! Process a rest request that requires (cookie) "authentication" - * Note, this is loaded as dlsym fixed symbol in plugin * @param[in] h Clixon handle * @param[in] arg Argument. Here: Fastcgi request handle * @retval -1 Fatal error * @retval 0 Unauth * @retval 1 Auth - * + * @note: Three hardwired users: adm1, wilma, guest w password "bar". + * Enabled by passing -- -a to the main function */ int -plugin_credentials(clicon_handle h, - void *arg) +example_restconf_credentials(clicon_handle h, + void *arg) { int retval = -1; FCGX_Request *r = (FCGX_Request *)arg; cxobj *xt = NULL; - cxobj *x; char *auth; char *user = NULL; char *passwd; @@ -204,22 +208,9 @@ plugin_credentials(clicon_handle h, size_t authlen; cbuf *cb = NULL; int ret; - char *xbody; - - clicon_debug(1, "%s", __FUNCTION__); - /* XXX This is a kludge to reset the user not remaining from previous */ - if (clicon_username_set(h, "admin") < 0) - goto done; - /* Check if basic_auth set, if not return OK */ - if (clicon_rpc_get_config(h, "running", "authentication", &xt) < 0) - goto done; - if (clicon_username_set(h, "none") < 0) - goto done; - if ((x = xpath_first(xt, "authentication/basic_auth")) == NULL) - goto ok; - if ((xbody = xml_body(x)) == NULL) - goto ok; - if (strcmp(xbody, "true")) + + /* HTTP basic authentication not enabled, pass with user "none" */ + if (basic_auth==0) goto ok; /* At this point in the code we must use HTTP basic authentication */ if ((auth = FCGX_GetParam("HTTP_AUTHORIZATION", r->envp)) == NULL) @@ -242,14 +233,15 @@ plugin_credentials(clicon_handle h, goto fail; *passwd = '\0'; passwd++; - clicon_debug(1, "%s user:%s passwd:%s", __FUNCTION__, user, passwd); + clicon_debug(1, "%s http user:%s passwd:%s", __FUNCTION__, user, passwd); /* Here get auth sub-tree whjere all the users are */ if ((cb = cbuf_new()) == NULL) goto done; - cprintf(cb, "authentication/auth[user=%s]", user); - if ((x = xpath_first(xt, cbuf_get(cb))) == NULL) - goto fail; - passwd2 = xml_find_body(x, "password"); + /* Hardcoded user/passwd */ + if (strcmp(user, "wilma")==0 || strcmp(user, "adm1")==0 || + strcmp(user, "quest")==0){ + passwd2 = "bar"; + } if (strcmp(passwd, passwd2)) goto fail; retval = 1; @@ -287,14 +279,41 @@ restconf_client_rpc(clicon_handle h, return 0; } +/*! Start example restonf plugin. Set authentication method + * Arguments are argc/argv after -- + * Currently defined: -a enable http basic authentication + * Note hardwired users adm1, wilma and guest + */ +int +example_restconf_start(clicon_handle h, + int argc, + char **argv) +{ + char c; + + clicon_debug(1, "%s argc:%d", __FUNCTION__, argc); + optind = 1; + opterr = 0; + while ((c = getopt(argc, argv, "a")) != -1){ + switch (c) { + case 'a': + basic_auth = 1; + break; + default: + break; + } + } + return 0; +} + clixon_plugin_api * clixon_plugin_init(clicon_handle h); static clixon_plugin_api api = { "example", /* name */ clixon_plugin_init, /* init */ - NULL, /* start */ + example_restconf_start,/* start */ NULL, /* exit */ - .ca_auth=plugin_credentials /* auth */ + .ca_auth=example_restconf_credentials /* auth */ }; /*! Restconf plugin initialization diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index ece605c7..f7aa7174 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -75,7 +75,9 @@ typedef int (*clicon_rpc_cb)( */ /* Called when backend started with cmd-line arguments from daemon call. - * @see plgstart_t + * Call plugin start functions with argc/argv multiple arguments. + * Typically the argc/argv are the ones appearing after "--", eg + * clicon_cli -f /etc/clicon.xml -- -a myopt */ typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */ diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 4b6112c2..c8ce70e7 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -288,6 +288,16 @@ done: /*! Call plugin_start in all plugins * @param[in] h Clicon handle + * @param[in] argc + * @param[in] argv + * Call plugin start functions (if defined) with argc/argv multiple + * arguments. + * Typically the argc/argv are the ones appearing after "--", eg + * clicon_cli -f /etc/clicon.xml -- -a myopt + * In the example above argc=3 and + * argv[0]: clicon_cli + * argv[1]: -a + * argv[2]: myopt */ int clixon_plugin_start(clicon_handle h, @@ -369,7 +379,7 @@ clixon_plugin_auth(clicon_handle h, if ((authfn = cp->cp_api.ca_auth) == NULL) continue; if ((retval = authfn(h, arg)) < 0) { - clicon_debug(1, "plugin_start() failed\n"); + clicon_debug(1, "plugin_auth() failed\n"); return -1; } break; diff --git a/test/test_auth.sh b/test/test_auth.sh index 163edfa8..225a3ba7 100755 --- a/test/test_auth.sh +++ b/test/test_auth.sh @@ -36,27 +36,6 @@ module $APPNAME{ import ietf-netconf-acm { prefix nacm; } - container authentication { - description "Example code for enabling www basic auth and some example - users"; - leaf basic_auth{ - description "Basic user / password authentication as in HTTP basic auth"; - type boolean; - default false; - } - list auth { - description "user / password entries. Valid if basic_auth=true"; - key user; - leaf user{ - description "User name"; - type string; - } - leaf password{ - description "Password"; - type string; - } - } - } leaf x{ type int32; description "something to edit"; @@ -65,18 +44,6 @@ module $APPNAME{ EOF RULES=$(cat < - true - - adm1bar - - - wilmabar - - - guestbar - - false deny @@ -172,8 +139,8 @@ fi new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf sleep 1 -new "start restconf daemon" -sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang +new "start restconf daemon (-a is enable basic authentication)" +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang -- -a sleep 1 @@ -214,7 +181,7 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x ' new2 "guest get nacm" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X GET 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 "admin edit nacm" expecteq "$(curl -u adm1:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" "" @@ -223,7 +190,7 @@ new2 "limited edit nacm" expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}} ' 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": "access denied"}}} ' +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 "Kill restconf daemon" sudo pkill -u www-data clixon_restconf diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh old mode 100644 new mode 100755 index 68339ab8..373fca1f --- a/test/test_auth_ext.sh +++ b/test/test_auth_ext.sh @@ -1,5 +1,6 @@ #!/bin/bash # Authentication and authorization and IETF NACM +# External NACM file # See RFC 8321 A.2 # But replaced ietf-netconf-monitoring with * @@ -10,12 +11,13 @@ APPNAME=example cfg=$dir/conf_yang.xml fyang=$dir/test.yang fyangerr=$dir/err.yang +nacmfile=$dir/nacmfile cat < $cfg $cfg /usr/local/share/$APPNAME/yang - $APPNAME + $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/restconf /usr/local/lib/$APPNAME/cli @@ -26,23 +28,21 @@ cat < $cfg /usr/local/var/$APPNAME /usr/local/lib/xmldb/text.so false - internal + external + $nacmfile EOF cat < $fyang module $APPNAME{ prefix ex; - import ietf-netconf-acm { - prefix nacm; - } container authentication { description "Example code for enabling www basic auth and some example users"; leaf basic_auth{ description "Basic user / password authentication as in HTTP basic auth"; type boolean; - default false; + default true; } list auth { description "user / password entries. Valid if basic_auth=true"; @@ -64,21 +64,9 @@ module $APPNAME{ } EOF -RULES=$(cat < - true - - adm1bar - - - wilmabar - - - guestbar - - +cat < $nacmfile - false + true deny deny deny @@ -151,12 +139,10 @@ RULES=$(cat < - 0 EOF -) # kill old backend (if any) -new "kill old backend" +new "kill old backend -zf $cfg -y $fyang" sudo clixon_backend -zf $cfg -y $fyang if [ $? -ne 0 ]; then err @@ -172,8 +158,8 @@ fi new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf sleep 1 -new "start restconf daemon" -sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang +new "start restconf daemon (-a is enable http basic auth)" +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang -- -a sleep 1 @@ -184,11 +170,8 @@ new2 "auth get" expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": null} ' -new "auth set authentication config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "$RULES]]>]]>" "^]]>]]>$" - -new "commit it" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +new "Set x to 0" +expecteq "$(curl -u adm1:bar -sS -X PUT -d '{"x": 0}' http://localhost/restconf/data/x)" "" new2 "auth get (no user: access denied)" expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' @@ -200,11 +183,6 @@ new2 "auth get (access)" expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0} ' -#----------------Enable NACM - -new "enable nacm" -expecteq "$(curl -u adm1:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" "" - new2 "admin get nacm" expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0} ' @@ -214,7 +192,7 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x ' new2 "guest get nacm" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X GET 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 "admin edit nacm" expecteq "$(curl -u adm1:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" "" @@ -223,7 +201,7 @@ new2 "limited edit nacm" expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}} ' 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": "access denied"}}} ' +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 "Kill restconf daemon" sudo pkill -u www-data clixon_restconf