aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: 575d2e8da3c573698d1cb9652b48fae250fa9abf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# SecDesk

An attempt to port Windows *secure desktop* to Linux.

## Background

On Windows, every window belongs to a [desktop](https://learn.microsoft.com/en-us/windows/win32/winstation/desktops),
and processes can (?) read the input of other windows, unless they are privileged (?).

Winlogon and UAC consent protects user inputs (e.g. passwords) from being stolen by 1) running as `NT Authority\SYSTEM`
, and running on the secure desktop, which is a separate desktop from the users'. This way, user processes cannot read
inputs.

macOS should (?) has a similar security measurement, but I am not sure.

On Unix, X11 does not have such a protection, and user processes are free to read any other process's input. This
includes your lock screen (e.g. `i3lock(1)`) or terminal (running `sudo(1)`).

Wayland fixes this issue by implementing a security control, while X11 users are left unprotected.

This proof-of-concept project aims at porting the secure desktop concept on Windows to Unix.

## Threat model

This project is aimed at preventing malware running as the unprivileged user to capture or hijack the input of
sensitive dialogues. It assumes that the operating system (kernel, libraries, binaries, daemons, and everything
privileged) is trusted.

## Basic idea

Without modifying the kernel, the easiest approach is to use virtual terminals. While most current Unix operating
systems has virtual terminal support, this project is mainly focused on Linux due to the author's familiarity. Porting
it to other Unix could be possible.

Linux has multiple virtual terminals (defined in `MAX_NR_CONSOLES`), and each one can run a text terminal or GUI. The
kernel provides text terminals: programs read and write to `/dev/ttyN`, and the kernel handles input and outputs from
the keyboard and to the screen. Display servers can use framebuffer or DRM, as well as `/dev/input/`, to draw their own
graphics.

Popular implementations of display servers, like Xorg or wayland compositors using `seatd` are all using DRM and
`/dev/input`.

Each virtual terminal is independent of each other, and users may switch from one to another using `Ctrl + Alt + Fx` or
`VT_ACTIVATE` ioctl.

Each virtual terminal is isolated from each other. For the text terminals, `/dev/ttyN` has the default permission of
`0620 root:tty`, and privileged display servers holding `/dev/input/*` does not pass keystrokes to user processes after
switching to other virtual terminals (Xorg will keep these files open, while `seated` will close them).
This is ideal for implementing a secure desktop on Linux.

The approach is to start a separate display server (e.g. Xorg) on a free (unused) virtual terminal, switch to it, read
sensitive data, switch back, and close the virtual terminal. This guarantees that unprivileged processes have no way to
hijack the password dialogue, with the limitation that the password dialogue must be trusted and ran by root.
For example, a dedicated X server coule be started using:

```shell
Xorg -background none :$NEW_DISPLAY vt$NEW_VT -nolisten tcp
# Setup XAuth, so only root can connect.
```

Then, start a password dialogue:

```shell
DISPLAY=:$NEW_DISPLAY /usr/lib/ssh/x11-ssh-askpass
```

This would be the simplest form of a secure desktop.

## Implementation

To make the startup process faster, more portable, and simpler, I wrote this PoC that uses the text terminal instead of
a display server. It is not a *desktop* in terms of the Windows secure desktop, but it satisfies the requirement of
securely reading sensitive data.

This PoC makes uses of various TTY and Console related ioctl (see `ioctl_console(2)` and `ioctl_tty(2)`), and it uses
codes from [kbd](http://kbd-project.org/). How this PoC works is obvious:

1. Open the TTY of the current process.
2. Find an open virtual terminal (`VT_OPENQRY`) and open it.
3. Make the virtual terminal the controlling terminal (`setsid(2)` and `TIOCSCTTY`).
4. Read / write as usual using the file descriptor from step 2.
5. Switch to it or switch back using `VT_ACTIVATE` and `VT_WAITACTIVATE`.

## SAK

Although the above process can safely read passwords, one more security measure must be taken into consideration: the
user must know the authenticity of the password dialogue. That is, a user process may create a full-screen window to
mimic the password dialogue in order to obtain the password.

Windows mitigates this issue by having a SAK (Secure attention key), which is `Ctrl + Alt + Delete`. The NT kernel
directly handles this key combo, and it notifies winlogon to show a privileged screen (either login screen or the
security options page). Users can trust the page is authentic because no other programs shall capture the key combo.
Moreover, domain administrators can enable the Require Ctrl Alt Del on Logon group policy to train users that they must
press the SAK before login to ensure the authenticity of the login screen.

However, this feature is missing on most current Unix operating systems. Linux is the only known Unix operating system
to have a SAK, and it is less known and has little use. On Linux, the SAK is `SysRq + K`. The kernel will kill all
processes (including the display server) in the current virtual terminal, so the service manager will restart the login
prompt (either the display manger or getty), and it is authentic. This behaviour makes SAK on Linux very limited, as
most users do not want their desktop programs to be killed just for logging in.

This project took the advantage of the Linux SAK by forking a child to display the message `Press SysRq + K to continue`
on the new virtual terminal and wait for it to be killed by `SIGKILL`. Although race condition could happen after
`waitpid(2)` and `TIOCSCTTY`, it is still safe because the `/dev/ttyN` file does not allow non-root processes to write.

## In the future

This project is far from perfect: its authentication UI / UX is naive, and it requires running as root to open the TTY.
In the future, I will make it an AskPass / PolKit agent, where unprivileged user can run use it to securely authenticate
themselves. I will also fix the behaviour when running from environments like SSH (pts) or serial, where virtual
terminals do not exist. In these environments, it should simply use the current terminal and inform the user that the
terminal is insecure, which is what Windows RDP does regarding remote UAC consent.

## Build and run

```shell
mkdir build
cd build
cmake ..
chown root ./secdesk && chmod 4755 ./secdesk
./secdesktop password test # mode prompt
```

The code is ugly: it is written in 4 hours. I will try to make it pretty.

## License

WTFPL