Child_process with impersonation? - linux

I'm trying to create a virtual bash in my node script that executes commands from within another user context.
var spawn = require('child_process').spawn;
var terminal = spawn('bash', [], { uid: 1001 });
terminal.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
terminal.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
terminal.on('close', function (code) {
console.log('child process exited with code ' + code);
});
terminal.stdin.write('echo Hello $USER');
terminal.stdin.end();
After executing this as root the output allways is
Hello root
As you can see in my example I've passed a user id to the spawn, but this doesn't effect anything.
My desired output is (assumed uid 1001's username is 'foobar'):
Hello foobar
Is there a way to do this in node or even a "fake" where I can start a bash process with given user credentials?

TL;DR: Your bash instance has in fact the permission of your foobar user, and the code you wrote is correct.
You can confirm this behavior if you run the following command, instead of bash:
terminal = spawn('id', ['-a'], { uid: 1001 });
which should output this:
stdout: uid=1001(foobar)
Which you can compare to a standard child spawn, without the uid parameter:
terminal = spawn('id', ['-a']);
which outputs:
stdout: uid=0(root)
Here is the relevant explanation, according to this question:
bash detects that it has been started SUID root (UID!=EUID) and uses
its root power to throw this power away, resetting EUID to UID.
which means that when bash starts, it's SUID is root, and then it sets its USER variable to root. Only after that does it uses setuid.

Related

Why do nodejs exec/spawn not show any output for bash 'history' command?

I am running this on Ubuntu and have tried many variations of exec/spawn functions (and their sync counterparts) and none of them can show me an output for bash 'history' command. One scenario is following:
const { spawnSync} = require('child_process');
const child = spawnSync('history', { shell: "/bin/bash" });
console.log('error: ', child.error);
console.log('stdout: ', child.stdout.toString());
console.log('stderr: ', child.stderr);
It does not show any errors and output is empty. I think this question has more to do with 'specialty' or category of the history command than nodejs's function since they work fine for normal commands like ls, pwd, whoami, etc work fine. I have looked at my .bash_history file and its filled with history so that's not the issue.
Another problem that might be similar is ll command also fails even though I have set bash as shell. But for ll, it does return an error:
/bin/bash: ll: command not found
Just to be sure, I tried running ll command in bash it worked just fine. What am I missing here?
edit: I have done some more testing it seems more like a bash thing than a node thing. When I simply write the history command, bash prints results but when I do bash -c history, it does not show any output but also no error.
.
You need to subscribe to messages from child process
child.on('error', (err) => {
});
child.stderr.on('data', (data) => {
});
child.on('exit', (code, signal) => {
});

Having trouble running child process command

I am trying to execute a nodejs file called create-MySwitch.js through a child process in my main.js file.
this is the code for main.js
const exec = require('child_process').exec;
var cmdStr = 'node /home/pi/Desktop/lan-device/create-MySwitch.js';
exec(cmdStr,
{argv: {
port:8080,
uuid:'MyThing'
}},
(error, stdout, stderr)=>{
if (error) {
console.error('exec error: ${error}');
console.log(error);
return;
}
console.log('stdout: ${stdout}');
console.log('stderr: ${stderr}');
});
This is how I am attempting to access the arguments in the create-MySwitch.js file
var port_value = process.argv.port;
var uuid_value = process.argv.uuid;
The output is
stdout: ${stdout}
stderr: ${stderr}
when I run the command
node main.js
I do not think it's working because the output from executing the create-MySwitch.js file should be 'ready', but clearly is not being printed in the stdout variable from the child process.
Essentially, what I am trying to do is run the command 'node createMySwitch.js ', but instead of just typing this in to the command prompt I want to run that command using a child process.
I am doing all of this using the raspbian operating system.

Use child_process.execSync but keep output in console

I'd like to use the execSync method which was added in NodeJS 0.12 but still have the output in the console window from which i ran the Node script.
E.g. if I run a NodeJS script which has the following line I'd like to see the full output of the rsync command "live" inside the console:
require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"');
I understand that execSync returns the ouput of the command and that I could print that to the console after execution but this way I don't have "live" output...
You can pass the parent´s stdio to the child process if that´s what you want:
require('child_process').execSync(
'rsync -avAXz --info=progress2 "/src" "/dest"',
{stdio: 'inherit'}
);
You can simply use .toString().
var result = require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"').toString();
console.log(result);
Edit: Looking back on this, I've realised that it doesn't actually answer the specific question because it doesn't show the output to you 'live' — only once the command has finished running.
However, I'm leaving this answer here because I know quite a few people come across this question just looking for how to print the result of the command after execution.
Unless you redirect stdout and stderr as the accepted answer suggests, this is not possible with execSync or spawnSync. Without redirecting stdout and stderr those commands only return stdout and stderr when the command is completed.
To do this without redirecting stdout and stderr, you are going to need to use spawn to do this but it's pretty straight forward:
var spawn = require('child_process').spawn;
//kick off process of listing files
var child = spawn('ls', ['-l', '/']);
//spit stdout to screen
child.stdout.on('data', function (data) { process.stdout.write(data.toString()); });
//spit stderr to screen
child.stderr.on('data', function (data) { process.stdout.write(data.toString()); });
child.on('close', function (code) {
console.log("Finished with code " + code);
});
I used an ls command that recursively lists files so that you can test it quickly. Spawn takes as first argument the executable name you are trying to run and as it's second argument it takes an array of strings representing each parameter you want to pass to that executable.
However, if you are set on using execSync and can't redirect stdout or stderr for some reason, you can open up another terminal like xterm and pass it a command like so:
var execSync = require('child_process').execSync;
execSync("xterm -title RecursiveFileListing -e ls -latkR /");
This will allow you to see what your command is doing in the new terminal but still have the synchronous call.
Simply:
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
console.log(`Status Code: ${error.status} with '${error.message}'`;
}
Ref: https://stackoverflow.com/a/43077917/104085
// nodejs
var execSync = require('child_process').execSync;
// typescript
const { execSync } = require("child_process");
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
error.status; // 0 : successful exit, but here in exception it has to be greater than 0
error.message; // Holds the message you typically want.
error.stderr; // Holds the stderr output. Use `.toString()`.
error.stdout; // Holds the stdout output. Use `.toString()`.
}
When command runs successful:
Add {"encoding": "utf8"} in options.
execSync(`pwd`, {
encoding: "utf8"
})

