* Experimental NACM RFC8341 Network Configuration Access Control Model.

* CLICON_NACM_MODE config option, default is disabled.
  * Added username attribute to all rpc:s from frontend to backend
  * Added NACM backend module in example
This commit is contained in:
Olof hagsand 2018-04-19 22:44:15 +02:00
parent 04a3f4db1b
commit 7650803475
32 changed files with 908 additions and 274 deletions

View file

@ -45,7 +45,7 @@ CFLAGS = @CFLAGS@ -rdynamic -fPIC
INCLUDES = -I$(includedir) @INCLUDES@
BE_PLUGIN = $(APPNAME)_backend.so
BE2_PLUGIN = $(APPNAME)_backend_secondary.so
BE2_PLUGIN = $(APPNAME)_backend_nacm.so
CLI_PLUGIN = $(APPNAME)_cli.so
NETCONF_PLUGIN = $(APPNAME)_netconf.so
RESTCONF_PLUGIN = $(APPNAME)_restconf.so
@ -75,8 +75,8 @@ BE_OBJ = $(BE_SRC:%.c=%.o)
$(BE_PLUGIN): $(BE_OBJ)
$(CC) -Wall -shared -o $@ -lc $<
# Secondary backend plugin
BE2_SRC = $(APPNAME)_backend_secondary.c
# Secondary NACM backend plugin
BE2_SRC = $(APPNAME)_backend_nacm.c
BE2_OBJ = $(BE2_SRC:%.c=%.o)
$(BE2_PLUGIN): $(BE2_OBJ)
$(CC) -Wall -shared -o $@ -lc $<

View file

