diff --git a/CHANGELOG.md b/CHANGELOG.md
index e56752d8..1d3d88b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
### Major changes:
* Experimental NACM RFC8341 Network Configuration Access Control Model.
* CLICON_NACM_MODE config option, default is disabled.
+ * CLICON_NACM_FILE config option, if CLICON_NACM_MODE is "external"
* Added username attribute to all rpc:s from frontend to backend
* Added NACM backend module in example
* Restructure and more generic plugin API (cli,backend,restconf,netconf).
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 1f8ac9c2..fc619640 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -904,7 +904,9 @@ nacm_match_rule(clicon_handle h,
/*! Make nacm access control
* @param[in] h Clicon handle
+ * @param[in] mode NACMmode, internal or external
* @param[in] name rpc name
+ * @param[in] username
* @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
* @retval -1 Error
* @retval 0 Not access and cbret set
@@ -913,6 +915,7 @@ nacm_match_rule(clicon_handle h,
*/
static int
nacm_access(clicon_handle h,
+ char *mode,
char *name,
char *username,
cbuf *cbret)
@@ -935,10 +938,26 @@ nacm_access(clicon_handle h,
int ret;
clicon_debug(1, "%s", __FUNCTION__);
+ /* 0. If nacm-mode is external, get NACM defintion from separet tree,
+ otherwise get it from internal configuration */
+ if (strcmp(mode, "external")==0){
+ if ((xtop = backend_nacm_list_get(h)) == NULL){
+ clicon_err(OE_XML, 0, "No nacm external tree");
+ goto done;
+ }
+ }
+ else if (strcmp(mode, "internal")==0){
+ if (xmldb_get(h, "running", "nacm", 0, &xtop) < 0)
+ goto done;
+ }
+ else{
+ clicon_err(OE_UNIX, 0, "Invalid NACM mode: %s", mode);
+ goto done;
+ }
+
/* 1. If the "enable-nacm" leaf is set to "false", then the protocol
operation is permitted. (or config does not exist) */
- if (xmldb_get(h, "running", "nacm", 0, &xtop) < 0)
- goto done;
+
if ((xacm = xpath_first(xtop, "nacm")) == NULL)
goto permit;
exec_default = xml_find_body(xacm, "exec-default");
@@ -1033,7 +1052,7 @@ nacm_access(clicon_handle h,
retval = 1;
done:
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
- if (xtop)
+ if (strcmp(mode, "internal")==0 && xtop)
xml_free(xtop);
if (gvec)
free(gvec);
@@ -1097,15 +1116,14 @@ from_client_msg(clicon_handle h,
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
name = xml_name(xe);
clicon_debug(1, "%s name:%s", __FUNCTION__, name);
-#if 1 /* NACM */
/* Make NACM access control if enabled as "internal"*/
nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
- if (nacm_mode && strcmp(nacm_mode,"internal") == 0)
- if ((ret = nacm_access(h, name, username, cbret)) < 0)
+ if (nacm_mode && strcmp(nacm_mode, "disabled") != 0){
+ if ((ret = nacm_access(h, nacm_mode, name, username, cbret)) < 0)
goto done;
- if (!ret)
- goto reply;
-#endif
+ if (!ret)
+ goto reply;
+ }
if (strcmp(name, "get-config") == 0){
if (from_client_get_config(h, xe, cbret) <0)
goto done;
diff --git a/apps/backend/backend_handle.h b/apps/backend/backend_handle.h
index 8eb3e073..676cc50c 100644
--- a/apps/backend/backend_handle.h
+++ b/apps/backend/backend_handle.h
@@ -52,4 +52,8 @@ struct client_entry *backend_client_list(clicon_handle h);
int backend_client_delete(clicon_handle h, struct client_entry *ce);
+int backend_nacm_list_set(clicon_handle h, cxobj *xnacm);
+
+cxobj * backend_nacm_list_get(clicon_handle h);
+
#endif /* _BACKEND_HANDLE_H_ */
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index 346cf859..9babb24e 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -260,6 +260,59 @@ plugin_start_useroptions(clicon_handle h,
return 0;
}
+/*! Load external NACM file
+ */
+static int
+nacm_load_external(clicon_handle h)
+{
+ int retval = -1;
+ char *filename; /* NACM config file */
+ yang_spec *yspec = NULL;
+ cxobj *xt = NULL;
+ struct stat st;
+ FILE *f = NULL;
+ int fd;
+
+ filename = clicon_option_str(h, "CLICON_NACM_FILE");
+ if (filename == NULL || strlen(filename)==0){
+ clicon_err(OE_UNIX, errno, "CLICON_NACM_FILE not set in NACM external mode");
+ goto done;
+ }
+ if (stat(filename, &st) < 0){
+ clicon_err(OE_UNIX, errno, "%s", filename);
+ goto done;
+ }
+ if (!S_ISREG(st.st_mode)){
+ clicon_err(OE_UNIX, 0, "%s is not a regular file", filename);
+ goto done;
+ }
+ if ((f = fopen(filename, "r")) == NULL) {
+ clicon_err(OE_UNIX, errno, "configure file: %s", filename);
+ return -1;
+ }
+ if ((yspec = yspec_new()) == NULL)
+ goto done;
+ if (yang_parse(h, CLIXON_DATADIR, "ietf-netconf-acm", NULL, yspec) < 0)
+ goto done;
+ fd = fileno(f);
+ /* Read configfile */
+ if (xml_parse_file(fd, "", yspec, &xt) < 0)
+ goto done;
+ if (xt == NULL){
+ clicon_err(OE_XML, 0, "No xml tree in %s", filename);
+ goto done;
+ }
+ if (backend_nacm_list_set(h, xt) < 0)
+ goto done;
+ retval = 0;
+ done:
+ if (yspec) /* The clixon yang-spec is not used after this */
+ yspec_free(yspec);
+ if (f)
+ fclose(f);
+ return retval;
+}
+
/*! Merge xml in filename into database
*/
static int
@@ -498,6 +551,7 @@ main(int argc,
int xml_cache;
int xml_pretty;
char *xml_format;
+ char *nacm_mode;
/* In the startup, logs to stderr & syslog and debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR|CLICON_LOG_SYSLOG);
@@ -551,7 +605,12 @@ main(int argc,
usage(argv[0], h);
return -1;
}
-
+ /* External NACM file? */
+ nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
+ if (nacm_mode && strcmp(nacm_mode, "external") == 0)
+ if (nacm_load_external(h) < 0)
+ goto done;
+
/* Now run through the operational args */
opterr = 1;
optind = 1;
diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c
index c4568b28..dfb9461a 100644
--- a/apps/backend/clixon_backend_handle.c
+++ b/apps/backend/clixon_backend_handle.c
@@ -90,6 +90,7 @@ struct backend_handle {
struct client_entry *bh_ce_list; /* The client list */
int bh_ce_nr; /* Number of clients, just increment */
struct handle_subscription *bh_subscription; /* Event subscription list */
+ cxobj *bh_nacm; /* NACM external struct */
};
/*! Creates and returns a clicon config handle for other CLICON API calls
@@ -106,11 +107,14 @@ backend_handle_init(void)
int
backend_handle_exit(clicon_handle h)
{
+ struct backend_handle *bh = handle(h);
struct client_entry *ce;
/* only delete client structs, not close sockets, etc, see backend_client_rm */
while ((ce = backend_client_list(h)) != NULL)
backend_client_delete(h, ce);
+ if (bh->bh_nacm)
+ xml_free(bh->bh_nacm);
clicon_handle_exit(h); /* frees h and options */
return 0;
}
@@ -431,3 +435,22 @@ subscription_each(clicon_handle h,
return hs;
}
+int
+backend_nacm_list_set(clicon_handle h,
+ cxobj *xnacm)
+{
+ struct backend_handle *bh = handle(h);
+
+ if (bh->bh_nacm)
+ xml_free(bh->bh_nacm);
+ bh->bh_nacm = xnacm;
+ return 0;
+}
+
+cxobj *
+backend_nacm_list_get(clicon_handle h)
+{
+ struct backend_handle *bh = handle(h);
+
+ return bh->bh_nacm;
+}
diff --git a/apps/cli/Makefile.in b/apps/cli/Makefile.in
index 1272bc83..bdea3b9e 100644
--- a/apps/cli/Makefile.in
+++ b/apps/cli/Makefile.in
@@ -103,7 +103,7 @@ distclean: clean
# Put config file in etc/
install: install-lib $(APPL)
install -d -m 0755 $(DESTDIR)$(bindir)
- install -m 0644 -s $(APPL) $(DESTDIR)$(bindir)
+ install -m 0755 -s $(APPL) $(DESTDIR)$(bindir)
install-lib: $(MYLIB)
install -d -m 0755 $(DESTDIR)$(libdir)
diff --git a/example/Makefile.in b/example/Makefile.in
index b043dbef..05433e6a 100644
--- a/example/Makefile.in
+++ b/example/Makefile.in
@@ -118,7 +118,7 @@ install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NET
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/cli
install -m 0644 -s $(CLI_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/cli
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/backend
- install -m 0644 -s $(BE_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/backend
+ install -m 0644 -s $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/backend
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/netconf
install -m 0644 -s $(NETCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/netconf
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/restconf
diff --git a/example/example.xml b/example/example.xml
index e93b840a..337048bb 100644
--- a/example/example.xml
+++ b/example/example.xml
@@ -11,8 +11,10 @@
/usr/local/var/example/example.sock
/usr/local/var/example/example.pidfile
1
+ VARS
/usr/local/var/example
/usr/local/lib/xmldb/text.so
0
init
+ disabled
diff --git a/example/example_restconf.c b/example/example_restconf.c
index f16199e1..bace08fd 100644
--- a/example/example_restconf.c
+++ b/example/example_restconf.c
@@ -197,7 +197,6 @@ plugin_credentials(clicon_handle h,
FCGX_Request *r = (FCGX_Request *)arg;
cxobj *xt = NULL;
cxobj *x;
- char *xbody;
char *auth;
char *user = NULL;
char *passwd;
@@ -205,11 +204,12 @@ 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;
- clicon_debug(1, "%s", __FUNCTION__);
/* Check if basic_auth set, if not return OK */
if (clicon_rpc_get_config(h, "running", "authentication", &xt) < 0)
goto done;
@@ -249,7 +249,6 @@ plugin_credentials(clicon_handle h,
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;
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index 43611c6a..45c642e0 100644
--- a/lib/src/clixon_options.c
+++ b/lib/src/clixon_options.c
@@ -209,7 +209,7 @@ clicon_options_main(clicon_handle h)
/* If file ends with .xml, assume it is new format */
if ((suffix = rindex(configfile, '.')) != NULL){
suffix++;
- xml = strcmp(suffix,"xml") == 0;
+ xml = strcmp(suffix, "xml") == 0;
}
if (xml){ /* Read clixon yang file */
if ((yspec = yspec_new()) == NULL)
diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c
index 8204b04b..15e25d1a 100644
--- a/lib/src/clixon_xml.c
+++ b/lib/src/clixon_xml.c
@@ -164,7 +164,7 @@ xml_name_set(cxobj *xn,
}
if (name){
if ((xn->x_name = strdup(name)) == NULL){
- clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
+ clicon_err(OE_XML, errno, "strdup");
return -1;
}
}
@@ -197,7 +197,7 @@ xml_namespace_set(cxobj *xn,
}
if (namespace){
if ((xn->x_namespace = strdup(namespace)) == NULL){
- clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
+ clicon_err(OE_XML, errno, "strdup");
return -1;
}
}
@@ -288,7 +288,7 @@ xml_value_set(cxobj *xn,
}
if (val){
if ((xn->x_value = strdup(val)) == NULL){
- clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
+ clicon_err(OE_XML, errno, "strdup");
return -1;
}
}
@@ -480,7 +480,7 @@ xml_child_append(cxobj *x,
x->x_childvec_len++;
x->x_childvec = realloc(x->x_childvec, x->x_childvec_len*sizeof(cxobj*));
if (x->x_childvec == NULL){
- clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
+ clicon_err(OE_XML, errno, "realloc");
return -1;
}
x->x_childvec[x->x_childvec_len-1] = xc;
@@ -538,7 +538,7 @@ xml_new(char *name,
cxobj *x;
if ((x = malloc(sizeof(cxobj))) == NULL){
- clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
+ clicon_err(OE_XML, errno, "malloc");
return NULL;
}
memset(x, 0, sizeof(cxobj));
@@ -1314,15 +1314,14 @@ xml_parse_file(int fd,
if (endtag != NULL)
endtaglen = strlen(endtag);
if ((xmlbuf = malloc(xmlbuflen)) == NULL){
- clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
+ clicon_err(OE_XML, errno, "malloc");
goto done;
}
memset(xmlbuf, 0, xmlbuflen);
ptr = xmlbuf;
while (1){
if ((ret = read(fd, &ch, 1)) < 0){
- clicon_err(OE_XML, errno, "%s: read: [pid:%d]\n",
- __FUNCTION__,
+ clicon_err(OE_XML, errno, "read: [pid:%d]\n",
(int)getpid());
break;
}
@@ -1345,7 +1344,7 @@ xml_parse_file(int fd,
oldxmlbuflen = xmlbuflen;
xmlbuflen *= 2;
if ((xmlbuf = realloc(xmlbuf, xmlbuflen)) == NULL){
- clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
+ clicon_err(OE_XML, errno, "realloc");
goto done;
}
memset(xmlbuf+oldxmlbuflen, 0, xmlbuflen-oldxmlbuflen);
@@ -1455,7 +1454,7 @@ xml_copy_one(cxobj *x0,
xml_type_set(x1, xml_type(x0));
if (xml_value(x0)){ /* malloced string */
if ((x1->x_value = strdup(x0->x_value)) == NULL){
- clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
+ clicon_err(OE_XML, errno, "strdup");
return -1;
}
}
@@ -1464,7 +1463,7 @@ xml_copy_one(cxobj *x0,
return -1;
if (xml_cv_get(x0)){
if ((cv1 = cv_dup(xml_cv_get(x0))) == NULL){
- clicon_err(OE_XML, errno, "%s: cv_dup", __FUNCTION__);
+ clicon_err(OE_XML, errno, "cv_dup");
return -1;
}
if ((xml_cv_set(x1, cv1)) < 0)
@@ -1561,7 +1560,7 @@ cxvec_append(cxobj *x,
int retval = -1;
if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
- clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
+ clicon_err(OE_XML, errno, "realloc");
goto done;
}
(*vec)[(*len)++] = x;
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 8524a413..1be9d750 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -116,26 +116,19 @@ xml2txt(FILE *f,
int children=0;
char *term = NULL;
int retval = -1;
- int encr=0;
xe = NULL; /* count children */
while ((xe = xml_child_each(x, xe, -1)) != NULL)
children++;
if (!children){
if (xml_type(x) == CX_BODY){
- /* Kludge for escaping encrypted passwords */
- if (strcmp(xml_name(xml_parent(x)), "encrypted-password")==0)
- encr++;
term = xml_value(x);
}
else{
fprintf(f, "%*s", 4*level, "");
term = xml_name(x);
}
- if (encr)
- fprintf(f, "\"%s\";\n", term);
- else
- fprintf(f, "%s;\n", term);
+ fprintf(f, "%s;\n", term);
retval = 0;
goto done;
}
diff --git a/test/test_auth.sh b/test/test_auth.sh
index 68339ab8..163edfa8 100755
--- a/test/test_auth.sh
+++ b/test/test_auth.sh
@@ -15,7 +15,7 @@ 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
diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh
new file mode 100644
index 00000000..68339ab8
--- /dev/null
+++ b/test/test_auth_ext.sh
@@ -0,0 +1,241 @@
+#!/bin/bash
+# Authentication and authorization and IETF NACM
+# See RFC 8321 A.2
+# But replaced ietf-netconf-monitoring with *
+
+APPNAME=example
+# include err() and new() functions and creates $dir
+. ./lib.sh
+
+cfg=$dir/conf_yang.xml
+fyang=$dir/test.yang
+fyangerr=$dir/err.yang
+
+cat < $cfg
+
+ $cfg
+ /usr/local/share/$APPNAME/yang
+ $APPNAME
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/lib/$APPNAME/restconf
+ /usr/local/lib/$APPNAME/cli
+ $APPNAME
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ 1
+ /usr/local/var/$APPNAME
+ /usr/local/lib/xmldb/text.so
+ false
+ internal
+
+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;
+ }
+ 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";
+ }
+}
+EOF
+
+RULES=$(cat <
+ true
+
+ adm1bar
+
+
+ wilmabar
+
+
+ guestbar
+
+
+
+ false
+ deny
+ deny
+ deny
+
+
+ admin
+ admin
+ adm1
+ olof
+
+
+ limited
+ wilma
+ bam-bam
+
+
+ guest
+ guest
+ guest@example.com
+
+
+
+ guest-acl
+ guest
+
+ deny-ncm
+ *
+ *
+ deny
+
+ Do not allow guests any access to any information.
+
+
+
+
+ limited-acl
+ limited
+
+ permit-get
+ get
+ *
+ exec
+ permit
+
+ Allow get
+
+
+
+ permit-get-config
+ get-config
+ *
+ exec
+ permit
+
+ Allow get-config
+
+
+
+
+ admin-acl
+ admin
+
+ permit-all
+ *
+ *
+ permit
+
+ Allow the 'admin' group complete access to all operations and data.
+
+
+
+
+ 0
+EOF
+)
+
+# kill old backend (if any)
+new "kill old backend"
+sudo clixon_backend -zf $cfg -y $fyang
+if [ $? -ne 0 ]; then
+ err
+fi
+
+new "start backend -s init -f $cfg -y $fyang"
+# start new backend
+sudo clixon_backend -s init -f $cfg -y $fyang
+if [ $? -ne 0 ]; then
+ err
+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
+
+sleep 1
+
+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}
+
'
+
+new "auth set authentication config"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" "$RULES]]>]]>" "^]]>]]>$"
+
+new "commit it"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$"
+
+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"}}}
'
+
+new2 "auth get (wrong passwd: access denied)"
+expecteq "$(curl -u adm1:foo -sS -X GET 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"}}}
'
+
+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}
+
'
+
+new2 "limited get nacm"
+expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+
'
+
+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"}}}
'
+
+new "admin edit nacm"
+expecteq "$(curl -u adm1:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
+
+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"}}}
'
+
+new "Kill restconf daemon"
+sudo pkill -u www-data clixon_restconf
+
+pid=`pgrep clixon_backend`
+if [ -z "$pid" ]; then
+ err "backend already dead"
+fi
+# kill backend
+sudo clixon_backend -zf $cfg
+if [ $? -ne 0 ]; then
+ err "kill backend"
+fi
+
+rm -rf $dir
diff --git a/yang/Makefile.in b/yang/Makefile.in
index e4a8aaf9..965c0655 100644
--- a/yang/Makefile.in
+++ b/yang/Makefile.in
@@ -42,6 +42,7 @@ YANGSPECS = clixon-config@2018-02-12.yang
YANGSPECS += ietf-netconf@2011-06-01.yang
YANGSPECS += ietf-netconf-acm@2018-02-14.yang
YANGSPECS += ietf-inet-types@2013-07-15.yang
+YANGSPECS += ietf-yang-types@2013-07-15.yang
APPNAME = clixon # subdir ehere these files are installed
diff --git a/yang/clixon-config@2018-02-12.yang b/yang/clixon-config@2018-02-12.yang
index 84dc8622..c629a3c7 100644
--- a/yang/clixon-config@2018-02-12.yang
+++ b/yang/clixon-config@2018-02-12.yang
@@ -341,6 +341,13 @@ module clixon-config {
leaf CLICON_NACM_MODE {
type nacm_mode;
default disabled;
- description "RFC8341 network access configuration control model"; }
+ description "RFC8341 network access configuration control model
+ (NACM) mode: disabled, in regular (internal) config
+ or separate external file given by CLICON_NACM_FILE";
+ }
+ leaf CLICON_NACM_FILE {
+ type string;
+ description "RFC8341 NACM external configuration file";
+ }
}
}
diff --git a/yang/ietf-yang-types@2013-07-15.yang b/yang/ietf-yang-types@2013-07-15.yang
new file mode 100644
index 00000000..07834130
--- /dev/null
+++ b/yang/ietf-yang-types@2013-07-15.yang
@@ -0,0 +1,481 @@
+ module ietf-yang-types {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+ prefix "yang";
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web:
+ WG List:
+
+ WG Chair: David Kessens
+
+
+ WG Chair: Juergen Schoenwaelder
+
+
+ Editor: Juergen Schoenwaelder
+ ";
+
+ description
+ "This module contains a collection of generally useful derived
+ YANG data types.
+
+ Copyright (c) 2013 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 6991; see
+ the RFC itself for full legal notices.";
+
+ revision 2013-07-15 {
+ description
+ "This revision adds the following new data types:
+ - yang-identifier
+ - hex-string
+ - uuid
+ - dotted-quad";
+ reference
+ "RFC 6991: Common YANG Data Types";
+ }
+
+ revision 2010-09-24 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 6021: Common YANG Data Types";
+ }
+
+ /*** collection of counter and gauge types ***/
+
+ typedef counter32 {
+ type uint32;
+ description
+ "The counter32 type represents a non-negative integer
+ that monotonically increases until it reaches a
+ maximum value of 2^32-1 (4294967295 decimal), when it
+ wraps around and starts increasing again from zero.
+
+ Counters have no defined 'initial' value, and thus, a
+ single value of a counter has (in general) no information
+ content. Discontinuities in the monotonically increasing
+ value normally occur at re-initialization of the
+ management system, and at other times as specified in the
+ description of a schema node using this type. If such
+ other times can occur, for example, the creation of
+ a schema node of type counter32 at times other than
+ re-initialization, then a corresponding schema node
+ should be defined, with an appropriate type, to indicate
+ the last discontinuity.
+
+ The counter32 type should not be used for configuration
+ schema nodes. A default statement SHOULD NOT be used in
+ combination with the type counter32.
+
+ In the value set and its semantics, this type is equivalent
+ to the Counter32 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef zero-based-counter32 {
+ type yang:counter32;
+ default "0";
+ description
+ "The zero-based-counter32 type represents a counter32
+ that has the defined 'initial' value zero.
+
+ A schema node of this type will be set to zero (0) on creation
+ and will thereafter increase monotonically until it reaches
+ a maximum value of 2^32-1 (4294967295 decimal), when it
+ wraps around and starts increasing again from zero.
+
+ Provided that an application discovers a new schema node
+ of this type within the minimum time to wrap, it can use the
+ 'initial' value as a delta. It is important for a management
+ station to be aware of this minimum time and the actual time
+ between polls, and to discard data if the actual time is too
+ long or there is no defined minimum time.
+
+ In the value set and its semantics, this type is equivalent
+ to the ZeroBasedCounter32 textual convention of the SMIv2.";
+ reference
+ "RFC 4502: Remote Network Monitoring Management Information
+ Base Version 2";
+ }
+
+ typedef counter64 {
+ type uint64;
+ description
+ "The counter64 type represents a non-negative integer
+ that monotonically increases until it reaches a
+ maximum value of 2^64-1 (18446744073709551615 decimal),
+ when it wraps around and starts increasing again from zero.
+
+ Counters have no defined 'initial' value, and thus, a
+ single value of a counter has (in general) no information
+ content. Discontinuities in the monotonically increasing
+ value normally occur at re-initialization of the
+ management system, and at other times as specified in the
+ description of a schema node using this type. If such
+ other times can occur, for example, the creation of
+ a schema node of type counter64 at times other than
+ re-initialization, then a corresponding schema node
+ should be defined, with an appropriate type, to indicate
+ the last discontinuity.
+
+ The counter64 type should not be used for configuration
+ schema nodes. A default statement SHOULD NOT be used in
+ combination with the type counter64.
+
+ In the value set and its semantics, this type is equivalent
+ to the Counter64 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef zero-based-counter64 {
+ type yang:counter64;
+ default "0";
+ description
+ "The zero-based-counter64 type represents a counter64 that
+ has the defined 'initial' value zero.
+
+
+
+
+ A schema node of this type will be set to zero (0) on creation
+ and will thereafter increase monotonically until it reaches
+ a maximum value of 2^64-1 (18446744073709551615 decimal),
+ when it wraps around and starts increasing again from zero.
+
+ Provided that an application discovers a new schema node
+ of this type within the minimum time to wrap, it can use the
+ 'initial' value as a delta. It is important for a management
+ station to be aware of this minimum time and the actual time
+ between polls, and to discard data if the actual time is too
+ long or there is no defined minimum time.
+
+ In the value set and its semantics, this type is equivalent
+ to the ZeroBasedCounter64 textual convention of the SMIv2.";
+ reference
+ "RFC 2856: Textual Conventions for Additional High Capacity
+ Data Types";
+ }
+
+ typedef gauge32 {
+ type uint32;
+ description
+ "The gauge32 type represents a non-negative integer, which
+ may increase or decrease, but shall never exceed a maximum
+ value, nor fall below a minimum value. The maximum value
+ cannot be greater than 2^32-1 (4294967295 decimal), and
+ the minimum value cannot be smaller than 0. The value of
+ a gauge32 has its maximum value whenever the information
+ being modeled is greater than or equal to its maximum
+ value, and has its minimum value whenever the information
+ being modeled is smaller than or equal to its minimum value.
+ If the information being modeled subsequently decreases
+ below (increases above) the maximum (minimum) value, the
+ gauge32 also decreases (increases).
+
+ In the value set and its semantics, this type is equivalent
+ to the Gauge32 type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef gauge64 {
+ type uint64;
+ description
+ "The gauge64 type represents a non-negative integer, which
+ may increase or decrease, but shall never exceed a maximum
+ value, nor fall below a minimum value. The maximum value
+ cannot be greater than 2^64-1 (18446744073709551615), and
+ the minimum value cannot be smaller than 0. The value of
+ a gauge64 has its maximum value whenever the information
+ being modeled is greater than or equal to its maximum
+ value, and has its minimum value whenever the information
+ being modeled is smaller than or equal to its minimum value.
+ If the information being modeled subsequently decreases
+ below (increases above) the maximum (minimum) value, the
+ gauge64 also decreases (increases).
+
+ In the value set and its semantics, this type is equivalent
+ to the CounterBasedGauge64 SMIv2 textual convention defined
+ in RFC 2856";
+ reference
+ "RFC 2856: Textual Conventions for Additional High Capacity
+ Data Types";
+ }
+
+ /*** collection of identifier-related types ***/
+
+ typedef object-identifier {
+ type string {
+ pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+ + '(\.(0|([1-9]\d*)))*';
+ }
+ description
+ "The object-identifier type represents administratively
+ assigned names in a registration-hierarchical-name tree.
+
+ Values of this type are denoted as a sequence of numerical
+ non-negative sub-identifier values. Each sub-identifier
+ value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers
+ are separated by single dots and without any intermediate
+ whitespace.
+
+ The ASN.1 standard restricts the value space of the first
+ sub-identifier to 0, 1, or 2. Furthermore, the value space
+ of the second sub-identifier is restricted to the range
+ 0 to 39 if the first sub-identifier is 0 or 1. Finally,
+ the ASN.1 standard requires that an object identifier
+ has always at least two sub-identifiers. The pattern
+ captures these restrictions.
+
+ Although the number of sub-identifiers is not limited,
+ module designers should realize that there may be
+ implementations that stick with the SMIv2 limit of 128
+ sub-identifiers.
+
+ This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+ since it is not restricted to 128 sub-identifiers. Hence,
+ this type SHOULD NOT be used to represent the SMIv2 OBJECT
+ IDENTIFIER type; the object-identifier-128 type SHOULD be
+ used instead.";
+ reference
+ "ISO9834-1: Information technology -- Open Systems
+ Interconnection -- Procedures for the operation of OSI
+ Registration Authorities: General procedures and top
+ arcs of the ASN.1 Object Identifier tree";
+ }
+
+ typedef object-identifier-128 {
+ type object-identifier {
+ pattern '\d*(\.\d*){1,127}';
+ }
+ description
+ "This type represents object-identifiers restricted to 128
+ sub-identifiers.
+
+ In the value set and its semantics, this type is equivalent
+ to the OBJECT IDENTIFIER type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef yang-identifier {
+ type string {
+ length "1..max";
+ pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+ pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+ }
+ description
+ "A YANG identifier string as defined by the 'identifier'
+ rule in Section 12 of RFC 6020. An identifier must
+ start with an alphabetic character or an underscore
+ followed by an arbitrary sequence of alphabetic or
+ numeric characters, underscores, hyphens, or dots.
+
+ A YANG identifier MUST NOT start with any possible
+ combination of the lowercase or uppercase character
+ sequence 'xml'.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the Network
+ Configuration Protocol (NETCONF)";
+ }
+
+ /*** collection of types related to date and time***/
+
+ typedef date-and-time {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+ + '(Z|[\+\-]\d{2}:\d{2})';
+ }
+ description
+ "The date-and-time type is a profile of the ISO 8601
+ standard for representation of dates and times using the
+ Gregorian calendar. The profile is defined by the
+ date-time production in Section 5.6 of RFC 3339.
+
+ The date-and-time type is compatible with the dateTime XML
+ schema type with the following notable exceptions:
+
+ (a) The date-and-time type does not allow negative years.
+
+ (b) The date-and-time time-offset -00:00 indicates an unknown
+ time zone (see RFC 3339) while -00:00 and +00:00 and Z
+ all represent the same time zone in dateTime.
+
+ (c) The canonical format (see below) of data-and-time values
+ differs from the canonical format used by the dateTime XML
+ schema type, which requires all times to be in UTC using
+ the time-offset 'Z'.
+
+ This type is not equivalent to the DateAndTime textual
+ convention of the SMIv2 since RFC 3339 uses a different
+ separator between full-date and full-time and provides
+ higher resolution of time-secfrac.
+
+ The canonical format for date-and-time values with a known time
+ zone uses a numeric time zone offset that is calculated using
+ the device's configured known offset to UTC time. A change of
+ the device's offset to UTC time will cause date-and-time values
+ to change accordingly. Such changes might happen periodically
+ in case a server follows automatically daylight saving time
+ (DST) time zone offset changes. The canonical format for
+ date-and-time values with an unknown time zone (usually
+ referring to the notion of local time) uses the time-offset
+ -00:00.";
+ reference
+ "RFC 3339: Date and Time on the Internet: Timestamps
+ RFC 2579: Textual Conventions for SMIv2
+ XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+ }
+
+ typedef timeticks {
+ type uint32;
+ description
+ "The timeticks type represents a non-negative integer that
+ represents the time, modulo 2^32 (4294967296 decimal), in
+ hundredths of a second between two epochs. When a schema
+ node is defined that uses this type, the description of
+ the schema node identifies both of the reference epochs.
+
+ In the value set and its semantics, this type is equivalent
+ to the TimeTicks type of the SMIv2.";
+ reference
+ "RFC 2578: Structure of Management Information Version 2
+ (SMIv2)";
+ }
+
+ typedef timestamp {
+ type yang:timeticks;
+ description
+ "The timestamp type represents the value of an associated
+ timeticks schema node at which a specific occurrence
+ happened. The specific occurrence must be defined in the
+ description of any schema node defined using this type. When
+ the specific occurrence occurred prior to the last time the
+ associated timeticks attribute was zero, then the timestamp
+ value is zero. Note that this requires all timestamp values
+ to be reset to zero when the value of the associated timeticks
+ attribute reaches 497+ days and wraps around to zero.
+
+ The associated timeticks schema node must be specified
+ in the description of any schema node using this type.
+
+ In the value set and its semantics, this type is equivalent
+ to the TimeStamp textual convention of the SMIv2.";
+ reference
+ "RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ /*** collection of generic address types ***/
+
+ typedef phys-address {
+ type string {
+ pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+ }
+
+
+
+
+ description
+ "Represents media- or physical-level addresses represented
+ as a sequence octets, each octet represented by two hexadecimal
+ numbers. Octets are separated by colons. The canonical
+ representation uses lowercase characters.
+
+ In the value set and its semantics, this type is equivalent
+ to the PhysAddress textual convention of the SMIv2.";
+ reference
+ "RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ typedef mac-address {
+ type string {
+ pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+ }
+ description
+ "The mac-address type represents an IEEE 802 MAC address.
+ The canonical representation uses lowercase characters.
+
+ In the value set and its semantics, this type is equivalent
+ to the MacAddress textual convention of the SMIv2.";
+ reference
+ "IEEE 802: IEEE Standard for Local and Metropolitan Area
+ Networks: Overview and Architecture
+ RFC 2579: Textual Conventions for SMIv2";
+ }
+
+ /*** collection of XML-specific types ***/
+
+ typedef xpath1.0 {
+ type string;
+ description
+ "This type represents an XPATH 1.0 expression.
+
+ When a schema node is defined that uses this type, the
+ description of the schema node MUST specify the XPath
+ context in which the XPath expression is evaluated.";
+ reference
+ "XPATH: XML Path Language (XPath) Version 1.0";
+ }
+
+ /*** collection of string types ***/
+
+ typedef hex-string {
+ type string {
+ pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+ }
+ description
+ "A hexadecimal string with octets represented as hex digits
+ separated by colons. The canonical representation uses
+ lowercase characters.";
+ }
+
+ typedef uuid {
+ type string {
+ pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+ + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+ }
+ description
+ "A Universally Unique IDentifier in the string representation
+ defined in RFC 4122. The canonical representation uses
+ lowercase characters.
+
+ The following is an example of a UUID in string representation:
+ f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+ ";
+ reference
+ "RFC 4122: A Universally Unique IDentifier (UUID) URN
+ Namespace";
+ }
+
+ typedef dotted-quad {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+ }
+ description
+ "An unsigned 32-bit number expressed in the dotted-quad
+ notation, i.e., four octets written as decimal numbers
+ and separated with the '.' (full stop) character.";
+ }
+ }
+