Allocate terminal with child_process

I'm using child_process to run a script that ultimately calls
ssh -t server "sudo command"
I get back:
Pseudo-terminal will not be allocated because stdin is not a terminal.
sudo: sorry, you must have a tty to run sudo
Is there any way to make the process' stdin a terminal? It's being run like so;
var proc = child_process.spawn(pathToCommand, ["args"]);
proc.stdout.on("data", function (data) {
socket.emit("running", "" + data);
});
proc.stderr.on("data", function (err) {
socket.emit("error", "" + err);
});
proc.on("close", function (code) {
socket.emit("finished", "" + code);
});
I would like to avoid forcing terminal allocation for ssh via -tt. Instead I would just like to allocate a terminal for the process. Is this doable?
The ssh2 module allows setting of a pty used by the remote end when using shell() or exec().

Cannot get output of child_process.spawn with interactive scripts

I cannot get any output in the following code:
var spawn = require('child_process').spawn,
script = 'ftp',
child = spawn(script);
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
child.on('close', function (code) {
console.log('child process exited with code ' + code);
});
It works for normal scripts such as 'ls', 'pwd' etc. But not for interactive programs such as 'ftp', 'telnet'. Any suggestions?
Edit:
Take another script for example:
#!/usr/bin/env python
name = raw_input("your name>")
print name
When spawn this script, I wish to fetch the prompt "your name>" with the data event, so that I can latter input something into stdin.
The problem is that I got nothing in the data event, and it seemed that none of these events are triggered.
ls, cat is controllable via input output and error stream.
ftp, telnet is controllable indirectly via tty.
The protocol is also base on input/output stream but it is more complicated. You can use available package to handle that protocol.
https://github.com/chjj/pty.js
var pty = require('pty.js');
var term = pty.spawn('ftp', [], options);
term.on('data', function(data) {
console.log(data);
});
term.write(ftpCmd + '\r');
The author of pty have some interesting examples, he forward pty to web via web socket, including terminal games:
https://github.com/chjj/tty.js
In interactive mode there is a command interpreter that reads user input from stdin, then accodingly prints output. So you have to write to stdin to do something. For example add following lines to your code with telnet command:
child.stdin.write('?\n');
child.stdin.write('quit\n');
Output:
stdout: Commands may be abbreviated. Commands are:
! cr mdir proxy send
$ delete mget sendport site
account debug mkdir put size
append dir mls pwd status
ascii disconnect mode quit struct
bell form modtime quote system
binary get mput recv sunique
bye glob newer reget tenex
case hash nmap rstatus trace
ccc help nlist rhelp type
cd idle ntrans rename user
cdup image open reset umask
chmod lcd passive restart verbose
clear ls private rmdir ?
close macdef prompt runique
cprotect mdelete protect safe
child process exited with code 0

Resources