* Added a "user" parameter to plugin_credentials() restconf callback.
To enable authentication and in preparation for access control a la RFC 6536. * yang string length "max" keyword set to MAXPATHLEN
This commit is contained in:
parent
3ffe68d124
commit
e40d785d5c
12 changed files with 514 additions and 34 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
## 3.5.0 (Upcoming)
|
## 3.5.0 (Upcoming)
|
||||||
|
|
||||||
### Major changes:
|
### Major changes:
|
||||||
|
* Added a "user" parameter to plugin_credentials() restconf callback. To enable authentication and in preparation for access control a la RFC 6536.
|
||||||
* Major Restconf feature update to compy to RFC 8040. Thanks Stephen Jones for getting right.
|
* Major Restconf feature update to compy to RFC 8040. Thanks Stephen Jones for getting right.
|
||||||
* GET well-known, top-level resource, yang library version,
|
* GET well-known, top-level resource, yang library version,
|
||||||
* PUT whole datastore, check for different keys in put lists.
|
* PUT whole datastore, check for different keys in put lists.
|
||||||
|
|
@ -10,7 +11,7 @@
|
||||||
|
|
||||||
### Minor changes:
|
### Minor changes:
|
||||||
|
|
||||||
|
* Added RFC 6536 ietf-netconf-acm@2012-02-22.yang access control (but not implemented).
|
||||||
* The following backward compatible options to configure have been obsoleted. If you havent already migrated this code you must do this now.
|
* The following backward compatible options to configure have been obsoleted. If you havent already migrated this code you must do this now.
|
||||||
* Backend startup modes prior to 3.3.3. As enabled with `configure --with-startup-compat`. Configure option CLICON_USE_STARTUP_CONFIG is also obsoleted.
|
* Backend startup modes prior to 3.3.3. As enabled with `configure --with-startup-compat`. Configure option CLICON_USE_STARTUP_CONFIG is also obsoleted.
|
||||||
* Configuration files (non-XML) prior to 3.3.3. As enabled with `configure --with-config-compat`. The template clicon.conf.cpp files are also removed.
|
* Configuration files (non-XML) prior to 3.3.3. As enabled with `configure --with-config-compat`. The template clicon.conf.cpp files are also removed.
|
||||||
|
|
@ -25,6 +26,7 @@
|
||||||
* /etc/clixon.xml
|
* /etc/clixon.xml
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
* yang string length "max" keyword set to MAXPATHLEN
|
||||||
* Corrected "No yang spec" printed on tty on leafref CLI usage
|
* Corrected "No yang spec" printed on tty on leafref CLI usage
|
||||||
* xml2cvec: range error (eg 1000 for int8) is not treated as error, just log and skip.
|
* xml2cvec: range error (eg 1000 for int8) is not treated as error, just log and skip.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -238,12 +238,21 @@ yang2cli_var_sub(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else{ /* Cligen does not have 'max' keyword in range so need to find actual
|
else{ /* Cligen does not have 'max' keyword in range so need to find actual
|
||||||
max value of type if yang range expression is 0..max */
|
max value of type if yang range expression is 0..max
|
||||||
if ((r = cvtype_max2str_dup(cvtype)) == NULL){
|
*/
|
||||||
clicon_err(OE_UNIX, errno, "cvtype_max2str");
|
if (cvtype==CGV_STRING){
|
||||||
goto done;
|
if ((r = malloc(512)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
snprintf(r, 512, "%d", MAXPATHLEN);
|
||||||
}
|
}
|
||||||
|
else if ((r = cvtype_max2str_dup(cvtype)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cvtype_max2str");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cprintf(cb, "%s]", r);
|
cprintf(cb, "%s]", r);
|
||||||
free(r);
|
free(r);
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
* Returns an expand-type list of commands as used by cligen 'expand'
|
* Returns an expand-type list of commands as used by cligen 'expand'
|
||||||
* functionality.
|
* functionality.
|
||||||
*
|
*
|
||||||
* Assume callback given in a cligen spec: a <x:int expand_dbvar("arg")
|
* Assume callback given in a cligen spec: a <x:int expand_dbvar("db" "<xmlkeyfmt>")
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
* @param[in] name Name of this function (eg "expand_dbvar")
|
* @param[in] name Name of this function (eg "expand_dbvar")
|
||||||
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
||||||
|
|
|
||||||
|
|
@ -296,11 +296,10 @@ readdata(FCGX_Request *r)
|
||||||
return cb;
|
return cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef int (credentials_t)(clicon_handle h, FCGX_Request *r);
|
|
||||||
|
|
||||||
static int nplugins = 0;
|
static int nplugins = 0;
|
||||||
static plghndl_t *plugins = NULL;
|
static plghndl_t *plugins = NULL;
|
||||||
static credentials_t *p_credentials = NULL; /* Credentials callback */
|
static plgcredentials_t *_credentials_fn = NULL; /* Credentials callback */
|
||||||
|
|
||||||
/*! Load all plugins you can find in CLICON_RESTCONF_DIR
|
/*! Load all plugins you can find in CLICON_RESTCONF_DIR
|
||||||
*/
|
*/
|
||||||
|
|
@ -330,7 +329,7 @@ restconf_plugin_load(clicon_handle h)
|
||||||
(int)strlen(filename), filename);
|
(int)strlen(filename), filename);
|
||||||
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
||||||
goto quit;
|
goto quit;
|
||||||
p_credentials = dlsym(handle, PLUGIN_CREDENTIALS);
|
_credentials_fn = dlsym(handle, PLUGIN_CREDENTIALS);
|
||||||
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
|
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
|
||||||
clicon_err(OE_UNIX, errno, "realloc");
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
@ -385,23 +384,24 @@ restconf_plugin_start(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
plugin_credentials(clicon_handle h,
|
restconf_credentials(clicon_handle h,
|
||||||
FCGX_Request *r,
|
FCGX_Request *r,
|
||||||
int *auth)
|
char **user)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
/* If no authentication callback then allow anything. Is this OK? */
|
/* If no authentication callback then allow anything. Is this OK? */
|
||||||
if (p_credentials == 0){
|
if (_credentials_fn == NULL){
|
||||||
*auth = 1;
|
if ((*user = strdup("none")) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (p_credentials(h, r) < 0)
|
if (_credentials_fn(h, r, user) < 0)
|
||||||
*auth = 0;
|
user = NULL;
|
||||||
else
|
|
||||||
*auth = 1;
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ cbuf *readdata(FCGX_Request *r);
|
||||||
int restconf_plugin_load(clicon_handle h);
|
int restconf_plugin_load(clicon_handle h);
|
||||||
int restconf_plugin_start(clicon_handle h, int argc, char **argv);
|
int restconf_plugin_start(clicon_handle h, int argc, char **argv);
|
||||||
int restconf_plugin_unload(clicon_handle h);
|
int restconf_plugin_unload(clicon_handle h);
|
||||||
int plugin_credentials(clicon_handle h, FCGX_Request *r, int *auth);
|
int restconf_credentials(clicon_handle h, FCGX_Request *r, char **user);
|
||||||
int get_user_cookie(char *cookiestr, char *attribute, char **val);
|
int get_user_cookie(char *cookiestr, char *attribute, char **val);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,8 @@ api_operations(clicon_handle h,
|
||||||
cvec *pcvec,
|
cvec *pcvec,
|
||||||
int pi,
|
int pi,
|
||||||
cvec *qvec,
|
cvec *qvec,
|
||||||
char *data)
|
char *data,
|
||||||
|
char *username)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *request_method;
|
char *request_method;
|
||||||
|
|
@ -153,7 +154,7 @@ api_operations(clicon_handle h,
|
||||||
if (strcmp(request_method, "GET")==0)
|
if (strcmp(request_method, "GET")==0)
|
||||||
retval = api_operation_get(h, r, path, pcvec, pi, qvec, data);
|
retval = api_operation_get(h, r, path, pcvec, pi, qvec, data);
|
||||||
else if (strcmp(request_method, "POST")==0)
|
else if (strcmp(request_method, "POST")==0)
|
||||||
retval = api_operation_post(h, r, path, pcvec, pi, qvec, data);
|
retval = api_operation_post(h, r, path, pcvec, pi, qvec, data, username);
|
||||||
else
|
else
|
||||||
retval = notfound(r);
|
retval = notfound(r);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -275,7 +276,7 @@ api_restconf(clicon_handle h,
|
||||||
cvec *pcvec = NULL; /* for rest api */
|
cvec *pcvec = NULL; /* for rest api */
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
char *data;
|
char *data;
|
||||||
int auth = 0;
|
char *username = NULL;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
path = FCGX_GetParam("REQUEST_URI", r->envp);
|
path = FCGX_GetParam("REQUEST_URI", r->envp);
|
||||||
|
|
@ -318,20 +319,21 @@ api_restconf(clicon_handle h,
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
test(r, 1);
|
test(r, 1);
|
||||||
/* If present, check credentials */
|
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||||
if (plugin_credentials(h, r, &auth) < 0)
|
* See RFC 8040 section 2.5
|
||||||
|
*/
|
||||||
|
if (restconf_credentials(h, r, &username) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
clicon_debug(1, "%s credentials ok auth:%d (should be 1)",
|
clicon_debug(1, "%s credentials ok username:%s (should be non-NULL)",
|
||||||
__FUNCTION__, auth);
|
__FUNCTION__, username);
|
||||||
if (auth == 0)
|
if (username == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
clicon_debug(1, "%s credentials ok 2", __FUNCTION__);
|
|
||||||
if (strcmp(method, "yang-library-version")==0)
|
if (strcmp(method, "yang-library-version")==0)
|
||||||
retval = api_yang_library_version(h, r);
|
retval = api_yang_library_version(h, r);
|
||||||
else if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
|
else if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
|
||||||
retval = api_data(h, r, path, pcvec, 2, qvec, data);
|
retval = api_data(h, r, path, pcvec, 2, qvec, data);
|
||||||
else if (strcmp(method, "operations") == 0) /* rpc */
|
else if (strcmp(method, "operations") == 0) /* rpc */
|
||||||
retval = api_operations(h, r, path, pcvec, 2, qvec, data);
|
retval = api_operations(h, r, path, pcvec, 2, qvec, data, username);
|
||||||
else if (strcmp(method, "test") == 0)
|
else if (strcmp(method, "test") == 0)
|
||||||
retval = test(r, 0);
|
retval = test(r, 0);
|
||||||
else
|
else
|
||||||
|
|
@ -348,6 +350,8 @@ api_restconf(clicon_handle h,
|
||||||
cvec_free(pcvec);
|
cvec_free(pcvec);
|
||||||
if (cb)
|
if (cb)
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
|
if (username)
|
||||||
|
free(username);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -846,7 +846,8 @@ api_operation_post(clicon_handle h,
|
||||||
cvec *pcvec,
|
cvec *pcvec,
|
||||||
int pi,
|
int pi,
|
||||||
cvec *qvec,
|
cvec *qvec,
|
||||||
char *data)
|
char *data,
|
||||||
|
char *username)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -869,6 +870,7 @@ api_operation_post(clicon_handle h,
|
||||||
char *media_accept;
|
char *media_accept;
|
||||||
int use_xml = 0; /* By default return JSON */
|
int use_xml = 0; /* By default return JSON */
|
||||||
int pretty;
|
int pretty;
|
||||||
|
cxobj *xa;
|
||||||
|
|
||||||
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
|
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
|
||||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||||
|
|
@ -939,8 +941,17 @@ api_operation_post(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Non-standard: add cookie as attribute for backend
|
/* Non-standard: add username attribute for backend ACM (RFC 6536)
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
if (username){
|
||||||
|
if ((xa = xml_new("username", xtop, NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
xml_type_set(xa, CX_ATTR);
|
||||||
|
if (xml_value_set(xa, username) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
#ifdef obsolete
|
||||||
{
|
{
|
||||||
cxobj *xa;
|
cxobj *xa;
|
||||||
char *cookie;
|
char *cookie;
|
||||||
|
|
@ -957,6 +968,7 @@ api_operation_post(clicon_handle h,
|
||||||
free(cookieval);
|
free(cookieval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/* Send to backend */
|
/* Send to backend */
|
||||||
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ int api_operation_get(clicon_handle h, FCGX_Request *r,
|
||||||
|
|
||||||
int api_operation_post(clicon_handle h, FCGX_Request *r,
|
int api_operation_post(clicon_handle h, FCGX_Request *r,
|
||||||
char *path,
|
char *path,
|
||||||
cvec *pcvec, int pi, cvec *qvec, char *data);
|
cvec *pcvec, int pi, cvec *qvec, char *data,
|
||||||
|
char *username);
|
||||||
|
|
||||||
#endif /* _RESTCONF_METHODS_H_ */
|
#endif /* _RESTCONF_METHODS_H_ */
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,10 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
||||||
* Returns 0 if credentials OK, -1 if failed
|
* Returns 0 if credentials OK, -1 if failed
|
||||||
*/
|
*/
|
||||||
#define PLUGIN_CREDENTIALS "plugin_credentials"
|
#define PLUGIN_CREDENTIALS "plugin_credentials"
|
||||||
typedef int (plgcredentials_t)(clicon_handle, void *); /* Plugin credentials */
|
/* Plugin credentials
|
||||||
|
* username should be freed after use
|
||||||
|
*/
|
||||||
|
typedef int (plgcredentials_t)(clicon_handle, void *, char **username);
|
||||||
|
|
||||||
/* Find a function in global namespace or a plugin. XXX clicon internal */
|
/* Find a function in global namespace or a plugin. XXX clicon internal */
|
||||||
void *clicon_find_func(clicon_handle h, char *plugin, char *func);
|
void *clicon_find_func(clicon_handle h, char *plugin, char *func);
|
||||||
|
|
|
||||||
|
|
@ -1704,6 +1704,7 @@ xml_apply_ancestor(cxobj *xn,
|
||||||
* @param[out] cvp CLIgen variable containing the parsed value
|
* @param[out] cvp CLIgen variable containing the parsed value
|
||||||
* @note free cv with cv_free after use.
|
* @note free cv with cv_free after use.
|
||||||
* @see xml_body_int32 etc, for type-specific parse functions
|
* @see xml_body_int32 etc, for type-specific parse functions
|
||||||
|
* @note range check failure returns 0
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_body_parse(cxobj *xb,
|
xml_body_parse(cxobj *xb,
|
||||||
|
|
@ -1751,6 +1752,7 @@ xml_body_parse(cxobj *xb,
|
||||||
* alloc error.
|
* alloc error.
|
||||||
* @note extend to all other cligen var types and generalize
|
* @note extend to all other cligen var types and generalize
|
||||||
* @note use yang type info?
|
* @note use yang type info?
|
||||||
|
* @note range check failure returns 0
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_body_int32(cxobj *xb,
|
xml_body_int32(cxobj *xb,
|
||||||
|
|
@ -1774,6 +1776,7 @@ xml_body_int32(cxobj *xb,
|
||||||
* alloc error.
|
* alloc error.
|
||||||
* @note extend to all other cligen var types and generalize
|
* @note extend to all other cligen var types and generalize
|
||||||
* @note use yang type info?
|
* @note use yang type info?
|
||||||
|
* @note range check failure returns 0
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_body_uint32(cxobj *xb,
|
xml_body_uint32(cxobj *xb,
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ datarootdir = @datarootdir@
|
||||||
|
|
||||||
YANGSPECS = clixon-config@2017-12-27.yang
|
YANGSPECS = clixon-config@2017-12-27.yang
|
||||||
YANGSPECS += ietf-netconf@2011-06-01.yang
|
YANGSPECS += ietf-netconf@2011-06-01.yang
|
||||||
|
YANGSPECS += ietf-netconf-acm@2012-02-22.yang
|
||||||
YANGSPECS += ietf-inet-types@2013-07-15.yang
|
YANGSPECS += ietf-inet-types@2013-07-15.yang
|
||||||
|
|
||||||
APPNAME = clixon # subdir ehere these files are installed
|
APPNAME = clixon # subdir ehere these files are installed
|
||||||
|
|
|
||||||
445
yang/ietf-netconf-acm@2012-02-22.yang
Normal file
445
yang/ietf-netconf-acm@2012-02-22.yang
Normal file
|
|
@ -0,0 +1,445 @@
|
||||||
|
module ietf-netconf-acm {
|
||||||
|
namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
|
||||||
|
prefix "nacm";
|
||||||
|
import ietf-yang-types {
|
||||||
|
prefix yang;
|
||||||
|
}
|
||||||
|
organization
|
||||||
|
"IETF NETCONF (Network Configuration) Working Group";
|
||||||
|
|
||||||
|
contact
|
||||||
|
"WG Web: <http://tools.ietf.org/wg/netconf/>
|
||||||
|
WG List: <mailto:netconf@ietf.org>
|
||||||
|
|
||||||
|
WG Chair: Mehmet Ersue
|
||||||
|
<mailto:mehmet.ersue@nsn.com>
|
||||||
|
|
||||||
|
WG Chair: Bert Wijnen
|
||||||
|
<mailto:bertietf@bwijnen.net>
|
||||||
|
|
||||||
|
Editor: Andy Bierman
|
||||||
|
<mailto:andy@yumaworks.com>
|
||||||
|
|
||||||
|
Editor: Martin Bjorklund
|
||||||
|
<mailto:mbj@tail-f.com>";
|
||||||
|
|
||||||
|
description
|
||||||
|
"NETCONF Access Control Model.
|
||||||
|
|
||||||
|
Copyright (c) 2012 IETF Trust and the persons identified as
|
||||||
|
authors of the code. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or
|
||||||
|
without modification, is permitted pursuant to, and subject
|
||||||
|
to the license terms contained in, the Simplified BSD
|
||||||
|
License set forth in Section 4.c of the IETF Trust's
|
||||||
|
Legal Provisions Relating to IETF Documents
|
||||||
|
(http://trustee.ietf.org/license-info).
|
||||||
|
|
||||||
|
This version of this YANG module is part of RFC 6536; see
|
||||||
|
the RFC itself for full legal notices.";
|
||||||
|
|
||||||
|
revision "2012-02-22" {
|
||||||
|
description
|
||||||
|
"Initial version";
|
||||||
|
reference
|
||||||
|
"RFC 6536: Network Configuration Protocol (NETCONF)
|
||||||
|
Access Control Model";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extension statements
|
||||||
|
*/
|
||||||
|
|
||||||
|
extension default-deny-write {
|
||||||
|
description
|
||||||
|
"Used to indicate that the data model node
|
||||||
|
represents a sensitive security system parameter.
|
||||||
|
|
||||||
|
If present, and the NACM module is enabled (i.e.,
|
||||||
|
/nacm/enable-nacm object equals 'true'), the NETCONF server
|
||||||
|
will only allow the designated 'recovery session' to have
|
||||||
|
write access to the node. An explicit access control rule is
|
||||||
|
required for all other users.
|
||||||
|
|
||||||
|
The 'default-deny-write' extension MAY appear within a data
|
||||||
|
definition statement. It is ignored otherwise.";
|
||||||
|
}
|
||||||
|
|
||||||
|
extension default-deny-all {
|
||||||
|
description
|
||||||
|
"Used to indicate that the data model node
|
||||||
|
controls a very sensitive security system parameter.
|
||||||
|
|
||||||
|
If present, and the NACM module is enabled (i.e.,
|
||||||
|
/nacm/enable-nacm object equals 'true'), the NETCONF server
|
||||||
|
will only allow the designated 'recovery session' to have
|
||||||
|
read, write, or execute access to the node. An explicit
|
||||||
|
access control rule is required for all other users.
|
||||||
|
|
||||||
|
The 'default-deny-all' extension MAY appear within a data
|
||||||
|
definition statement, 'rpc' statement, or 'notification'
|
||||||
|
statement. It is ignored otherwise.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Derived types
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef user-name-type {
|
||||||
|
type string {
|
||||||
|
length "1..max";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"General Purpose Username string.";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef matchall-string-type {
|
||||||
|
type string {
|
||||||
|
pattern "\*";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"The string containing a single asterisk '*' is used
|
||||||
|
to conceptually represent all possible values
|
||||||
|
for the particular leaf using this data type.";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef access-operations-type {
|
||||||
|
type bits {
|
||||||
|
bit create {
|
||||||
|
description
|
||||||
|
"Any protocol operation that creates a
|
||||||
|
new data node.";
|
||||||
|
}
|
||||||
|
bit read {
|
||||||
|
description
|
||||||
|
"Any protocol operation or notification that
|
||||||
|
returns the value of a data node.";
|
||||||
|
}
|
||||||
|
bit update {
|
||||||
|
description
|
||||||
|
"Any protocol operation that alters an existing
|
||||||
|
data node.";
|
||||||
|
}
|
||||||
|
bit delete {
|
||||||
|
description
|
||||||
|
"Any protocol operation that removes a data node.";
|
||||||
|
}
|
||||||
|
bit exec {
|
||||||
|
description
|
||||||
|
"Execution access to the specified protocol operation.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"NETCONF Access Operation.";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef group-name-type {
|
||||||
|
type string {
|
||||||
|
length "1..max";
|
||||||
|
pattern "[^\*].*";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"Name of administrative group to which
|
||||||
|
users can be assigned.";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef action-type {
|
||||||
|
type enumeration {
|
||||||
|
enum permit {
|
||||||
|
description
|
||||||
|
"Requested action is permitted.";
|
||||||
|
}
|
||||||
|
enum deny {
|
||||||
|
description
|
||||||
|
"Requested action is denied.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"Action taken by the server when a particular
|
||||||
|
rule matches.";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef node-instance-identifier {
|
||||||
|
type yang:xpath1.0;
|
||||||
|
description
|
||||||
|
"Path expression used to represent a special
|
||||||
|
data node instance identifier string.
|
||||||
|
|
||||||
|
A node-instance-identifier value is an
|
||||||
|
unrestricted YANG instance-identifier expression.
|
||||||
|
All the same rules as an instance-identifier apply
|
||||||
|
except predicates for keys are optional. If a key
|
||||||
|
predicate is missing, then the node-instance-identifier
|
||||||
|
represents all possible server instances for that key.
|
||||||
|
|
||||||
|
This XPath expression is evaluated in the following context:
|
||||||
|
|
||||||
|
o The set of namespace declarations are those in scope on
|
||||||
|
the leaf element where this type is used.
|
||||||
|
|
||||||
|
o The set of variable bindings contains one variable,
|
||||||
|
'USER', which contains the name of the user of the current
|
||||||
|
session.
|
||||||
|
|
||||||
|
o The function library is the core function library, but
|
||||||
|
note that due to the syntax restrictions of an
|
||||||
|
instance-identifier, no functions are allowed.
|
||||||
|
|
||||||
|
o The context node is the root node in the data tree.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data definition statements
|
||||||
|
*/
|
||||||
|
|
||||||
|
container nacm {
|
||||||
|
/* nacm:default-deny-all; XXX How is this parsed ?? */
|
||||||
|
|
||||||
|
description
|
||||||
|
"Parameters for NETCONF Access Control Model.";
|
||||||
|
|
||||||
|
leaf enable-nacm {
|
||||||
|
type boolean;
|
||||||
|
default true;
|
||||||
|
description
|
||||||
|
"Enables or disables all NETCONF access control
|
||||||
|
enforcement. If 'true', then enforcement
|
||||||
|
is enabled. If 'false', then enforcement
|
||||||
|
is disabled.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf read-default {
|
||||||
|
type action-type;
|
||||||
|
default "permit";
|
||||||
|
description
|
||||||
|
"Controls whether read access is granted if
|
||||||
|
no appropriate rule is found for a
|
||||||
|
particular read request.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf write-default {
|
||||||
|
type action-type;
|
||||||
|
default "deny";
|
||||||
|
description
|
||||||
|
"Controls whether create, update, or delete access
|
||||||
|
is granted if no appropriate rule is found for a
|
||||||
|
particular write request.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf exec-default {
|
||||||
|
type action-type;
|
||||||
|
default "permit";
|
||||||
|
description
|
||||||
|
"Controls whether exec access is granted if no appropriate
|
||||||
|
rule is found for a particular protocol operation request.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf enable-external-groups {
|
||||||
|
type boolean;
|
||||||
|
default true;
|
||||||
|
description
|
||||||
|
"Controls whether the server uses the groups reported by the
|
||||||
|
NETCONF transport layer when it assigns the user to a set of
|
||||||
|
NACM groups. If this leaf has the value 'false', any group
|
||||||
|
names reported by the transport layer are ignored by the
|
||||||
|
server.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf denied-operations {
|
||||||
|
type yang:zero-based-counter32;
|
||||||
|
config false;
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"Number of times since the server last restarted that a
|
||||||
|
protocol operation request was denied.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf denied-data-writes {
|
||||||
|
type yang:zero-based-counter32;
|
||||||
|
config false;
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"Number of times since the server last restarted that a
|
||||||
|
protocol operation request to alter
|
||||||
|
a configuration datastore was denied.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf denied-notifications {
|
||||||
|
type yang:zero-based-counter32;
|
||||||
|
config false;
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"Number of times since the server last restarted that
|
||||||
|
a notification was dropped for a subscription because
|
||||||
|
access to the event type was denied.";
|
||||||
|
}
|
||||||
|
|
||||||
|
container groups {
|
||||||
|
description
|
||||||
|
"NETCONF Access Control Groups.";
|
||||||
|
|
||||||
|
list group {
|
||||||
|
key name;
|
||||||
|
|
||||||
|
description
|
||||||
|
"One NACM Group Entry. This list will only contain
|
||||||
|
configured entries, not any entries learned from
|
||||||
|
any transport protocols.";
|
||||||
|
|
||||||
|
leaf name {
|
||||||
|
type group-name-type;
|
||||||
|
description
|
||||||
|
"Group name associated with this entry.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf-list user-name {
|
||||||
|
type user-name-type;
|
||||||
|
description
|
||||||
|
"Each entry identifies the username of
|
||||||
|
a member of the group associated with
|
||||||
|
this entry.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list rule-list {
|
||||||
|
key "name";
|
||||||
|
ordered-by user;
|
||||||
|
description
|
||||||
|
"An ordered collection of access control rules.";
|
||||||
|
|
||||||
|
leaf name {
|
||||||
|
type string {
|
||||||
|
length "1..max";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"Arbitrary name assigned to the rule-list.";
|
||||||
|
}
|
||||||
|
leaf-list group {
|
||||||
|
type union {
|
||||||
|
type matchall-string-type;
|
||||||
|
type group-name-type;
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"List of administrative groups that will be
|
||||||
|
assigned the associated access rights
|
||||||
|
defined by the 'rule' list.
|
||||||
|
|
||||||
|
The string '*' indicates that all groups apply to the
|
||||||
|
entry.";
|
||||||
|
}
|
||||||
|
|
||||||
|
list rule {
|
||||||
|
key "name";
|
||||||
|
ordered-by user;
|
||||||
|
description
|
||||||
|
"One access control rule.
|
||||||
|
|
||||||
|
Rules are processed in user-defined order until a match is
|
||||||
|
found. A rule matches if 'module-name', 'rule-type', and
|
||||||
|
'access-operations' match the request. If a rule
|
||||||
|
matches, the 'action' leaf determines if access is granted
|
||||||
|
or not.";
|
||||||
|
|
||||||
|
leaf name {
|
||||||
|
type string {
|
||||||
|
length "1..max";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"Arbitrary name assigned to the rule.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf module-name {
|
||||||
|
type union {
|
||||||
|
type matchall-string-type;
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
default "*";
|
||||||
|
description
|
||||||
|
"Name of the module associated with this rule.
|
||||||
|
|
||||||
|
This leaf matches if it has the value '*' or if the
|
||||||
|
object being accessed is defined in the module with the
|
||||||
|
specified module name.";
|
||||||
|
}
|
||||||
|
choice rule-type {
|
||||||
|
description
|
||||||
|
"This choice matches if all leafs present in the rule
|
||||||
|
match the request. If no leafs are present, the
|
||||||
|
choice matches all requests.";
|
||||||
|
case protocol-operation {
|
||||||
|
leaf rpc-name {
|
||||||
|
type union {
|
||||||
|
type matchall-string-type;
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"This leaf matches if it has the value '*' or if
|
||||||
|
its value equals the requested protocol operation
|
||||||
|
name.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case notification {
|
||||||
|
leaf notification-name {
|
||||||
|
type union {
|
||||||
|
type matchall-string-type;
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"This leaf matches if it has the value '*' or if its
|
||||||
|
value equals the requested notification name.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case data-node {
|
||||||
|
leaf path {
|
||||||
|
type node-instance-identifier;
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"Data Node Instance Identifier associated with the
|
||||||
|
data node controlled by this rule.
|
||||||
|
|
||||||
|
Configuration data or state data instance
|
||||||
|
identifiers start with a top-level data node. A
|
||||||
|
complete instance identifier is required for this
|
||||||
|
type of path value.
|
||||||
|
|
||||||
|
The special value '/' refers to all possible
|
||||||
|
datastore contents.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf access-operations {
|
||||||
|
type union {
|
||||||
|
type matchall-string-type;
|
||||||
|
type access-operations-type;
|
||||||
|
}
|
||||||
|
default "*";
|
||||||
|
description
|
||||||
|
"Access operations associated with this rule.
|
||||||
|
|
||||||
|
This leaf matches if it has the value '*' or if the
|
||||||
|
bit corresponding to the requested operation is set.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf action {
|
||||||
|
type action-type;
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The access control action associated with the
|
||||||
|
rule. If a rule is determined to match a
|
||||||
|
particular request, then this object is used
|
||||||
|
to determine whether to permit or deny the
|
||||||
|
request.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf comment {
|
||||||
|
type string;
|
||||||
|
description
|
||||||
|
"A textual description of the access rule.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue