restricted NACM recovery user

This commit is contained in:
Olof hagsand 2020-08-06 21:43:27 +02:00
parent 5c3f18c2da
commit a7737c283c
7 changed files with 126 additions and 99 deletions

View file

@ -1,6 +1,6 @@
# Clixon Changelog # Clixon Changelog
* [4.6.0](#460) Expected: July 2020 * [4.6.0](#460) Expected: August 2020
* [4.5.0](#450) 12 May 2020 * [4.5.0](#450) 12 May 2020
* [4.4.0](#440) 5 April 2020 * [4.4.0](#440) 5 April 2020
* [4.3.0](#430) 1 January 2020 * [4.3.0](#430) 1 January 2020
@ -49,22 +49,6 @@ Expected: July 2020
* NACM default behaviour is read-only (empty configs are dead-lockedd) * NACM default behaviour is read-only (empty configs are dead-lockedd)
* This applies if NACM is loaded and `CLICON_NACM_MODE` is `internal` * This applies if NACM is loaded and `CLICON_NACM_MODE` is `internal`
* Due to the previous bult (top-level default leafs) * Due to the previous bult (top-level default leafs)
* This means that empty configs or empty NACM configs are not writable (deadlocked).
* Workarounds:
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
* This means that empty configs or empty NACM configs are not writable (deadlocked).
* Workarounds:
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
* This means that empty configs or empty NACM configs are not writable (deadlocked).
* Workarounds:
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
* This means that empty configs or empty NACM configs are not writable (deadlocked).
* Workarounds:
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER
* This means that empty configs or empty NACM configs are not writable (deadlocked). * This means that empty configs or empty NACM configs are not writable (deadlocked).
* Workarounds: * Workarounds:
1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER 1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER

View file

@ -1490,69 +1490,6 @@ from_client_restart_plugin(clicon_handle h,
return retval; return retval;
} }
/*! Verify nacm user with peer uid credentials
* @param[in] mode Peer credential mode: none, exact or except
* @param[in] peername Peer username if any
* @param[in] username username received in XML (eg for NACM)
* @param[out] cbret Set with netconf error message if ret == 0
* @retval -1 Error
* @retval 0 Not verified (cbret set)
* @retval 1 Verified
* Credentials OK if both NACM user and peer useri s defined AND one of the
* following is true:
* - peer user is same as NACM user
* - peer user is root (can be any NACM user)
* - peer user is www (can be any NACM user)
*/
static int
verify_nacm_user(enum nacm_credentials_t mode,
char *peername,
char *nacmname,
cbuf *cbret)
{
int retval = -1;
cbuf *cbmsg = NULL;
if (mode == NC_NONE)
return 1;
if (peername == NULL){
if (netconf_access_denied(cbret, "application", "No peer user credentials available") < 0)
goto done;
goto fail;
}
if (nacmname == NULL){
if (netconf_access_denied(cbret, "application", "No NACM available") < 0)
goto done;
goto fail;
}
if (mode == NC_EXCEPT){
if (strcmp(peername, "root") == 0)
goto ok;
#ifdef WITH_RESTCONF
if (strcmp(peername, WWWUSER) == 0)
goto ok;
#endif
}
if (strcmp(peername, nacmname) != 0){
if ((cbmsg = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cbmsg, "User %s credential not matching NACM user %s", peername, nacmname);
if (netconf_access_denied(cbret, "application", cbuf_get(cbmsg)) < 0)
goto done;
goto fail;
}
ok:
retval = 1;
done:
if (cbmsg)
cbuf_free(cbmsg);
return retval;
fail:
retval = 0;
goto done;
}
/*! /*!
* @retval 0 OK * @retval 0 OK
@ -1670,9 +1607,16 @@ from_client_msg(clicon_handle h,
/* Pre-NACM access step */ /* Pre-NACM access step */
xnacm = NULL; xnacm = NULL;
if ((ret = nacm_access_pre(h, username, &xnacm)) < 0) /* NACM intial pre- access control enforcements. Retval:
* 0: Use NACM validation and xnacm is set.
* 1: Permit, skip NACM
* Therefore, xnacm=NULL means no NACM checks needed.
*/
if ((ret = nacm_access_pre(h, ce->ce_username, username, &xnacm)) < 0)
goto done; goto done;
/* Cache XML NACM tree here. Use with caution, only valid on from_client_msg stack */ /* Cache XML NACM tree here. Use with caution, only valid on from_client_msg stack
*
*/
if (clicon_nacm_cache_set(h, xnacm) < 0) if (clicon_nacm_cache_set(h, xnacm) < 0)
goto done; goto done;
if (ret == 0){ /* Do NACM RPC validation */ if (ret == 0){ /* Do NACM RPC validation */

View file

@ -217,7 +217,7 @@ startup_common(clicon_handle h,
/* Print upgraded db: -q backend switch */ /* Print upgraded db: -q backend switch */
if (clicon_quit_upgrade_get(h) == 1){ if (clicon_quit_upgrade_get(h) == 1){
/* bind yang */ /* bind yang */
if (ret = (xml_bind_yang(xt, YB_MODULE, yspec, &xret) < 1)){ if ((ret = (xml_bind_yang(xt, YB_MODULE, yspec, &xret)) < 1)){
if (ret == 0){ if (ret == 0){
/* invalid */ /* invalid */
clicon_err(OE_XML, EFAULT, "invalid configuration"); clicon_err(OE_XML, EFAULT, "invalid configuration");

View file

@ -62,6 +62,7 @@ int nacm_datanode_read(clicon_handle h, cxobj *xt, cxobj **xvec, size_t xlen, ch
int nacm_datanode_write(clicon_handle h, cxobj *xr, cxobj *xt, int nacm_datanode_write(clicon_handle h, cxobj *xr, cxobj *xt,
enum nacm_access access, enum nacm_access access,
char *username, cxobj *xnacm, cbuf *cbret); char *username, cxobj *xnacm, cbuf *cbret);
int nacm_access_pre(clicon_handle h, char *username, cxobj **xnacmp); int nacm_access_pre(clicon_handle h, char *peername, char *username, cxobj **xnacmp);
int verify_nacm_user(enum nacm_credentials_t cred, char *peername, char *nacmname, cbuf *cbret);
#endif /* _CLIXON_NACM_H */ #endif /* _CLIXON_NACM_H */

View file

@ -95,7 +95,7 @@ enum priv_mode_t{
/*! See clixon-config.yang type nacm_cred_mode (user credentials) */ /*! See clixon-config.yang type nacm_cred_mode (user credentials) */
enum nacm_credentials_t{ enum nacm_credentials_t{
NC_NONE=0, /* "Dont match NACM user to any user credentials. */ NC_NONE=0, /* Dont match NACM user to any user credentials. */
NC_EXACT, /* Exact match between NACM user and unix socket peer user. */ NC_EXACT, /* Exact match between NACM user and unix socket peer user. */
NC_EXCEPT /* Exact match except for root and www user */ NC_EXCEPT /* Exact match except for root and www user */
}; };

View file

@ -1075,7 +1075,7 @@ nacm_datanode_read(clicon_handle h,
* @retval 0 OK but not validated. Need to do NACM step using xnacm * @retval 0 OK but not validated. Need to do NACM step using xnacm
* @retval 1 OK permitted. You do not need to do next NACM step * @retval 1 OK permitted. You do not need to do next NACM step
* @code * @code
* if ((ret = nacm_access(h, mode, xnacm, username)) < 0) * if ((ret = nacm_access_check(h, mode, xnacm, peername, username)) < 0)
* err; * err;
* if (ret == 0){ * if (ret == 0){
* // Next step NACM processing * // Next step NACM processing
@ -1085,14 +1085,16 @@ nacm_datanode_read(clicon_handle h,
* @see RFC8341 3.4 Access Control Enforcement Procedures * @see RFC8341 3.4 Access Control Enforcement Procedures
*/ */
static int static int
nacm_access(clicon_handle h, nacm_access_check(clicon_handle h,
cxobj *xnacm, cxobj *xnacm,
char *username) char *peername,
char *username)
{ {
int retval = -1; int retval = -1;
char *enabled; char *enabled;
cxobj *x; cxobj *x;
cvec *nsc = NULL; cvec *nsc = NULL;
char *recovery_user;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL) if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
@ -1108,11 +1110,40 @@ nacm_access(clicon_handle h,
enabled = xml_body(x); enabled = xml_body(x);
if (strcmp(enabled, "true") != 0) if (strcmp(enabled, "true") != 0)
goto permit; goto permit;
recovery_user=clicon_nacm_recovery_user(h);
/* 2. If the requesting session is identified as a recovery session, /* 2. If the requesting session is identified as a recovery session,
then the protocol operation is permitted. NYI */ * then the protocol operation is permitted.
if (username && strcmp(username, clicon_nacm_recovery_user(h)) == 0) */
goto permit; if (username && peername && recovery_user &&
strcmp(username, recovery_user) == 0){
/* Recovery session in clixon is defined as
* 1) username (sent in message) is recovery user
* AND
* if cred is EXACT:
* 2a) peername is also recovery user
* if cred is EXCEPT/NONE:;
* 2b) peername is recovery user/root/WWWUSER
*/
if (strcmp(peername, recovery_user) == 0)
goto permit;
switch(clicon_nacm_credentials(h)){
case NC_EXACT:
break;
case NC_NONE:
goto permit;
break;
case NC_EXCEPT:
if (strcmp(username, recovery_user) == 0 &&
strcmp(peername, "root") == 0)
goto permit;
#ifdef WITH_RESTCONF
if (strcmp(username, recovery_user) == 0 &&
strcmp(peername, WWWUSER) == 0)
goto permit;
#endif
break;
}
}
retval = 0; /* not permitted yet. continue with next NACM step */ retval = 0; /* not permitted yet. continue with next NACM step */
done: done:
if (nsc) if (nsc)
@ -1133,10 +1164,10 @@ nacm_access(clicon_handle h,
* @param[out] xncam NACM XML tree, set if retval=0. Free after use * @param[out] xncam NACM XML tree, set if retval=0. Free after use
* @retval -1 Error * @retval -1 Error
* @retval 0 OK but not validated. Need to do NACM step using xnacm * @retval 0 OK but not validated. Need to do NACM step using xnacm
* @retval 1 OK permitted. You do not need to do next NACM step * @retval 1 OK permitted. You do not need to do next NACM step.
* @code * @code
* cxobj *xnacm = NULL; * cxobj *xnacm = NULL;
* if ((ret = nacm_access_pre(h, username, &xnacm)) < 0) * if ((ret = nacm_access_pre(h, peername, username, &xnacm)) < 0)
* err; * err;
* if (ret == 0){ * if (ret == 0){
* // Next step NACM processing * // Next step NACM processing
@ -1147,6 +1178,7 @@ nacm_access(clicon_handle h,
*/ */
int int
nacm_access_pre(clicon_handle h, nacm_access_pre(clicon_handle h,
char *peername,
char *username, char *username,
cxobj **xnacmp) cxobj **xnacmp)
{ {
@ -1189,7 +1221,7 @@ nacm_access_pre(clicon_handle h,
goto done; goto done;
xnacm0 = NULL; xnacm0 = NULL;
/* Initial NACM steps and common to all NACM access validation. */ /* Initial NACM steps and common to all NACM access validation. */
if ((retval = nacm_access(h, xnacm, username)) < 0) if ((retval = nacm_access_check(h, xnacm, peername, username)) < 0)
goto done; goto done;
if (retval == 0){ /* if retval == 0 then return an xml nacm tree */ if (retval == 0){ /* if retval == 0 then return an xml nacm tree */
*xnacmp = xnacm; *xnacmp = xnacm;
@ -1208,3 +1240,70 @@ nacm_access_pre(clicon_handle h,
goto done; goto done;
} }
/*! Verify nacm user with peer uid credentials
* @param[in] mode Peer credential mode: none, exact or except
* @param[in] peername Peer username if any
* @param[in] username username received in XML (eg for NACM)
* @param[out] cbret Set with netconf error message if ret == 0
* @retval -1 Error
* @retval 0 Not verified (cbret set)
* @retval 1 Verified
* Credentials OK if
* - cred mode is NONE,
* Otherwise both NACM user AND peer user must exist, and
* if cred mode is EXACT and
* - peer user is same as NACM user
* or if cred mode is EXCEPT and one of the following is true
* - peer user is same as NACM user
* - peer user is root (can be any NACM user)
* - peer user is www (can be any NACM user)
*/
int
verify_nacm_user(enum nacm_credentials_t cred,
char *peername,
char *nacmname,
cbuf *cbret)
{
int retval = -1;
cbuf *cbmsg = NULL;
if (cred == NC_NONE)
return 1;
if (peername == NULL){
if (netconf_access_denied(cbret, "application", "No peer user credentials available") < 0)
goto done;
goto fail;
}
if (nacmname == NULL){
if (netconf_access_denied(cbret, "application", "No NACM available") < 0)
goto done;
goto fail;
}
if (cred == NC_EXCEPT){
if (strcmp(peername, "root") == 0)
goto ok;
#ifdef WITH_RESTCONF
if (strcmp(peername, WWWUSER) == 0)
goto ok;
#endif
}
if (strcmp(peername, nacmname) != 0){
if ((cbmsg = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cbmsg, "User %s credential not matching NACM user %s", peername, nacmname);
if (netconf_access_denied(cbret, "application", cbuf_get(cbmsg)) < 0)
goto done;
goto fail;
}
ok:
retval = 1;
done:
if (cbmsg)
cbuf_free(cbmsg);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -704,8 +704,7 @@ module clixon-config {
description description
"Verify nacm user credentials with unix socket peer cred. "Verify nacm user credentials with unix socket peer cred.
This means nacm user must match unix user accessing the backend This means nacm user must match unix user accessing the backend
socket. socket.";
Except for recovery user and www user (for restconf)";
} }
leaf CLICON_NACM_RECOVERY_USER { leaf CLICON_NACM_RECOVERY_USER {
type string; type string;
@ -716,7 +715,7 @@ module clixon-config {
all access control enforcements. all access control enforcements.
Note setting of CLICON_NACM_CREDENTIALS is important, if set to Note setting of CLICON_NACM_CREDENTIALS is important, if set to
exact for example, this user must exist and be used, otherwise exact for example, this user must exist and be used, otherwise
another user (such as root or www) can pose as it."; another user (such as root or www) can pose as the recovery user.";
} }
leaf CLICON_NACM_DISABLED_ON_EMPTY { leaf CLICON_NACM_DISABLED_ON_EMPTY {
type boolean; type boolean;