From c20c672d83952c1457f19306d53f5547ee393158 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 21 May 2021 15:12:06 +0200 Subject: [PATCH] * Changed config and install options for Restconf * clixon_restconf daemon is installed in /usr/local/sbin (as clixon_backend), instead of /www-data * `configure --with-wwwdir=` remains but only applies to fcgi socket and log * New option `CLICON_RESTCONF_INSTALL_DIR` is set to where clixon_restconf is installed, with default `/usr/local/sbin/` * Restconf drop privileges user is defined by `CLICON_RESTCONF_USER` * `configure --with-wwwuser=` is removed * clixon_restconf drop of privileges is defined by `CLICON_RESTCONF_PRIVILEGES` option * New clixon-restconf@2020-05-20.yang revision * Added: restconf `log-destination` --- CHANGELOG.md | 13 +++ apps/backend/Makefile.in | 1 + apps/backend/backend_client.c | 2 +- apps/backend/backend_main.c | 7 +- apps/backend/backend_plugin_restconf.c | 82 +++++++++++------ apps/restconf/Makefile.in | 16 ++-- apps/restconf/README.md | 8 +- apps/restconf/restconf_lib.c | 39 ++++++--- apps/restconf/restconf_lib.h | 2 +- apps/restconf/restconf_main_fcgi.c | 28 +++--- apps/restconf/restconf_main_native.c | 57 ++++++------ configure | 29 ------- configure.ac | 22 +---- docker/main/Dockerfile.fcgi | 4 +- docker/main/Dockerfile.native | 4 +- docker/main/startsystem_fcgi.sh | 5 +- docker/main/startsystem_native.sh | 8 +- include/clixon_config.h.in | 3 - lib/clixon/clixon_nacm.h | 2 +- lib/clixon/clixon_options.h | 1 + lib/src/clixon_nacm.c | 21 +++-- lib/src/clixon_options.c | 16 +++- lib/src/clixon_proc.c | 10 ++- test/cicd/clixon-config.sh | 2 +- test/config.sh.in | 11 +-- test/lib.sh | 3 +- test/test_install.sh | 5 +- test/test_restconf_internal.sh | 34 +++++++- test/test_restconf_internal_usecases.sh | 19 +++- yang/clixon/Makefile.in | 6 +- ...-30.yang => clixon-config@2021-05-20.yang} | 87 +++++++++++++++++-- ...0.yang => clixon-restconf@2021-05-20.yang} | 84 +++++++++++++----- 32 files changed, 410 insertions(+), 221 deletions(-) rename yang/clixon/{clixon-config@2020-12-30.yang => clixon-config@2021-05-20.yang} (90%) rename yang/clixon/{clixon-restconf@2020-12-30.yang => clixon-restconf@2021-05-20.yang} (80%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef465001..6a1853a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,19 @@ Expected: June 2021 Users may have to change how they access the system +* Changed config and install options for Restconf + * clixon_restconf daemon is installed in /usr/local/sbin (as clixon_backend), instead of /www-data + * `configure --with-wwwdir=` remains but only applies to fcgi socket and log + * New option `CLICON_RESTCONF_INSTALL_DIR` is set to where clixon_restconf is installed, with default `/usr/local/sbin/` + * Restconf drop privileges user is defined by `CLICON_RESTCONF_USER` + * `configure --with-wwwuser=` is removed + * clixon_restconf drop of privileges is defined by `CLICON_RESTCONF_PRIVILEGES` option +* New clixon-config@2020-05-20.yang revision + * Added: `CLICON_RESTCONF_USER` + * Added: `CLICON_RESTCONF_PRIVILEGES` + * Added: `CLICON_RESTCONF_INSTALL_DIR` +* New clixon-restconf@2020-05-20.yang revision + * Added: restconf `log-destination` * RESTCONF error replies have changed * Added Restconf-style xml/json message bodies everywhere * Clixon removed the message body from many errors in the 4.6 version since they used html encoding. diff --git a/apps/backend/Makefile.in b/apps/backend/Makefile.in index 66c70d10..bd8bc619 100644 --- a/apps/backend/Makefile.in +++ b/apps/backend/Makefile.in @@ -43,6 +43,7 @@ CPPFLAGS = @CPPFLAGS@ ifeq ($(LINKAGE),dynamic) CPPFLAGS += -fPIC endif + SH_SUFFIX = @SH_SUFFIX@ INSTALLFLAGS = @INSTALLFLAGS@ LDFLAGS = @LDFLAGS@ diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 7d0a1c91..c4ffbe4a 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -1796,7 +1796,7 @@ from_client_msg(clicon_handle h, goto done; if (ret == 0){ /* Do NACM RPC validation */ creds = clicon_nacm_credentials(h); - if ((ret = verify_nacm_user(creds, ce->ce_username, username, cbret)) < 0) + if ((ret = verify_nacm_user(h, creds, ce->ce_username, username, cbret)) < 0) goto done; if (ret == 0) /* credentials fail */ goto reply; diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index fd4e6170..03529fbd 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -287,9 +287,9 @@ check_drop_priv(clicon_handle h, 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) + if ((priv_mode = clicon_backend_privileges_mode(h)) == PM_NONE) goto ok; /* From here, drop privileges */ @@ -575,9 +575,6 @@ main(int argc, usage(h, argv[0]); goto done; } - /* Add some specific options from autotools configure NOT config file */ - clicon_option_str_set(h, "CLICON_WWWUSER", WWWUSER); - clicon_option_str_set(h, "CLICON_WWWDIR", WWWDIR); /* Initialize plugin module by creating a handle holding plugin and callback lists */ if (clixon_plugin_module_init(h) < 0) diff --git a/apps/backend/backend_plugin_restconf.c b/apps/backend/backend_plugin_restconf.c index d796b8c6..f808e4ca 100644 --- a/apps/backend/backend_plugin_restconf.c +++ b/apps/backend/backend_plugin_restconf.c @@ -65,32 +65,62 @@ * * process argv list including -D is set on start. But the debug flags may change and this is a way * to set it dynamically, ie at the time the process is started, not when the backend is started. - * @param[in] h Clixon backend - * @param[in] dbg Debug string , eg "0" or "1" + * @param[in] h Clixon backend + * @param[in] xt XML target */ static int -restconf_pseudo_set_debug(clicon_handle h, - char *dbg) +restconf_pseudo_set_log(clicon_handle h, + cxobj *xt) { int retval = -1; char **argv; int argc; - int i; + int i; + char *log = NULL; + char *dbg = NULL; + cxobj *xb; - if (dbg == NULL) + if ((xb = xpath_first(xt, NULL, "/restconf/log-destination")) != NULL) + log = xml_body(xb); + if ((xb = xpath_first(xt, NULL, "/restconf/debug")) != NULL) + dbg = xml_body(xb); + if (dbg == NULL && log == NULL) goto ok; if (clixon_process_argv_get(h, RESTCONF_PROCESS, &argv, &argc) < 0) goto done; for (i=0; i i+1 && argv[i+1]){ - free(argv[i+1]); - if ((argv[i+1] = strdup(dbg)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; + if (strcmp(argv[i], "-l") == 0 && argc > i+1 && argv[i+1]){ + if (log){ + if (strcmp(log, "syslog") == 0){ + if (argv[i+1]) + free(argv[i+1]); + if ((argv[i+1] = strdup("s")) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + } + else if (strcmp(log, "file") == 0){ + if (argv[i+1]) + free(argv[i+1]); + if ((argv[i+1] = strdup("f/var/log/clixon_restconf.log")) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + } } - break; + i++; + } + if (strcmp(argv[i], "-D") == 0 && argc > i+1 && argv[i+1]){ + if (dbg){ + free(argv[i+1]); + if ((argv[i+1] = strdup(dbg)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + } + i++; } } ok: @@ -111,7 +141,6 @@ restconf_rpc_wrapper(clicon_handle h, { int retval = -1; cxobj *xt = NULL; - cxobj *xb; clicon_debug(1, "%s", __FUNCTION__); switch (*operation){ @@ -133,10 +162,8 @@ restconf_rpc_wrapper(clicon_handle h, * but in this way it is set directly in its input args. * Its a trick. */ - if ((xb = xpath_first(xt, NULL, "/restconf/debug")) != NULL){ - if (restconf_pseudo_set_debug(h, xml_body(xb)) < 0) - goto done; - } + if (restconf_pseudo_set_log(h, xt) < 0) + goto done; } break; default: @@ -165,7 +192,7 @@ restconf_pseudo_process_control(clicon_handle h) int nr; cbuf *cb = NULL; - nr = 6; + nr = 8; if ((argv = calloc(nr, sizeof(char *))) == NULL){ clicon_err(OE_UNIX, errno, "calloc"); goto done; @@ -175,15 +202,23 @@ restconf_pseudo_process_control(clicon_handle h) clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - cprintf(cb, "%s/clixon_restconf", clicon_option_str(h, "CLICON_WWWDIR")); + /* CLICON_RESTCONF_INSTALL_DIR is where we think clixon_restconf is installed + * Problem is where to define it? Now in config file, but maybe it should be in configure? + * Tried Makefile but didnt work on Docker since it was moved around. + * Use PATH? + */ + cprintf(cb, "%s/clixon_restconf", clicon_option_str(h, "CLICON_RESTCONF_INSTALL_DIR")); argv[i++] = cbuf_get(cb); argv[i++] = "-f"; argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE"); /* Add debug if backend has debug. * There is also a debug flag in clixon-restconf.yang but it kicks in after it starts + * see restconf_pseudo_set_log which sets flag when process starts */ argv[i++] = "-D"; argv[i++] = "0"; + argv[i++] = "-l"; + argv[i++] = "s"; /* There is also log-destination in clixon-restconf.yang */ argv[i++] = NULL; assert(i==nr); if (clixon_process_register(h, RESTCONF_PROCESS, @@ -201,7 +236,7 @@ restconf_pseudo_process_control(clicon_handle h) return retval; } -/*! Restconf pseduo-plugin process validate +/*! Restconf pseudo-plugin process validate */ static int restconf_pseudo_process_validate(clicon_handle h, @@ -241,7 +276,6 @@ restconf_pseudo_process_commit(clicon_handle h, cxobj *xsource; cxobj *cx; int enabled = 0; - cxobj *xb; clicon_debug(1, "%s", __FUNCTION__); xtarget = transaction_target(td); @@ -253,10 +287,8 @@ restconf_pseudo_process_commit(clicon_handle h, * but in this way it is set directly in its input args. * Its a trick. */ - if ((xb = xpath_first(xtarget, NULL, "/restconf/debug")) != NULL){ - if (restconf_pseudo_set_debug(h, xml_body(xb)) < 0) - goto done; - } + if (restconf_pseudo_set_log(h, xtarget) < 0) + goto done; /* Toggle start/stop if enable flag changed */ if ((cx = xpath_first(xtarget, NULL, "/restconf/enable")) != NULL && xml_flag(cx, XML_FLAG_CHANGE|XML_FLAG_ADD)){ diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in index b67e67c8..98527f92 100644 --- a/apps/restconf/Makefile.in +++ b/apps/restconf/Makefile.in @@ -49,6 +49,7 @@ datarootdir = @datarootdir@ exec_prefix = @exec_prefix@ bindir = @bindir@ libdir = @libdir@ +sbindir = @sbindir@ mandir = @mandir@ libexecdir = @libexecdir@ localstatedir = @localstatedir@ @@ -56,9 +57,6 @@ sysconfdir = @sysconfdir@ includedir = @includedir@ HOST_VENDOR = @host_vendor@ -# XXX why is not wwwdir under prefix? -wwwdir = @wwwdir@ -wwwuser = @wwwuser@ # one of fcgi or native: with_restconf = @with_restconf@ @@ -79,6 +77,8 @@ LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBS = -L$(top_srcdir)/lib/src $(top_srcdir)/lib/src/$(CLIXON_LIB) @LIBS@ CPPFLAGS = @CPPFLAGS@ + + ifeq ($(LINKAGE),dynamic) CPPFLAGS += -fPIC endif @@ -149,12 +149,8 @@ distclean: clean # Put config file in etc/ # Also a rule for letting www-dir be owned by www-data, which only works for sudo install: install-lib $(APPL) -ifeq ($(shell whoami),root) - install -d -m 0755 -o $(wwwuser) -g $(wwwuser) $(DESTDIR)$(wwwdir) -else - install -d -m 0755 $(DESTDIR)$(wwwdir) -endif - install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(wwwdir) + install -d -m 0755 $(DESTDIR)$(sbindir) + install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(sbindir) install-lib: $(MYLIB) install -d -m 0755 $(DESTDIR)$(libdir) @@ -169,7 +165,7 @@ install-include: clixon_restconf.h install -m 0644 $^ $(DESTDIR)$(includedir)/clixon uninstall: - rm -f $(DESTDIR)$(wwwdir)/$(APPL) + rm -f $(DESTDIR)$(sbindir)/$(APPL) rm -f $(DESTDIR)$(libdir)/$(MYLIBLINK)* .SUFFIXES: diff --git a/apps/restconf/README.md b/apps/restconf/README.md index 308b20a0..aee444c1 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -82,11 +82,11 @@ Start clixon backend daemon (if not already started) Start clixon restconf daemon ``` - sudo -u www-data -s /www-data/clixon_restconf -f /usr/local/etc/example.xml + sudo clixon_restconf -f /usr/local/etc/example.xml ``` On FreeBSD: ``` - sudo -u www -s /www/clixon_restconf -f /usr/local/etc/example.xml + sudo clixon_restconf -f /usr/local/etc/example.xml ``` Make restconf calls with curl (or other http client). Example of writing a new interface specification: @@ -226,7 +226,7 @@ See (https://nchan.io/#eventsource) on more info on how to access an SSE sub end Start the restconf fastcgi program with debug flag: ``` -sudo su -c "/www-data/clixon_restconf -D 1 -f /usr/local/etc/example.xml" -s /bin/sh www-data +sudo clixon_restconf -D 1 -f /usr/local/etc/example.xml ``` Look at syslog: ``` @@ -240,7 +240,7 @@ curl -G http://127.0.0.1/restconf/data/* You can also run restconf in a debugger. ``` -sudo gdb /www-data/clixon_restconf +sudo gdb clixon_restconf (gdb) run -D 1 -f /usr/local/etc/example.xml ``` but you need to ensure /www-data/fastcgi_restconf.sock has the following access (may need to be done after restconf has started) diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 14358a2e..40e237f6 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -312,7 +312,7 @@ restconf_terminate(clicon_handle h) xpath_optimize_exit(); restconf_handle_exit(h); clixon_err_exit(); - clicon_debug(1, "%s done", __FUNCTION__); + clicon_debug(1, "%s pid:%u done", __FUNCTION__, getpid()); clicon_log_exit(); /* Must be after last clicon_debug */ return 0; } @@ -495,18 +495,18 @@ restconf_uripath(clicon_handle h) /*! Drop privileges from root to user (or already at user) * @param[in] h Clicon handle - * @param[in] user Drop to this level * Group set to clicon to communicate with backend */ int -restconf_drop_privileges(clicon_handle h, - char *user) +restconf_drop_privileges(clicon_handle h) { int retval = -1; uid_t newuid = -1; uid_t uid; char *group; gid_t gid = -1; + char *user; + enum priv_mode_t priv_mode = PM_NONE; clicon_debug(1, "%s", __FUNCTION__); /* Sanity check: backend group exists */ @@ -523,12 +523,19 @@ restconf_drop_privileges(clicon_handle h, clicon_configfile(h)); goto done; } + + /* Get privileges mode (for dropping privileges) */ + if ((priv_mode = clicon_restconf_privileges_mode(h)) == PM_NONE) + goto ok; + if ((user = clicon_option_str(h, "CLICON_RESTCONF_USER")) == NULL) + goto ok; + /* Get (wanted) new www user id */ if (name2uid(user, &newuid) < 0){ clicon_err(OE_DAEMON, errno, "'%s' is not a valid user .\n", user); goto done; } - /* get current backend userid, if already at this level OK */ + /* get current userid, if already at this level OK */ if ((uid = getuid()) == newuid) goto ok; if (uid != 0){ @@ -539,12 +546,22 @@ restconf_drop_privileges(clicon_handle h, clicon_err(OE_DAEMON, errno, "setgid %d", gid); goto done; } - if (drop_priv_perm(newuid) < 0) - goto done; - /* Verify you cannot regain root privileges */ - if (setuid(0) != -1){ - clicon_err(OE_DAEMON, 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_DAEMON, 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 */ } clicon_debug(1, "%s dropped privileges from root to %s(%d)", __FUNCTION__, user, newuid); diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index 966da104..89bef5c6 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -81,7 +81,7 @@ int restconf_terminate(clicon_handle h); int restconf_insert_attributes(cxobj *xdata, cvec *qvec); int restconf_main_extension_cb(clicon_handle h, yang_stmt *yext, yang_stmt *ys); char *restconf_uripath(clicon_handle h); -int restconf_drop_privileges(clicon_handle h, char *user); +int restconf_drop_privileges(clicon_handle h); int restconf_authentication_cb(clicon_handle h, void *req, int pretty, restconf_media media_out); int restconf_config_init(clicon_handle h, cxobj *xrestconf); int restconf_socket_init(const char *netns0, const char *addrstr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss); diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c index f10c55e5..6e1d93bf 100644 --- a/apps/restconf/restconf_main_fcgi.c +++ b/apps/restconf/restconf_main_fcgi.c @@ -88,7 +88,7 @@ #include "restconf_stream.h" /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:" +#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:rW:o:" /*! Convert FCGI parameters to clixon runtime data * @param[in] h Clixon handle @@ -192,6 +192,7 @@ usage(clicon_handle h, "\t-u \t Internal socket domain path or IP addr (see -a)\n" "\t-r \t\t Do not drop privileges if run as root\n" + "\t-W \tRun restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n" "\t-o \"