diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b5a8f36..da4150e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Clixon Changelog -* [4.6.0](#460) Expected: July 2020 +* [4.6.0](#460) Expected: August 2020 * [4.5.0](#450) 12 May 2020 * [4.4.0](#440) 5 April 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) * This applies if NACM is loaded and `CLICON_NACM_MODE` is `internal` * 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). * Workarounds: 1. Access the system with the recovery user, see clixon option CLICON_NACM_RECOVERY_USER diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index f92f624d..e3be78a5 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -1490,69 +1490,6 @@ from_client_restart_plugin(clicon_handle h, 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 @@ -1670,9 +1607,16 @@ from_client_msg(clicon_handle h, /* Pre-NACM access step */ 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; - /* 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) goto done; if (ret == 0){ /* Do NACM RPC validation */ diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index cbcc5314..cf020906 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -217,7 +217,7 @@ startup_common(clicon_handle h, /* Print upgraded db: -q backend switch */ if (clicon_quit_upgrade_get(h) == 1){ /* 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){ /* invalid */ clicon_err(OE_XML, EFAULT, "invalid configuration"); diff --git a/lib/clixon/clixon_nacm.h b/lib/clixon/clixon_nacm.h index dd5e7a74..6c833679 100644 --- a/lib/clixon/clixon_nacm.h +++ b/lib/clixon/clixon_nacm.h @@ -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, enum nacm_access access, 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 */ diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index 03a5d2c6..4830d849 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -95,7 +95,7 @@ enum priv_mode_t{ /*! See clixon-config.yang type nacm_cred_mode (user credentials) */ 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_EXCEPT /* Exact match except for root and www user */ }; diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c index b9fc32fa..55a1a435 100644 --- a/lib/src/clixon_nacm.c +++ b/lib/src/clixon_nacm.c @@ -1075,7 +1075,7 @@ nacm_datanode_read(clicon_handle h, * @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 * @code - * if ((ret = nacm_access(h, mode, xnacm, username)) < 0) + * if ((ret = nacm_access_check(h, mode, xnacm, peername, username)) < 0) * err; * if (ret == 0){ * // Next step NACM processing @@ -1085,14 +1085,16 @@ nacm_datanode_read(clicon_handle h, * @see RFC8341 3.4 Access Control Enforcement Procedures */ static int -nacm_access(clicon_handle h, - cxobj *xnacm, - char *username) +nacm_access_check(clicon_handle h, + cxobj *xnacm, + char *peername, + char *username) { int retval = -1; char *enabled; cxobj *x; cvec *nsc = NULL; + char *recovery_user; clicon_debug(1, "%s", __FUNCTION__); if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL) @@ -1108,11 +1110,40 @@ nacm_access(clicon_handle h, enabled = xml_body(x); if (strcmp(enabled, "true") != 0) goto permit; + recovery_user=clicon_nacm_recovery_user(h); /* 2. If the requesting session is identified as a recovery session, - then the protocol operation is permitted. NYI */ - if (username && strcmp(username, clicon_nacm_recovery_user(h)) == 0) - goto permit; - + * then the protocol operation is permitted. + */ + 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 */ done: if (nsc) @@ -1133,10 +1164,10 @@ nacm_access(clicon_handle h, * @param[out] xncam NACM XML tree, set if retval=0. Free after use * @retval -1 Error * @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 * cxobj *xnacm = NULL; - * if ((ret = nacm_access_pre(h, username, &xnacm)) < 0) + * if ((ret = nacm_access_pre(h, peername, username, &xnacm)) < 0) * err; * if (ret == 0){ * // Next step NACM processing @@ -1147,6 +1178,7 @@ nacm_access(clicon_handle h, */ int nacm_access_pre(clicon_handle h, + char *peername, char *username, cxobj **xnacmp) { @@ -1189,7 +1221,7 @@ nacm_access_pre(clicon_handle h, goto done; xnacm0 = NULL; /* 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; if (retval == 0){ /* if retval == 0 then return an xml nacm tree */ *xnacmp = xnacm; @@ -1208,3 +1240,70 @@ nacm_access_pre(clicon_handle h, 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; +} diff --git a/yang/clixon/clixon-config@2020-06-17.yang b/yang/clixon/clixon-config@2020-06-17.yang index 3ae88cd9..2a3c7de0 100644 --- a/yang/clixon/clixon-config@2020-06-17.yang +++ b/yang/clixon/clixon-config@2020-06-17.yang @@ -704,8 +704,7 @@ module clixon-config { description "Verify nacm user credentials with unix socket peer cred. This means nacm user must match unix user accessing the backend - socket. - Except for recovery user and www user (for restconf)"; + socket."; } leaf CLICON_NACM_RECOVERY_USER { type string; @@ -716,7 +715,7 @@ module clixon-config { all access control enforcements. Note setting of CLICON_NACM_CREDENTIALS is important, if set to 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 { type boolean;