@ -6,23 +6,30 @@ module example {
import ietf-routing {
prefix rt;
}
import ietf-netconf-acm {
prefix nacm;
}
description
"Example code that includes ietf-ip and ietf-routing";
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;
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;
}
leaf password{
description "Password";
type string;
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 {

View file

@ -251,22 +251,18 @@ plugin_start(clicon_handle h,
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
"example", /* name */
"example", /* name */ /*--- Common fields. ---*/
clixon_plugin_init, /* init */
plugin_start, /* start */
NULL, /* exit */
NULL, /* auth */
NULL, /* cli prompt */
NULL, /* cli suspend */
NULL, /* cli interrupt */
plugin_reset, /* reset */
plugin_statedata, /* statedata */
NULL, /* trans begin */
transaction_validate,/* trans validate */
NULL, /* trans complete */
transaction_commit, /* trans commit */
NULL, /* trans end */
NULL /* trans abort */
.ca_reset=plugin_reset,/* reset */ /*--- Backend plugin only ---*/
.ca_statedata=plugin_statedata, /* statedata */
.ca_trans_begin=NULL, /* trans begin */
.ca_trans_validate=transaction_validate,/* trans validate */
.ca_trans_complete=NULL, /* trans complete */
.ca_trans_commit=transaction_commit, /* trans commit */
.ca_trans_end=NULL, /* trans end */
.ca_trans_abort=NULL /* trans abort */
};
/*! Backend plugin initialization

View file

@ -55,16 +55,41 @@
#include <clixon/clixon_backend.h>
int
transaction_commit_2(clicon_handle h,
transaction_data td)
/*! Called to get NACM state data
* @param[in] h Clicon handle
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] xtop XML tree, <config/> on entry.
* @retval 0 OK
* @retval -1 Error
* @see xmldb_get
* @note this example code returns a static statedata used in testing.
* Real code would poll state
*/
int
nacm_statedata(clicon_handle h,
char *xpath,
cxobj *xstate)
{
clicon_debug(1, "%s", __FUNCTION__);
return 0;
int retval = -1;
cxobj **xvec = NULL;
/* Example of (static) statedata, real code would poll state */
if (xml_parse_string("<nacm>"
"<denied-data-writes>0</denied-data-writes>"
"<denied-operations>0</denied-operations>"
"<denied-notifications>0</denied-notifications>"
"</nacm>", NULL, &xstate) < 0)
goto done;
retval = 0;
done:
if (xvec)
free(xvec);
return retval;
}
int
plugin_start_2(clicon_handle h,
plugin_start(clicon_handle h,
int argc,
char **argv)
{
@ -74,19 +99,12 @@ plugin_start_2(clicon_handle h,
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
"secondary", /* name */
"nacm", /* name */ /*--- Common fields. ---*/
clixon_plugin_init, /* init */
plugin_start_2, /* start */
plugin_start, /* start */
NULL, /* exit */
NULL, /* auth */
NULL, /* reset */
NULL, /* statedata */
NULL, /* trans begin */
NULL, /* trans validate */
NULL, /* trans complete */
transaction_commit_2,/* trans commit */
NULL, /* trans end */
NULL /* trans abort */
.ca_reset=NULL, /* reset */ /*--- Backend plugin only ---*/
.ca_statedata=nacm_statedata, /* statedata */
};
/*! Backend plugin initialization

View file

@ -117,10 +117,9 @@ static clixon_plugin_api api = {
clixon_plugin_init, /* init */
NULL, /* start */
NULL, /* exit */
NULL, /* auth */
NULL, /* cli_prompthook_t */
NULL, /* cligen_susp_cb_t */
NULL, /* cligen_interrupt_cb_t */
.ca_prompt=NULL, /* cli_prompthook_t */
.ca_suspend=NULL, /* cligen_susp_cb_t */
.ca_interrupt=NULL, /* cligen_interrupt_cb_t */
};
/*! CLI plugin initialization

View file

@ -81,8 +81,7 @@ static struct clixon_plugin_api api = {
"example", /* name */
clixon_plugin_init, /* init */
plugin_start, /* start */
plugin_exit, /* exit */
NULL /* auth */
plugin_exit /* exit */
};
/*! Netconf plugin initialization

View file

@ -180,7 +180,6 @@ b64_decode(const char *src,
return (tarindex);
}
/*! Process a rest request that requires (cookie) "authentication"
* Note, this is loaded as dlsym fixed symbol in plugin
* @param[in] h Clixon handle
@ -188,7 +187,7 @@ b64_decode(const char *src,
* @retval -1 Fatal error
* @retval 0 Unauth
* @retval 1 Auth
* For grideye, return "u" entry name if it has a valid "user" entry.
*
*/
int
plugin_credentials(clicon_handle h,
@ -206,12 +205,17 @@ plugin_credentials(clicon_handle h,
size_t authlen;
cbuf *cb = NULL;
int ret;
/* XXX This is a kludge to reset the user not remaining from previous */
if (clicon_username_set(h, "admin") < 0)
goto done;
clicon_debug(1, "%s", __FUNCTION__);
/* Check if basic_auth set, if not return OK */
if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
if (clicon_rpc_get_config(h, "running", "authentication", &xt) < 0)
goto done;
if ((x = xpath_first(xt, "basic_auth")) == NULL)
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;
@ -219,8 +223,8 @@ plugin_credentials(clicon_handle h,
goto ok;
/* At this point in the code we must use HTTP basic authentication */
if ((auth = FCGX_GetParam("HTTP_AUTHORIZATION", r->envp)) == NULL)
goto fail;
if (strlen(auth) < strlen("Basic "))
goto fail;
if (strlen(auth) < strlen("Basic "))
goto fail;
if (strncmp("Basic ", auth, strlen("Basic ")))
goto fail;
@ -239,15 +243,18 @@ plugin_credentials(clicon_handle h,
*passwd = '\0';
passwd++;
clicon_debug(1, "%s 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, "auth[user=%s]", user);
cprintf(cb, "authentication/auth[user=%s]", user);
if ((x = xpath_first(xt, cbuf_get(cb))) == NULL)
goto fail;
passwd2 = xml_find_body(x, "password");
if (strcmp(passwd, passwd2))
goto fail;
retval = 1;
clicon_debug(1, "%s user:%s", __FUNCTION__, user);
if (clicon_username_set(h, user) < 0)
goto done;
ok: /* authenticated */
@ -281,7 +288,6 @@ restconf_client_rpc(clicon_handle h,
return 0;
}
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
@ -289,7 +295,7 @@ static clixon_plugin_api api = {
clixon_plugin_init, /* init */
NULL, /* start */
NULL, /* exit */
plugin_credentials /* auth */
.ca_auth=plugin_credentials /* auth */
};
/*! Restconf plugin initialization