luci-base: drop init_action() and the luci.setInitAction ubus method

Both duplicate rpcd's rc ubus namespace, which exposes the same
enable/disable/start/stop/restart/reload set through rc.init and is what
downstream code wishing to control services should be using.

The duplicate was also the weaker of the two. init_action() interpolated
both of its arguments into a shell command run as root:

    system(`env -i /etc/init.d/${basename(name)} ${action} >/dev/null`);

basename() kept name inside /etc/init.d, but it strips directory
components only - `;`, backticks, `$(`, `|` and newlines survive it -
and action reached the shell with nothing checking it at all. Nothing
untrusted got there: setInitAction switched on a fixed action list, and
for name the stat() gate required the interpolated string to exist as a
file under /etc/init.d, an existence check rather than a validation.
But init_action() was exported from luci-base, so any future caller
forwarding an RPC argument straight through inherited root command
execution.

rc.init has no shell to escape from - it execs the script - and it
additionally refuses scripts that are not root-owned or that are
world-writable.

The in-tree users are ported in the preceding commits.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
This commit is contained in:
Julius Bairaktaris authored and Jo-Philipp Wich committed 2026-08-03 11:58:22 +02:00
1 parent a9946cb2c8
commit 4440b267dd
2 files changed
+1 -33

No files matched your search

@@ -6,7 +6,7 @@
import { stdin, access, dirname, basename, open, popen, glob, lsdir, readfile, readlink, realpath, error } from 'fs';
import { cursor } from 'uci';
import { init_list, init_index, init_enabled, init_action, conntrack_list, process_list } from 'luci.sys';
import { init_list, init_index, init_enabled, conntrack_list, process_list } from 'luci.sys';
import { revision, branch } from 'luci.version';
import { statvfs, uname } from 'luci.core';
import { connect } from 'ubus';
@@ -135,29 +135,6 @@ const methods = {
}
},
setInitAction: {
args: { name: 'name', action: 'action' },
call: function(request) {
switch (request.args.action) {
case 'enable':
case 'disable':
case 'start':
case 'stop':
case 'restart':
case 'reload':
const rc = init_action(request.args.name, request.args.action);
if (rc === false)
return { error: 'No such init script' };
return { result: rc == 0 };
default:
return { error: 'Invalid action' };
}
}
},
getUnixtime: {
call: function(request) {
// time() ; Returns the current UNIX epoch.
-9
View File
@@ -149,12 +149,3 @@ export function init_enabled(name) {
return false;
};
export function init_action(name, action) {
const s = stat(`/etc/init.d/${basename(name)}`);
if (s?.type != 'file' || s?.user_exec == false)
return false;
return system(`env -i /etc/init.d/${basename(name)} ${action} >/dev/null`);
};