* Implemented backend daemon drop privileges after initialization to
run as non-privileged user
This commit is contained in:
parent
cacba627b5
commit
27fd99e7cd
61 changed files with 673 additions and 207 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -3,11 +3,17 @@
|
|||
## 4.2.0 (Expected: September)
|
||||
|
||||
### Major New features
|
||||
* Backend daemon drops privileges after initialization (to not run as root)
|
||||
* New config option `CLICON_USER` with default value `clicon`
|
||||
* Backend daemon can drop privileges after initialization to run as non-privileged user
|
||||
* You can start as root and drop privileges either permanently or temporary
|
||||
* Controlled by options: CLICON_BACKEND_USER and CLICON_BACKEND_PRIVELEGES
|
||||
* Can also be set with `-U <user>` clixon_backend command-line option
|
||||
* If dropped temporary, you can restore privileges with `restore_priv()`
|
||||
|
||||
### API changes on existing features (you may need to change your code)
|
||||
* Typical installation should now add a `clicon` user (as well as group)
|
||||
* New clixon-config@2019-09-11.yang revision
|
||||
* Added: CLICON_BACKEND_USER: drop of privileges to user,
|
||||
* Added: CLICON_BACKEND_PRIVELEGES: how to drop privileges
|
||||
* Restconf top-level operations GET root resource modified to comply with RFC 8040 Sec 3.1
|
||||
* non-pretty print remove all spaces, eg `{"operations":{"clixon-example:client-rpc":[null]`
|
||||
* Replaced JSON `null` with `[null]` as proper empty JSON leaf/leaf-list encoding.
|
||||
|
|
|
|||
|
|
@ -242,45 +242,94 @@ xmldb_drop_priv(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Drop root privileges uid and gid to Clixon user/group
|
||||
* @param[in] h Clicon handle
|
||||
/*! Drop root privileges uid and gid to Clixon user/group and
|
||||
*
|
||||
* If config options are right, drop process uid/guid privileges and change some
|
||||
* file ownerships.
|
||||
* "Right" means:
|
||||
* - uid is currently 0 (started as root)
|
||||
* - CLICON_BACKEND_USER is set
|
||||
* - CLICON_BACKEND_PRIVILEGES is not "none"
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] gid Group id (assume already known)
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
drop_priv(clicon_handle h,
|
||||
uid_t uid,
|
||||
gid_t gid)
|
||||
check_drop_priv(clicon_handle h,
|
||||
gid_t gid)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
int retval = -1;
|
||||
uid_t uid;
|
||||
uid_t newuid = -1;
|
||||
enum priv_mode_t priv_mode = PM_NONE;
|
||||
char *backend_user = NULL;
|
||||
|
||||
/* Get privileges mode (for dropping privileges) */
|
||||
priv_mode = clicon_backend_privileges_mode(h);
|
||||
if (priv_mode == PM_NONE)
|
||||
goto ok;
|
||||
|
||||
/* From here, drop privileges */
|
||||
/* Check backend user exists */
|
||||
if ((backend_user = clicon_backend_user(h)) == NULL){
|
||||
clicon_err(OE_DEMON, EPERM, "Privileges cannot be dropped without specifying CLICON_BACKEND_USER\n");
|
||||
goto done;
|
||||
}
|
||||
/* Get (wanted) new backend user id */
|
||||
if (name2uid(backend_user, &newuid) < 0){
|
||||
clicon_err(OE_DEMON, errno, "'%s' is not a valid user .\n", backend_user);
|
||||
goto done;
|
||||
}
|
||||
/* get current backend userid, if already at this level OK */
|
||||
if ((uid = getuid()) == newuid)
|
||||
goto ok;
|
||||
if (uid != 0){
|
||||
clicon_err(OE_DEMON, EPERM, "Privileges can only be dropped from root user (uid is %u)\n", uid);
|
||||
goto done;
|
||||
}
|
||||
/* When dropping priveleges, datastores are created if they do not exist.
|
||||
* But when drops are not made, datastores are created on demand.
|
||||
* XXX: move the creation to top-level so they are always created at init?
|
||||
*/
|
||||
if (xmldb_exists(h, "running") != 1)
|
||||
if (xmldb_create(h, "running") < 0)
|
||||
goto done;
|
||||
if (xmldb_drop_priv(h, "running", uid, gid) < 0)
|
||||
if (xmldb_drop_priv(h, "running", newuid, gid) < 0)
|
||||
goto done;
|
||||
if (xmldb_exists(h, "candidate") != 1)
|
||||
if (xmldb_create(h, "candidate") < 0)
|
||||
goto done;
|
||||
if (xmldb_drop_priv(h, "candidate", uid, gid) < 0)
|
||||
if (xmldb_drop_priv(h, "candidate", newuid, gid) < 0)
|
||||
goto done;
|
||||
if (xmldb_exists(h, "startup") != 1)
|
||||
if (xmldb_create(h, "startup") < 0)
|
||||
goto done;
|
||||
if (xmldb_drop_priv(h, "startup", uid, gid) < 0)
|
||||
if (xmldb_drop_priv(h, "startup", newuid, gid) < 0)
|
||||
goto done;
|
||||
|
||||
if (setgid(gid) == -1) {
|
||||
clicon_err(OE_DEMON, errno, "setgid %d", gid);
|
||||
goto done;
|
||||
}
|
||||
if (setuid(uid) == -1) {
|
||||
clicon_err(OE_DEMON, errno, "setuid %d", uid);
|
||||
goto done;
|
||||
}
|
||||
/* Verify you cannot regain root privileges */
|
||||
if (setuid(0) != -1){
|
||||
clicon_err(OE_DEMON, EPERM, "Could regain root privilieges");
|
||||
goto done;
|
||||
switch (priv_mode){
|
||||
case PM_DROP_PERM:
|
||||
if (drop_priv_perm(newuid) < 0)
|
||||
goto done;
|
||||
/* Verify you cannot regain root privileges */
|
||||
if (setuid(0) != -1){
|
||||
clicon_err(OE_DEMON, EPERM, "Could regain root privilieges");
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case PM_DROP_TEMP:
|
||||
if (drop_priv_temp(newuid) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case PM_NONE:
|
||||
break; /* catched above */
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -356,7 +405,7 @@ usage(clicon_handle h,
|
|||
"\t-1\t\tRun once and then quit (dont wait for events)\n"
|
||||
"\t-s <mode>\tSpecify backend startup mode: none|startup|running|init)\n"
|
||||
"\t-c <file>\tLoad extra xml configuration, but don't commit.\n"
|
||||
"\t-U <user>\tRun backend daemon as this user\n"
|
||||
"\t-U <user>\tRun backend daemon as this user AND drop privileges permanently\n"
|
||||
"\t-g <group>\tClient membership required to this group (default: %s)\n"
|
||||
|
||||
"\t-y <file>\tLoad yang spec file (override yang main module)\n"
|
||||
|
|
@ -382,7 +431,6 @@ main(int argc,
|
|||
int once;
|
||||
enum startup_mode_t startup_mode;
|
||||
char *extraxml_file;
|
||||
char *backend_user = NULL;
|
||||
char *backend_group = NULL;
|
||||
char *argv0 = argv[0];
|
||||
struct stat st;
|
||||
|
|
@ -403,7 +451,6 @@ main(int argc,
|
|||
int ret;
|
||||
char *dir;
|
||||
gid_t gid = -1;
|
||||
uid_t uid = -1;
|
||||
|
||||
/* In the startup, logs to stderr & syslog and debug flag set later */
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
|
||||
|
|
@ -536,7 +583,9 @@ main(int argc,
|
|||
extraxml_file = optarg;
|
||||
break;
|
||||
case 'U': /* config user (for socket and drop privileges) */
|
||||
if (clicon_option_add(h, "CLICON_SOCK", optarg) < 0)
|
||||
if (clicon_option_add(h, "CLICON_USER", optarg) < 0)
|
||||
goto done;
|
||||
if (clicon_option_add(h, "CLICON_BACKEND_PRIVILEGES", "drop_permanent") < 0)
|
||||
goto done;
|
||||
break;
|
||||
case 'g': /* config socket group */
|
||||
|
|
@ -616,17 +665,6 @@ main(int argc,
|
|||
if (sockfamily==AF_UNIX && lstat(sock, &st) == 0)
|
||||
unlink(sock);
|
||||
|
||||
/* XXX maybe only if !foreground */
|
||||
/* Sanity check: backend user exists */
|
||||
if ((backend_user = clicon_user(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "clicon_user option not set");
|
||||
return -1;
|
||||
}
|
||||
if (name2uid(backend_user, &uid) < 0){
|
||||
clicon_log(LOG_ERR, "'%s' does not seem to be a valid user .\n", backend_user);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Sanity check: backend group exists */
|
||||
if ((backend_group = clicon_sock_group(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "clicon_sock_group option not set");
|
||||
|
|
@ -634,13 +672,11 @@ main(int argc,
|
|||
}
|
||||
if (group_name2gid(backend_group, &gid) < 0){
|
||||
clicon_log(LOG_ERR, "'%s' does not seem to be a valid user group.\n" /* \n required here due to multi-line log */
|
||||
"The config demon requires a valid group to create a server UNIX socket\n"
|
||||
"Define a valid CLICON_SOCK_GROUP in %s or via the -g option\n"
|
||||
"or create the group and add the user to it. On linux for example:"
|
||||
" sudo groupadd %s\n"
|
||||
" sudo usermod -a -G %s user\n",
|
||||
backend_group, clicon_configfile(h),
|
||||
backend_group, backend_group);
|
||||
"The config demon requires a valid group to create a server UNIX socket\n"
|
||||
"Define a valid CLICON_SOCK_GROUP in %s or via the -g option\n"
|
||||
"or create the group and add the user to it. Check documentation for how to do this on your platform",
|
||||
backend_group,
|
||||
clicon_configfile(h));
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -845,10 +881,10 @@ main(int argc,
|
|||
goto done;
|
||||
if (debug)
|
||||
clicon_option_dump(h, debug);
|
||||
/* Drop root privileges (unless root) */
|
||||
if (uid != 0)
|
||||
if (drop_priv(h, uid, gid) < 0)
|
||||
goto done;
|
||||
/* Depending on configure setting, privileges may be dropped here after
|
||||
* initializations */
|
||||
if (check_drop_priv(h, gid) < 0)
|
||||
goto done;
|
||||
|
||||
if (stream_timer_setup(0, h) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -106,15 +106,15 @@ Define a valid CLICON_SOCK_GROUP in the config file or via the -g option
|
|||
or create the group and add the user to it. The default group is 'clicon'.
|
||||
Add yourself and www-data, if you intend to use restconf.
|
||||
|
||||
Using groupadd and usermod:
|
||||
Using useradd and usermod:
|
||||
```
|
||||
sudo groupadd clicon #
|
||||
sudo useradd clicon #
|
||||
sudo usermod -a -G clicon <user>
|
||||
sudo usermod -a -G clicon www-data
|
||||
```
|
||||
Using addgroup and adduser (eg on busybox):
|
||||
Using adduser (eg on busybox):
|
||||
```
|
||||
sudo addgroup clicon
|
||||
sudo adduser -D -H clicon
|
||||
sudo adduser <user> clicon
|
||||
```
|
||||
(you may have to restart shell)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#define LIBCLIXON_API 1
|
||||
|
||||
#include <clixon/clixon_sig.h>
|
||||
#include <clixon/clixon_uid.h>
|
||||
#include <clixon/clixon_err.h>
|
||||
#include <clixon/clixon_queue.h>
|
||||
#include <clixon/clixon_hash.h>
|
||||
|
|
|
|||
|
|
@ -43,8 +43,4 @@ int clicon_file_dirent(const char *dir, struct dirent **ent,
|
|||
|
||||
int clicon_file_copy(char *src, char *target);
|
||||
|
||||
int group_name2gid(const char *name, gid_t *gid);
|
||||
|
||||
int name2uid(const char *name, uid_t *uid);
|
||||
|
||||
#endif /* _CLIXON_FILE_H_ */
|
||||
|
|
|
|||
|
|
@ -73,6 +73,13 @@ enum startup_mode_t{
|
|||
SM_INIT
|
||||
};
|
||||
|
||||
/*! See clixon-config.yang type priv_mode (privileges mode) */
|
||||
enum priv_mode_t{
|
||||
PM_NONE=0, /* Make no drop/change in privileges */
|
||||
PM_DROP_PERM, /* Drop privileges permanently */
|
||||
PM_DROP_TEMP, /* Drop privileges temporary */
|
||||
};
|
||||
|
||||
/*! Datastore cache behaviour, see clixon_datastore.[ch]
|
||||
* See config option type datastore_cache in clixon-config.yang
|
||||
*/
|
||||
|
|
@ -167,8 +174,8 @@ static inline char *clicon_sock(clicon_handle h){
|
|||
static inline char *clicon_sock_group(clicon_handle h){
|
||||
return clicon_option_str(h, "CLICON_SOCK_GROUP");
|
||||
}
|
||||
static inline char *clicon_user(clicon_handle h){
|
||||
return clicon_option_str(h, "CLICON_USER");
|
||||
static inline char *clicon_backend_user(clicon_handle h){
|
||||
return clicon_option_str(h, "CLICON_BACKEND_USER");
|
||||
}
|
||||
static inline char *clicon_backend_pidfile(clicon_handle h){
|
||||
return clicon_option_str(h, "CLICON_BACKEND_PIDFILE");
|
||||
|
|
@ -186,6 +193,7 @@ int clicon_sock_family(clicon_handle h);
|
|||
int clicon_sock_port(clicon_handle h);
|
||||
int clicon_autocommit(clicon_handle h);
|
||||
int clicon_startup_mode(clicon_handle h);
|
||||
int clicon_backend_privileges_mode(clicon_handle h);
|
||||
enum datastore_cache clicon_datastore_cache(clicon_handle h);
|
||||
enum regexp_mode clicon_yang_regexp(clicon_handle h);
|
||||
/*-- Specific option access functions for non-yang options --*/
|
||||
|
|
|
|||
50
lib/clixon/clixon_uid.h
Normal file
50
lib/clixon/clixon_uid.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* uid, gid, privileges
|
||||
*/
|
||||
|
||||
#ifndef _CLIXON_UID_H_
|
||||
#define _CLIXON_UID_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int group_name2gid(const char *name, gid_t *gid);
|
||||
|
||||
int name2uid(const char *name, uid_t *uid);
|
||||
int drop_priv_temp(uid_t new_uid);
|
||||
int drop_priv_perm(uid_t new_uid);
|
||||
int restore_priv(void);
|
||||
|
||||
#endif /* _CLIXON_UID_H_ */
|
||||
|
|
@ -66,7 +66,7 @@ CPPFLAGS = @CPPFLAGS@
|
|||
|
||||
INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$(top_srcdir)
|
||||
|
||||
SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
|
||||
SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
|
||||
clixon_string.c clixon_regex.c clixon_handle.c \
|
||||
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
|
||||
|
|
|
|||
|
|
@ -43,14 +43,12 @@
|
|||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <regex.h>
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <grp.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -210,61 +208,3 @@ clicon_file_copy(char *src,
|
|||
errno = err;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Translate group name to gid. Return -1 if error or not found.
|
||||
* @param[in] name Name of group
|
||||
* @param[out] gid Group id
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error. or not found
|
||||
*/
|
||||
int
|
||||
group_name2gid(const char *name,
|
||||
gid_t *gid)
|
||||
{
|
||||
int retval = -1;
|
||||
char buf[1024];
|
||||
struct group g0;
|
||||
struct group *gr = &g0;
|
||||
struct group *gtmp;
|
||||
|
||||
gr = &g0;
|
||||
/* This leaks memory in ubuntu */
|
||||
if (getgrnam_r(name, gr, buf, sizeof(buf), >mp) < 0){
|
||||
clicon_err(OE_UNIX, errno, "getgrnam_r(%s)", name);
|
||||
goto done;
|
||||
}
|
||||
if (gtmp == NULL){
|
||||
clicon_err(OE_UNIX, 0, "No such group: %s", name);
|
||||
goto done;
|
||||
}
|
||||
if (gid)
|
||||
*gid = gr->gr_gid;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
name2uid(const char *name,
|
||||
uid_t *uid)
|
||||
{
|
||||
int retval = -1;
|
||||
char buf[1024];
|
||||
struct passwd pwbuf;
|
||||
struct passwd *pwbufp = NULL;
|
||||
|
||||
if (getpwnam_r(name, &pwbuf, buf, sizeof(buf), &pwbufp) != 0){
|
||||
clicon_err(OE_UNIX, errno, "getpwnam_r(%s)", name);
|
||||
goto done;
|
||||
}
|
||||
if (pwbufp == NULL){
|
||||
clicon_err(OE_UNIX, 0, "No such user: %s", name);
|
||||
goto done;
|
||||
}
|
||||
if (uid)
|
||||
*uid = pwbufp->pw_uid;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,15 @@
|
|||
*/
|
||||
#define CLIXON_CONF_NS "http://clicon.org/config"
|
||||
|
||||
/* Mapping between Cli generation from Yang string <--> constants,
|
||||
see clixon-config.yang type cli_genmodel_type */
|
||||
static const map_str2int cli_genmodel_map[] = {
|
||||
{"NONE", GT_NONE},
|
||||
{"VARS", GT_VARS},
|
||||
{"ALL", GT_ALL},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* Mapping between Clicon startup modes string <--> constants,
|
||||
see clixon-config.yang type startup_mode */
|
||||
static const map_str2int startup_mode_map[] = {
|
||||
|
|
@ -92,6 +101,32 @@ static const map_str2int startup_mode_map[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* Mapping between Clicon privilegese modes string <--> constants,
|
||||
* see clixon-config.yang type priv_mode */
|
||||
static const map_str2int priv_mode_map[] = {
|
||||
{"none", PM_NONE},
|
||||
{"drop_perm", PM_DROP_PERM},
|
||||
{"drop_temp", PM_DROP_TEMP},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* Mapping between datastore cache string <--> constants,
|
||||
* see clixon-config.yang type datastore_cache */
|
||||
static const map_str2int datastore_cache_map[] = {
|
||||
{"nocache", DATASTORE_NOCACHE},
|
||||
{"cache", DATASTORE_CACHE},
|
||||
{"cache-zerocopy", DATASTORE_CACHE_ZEROCOPY},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* Mapping between regular expression type string <--> constants,
|
||||
* see clixon-config.yang type regexp_mode */
|
||||
static const map_str2int yang_regexp_map[] = {
|
||||
{"posix", REGEXP_POSIX},
|
||||
{"libxml2", REGEXP_LIBXML2},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/*! Print registry on file. For debugging.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] dbglevel Debug level
|
||||
|
|
@ -453,6 +488,9 @@ clicon_option_int(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Set option given as int.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name Name of option to set
|
||||
* @param[in] val Integer value
|
||||
*/
|
||||
int
|
||||
clicon_option_int_set(clicon_handle h,
|
||||
|
|
@ -468,7 +506,7 @@ clicon_option_int_set(clicon_handle h,
|
|||
|
||||
/*! Get options as bool but stored as string
|
||||
*
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name name of option
|
||||
* @retval 0 false, or does not exist, or does not have a boolean value
|
||||
* @retval 1 true
|
||||
|
|
@ -496,6 +534,9 @@ clicon_option_bool(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Set option given as bool
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name Name of option to set
|
||||
* @param[in] val Boolean value, 0 or 1
|
||||
*/
|
||||
int
|
||||
clicon_option_bool_set(clicon_handle h,
|
||||
|
|
@ -510,6 +551,8 @@ clicon_option_bool_set(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Delete option
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name Name of option to delete
|
||||
*/
|
||||
int
|
||||
clicon_option_del(clicon_handle h,
|
||||
|
|
@ -530,8 +573,10 @@ clicon_option_del(clicon_handle h,
|
|||
* But sometimes there are type conversions, etc which makes it more
|
||||
* convenient to make wrapper functions. Or not?
|
||||
*-----------------------------------------------------------------*/
|
||||
/*! Whether to generate CLIgen syntax from datamodel or not (0 or 1)
|
||||
/*! Wether to generate CLIgen syntax from datamodel or not (0 or 1)
|
||||
* Must be used with a previous clicon_option_exists().
|
||||
* @param[in] h Clicon handle
|
||||
* @retval flag If set, generate CLI code from yang model, otherwise not
|
||||
* @see clixon-config@<date>.yang CLICON_CLI_GENMODEL
|
||||
*/
|
||||
int
|
||||
|
|
@ -546,6 +591,8 @@ clicon_cli_genmodel(clicon_handle h)
|
|||
}
|
||||
|
||||
/*! Generate code for CLI completion of existing db symbols
|
||||
* @param[in] h Clicon handle
|
||||
* @retval flag If set, generate auto-complete CLI specs
|
||||
* @see clixon-config@<date>.yang CLICON_CLI_GENMODEL_COMPLETION
|
||||
*/
|
||||
int
|
||||
|
|
@ -559,14 +606,9 @@ clicon_cli_genmodel_completion(clicon_handle h)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const map_str2int cli_genmodel_map[] = {
|
||||
{"NONE", GT_NONE},
|
||||
{"VARS", GT_VARS},
|
||||
{"ALL", GT_ALL},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/*! How to generate and show CLI syntax: VARS|ALL
|
||||
* @param[in] h Clicon handle
|
||||
* @retval mode
|
||||
* @see clixon-config@<date>.yang CLICON_CLI_GENMODEL_TYPE
|
||||
*/
|
||||
enum genmodel_type
|
||||
|
|
@ -580,7 +622,9 @@ clicon_cli_genmodel_type(clicon_handle h)
|
|||
return clicon_str2int(cli_genmodel_map, str);
|
||||
}
|
||||
|
||||
/*! Get Dont include keys in cvec in cli vars callbacks
|
||||
/*! Get "do not include keys in cvec" in cli vars callbacks
|
||||
* @param[in] h Clicon handle
|
||||
* @retval flag If set, get only vars
|
||||
* @see clixon-config@<date>.yang CLICON_CLI_VARONLY
|
||||
*/
|
||||
int
|
||||
|
|
@ -596,6 +640,8 @@ clicon_cli_varonly(clicon_handle h)
|
|||
|
||||
/*! Get family of backend socket: AF_UNIX, AF_INET or AF_INET6
|
||||
* @see clixon-config@<date>.yang CLICON_SOCK_FAMILY
|
||||
* @param[in] h Clicon handle
|
||||
* @retval fam Socket family
|
||||
*/
|
||||
int
|
||||
clicon_sock_family(clicon_handle h)
|
||||
|
|
@ -613,6 +659,8 @@ clicon_sock_family(clicon_handle h)
|
|||
}
|
||||
|
||||
/*! Get port for backend socket in case of AF_INET or AF_INET6
|
||||
* @param[in] h Clicon handle
|
||||
* @retval port Socket port
|
||||
* @see clixon-config@<date>.yang CLICON_SOCK_PORT
|
||||
*/
|
||||
int
|
||||
|
|
@ -626,6 +674,8 @@ clicon_sock_port(clicon_handle h)
|
|||
}
|
||||
|
||||
/*! Set if all configuration changes are committed automatically
|
||||
* @param[in] h Clicon handle
|
||||
* @retval flag Autocommit (or not)
|
||||
*/
|
||||
int
|
||||
clicon_autocommit(clicon_handle h)
|
||||
|
|
@ -638,25 +688,37 @@ clicon_autocommit(clicon_handle h)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Which method to boot/start clicon backen
|
||||
/*! Which method to boot/start clicon backend
|
||||
* @param[in] h Clicon handle
|
||||
* @retval mode Startup mode
|
||||
*/
|
||||
int
|
||||
clicon_startup_mode(clicon_handle h)
|
||||
{
|
||||
char *mode;
|
||||
|
||||
if ((mode = clicon_option_str(h, "CLICON_STARTUP_MODE")) == NULL)
|
||||
return -1;
|
||||
return clicon_str2int(startup_mode_map, mode);
|
||||
}
|
||||
|
||||
static const map_str2int datastore_cache_map[] = {
|
||||
{"nocache", DATASTORE_NOCACHE},
|
||||
{"cache", DATASTORE_CACHE},
|
||||
{"cache-zerocopy", DATASTORE_CACHE_ZEROCOPY},
|
||||
{NULL, -1}
|
||||
};
|
||||
/*! Which privileges drop method to use
|
||||
* @param[in] h Clicon handle
|
||||
* @retval mode Privileges mode
|
||||
*/
|
||||
int
|
||||
clicon_backend_privileges_mode(clicon_handle h)
|
||||
{
|
||||
char *mode;
|
||||
|
||||
if ((mode = clicon_option_str(h, "CLICON_BACKEND_PRIVILEGES")) == NULL)
|
||||
return -1;
|
||||
return clicon_str2int(priv_mode_map, mode);
|
||||
}
|
||||
|
||||
/*! Which datastore cache method to use
|
||||
* @param[in] h Clicon handle
|
||||
* @retval method Datastore cache method
|
||||
* @see clixon-config@<date>.yang CLICON_DATASTORE_CACHE
|
||||
*/
|
||||
enum datastore_cache
|
||||
|
|
@ -670,13 +732,9 @@ clicon_datastore_cache(clicon_handle h)
|
|||
return clicon_str2int(datastore_cache_map, str);
|
||||
}
|
||||
|
||||
static const map_str2int yang_regexp_map[] = {
|
||||
{"posix", REGEXP_POSIX},
|
||||
{"libxml2", REGEXP_LIBXML2},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/*! Which Yang regexp/pattern engine to use
|
||||
* @param[in] h Clicon handle
|
||||
* @retval mode Regexp engine to use
|
||||
* @see clixon-config@<date>.yang CLICON_YANG_REGEXP
|
||||
*/
|
||||
enum regexp_mode
|
||||
|
|
@ -696,7 +754,10 @@ clicon_yang_regexp(clicon_handle h)
|
|||
* Such as handles to plugins, API:s and parsed structures
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
/* eg -q option, dont print notifications on stdout */
|
||||
/*! Get quiet mode eg -q option, do not print notifications on stdout
|
||||
* @param[in] h Clicon handle
|
||||
* @retval flag quiet mode on or off
|
||||
*/
|
||||
int
|
||||
clicon_quiet_mode(clicon_handle h)
|
||||
{
|
||||
|
|
@ -705,8 +766,14 @@ clicon_quiet_mode(clicon_handle h)
|
|||
return 0; /* default */
|
||||
return atoi(s);
|
||||
}
|
||||
|
||||
/*! Set quiet mode
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] val Flag value
|
||||
*/
|
||||
int
|
||||
clicon_quiet_mode_set(clicon_handle h, int val)
|
||||
clicon_quiet_mode_set(clicon_handle h,
|
||||
int val)
|
||||
{
|
||||
return clicon_option_int_set(h, "CLICON_QUIET", val);
|
||||
}
|
||||
|
|
|
|||
200
lib/src/clixon_uid.c
Normal file
200
lib/src/clixon_uid.c
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* uid, gid, privileges
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#define __USE_GNU /* For setresuid */
|
||||
#include <unistd.h>
|
||||
#undef __USE_GNU
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_uid.h"
|
||||
|
||||
/*! Translate group name to gid. Return -1 if error or not found.
|
||||
* @param[in] name Name of group
|
||||
* @param[out] gid Group id
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error. or not found
|
||||
*/
|
||||
int
|
||||
group_name2gid(const char *name,
|
||||
gid_t *gid)
|
||||
{
|
||||
int retval = -1;
|
||||
char buf[1024];
|
||||
struct group g0;
|
||||
struct group *gr = &g0;
|
||||
struct group *gtmp;
|
||||
|
||||
gr = &g0;
|
||||
/* This leaks memory in ubuntu */
|
||||
if (getgrnam_r(name, gr, buf, sizeof(buf), >mp) < 0){
|
||||
clicon_err(OE_UNIX, errno, "getgrnam_r(%s)", name);
|
||||
goto done;
|
||||
}
|
||||
if (gtmp == NULL){
|
||||
clicon_err(OE_UNIX, 0, "No such group: %s", name);
|
||||
goto done;
|
||||
}
|
||||
if (gid)
|
||||
*gid = gr->gr_gid;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate user name to uid. Return -1 if error or not found.
|
||||
* @param[in] name Name of user
|
||||
* @param[out] uid User id
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error. or not found
|
||||
*/
|
||||
int
|
||||
name2uid(const char *name,
|
||||
uid_t *uid)
|
||||
{
|
||||
int retval = -1;
|
||||
char buf[1024];
|
||||
struct passwd pwbuf;
|
||||
struct passwd *pwbufp = NULL;
|
||||
|
||||
if (getpwnam_r(name, &pwbuf, buf, sizeof(buf), &pwbufp) != 0){
|
||||
clicon_err(OE_UNIX, errno, "getpwnam_r(%s)", name);
|
||||
goto done;
|
||||
}
|
||||
if (pwbufp == NULL){
|
||||
clicon_err(OE_UNIX, 0, "No such user: %s", name);
|
||||
goto done;
|
||||
}
|
||||
if (uid)
|
||||
*uid = pwbufp->pw_uid;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Privileges drop perm, temp and restore
|
||||
* @see https://www.usenix.org/legacy/events/sec02/full_papers/chen/chen.pdf
|
||||
*/
|
||||
/*! Temporarily drop privileges
|
||||
* @param[in] new_uid
|
||||
*/
|
||||
int
|
||||
drop_priv_temp(uid_t new_uid)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (setresuid(-1, new_uid, geteuid()) < 0){
|
||||
clicon_err(OE_UNIX, errno, "setresuid");
|
||||
goto done;
|
||||
}
|
||||
if (geteuid() != new_uid){
|
||||
clicon_err(OE_UNIX, errno, "geteuid");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Permanently drop privileges
|
||||
* @param[in] new_uid
|
||||
*/
|
||||
int
|
||||
drop_priv_perm(uid_t new_uid)
|
||||
{
|
||||
int retval = -1;
|
||||
uid_t ruid;
|
||||
uid_t euid;
|
||||
uid_t suid;
|
||||
|
||||
if (setresuid(new_uid, new_uid, new_uid) < 0){
|
||||
clicon_err(OE_UNIX, errno, "setresuid");
|
||||
goto done;
|
||||
}
|
||||
if (getresuid(&ruid, &euid, &suid) < 0){
|
||||
clicon_err(OE_UNIX, errno, "getresuid");
|
||||
goto done;
|
||||
}
|
||||
if (ruid != new_uid ||
|
||||
euid != new_uid ||
|
||||
suid != new_uid){
|
||||
clicon_err(OE_UNIX, EINVAL, "Non-matching uid");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Restore privileges to saved level */
|
||||
int
|
||||
restore_priv(void)
|
||||
{
|
||||
int retval = -1;
|
||||
uid_t ruid;
|
||||
uid_t euid;
|
||||
uid_t suid;
|
||||
|
||||
if (getresuid(&ruid, &euid, &suid) < 0){
|
||||
clicon_err(OE_UNIX, errno, "setresuid");
|
||||
goto done;
|
||||
}
|
||||
if (setresuid(-1, suid, -1) < 0){
|
||||
clicon_err(OE_UNIX, errno, "setresuid");
|
||||
goto done;
|
||||
}
|
||||
if (geteuid() != suid){
|
||||
clicon_err(OE_UNIX, EINVAL, "Non-matching uid");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -109,6 +109,14 @@ if [ ! -d $dir ]; then
|
|||
mkdir $dir
|
||||
fi
|
||||
|
||||
# Some tests may set owner of testdir to something strange and quit, need
|
||||
# to reset to me
|
||||
if [ ! -G $dir ]; then
|
||||
u=$(whoami)
|
||||
sudo chown $u $dir
|
||||
sudo chgrp $u $dir
|
||||
fi
|
||||
|
||||
# If you bring your own backend BE=0 (it is already started),the backend may
|
||||
# have created some files (eg unix socket) in $dir and therefore cannot
|
||||
# be deleted
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ EOF
|
|||
if [ $BE -ne 0 ]; then # Bring your own backend
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -743,7 +743,7 @@ expectfn "$clixon_cli -1f $cfg -l o set c threematch abcg" 255 '^CLI syntax erro
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
121
test/test_privileges.sh
Executable file
121
test/test_privileges.sh
Executable file
|
|
@ -0,0 +1,121 @@
|
|||
#!/bin/bash
|
||||
# Start clixon backend as root and unprivileged user (clicon)
|
||||
# Drop privileges from root to clicon
|
||||
# Test could do more:
|
||||
# - test file ownership
|
||||
# - drop_temp check if you can restore
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
APPNAME=example
|
||||
|
||||
cfg=$dir/conf_startup.xml
|
||||
|
||||
if [ $valgrindtest -ne 0 ]; then
|
||||
return -1 # skip
|
||||
fi
|
||||
|
||||
# Here $dir is created by the user that runs the script
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MODULE_MAIN>clixon-example</CLICON_YANG_MODULE_MAIN>
|
||||
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
|
||||
# Create a pre-set running, startup and (extra) config.
|
||||
# The configs are identified by an interface called run, startup, extra.
|
||||
# Depending on startup mode (init, none, running, or startup)
|
||||
# expect different output of an initial get-config of running
|
||||
testrun(){
|
||||
startuser=$1
|
||||
beuser=$2
|
||||
expectuser=$3
|
||||
priv_mode=$4
|
||||
expecterr=$5
|
||||
|
||||
# change owner (recursively) of all files in the test dir
|
||||
sudo chown -R $startuser $dir
|
||||
|
||||
# change group (recursively) of all files in the test dir
|
||||
sudo chgrp -R $startuser $dir
|
||||
|
||||
# kill old backend (if any)
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
# Kill all backends regardless of user or pid files (we mess with them in this test)
|
||||
sudo pkill clixon_backend
|
||||
|
||||
# start backend as user
|
||||
|
||||
new "start backend -f $cfg -s init -D $DBG -o CLICON_BACKEND_PRIVILEGES=$priv_mode -o CLICON_BACKEND_USER=$beuser"
|
||||
sudo -u $startuser $clixon_backend -f $cfg -s init -D $DBG -o CLICON_BACKEND_PRIVILEGES=$priv_mode -o CLICON_BACKEND_USER=$beuser
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
|
||||
pid=$(pgrep -f clixon_backend)
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ $expecterr -eq 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
err
|
||||
fi
|
||||
new "waiting"
|
||||
wait_backend
|
||||
|
||||
if [ $expecterr -eq 1 ]; then
|
||||
err "Expected error"
|
||||
fi
|
||||
|
||||
# Get uid now, and compare with expected user
|
||||
u=$(ps -p $pid -uh | awk '{print $1}')
|
||||
if [ $u != $expectuser ]; then
|
||||
err "$expectuser but user is $u"
|
||||
fi
|
||||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
# kill backend
|
||||
stop_backend -f $cfg
|
||||
} # testrun
|
||||
|
||||
new "Start as non-privileged user, expect same"
|
||||
testrun $BUSER $BUSER $BUSER none 0
|
||||
|
||||
new "Start as privileged user , expect same"
|
||||
testrun root root root none 0
|
||||
|
||||
new "Start as privileged user, drop privileges permanent"
|
||||
testrun root $BUSER $BUSER drop_perm 0
|
||||
|
||||
new "Start as privileged user, drop privileges temporary"
|
||||
testrun root $BUSER $BUSER drop_temp 0
|
||||
|
||||
new "Start as root, drop to root (strange usecase)"
|
||||
testrun root root root drop_perm 0
|
||||
|
||||
new "Start as root, drop to root (strange usecase)"
|
||||
testrun root root root drop_perm 0
|
||||
|
||||
new "Start as root, set user but dont drop (expect still root)"
|
||||
testrun root $BUSER root none 0
|
||||
|
||||
new "Start as non-privileged, try to drop"
|
||||
testrun $(whoami) $BUSER $BUSER drop_perm 1
|
||||
|
||||
sudo rm -rf $dir
|
||||
|
|
@ -278,7 +278,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ testrun(){
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -108,7 +108,7 @@ new "Check running and startup exists and are same"
|
|||
if [ ! -f $dir/startup_db ]; then
|
||||
err "startup should exist but does not"
|
||||
fi
|
||||
echo "diff $dir/startup_db $dir/running_db"
|
||||
|
||||
d=$(sudo diff $dir/startup_db $dir/running_db)
|
||||
if [ -n "$d" ]; then
|
||||
err "running and startup should be equal" "$d"
|
||||
|
|
@ -117,14 +117,12 @@ fi
|
|||
# clear startup
|
||||
sudo rm -f $dir/startup_db;
|
||||
|
||||
new "Run without startup option, check running is copied"
|
||||
new "Run without startup option, check running is not copied"
|
||||
testrun ""
|
||||
|
||||
new "Check startup is empty"
|
||||
if [ ! -f $dir/startup_db ]; then
|
||||
err "startup does not exist"
|
||||
fi
|
||||
if [ -s $dir/startup_db ]; then
|
||||
err "startup is not empty"
|
||||
if [ -f $dir/startup_db ]; then
|
||||
err "startup should not exist"
|
||||
fi
|
||||
|
||||
rm -rf $dir
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ testrun(){
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ EOF
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ testrange string "012" "01234567890" ""
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ runtest(){
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ stop_restconf
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ testrun(){
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ stop_restconf
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ fi
|
|||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc><get-config><source><candid
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -149,7 +149,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -193,7 +193,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -237,7 +237,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -281,7 +281,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -326,7 +326,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -372,7 +372,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
@ -418,7 +418,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ if [ $BE -eq 0 ]; then
|
|||
fi
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u $BUSER -f clixon_backend)
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ module clixon-config {
|
|||
|
||||
revision 2019-09-11 {
|
||||
description
|
||||
"Added: CLICON_USER: user that backend daemon drops privileges to";
|
||||
"Added: CLICON_BACKEND_USER: drop of privileges to user,
|
||||
CLICON_BACKEND_PRIVILEGES: how to drop privileges";
|
||||
}
|
||||
revision 2019-06-05 {
|
||||
description
|
||||
|
|
@ -183,6 +184,26 @@ module clixon-config {
|
|||
}
|
||||
}
|
||||
}
|
||||
typedef priv_mode{
|
||||
description
|
||||
"Privilege mode, used for dropping (or not) priveleges to a non-provileged
|
||||
user after initialization";
|
||||
type enumeration{
|
||||
enum none {
|
||||
description
|
||||
"Make no drop/change in privileges.";
|
||||
}
|
||||
enum drop_perm {
|
||||
description
|
||||
"After initialization, drop privileges permanently to a uid";
|
||||
}
|
||||
enum drop_temp {
|
||||
description
|
||||
"After initialization, drop privileges temporarily to a euid";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
container clixon-config {
|
||||
leaf-list CLICON_FEATURE {
|
||||
description
|
||||
|
|
@ -423,11 +444,25 @@ module clixon-config {
|
|||
"Group membership to access clixon_backend unix socket and gid for
|
||||
deamon";
|
||||
}
|
||||
leaf CLICON_USER {
|
||||
leaf CLICON_BACKEND_USER {
|
||||
type string;
|
||||
default "clicon";
|
||||
description
|
||||
"User to access clixon_backend unix socket and uid for deamon";
|
||||
"User name for backend (both foreground and daemonized).
|
||||
If you set this value the backend if started as root will lower
|
||||
the privileges after initialization.
|
||||
The ownership of files created by the backend will also be set to this
|
||||
user (eg datastores).
|
||||
It also sets the backend unix socket owner to this user, but its group
|
||||
is set by CLICON_SOCK_GROUP.
|
||||
See also CLICON_PRIVILEGES setting";
|
||||
}
|
||||
leaf CLICON_BACKEND_PRIVILEGES {
|
||||
type priv_mode;
|
||||
default none;
|
||||
description
|
||||
"Backend privileges mode.
|
||||
If CLICON_BACKEND_USER user is set, mode can be set to drop_perm or
|
||||
drop_temp.";
|
||||
}
|
||||
leaf CLICON_BACKEND_PIDFILE {
|
||||
type string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue