Skip to content

Commit f5e0a0e

Browse files
committed
2 parents 3375d01 + b37f381 commit f5e0a0e

File tree

10 files changed

+155
-37
lines changed

10 files changed

+155
-37
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,34 @@ It works in a similar way compared with popular Android root solutions like Magi
2525

2626
All communication between the `sudo` server and client is done via Unix socket.
2727

28+
![How it works](images/how-it-works.svg)
29+
30+
## Usage
31+
All you need to do is start the `crew-sudo` daemon in VT-2 and `sudo` will work again in `crosh` :)
32+
33+
```text
34+
sudo <command to execute or sudo options>
35+
---
36+
crew-sudo [command] <options>
37+
crew-sudo -h|--help
38+
crew-sudo -V|--version
39+
```
40+
41+
|Command |Description|
42+
|:-------------|:----------|
43+
|`client` |Run as client mode, pass all given command arguments to daemon|
44+
|`daemon` |Run as daemon mode, listen incoming requests at `/tmp/crew-sudo.socket`|
45+
|`stop-daemon` |Stop currently running `crew-sudo` daemon|
46+
47+
---
48+
49+
|Options |Description|
50+
|:-------------|:----------|
51+
|`--bashrc` |Suppress `daemon is already running` error|
52+
|`--foreground`|Run `crew-sudo` daemon in foreground|
53+
|`--replace` |Replace the currently running `crew-sudo` daemon|
54+
2855
## What works currently
2956
- Send terminal input to command/send command output back to `crosh`
3057
- Handle terminal size events
31-
- Sync exit code
58+
- Sync exit code

autocomplete/crew-sudo.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# bash completion for crew-sudo command
2+
3+
function _crew-sudo () {
4+
# available options
5+
local avail_opts=(--bashrc --foreground --replace)
6+
7+
# available commands
8+
local cmds=(client daemon stop-daemon)
9+
10+
# parameter that trigger this function
11+
local current="${COMP_WORDS[COMP_CWORD]}"
12+
13+
if [[ "${current:0:1}" == '-' ]]; then
14+
# match ${current} with available option list if ${current} start with '-'
15+
# save result to $COMPREPLY variable
16+
COMPREPLY=( $(compgen -W "${avail_opts[*]}" -- "${current}") )
17+
elif [[ ${COMP_CWORD} == 1 ]]; then
18+
# match ${current} with command list if ${current} is the first argument
19+
COMPREPLY=( $(compgen -W "${cmds[*]}" -- "${current}") )
20+
else
21+
return 1
22+
fi
23+
}
24+
25+
# register this function as completion function for crew-sudo command
26+
complete -F _crew-sudo crew-sudo

autostart/crew-sudo.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ script
1111
# - It dropped a controlling tty (std{in,err,out} are /dev/null).
1212
# - It calls setsid() for every job.
1313
# - It forked for us.
14-
exec /usr/local/bin/crew-sudo --daemon --foreground
14+
exec /usr/local/bin/crew-sudo daemon --foreground
1515
end script

autostart/crew-sudo.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
if grep -q '^/sbin/frecon' < "/proc/$(ps -p $PPID -o ppid= | tr -d ' ')/cmdline"; then
22
# start crew-sudo daemon if running in VT-2
3-
[ -f /tmp/crew-sudo.socket ] || crew-sudo --daemon
3+
[ -f /tmp/crew-sudo.socket ] || crew-sudo daemon --bashrc
44
fi

component/client.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ def restore_console
1010
end
1111

1212
def runas_client(argv)
13-
$mode = :client
13+
$mode = :client
14+
15+
unless Process.euid == 1000
16+
message 'Client executed by non-chronos user, falling back to sudo...'
17+
exec($0, *ARGV)
18+
end
19+
1420
is_tty = $stdin.isatty && $stdout.isatty && $stderr.isatty
1521
$tty_attr = %x[/bin/stty -g].chomp if is_tty
1622
socket = UNIXSocket.open(SOCKET_PATH) # connect to daemon

component/daemon.rb

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,67 @@
77
def runas_daemon(argv)
88
$mode = :daemon
99

10+
if File.exist?(SOCKET_PATH) && File.exist?(PID_FILE_PATH)
11+
if ARGV.include?('--replace')
12+
Process.kill('TERM', File.read(PID_FILE_PATH).to_i)
13+
sleep 0.1 while File.exist?(SOCKET_PATH)
14+
else
15+
if IS_BASHRC
16+
warn "crew-sudo: Daemon started with PID #{File.read(PID_FILE_PATH)}"
17+
else
18+
message <<~EOT, loglevel: :error
19+
crew-sudo daemon (process #{File.read(PID_FILE_PATH)}) is already running.
20+
21+
Use `#{PROGNAME} daemon --replace` to replace the running daemon.
22+
EOT
23+
end
24+
exit 1
25+
end
26+
end
27+
28+
@server = UNIXServer.new(SOCKET_PATH) # create unix socket
29+
30+
# fix permission if we are running as root
31+
File.chown(0, 1000, SOCKET_PATH) if Process.euid.zero?
32+
File.chmod(0o660, SOCKET_PATH)
33+
1034
# daemonize
11-
Process.daemon(false, true) unless ARGV.include?('--foreground')
35+
if ARGV.include?('--foreground')
36+
warn "crew-sudo: Daemon started with PID #{Process.pid}"
37+
else
38+
Process.daemon(false, true)
39+
warn "crew-sudo: Daemon started with PID #{Process.pid}"
1240

13-
Process.setproctitle('crew-sudo daemon process')
41+
# redirect output to log
42+
$log = File.open(DAEMON_LOG_PATH, 'w')
1443

15-
warn "crew-sudo: Daemon started with PID #{Process.pid}"
44+
$stdin.reopen('/dev/null')
45+
$stdout.reopen($log)
46+
$stderr.reopen($log)
1647

17-
# redirect output to log
18-
$log = File.open(DAEMON_LOG_PATH, 'w')
48+
[$log, $stdout, $stderr].each {|io| io.sync = true }
49+
end
1950

20-
$stdin.reopen('/dev/null')
21-
$stdout.reopen($log)
22-
$stderr.reopen($log)
51+
Process.setproctitle('crew-sudo daemon process')
52+
File.write(PID_FILE_PATH, Process.pid)
2353

24-
[$log, $stdout, $stderr].each {|io| io.sync = true }
54+
message "Daemon running with PID #{Process.pid}"
2555

26-
if Process.uid.zero?
56+
if Process.euid.zero?
2757
message 'Daemon running with root permission.'
2858
else
29-
message "Daemon running under UID #{Process.uid}."
59+
message "Daemon running under UID #{Process.euid}."
3060
end
3161

32-
message "Started with PID #{Process.pid}"
33-
34-
# create unix socket
35-
@server = UNIXServer.new(SOCKET_PATH)
36-
File.chmod(0o660, SOCKET_PATH)
62+
Socket.accept_loop(@server) do |socket, _|
63+
client_pid, client_uid, client_gid = socket.getsockopt(:SOL_SOCKET, :SO_PEERCRED).unpack('L*')
3764

38-
# fix permission if we are running as root
39-
File.chown(0, 1000, SOCKET_PATH) if Process.uid.zero?
65+
unless client_uid == 1000
66+
message "Request from PID #{client_pid} rejected (only chronos user is allowed)"
67+
socket.close
68+
next
69+
end
4070

41-
Socket.accept_loop(@server) do |socket, _|
4271
Thread.new do
4372
# receive client's stdin/stdout/stderr io from client
4473
client_stdin, client_stdout, client_stderr = [socket.recv_io, socket.recv_io, socket.recv_io]
@@ -62,7 +91,7 @@ def runas_daemon(argv)
6291
chdir: client_request[:cwd]
6392
end
6493

65-
message "Process #{pid} spawned: #{cmdline}"
94+
message "Process #{pid} spawned by client (#{client_pid}): #{cmdline}"
6695
send_event(socket, 'cmdSpawned', { pid: pid })
6796

6897
# listen to client events
@@ -103,6 +132,7 @@ def runas_daemon(argv)
103132
end
104133
end
105134
ensure
106-
@server.close
135+
@server&.close
107136
File.delete(SOCKET_PATH) if File.exist?(SOCKET_PATH)
108-
end
137+
File.delete(PID_FILE_PATH) if File.exist?(PID_FILE_PATH)
138+
end

crew-sudo

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,36 @@ $verbose = ENV['CREW_SUDO_VERBOSE'].eql?('1')
99
case PROGNAME
1010
when 'crew-sudo'
1111
case ARGV[0]
12-
when '-d', '--daemon'
12+
when 'daemon'
1313
runas_daemon(ARGV[1..-1])
14-
when '-c', '--client'
14+
when 'client'
1515
runas_client(ARGV[1..-1])
16+
when 'stop-daemon'
17+
exit(0) unless File.exist?(PID_FILE_PATH)
18+
pid = File.read(PID_FILE_PATH).to_i
19+
20+
begin
21+
Process.kill('TERM', pid)
22+
warn "crew-sudo: Daemon with PID #{pid} stopped"
23+
rescue Errno::ESRCH
24+
end
1625
when '-h', '--help'
1726
warn <<~EOT
1827
#{File.basename($0)} multi-purpose launcher
1928
20-
Usage: #{PROGNAME} [mode]
29+
Usage: #{PROGNAME} [command] <options>
2130
#{PROGNAME} -h|--help
2231
#{PROGNAME} -V|--version
2332
24-
Available modes:
25-
--daemon: Run as daemon mode, listen incoming requests at #{SOCKET_PATH}
26-
--client: Run as client mode, pass all given command arguments to daemon
33+
Available commands:
34+
client Run as client mode, pass all given command arguments to daemon
35+
daemon Run as daemon mode, listen incoming requests at #{SOCKET_PATH}
36+
stop-daemon Stop currently running crew-sudo daemon
37+
38+
Available options:
39+
--bashrc Suppress "daemon is already running" error
40+
--foreground Run crew-sudo daemon in foreground
41+
--replace Replace the currently running crew-sudo daemon
2742
2843
EOT
2944
when '-V', '--version'

images/how-it-works.svg

Lines changed: 4 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)