├── LICENSE.txt
├── README.md
├── contrib
├── README.md
├── tio-limit-ab678e6c882e19.patch
└── tio-noprefix-bfefd04b5567ba2b5.patch
└── src
├── Makefile
├── Makefile.android.aarch64
├── client.cc
├── external
├── aes.cc
├── aes.h
├── sha512.cc
└── sha512.h
├── misc.cc
├── misc.h
├── net.cc
├── net.h
├── pcwrap.cc
├── pcwrap.h
├── pscsh.cc
├── pty.cc
├── pty.h
├── pty98.cc
├── script_
└── helloworld
└── server.cc
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of port shell crypter (psc).
3 | *
4 | * (C) 2006-2021 by Sebastian Krahmer,
5 | * sebastian [dot] krahmer [at] gmail [dot] com
6 | *
7 | * psc is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * psc is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with psc. If not, see .
19 | */
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PortShellCrypter -- PSC
2 | =======================
3 |
4 | This project - as well as its sister project [crash](https://github.com/stealth/crash) - belongs
5 | to my anti-censorship tool-set that allows to setup fully working encrypted shells and TCP/UDP
6 | forwarding in hostile censoring environments. It is also useful for forensics to dump data from
7 | devices via UART or adb when no other means are available.
8 |
9 | [](https://asciinema.org/a/383043)
10 | *DNS lookup and SSH session forwarded across an UART connection to a Pi*
11 |
12 | PSC allows to e2e encrypt shell sessions, single- or multip-hop, being
13 | agnostic of the underlying transport, as long as it is reliable and can send/receive
14 | Base64 encoded data without modding/filtering. Along with the e2e pty that
15 | you receive (for example inside a port-shell), you can forward TCP and UDP
16 | connections, similar to OpenSSH's `-L` parameter. This works transparently
17 | and without the need of an IP address assigned locally at the starting
18 | point. This allows forensicans and pen-testers to create network connections
19 | for example via:
20 |
21 | * UART sessions to a device
22 | * `adb shell` sessions, if the OEM `adbd` doesn't support TCP forwarding
23 | * telnet sessions
24 | * modem dial-ups without ppp
25 | * other kinds of console logins
26 | * mixed SSH/telnet/modem sessions
27 | * ...
28 |
29 | Just imagine you would have an invisible ppp session inside your shell session,
30 | without the remote peer actually supporting ppp.
31 |
32 | It runs on *Linux, Android, OSX, Windows, FreeBSD, NetBSD* and (possibly) *OpenBSD*.
33 |
34 | PSC also includes *SOCKS4* and *SOCKS5* proxy support in order to have actual
35 | web browsing sessions via port-shells or modem dial-ups remotely.
36 |
37 | Build
38 | -----
39 |
40 | Edit the `Makefile` to reflect your pre shared keys, as defined at the top of the `Makefile`.
41 |
42 | Then just type `make` on *Linux* and *OSX*.
43 |
44 | On *BSD* you need to install *GNU make* and invoke `gmake` instead.
45 |
46 | On *Windows* you need to install [cygwin](https://cygwin.com/install.html) and select
47 | the appropriate `gcc, gcc-g++, make` and `git` packages.
48 |
49 | On *Linux*, PSC will use *Unix98* pseudo terminals, on other systems it will use *POSIX*
50 | pty's but that should be transparent to you. I once added *4.4BSD* pty and *SunOS*
51 | support back in the stone age for a particular reason, so it may or may not
52 | build even with *Solaris*.
53 |
54 | *proudly sponsored by:*
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Usage
63 | -----
64 |
65 | Plain and simple. On your local box, execute `pscl`, and pass any
66 | TCP or UDP ports you want to forward *from* the remote site to a particular
67 | address. For example:
68 |
69 | ```
70 | linux:~ > ./pscl -T 1234:[192.168.0.254]:22 -U 1234:[8.8.8.8]:53
71 |
72 | PortShellCrypter [pscl] v0.60 (C) 2006-2020 stealth -- github.com/stealth/psc
73 |
74 | pscl: set up local TCP port 1234 to proxy to 192.168.0.254:22 @ remote.
75 | pscl: set up local UDP port 1234 to proxy to 8.8.8.8:53 @ remote.
76 |
77 | pscl: Waiting for [pscr] session to appear ...
78 | linux:~ >
79 |
80 | [ UART / SSH / ... login to remote side ... ]
81 | ```
82 |
83 | On the remote site (the last hop) with the shell session, no matter if its in
84 | a port-shell, SSH, console login etc, you execute `pscr`:
85 |
86 |
87 | ```
88 | linux:~ > ./pscr
89 |
90 | PortShellCrypter [pscr] v0.60 (C) 2006-2020 stealth -- github.com/stealth/psc
91 |
92 |
93 | pscl: Seen STARTTLS sequence, enabling crypto.
94 | linux:~ >
95 | ```
96 |
97 | Once you execute `pscr`, both ends establish a crypto handshake and lay an additional
98 | protocol over your existing session that is transparent for you. You can then
99 | connect to `127.0.0.1:1234` on your local box to reach `192.168.0.254:22` via
100 | TCP or the `8.8.8.8` resolver via UDP. This also works with [IPv6] addresses,
101 | if the remote site has IPv6 connectivity. Actually, you can even use it to translate
102 | IPv4 software to IPv6, since you always connect to `127.0.0.1` on the local side.
103 |
104 | You can pass multiple `-T` and `-U` parameters. If you lost track if your session
105 | is already e2e encrypted, you can send a `SIGUSR1` to the local `pscl` process, and it
106 | will tell you.
107 |
108 | PSC is also useful if you want to use tor from a remote SSH shell, where you
109 | can forward the socks5 and the DNS port to the remote hosts `127.0.0.1` address.
110 | Since SSH does not forward UDP packets, you would normally use two `socat` connectors
111 | or similar to resolve via the tor node. PSC has the advantage of keeping the UDP
112 | datagram boundaries, while `socat` over `SSH -L` may break datagram boundaries
113 | and create malformed DNS requests.
114 |
115 | The session will be encrypted with `aes_256_ctr` of a PSK that you choose in the
116 | `Makefile`. This crypto scheme is malleable, but adding AAD or OAD data blows up
117 | the packet size, where every byte counts since on interactive sessions and due to
118 | Base64 encoding, each typed character already causes much more data to be sent.
119 |
120 | UART sessions may be used via `screen` but for example not via `minicom` since
121 | minicom will create invisible windows with status lines and acts like a filter
122 | that destroys PSC's protocol. PSC tries to detect filtering and can live with
123 | certain amount of data mangling, but in some situations it is not possible to recover.
124 | Similar thing with `tmux`. You should avoid stacking pty handlers with PSC that
125 | mess/handle their incoming data too much.
126 |
127 | The `SHELL` environment variable needs to be set for both `pscl` and `pscr` in order
128 | for PSC to know which shell to execute on the pty. `SHELL` is set in most environments
129 | by default, but in case it isn't, PSC needs to be executed like `SHELL=/bin/bash pscl`
130 | etc.
131 |
132 |
133 | SOCKS4 and SOCKS5 support
134 | -------------------------
135 |
136 | `pscl` also supports forwarding of TCP connections via *SOCKS4* (`-4 port`) and *SOCKS5*
137 | (`-5 port`). This sets up *port* as SOCKS port for TCP connections, so for instance you
138 | can browse remote networks from a port-shell session without the need to open any other
139 | connection during a pen-test. If you pass `-N` to `pscl`, it enables DNS name resolution
140 | on the remote side, so you can also use chrome with it. But be warned: There is a privacy
141 | problem with browsers that try to resolve a sequence of DNS names upon startup that
142 | is not under your control. Also, if your remote side has a broken DNS setup, your typing
143 | shell may block for several seconds if DNS reply packets are missing. There are no good
144 | async resolver functions which are embeddable and portable so I had to rely on
145 | `getaddrinfo()` in the single thread at the price of possible blockings for several seconds
146 | if DNS problems exist. Thats why name resolving has to be enabled explicitly. `pscr`
147 | tries to minimize this potential problem with DNS lookup caches though, so in most
148 | situation it should just work painlessly.
149 | If you pass `-X IP-address` (must be the first argument), you can bind your local proxy
150 | to an address different from `127.0.0.1`, so you can share the proxy in your local network.
151 |
152 |
153 | Bounce commands
154 | ---------------
155 |
156 | *psc* features allow TCP-connections or binary data blobs being forwarded from/to remote
157 | devices across multiple hops even if it is not possible to install the `pscr` binary at
158 | the remote site. This is very useful for forensic purposes if you do not have any means
159 | to otherwise download artefacts from the device (which can be an UART connected phone for example)
160 | or need to forward connections without touching the FS to not destroy evidence on the system
161 | or when the root-FS is ro mounted and you can't upload your tool-set.
162 |
163 | This is a really cool feature, as you can see your TCP connection hop through your local tty
164 | to a remote box without the need to install anything remotely.
165 |
166 | This solely works by local pty punkrock and handing over a bounce-command to `pscl` that it will
167 | drop on the remote shell (without `pscr` running) and some state engine magic that filters out
168 | and handles the data at the local side. Usually this requires to set the remote pty to raw mode
169 | at first before issuing the actual command and some other details that are passed to `-B`. The
170 | argument is split into the following parts:
171 |
172 | * The local port to trigger the command upon connect, followed by `:`, e.g. `1234:`.
173 | * The cmd that sets the remote tty to raw mode, usually `stty -echo raw` or
174 | `python -c "import tty;tty.setraw(0)"` (take care to get the quotes right, as `-B` also needs
175 | to be quoted) or anything similar.
176 | * A "GO" marker issued by remote that tells `pscl` to start sending data to avoid a race between
177 | `stty` actually happen and the start of the cmd, e.g. a `echo GO` is perfect.
178 | * The trigger command itself, e.g. `nc 127.0.0.1 22` to bounce local port 1234 to remote's SSH
179 | server
180 | * optionally a FIN marker issued by remote so you notice that trigger command has been finished
181 | i.e. you can kill your local connection to port 1234, which allows `pscl` to reset its tty state.
182 | `echo FIN` will do it. Recommended, as otherwise you can have trouble recognizing the end of
183 | your command.
184 | * All four previous commands are separated by `;` and enclosed in brackets.
185 |
186 | Examples:
187 |
188 | If you want to forward a TCP connection, this example requires `stty` and `nc` installed on the
189 | device, but it could theoretically be anything else that does equivalent.
190 |
191 | Start a local session:
192 |
193 | `./pscl -B '1234:[stty -echo raw;echo GO;nc example.com 22;echo FIN]'`
194 |
195 | This will issue the command `stty -echo raw;echo GO;nc example.com 22;echo FIN` to the remote
196 | device if you connect locally to port 1234 and then just forwards any data it sees back and forth
197 | and rate-limiting the traffic so it will not exceed the devices' tty speed (115200 is the default).
198 |
199 | When the pscl session is started, connect to the remote device by UART, `ssh -e none ...` or
200 | whatever it is and once you have the remote shell, also type locally:
201 |
202 | `ssh root@127.0.0.1 -p 1234` to bounce the SSH connection from your local box across the remote
203 | device to the `example.com` destination. Of course the `pscr` variant is preferred as `-B` can only
204 | bounce a single connection at a time (although you can pass multiple `-B` commands for various
205 | forwards) and theres a chance to hang the shell after the TCP session since the pty is in `raw -echo`
206 | mode and depending on whether the final remote peer also closes the connection, it might be
207 | that the shell just hangs after that. If you happen to find a pscl notification that the connection
208 | has finished and see a prompt, you should `reset` it, so that a new connection can be started.
209 | While data is being forwarded, you will see 7bit ASCII `<` and `>` notifications in `pscl` which
210 | are just local for easier debugging and progress detection.
211 |
212 | Note that the connection to the remote site has to be 8bit clean, i.e. the ssh, telnet, UART or
213 | whatever channel *must not handle escape sequences* (unlike when using `pscr`). For ssh connections
214 | this means you have to use `ssh -e none` in the `pscl` session.
215 |
216 | Next, following some examples to handle binary file xfer where *rfile* denotes the remote file and
217 | *lfile* the local file.
218 |
219 | To start a session to drop remote files, locally:
220 |
221 | `./pscl -B '1234:[stty -echo raw;echo GO;dd of=rfile.bin bs=1 count=7350;echo FIN]'`
222 |
223 | Where you need to specify the amount of data that the remote side is expecting. It would also
224 | work without (e.g. `cat>...`) but then the session will hang after transmission has finished as
225 | `cat` is endlessly expecting input. By using `dd count=...`, you will get a clean exit and be notified
226 | about it by the FIN marker.
227 |
228 | Then, ssh or whatever is necessary to get a shell on the remote device from within the just
229 | started `pscl` session. On a second terminal locally:
230 |
231 | `dd if=lfile.bin|nc 127.0.0.1 1234`
232 |
233 | which will connect to `pscl`'s local port 1234 and trigger the dump command on the remote side,
234 | forwarding the binary data of the local `lfile.bin` to remotes `rfile.bin`. Due to rate-limiting
235 | this can take a while and you *only trust your psc progress screen* whether the transfer is finished.
236 | The local `dd ...|nc ...` command will only show you the local status which can eat entire files
237 | in msecs due to local TCP buffers while the file is still being transfered through the pty.
238 | So make sure you only press `Ctrl-C` when the *pscl* screen tells you it is finished or you see
239 | the `FIN` end marker being echoed back to you on the `dd ...|nc ...` session.
240 |
241 | Likewise, similar commands could be used to transfer binary data from a remote device to the
242 | local box for forensic purposes. Again, start of the session locally:
243 |
244 | `./pscl -B '1234:[stty -echo raw;echo GO;dd if=rfile.bin]'` or
245 |
246 | `./pscl -B '1234:[stty -echo raw;echo GO;cat rfile.bin]'`
247 |
248 | Then, ssh to remote device to get the shell, then again locally:
249 |
250 | `nc 127.0.0.1 1234|dd of=lfile.bin bs=1 count=7350`
251 |
252 | To obtain `rfile.bin` of size 7350 copied to local file `lfile.bin`
253 |
254 | If `stty -echo raw` is not available on the device, something like
255 | `python -c "import tty;tty.setraw(0)"` also works. Note that on the remote device you need to have
256 | a tty (not just a port-shell) when using bounce commands, since the `stty` command to set raw mode
257 | requires a real tty.
258 |
259 |
260 | UART / modems / Flow Control
261 | ----------------------------
262 |
263 | If *psc* runs across a serial connection, lost bits can kill all your fun. If you run
264 | without HW FC you will eventually experience bitloss and hung connections, in particular
265 | as there is no throttling when the device is sending data in your direction when using
266 | *bounce commands*. Dumping data to the device works better as this data goes through
267 | the `pscl` rate limits.
268 |
269 | However, here are some tips that worked for me under circumstances when it is not
270 | possible to use `pscr` on the device and HW FC. This only applies when using UARTs, as this
271 | is a potentially unreliable transport channel.
272 |
273 | * do not enable soft FC, as this would tamper the 8-bit channel
274 | * when possible use HW FC - or if not - you have to disable FC alltogether
275 | * Use `pscr` on the device so you can set rate limiting for data being sent into your direction.
276 | As the direction towards the device is always rate limited, you can use bounce commands to
277 | dump a cross-compiled `pscr` binary to the device and start a two-way rate limited session with it.
278 | * use high quality cables with proper shielding and UART chipsets with large buffers
279 | * apply the tio-limit patch from the contrib folder, as tio is buffering input bytes which could
280 | lead to writing-peeks that exceed the set rate
281 | * use `tio -o 1` or `-o 2` to add delays between sent output-bytes
282 | * use a conservative rate limitig (i.e. prefer `38400` although serial line has set `115200`)
283 | * compile `psc` with `-DRESPECT_UART_BUFSIZE=4096`, however this will make the session very slow
284 |
285 | Inside the `contrib` folder you will also find a `tio-noprefix` patch to disable escape-character
286 | processing but this patch is only necessary for older versions, as upstream already accepted and
287 | integrated this patch. I really recommend using `tio` when using UARTs.
288 |
289 |
290 | When using bounce commands across *tio*, you have to add to your `~/.tioconfig` file:
291 |
292 | ```
293 | [default]
294 |
295 | prefix-ctrl-key = none
296 | ```
297 |
298 | which disables ESC-handling and gives you an 8-bit clean channel.
299 |
300 |
301 | SIGUSR1 / SIGUSR2
302 | -----------------
303 |
304 | You can send `SIGUSR1` to `pscl` so it tells you whether the session is encrypted. If the remote
305 | `pscr` dies or exits without possibility to signal that to the local part, `pscl` will stay
306 | in encryption mode and therefore hang. In this case you can force a reset to plaintext mode
307 | by sending `SIGUSR2`, so a new session can be started.
308 |
309 | Scripting
310 | ---------
311 |
312 | As of version 0.64, *psc* supports scripting-sockets so you no longer need `screen` to
313 | get/put files or dump paste buffers to the remote console. Instead, you start your local
314 | session like so:
315 |
316 | ```
317 | ~ > ./pscl -S ~/psc.script_sock
318 | ```
319 |
320 | You can then go ahead and use it as before. If you need to 'paste' something you do like:
321 |
322 | ```
323 | ~ > ./pscsh -S ~/psc.script_sock -f script_/helloworld
324 | ```
325 |
326 | This will 'type' the content of `script_/helloworld` to the console. While scripting,
327 | the stdin of `pscl` is blocked so that the injected input does not mix up with any
328 | typing. If `-S` is omitted in `pscsh`, `~/psc.script_sock` is used automatically.
329 | For safety reasons, scripts must start with the `script_` prefix.
330 |
331 | As a bonus, `pscr` now contains the ability to base64 en/decode files, even with CR
332 | embedded characters for convenience. It is compatible to `uuencode -m`.
333 |
334 |
--------------------------------------------------------------------------------
/contrib/README.md:
--------------------------------------------------------------------------------
1 | contrib
2 | =======
3 |
4 | This directory contains some contributions.
5 |
6 | tio noprefix patch
7 | ------------------
8 |
9 | tio is a nice serial console client that has all the nice magic switches
10 | and cmdline friendlieness that you expect on Linux-like systems.
11 |
12 | Apply the patch to [tio](https://github.com/tio/tio) @commit `bfefd04b5567ba2b5`
13 | in order to allow serial console session w/o tapering of the I/O stream so you
14 | can pipe arbitrary data through your pty for the bounce command (this patch
15 | works like ssh's `-e none`).
16 |
17 | Adding this patch to current versions is not necessary anymore as its already
18 | been integrated.
19 |
20 | Then add to your `~/.tioconfig`:
21 | ```
22 | prefix-ctrl-key = none
23 | ```
24 |
25 | Which disables prefix parsing.
26 |
27 |
28 |
29 | tio limit patch
30 | ---------------
31 |
32 | tio will collect input bytes into a buffer before sending it to the serial line,
33 | which could lead to peeks above the acceptable baudrate. This dirty patch adds rate
34 | limiting in the write cycle. Also consider using `tio -o 1` if you get bit flips
35 | during transmission.
36 |
37 |
--------------------------------------------------------------------------------
/contrib/tio-limit-ab678e6c882e19.patch:
--------------------------------------------------------------------------------
1 | diff --git a/src/tty.c b/src/tty.c
2 | index 74f6ab3..9392552 100644
3 | --- a/src/tty.c
4 | +++ b/src/tty.c
5 | @@ -27,6 +27,7 @@
6 | #include
7 | #include
8 | #include
9 | +#include
10 | #include
11 | #include
12 | #include
13 | @@ -224,16 +225,27 @@ inline static unsigned char char_to_nibble(char c)
14 | void tty_sync(int fd)
15 | {
16 | ssize_t count;
17 | + struct timeval tv;
18 | + static struct timeval last_tv = {0, 0};
19 | + uint32_t byte_rate = option.baudrate/10, idx = 0;
20 |
21 | while (tty_buffer_count > 0)
22 | {
23 | - count = write(fd, tty_buffer, tty_buffer_count);
24 | + gettimeofday(&tv, NULL);
25 | + uint64_t tdiff_usec = tv.tv_sec*1000000 + tv.tv_usec - (last_tv.tv_sec*1000000 + last_tv.tv_usec);
26 | + if (tdiff_usec < (1000000*1.0/byte_rate))
27 | + continue;
28 | + last_tv.tv_sec = tv.tv_sec;
29 | + last_tv.tv_usec = tv.tv_usec;
30 | +
31 | + count = write(fd, tty_buffer + idx, 1);
32 | if (count < 0)
33 | {
34 | // Error
35 | tio_debug_printf("Write error while flushing tty buffer (%s)", strerror(errno));
36 | break;
37 | }
38 | + idx += count;
39 | tty_buffer_count -= count;
40 | fsync(fd);
41 | tcdrain(fd);
42 |
--------------------------------------------------------------------------------
/contrib/tio-noprefix-bfefd04b5567ba2b5.patch:
--------------------------------------------------------------------------------
1 | diff --git a/src/configfile.c b/src/configfile.c
2 | index 387063b..a96e0c3 100644
3 | --- a/src/configfile.c
4 | +++ b/src/configfile.c
5 | @@ -259,7 +259,10 @@ static int data_handler(void *user, const char *section, const char *name,
6 | }
7 | else if (!strcmp(name, "prefix-ctrl-key"))
8 | {
9 | - if (ctrl_key_code(value[0]) > 0)
10 | + if (!strcmp(value, "none")) {
11 | + option.prefix_off = 1;
12 | + }
13 | + else if (ctrl_key_code(value[0]) > 0)
14 | {
15 | option.prefix_code = ctrl_key_code(value[0]);
16 | option.prefix_key = value[0];
17 | diff --git a/src/options.c b/src/options.c
18 | index 4f08e6d..1f88bbb 100644
19 | --- a/src/options.c
20 | +++ b/src/options.c
21 | @@ -86,6 +86,7 @@ struct option_t option =
22 | .hex_mode = false,
23 | .prefix_code = 20, // ctrl-t
24 | .prefix_key = 't',
25 | + .prefix_off = 0,
26 | .response_wait = false,
27 | .response_timeout = 100,
28 | .mute = false,
29 | diff --git a/src/options.h b/src/options.h
30 | index b18d8ff..64f8b96 100644
31 | --- a/src/options.h
32 | +++ b/src/options.h
33 | @@ -59,6 +59,7 @@ struct option_t
34 | bool hex_mode;
35 | unsigned char prefix_code;
36 | unsigned char prefix_key;
37 | + unsigned char prefix_off;
38 | bool response_wait;
39 | int response_timeout;
40 | bool mute;
41 | diff --git a/src/tty.c b/src/tty.c
42 | index cdab4b9..19cfda0 100644
43 | --- a/src/tty.c
44 | +++ b/src/tty.c
45 | @@ -338,7 +338,7 @@ void *tty_stdin_input_thread(void *arg)
46 |
47 | input_char = input_buffer[i];
48 |
49 | - if (previous_char == option.prefix_code)
50 | + if (!option.prefix_off && previous_char == option.prefix_code)
51 | {
52 | if (input_char == option.prefix_code)
53 | {
54 | @@ -568,7 +568,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
55 | }
56 |
57 | /* Handle escape key commands */
58 | - if (previous_char == option.prefix_code)
59 | + if (!option.prefix_off && previous_char == option.prefix_code)
60 | {
61 | /* Do not forward input char to output by default */
62 | *forward = false;
63 | @@ -1535,7 +1535,7 @@ int tty_connect(void)
64 | if (interactive_mode)
65 | {
66 | /* Do not forward prefix key */
67 | - if (input_char == option.prefix_code)
68 | + if (!option.prefix_off && input_char == option.prefix_code)
69 | {
70 | forward = false;
71 | }
72 |
--------------------------------------------------------------------------------
/src/Makefile:
--------------------------------------------------------------------------------
1 | # PSC Makefile
2 |
3 | # MUST use different keys for KEY1 and KEY2
4 | KEY1=\"secret1\"
5 | KEY2=\"secret2\"
6 |
7 | # 'p' encoded in hex so you can dump Makefile in pscl
8 | BANNER=\"\\x70sc-2022-STARTTLS-\"
9 |
10 | POSIX=-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600
11 |
12 | CXX=c++
13 | DEFS=-DPSC_READ_KEY=$(KEY1) -DPSC_WRITE_KEY=$(KEY2) -DSTART_BANNER=$(BANNER)
14 | #DEFS+=-DRESPECT_UART_BUFSIZE=4096
15 | CXXFLAGS=-c -Wall -O2 -std=c++11 -pedantic
16 |
17 | .PHONY: all clean
18 |
19 | ifeq ($(shell uname), Linux)
20 | DEFS+=-DHAVE_UNIX98
21 | DEFS+=$(POSIX)
22 | else ifeq ($(shell uname), NetBSD)
23 |
24 | else ifeq ($(shell uname), FreeBSD)
25 |
26 | else ifeq ($(shell uname), OpenBSD)
27 |
28 | else ifeq ($(shell uname), Solaris)
29 |
30 | else ifeq ($(shell uname), Darwin)
31 |
32 | # Cygwin
33 | else
34 | DEFS+=$(POSIX)
35 | endif
36 |
37 | all: warn pscl pscr pscsh
38 |
39 | warn:
40 | @echo "Hint: Do not 'make' inside pscl session, as it echos PSC STARTTLS sequence." || true
41 |
42 | clean:
43 | rm -f *.o
44 |
45 | pscl: misc.o client.o pcwrap.o pty.o pty98.o net.o sha512.o aes.o
46 | $(CXX) $^ -o $@
47 |
48 | pscr: misc.o server.o pty.o pty98.o pcwrap.o net.o sha512.o aes.o
49 | $(CXX) $^ -o $@
50 |
51 | pscsh: pscsh.o
52 | $(CXX) $^ -o $@
53 |
54 | pcwrap.o: pcwrap.cc
55 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
56 |
57 | client.o: client.cc
58 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
59 |
60 | server.o: server.cc
61 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
62 |
63 | misc.o: misc.cc
64 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
65 |
66 | pty.o: pty.cc
67 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
68 |
69 | pty98.o: pty98.cc
70 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
71 |
72 | net.o: net.cc
73 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
74 |
75 | aes.o: external/aes.cc
76 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
77 |
78 | sha512.o: external/sha512.cc
79 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
80 |
81 | pscsh.o: pscsh.cc
82 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
83 |
84 |
--------------------------------------------------------------------------------
/src/Makefile.android.aarch64:
--------------------------------------------------------------------------------
1 | #
2 | # This is the Makefile for the Android flavor, Aarch64
3 | #
4 |
5 |
6 | # MUST use different keys for KEY1 and KEY2
7 | KEY1=\"secret1\"
8 | KEY2=\"secret2\"
9 |
10 | ROOT=/opt
11 | NDK=android-ndk-r17b
12 |
13 | PREFIX=$(ROOT)/$(NDK)/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-
14 | SYSROOT=--sysroot=$(ROOT)/$(NDK)/platforms/android-24/arch-arm64/
15 | SYSROOT+=-isysroot $(ROOT)/$(NDK)/sysroot
16 |
17 | INC=-isystem $(ROOT)/$(NDK)/sysroot/usr/include/aarch64-linux-android\
18 | -I$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/include\
19 | -I$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/4.9/include/\
20 | -I$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/include
21 |
22 | LIB=-Wl,$(ROOT)/$(NDK)/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/libgnustl_static.a
23 |
24 | DEFS=-DPSC_READ_KEY=$(KEY1) -DPSC_WRITE_KEY=$(KEY2)
25 | DEFS+=-DHAVE_UNIX98
26 |
27 | CXX=$(PREFIX)gcc $(SYSROOT) $(INC)
28 | LD=$(PREFIX)gcc -pie $(SYSROOT)
29 |
30 | CXXFLAGS=-c -O2 -Wall -pedantic -std=c++11 -fPIC
31 |
32 |
33 | .PHONY: all clean
34 |
35 |
36 | all: pscl pscr pscsh
37 |
38 | clean:
39 | rm -f *.o
40 |
41 | pscl: misc.o client.o pcwrap.o pty.o pty98.o net.o sha512.o aes.o
42 | $(LD) $^ -o $@ $(LIB)
43 |
44 | pscr: misc.o server.o pty.o pty98.o pcwrap.o net.o sha512.o aes.o
45 | $(LD) $^ -o $@ $(LIB)
46 |
47 | pscsh: pscsh.o
48 | $(LD) $^ -o $@ $(LIB)
49 |
50 | pcwrap.o: pcwrap.cc
51 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
52 |
53 | client.o: client.cc
54 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
55 |
56 | server.o: server.cc
57 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
58 |
59 | misc.o: misc.cc
60 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
61 |
62 | pty.o: pty.cc
63 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
64 |
65 | pty98.o: pty98.cc
66 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
67 |
68 | net.o: net.cc
69 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
70 |
71 | aes.o: external/aes.cc
72 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
73 |
74 | sha512.o: external/sha512.cc
75 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
76 |
77 | pscsh.o: pscsh.cc
78 | $(CXX) $(DEFS) $(CXXFLAGS) $^ -o $@
79 |
80 |
--------------------------------------------------------------------------------
/src/client.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of port shell crypter (psc).
3 | *
4 | * (C) 2006-2024 by Sebastian Krahmer,
5 | * sebastian [dot] krahmer [at] gmail [dot] com
6 | *
7 | * psc is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * psc is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with psc. If not, see .
19 | */
20 |
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | #include "net.h"
41 | #include "pty.h"
42 | #include "pcwrap.h"
43 | #include "misc.h"
44 |
45 |
46 | using namespace std;
47 | using namespace ns_psc;
48 |
49 | namespace ns_psc {
50 |
51 | pc_wrap *psc = nullptr;
52 |
53 | struct termios global_tcattr, exit_tattr;
54 |
55 | }
56 |
57 | void sig_chld(int)
58 | {
59 | tcsetattr(0, TCSANOW, &exit_tattr);
60 | printf("pscl: exiting\n");
61 | _exit(0);
62 | }
63 |
64 |
65 | void sig_usr1(int)
66 | {
67 | if (!psc)
68 | return;
69 |
70 | if (psc->is_crypted())
71 | printf("\r\npscl: encryption enabled\r\n");
72 | else
73 | printf("\r\npscl: encryption disabled\r\n");
74 | }
75 |
76 |
77 | void sig_usr2(int)
78 | {
79 | if (!psc)
80 | return;
81 |
82 | printf("\r\npscl: Disabling encryption on USR2\r\n");
83 | psc->reset();
84 | }
85 |
86 |
87 | bool winsize_changed = 0;
88 |
89 | void sig_win(int)
90 | {
91 | winsize_changed = 1;
92 | }
93 |
94 |
95 | void usage(const char *argv0)
96 | {
97 | printf("Usage: %s\t[-4 socks4 lport] [-5 socks5 lport] [-T lport:[ip]:rport]\n"
98 | "\t\t[-U lport:[ip]:rport] [-X local proxy IP (127.0.0.1 dflt)] [-N]\n"
99 | "\t\t[-B lport:[bounce cmd] [-S script socket] [-l baud limit]\n\n", argv0);
100 | }
101 |
102 |
103 | // do not use replace_if() for easier C++98 ports
104 | string trim_bcmd_output(const string &s)
105 | {
106 | string ret = s.substr(0, 75);
107 | size_t retl = ret.size();
108 | for (size_t i = 0; i < retl; ++i) {
109 | if (ret[i] < 32 || ret[i] > 125)
110 | ret[i] = '.';
111 | }
112 | return ret;
113 | }
114 |
115 |
116 | int proxy_loop()
117 | {
118 |
119 | #ifdef HAVE_UNIX98
120 | pty98 pt;
121 | #else
122 | pty pt;
123 | #endif
124 | pid_t pid;
125 | int r, afd = -1, i, bcmd_accept_fd = -1;
126 |
127 | char sbuf[BLOCK_SIZE/2] = {0}; // 1 MTU
128 | struct termios tattr;
129 |
130 | if (pt.open() < 0)
131 | die(pt.why());
132 | fix_size(pt.slave());
133 |
134 | if (tcgetattr(0, &tattr) < 0)
135 | die("pscl: tcgetattr");
136 |
137 | exit_tattr = tattr;
138 |
139 | cfmakeraw(&tattr);
140 | tattr.c_cc[VMIN] = 1;
141 | tattr.c_cc[VTIME] = 0;
142 | tattr.c_lflag &= ~ISIG;
143 |
144 | //tattr.c_lflag &= ~ECHO;
145 |
146 | global_tcattr = tattr;
147 | if (tcsetattr(0, TCSANOW, &tattr) < 0)
148 | die("pscl: tcsetattr");
149 |
150 | struct rlimit rl;
151 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
152 | die("getrlimit");
153 | if (rl.rlim_cur > FDID_MAX) {
154 | rl.rlim_cur = rl.rlim_max = FDID_MAX;
155 | setrlimit(RLIMIT_NOFILE, &rl);
156 | }
157 |
158 | if ((pid = fork()) == 0) {
159 | char *a[] = {getenv("SHELL"), nullptr};
160 | extern char **environ;
161 |
162 | if (!*a) {
163 | die("pscl: no shell set via $SHELL");
164 | }
165 |
166 | close(0); close(1); close(2);
167 | dup2(pt.slave(), 0); dup2(pt.slave(), 1);
168 | dup2(pt.slave(), 2);
169 | setsid();
170 | ioctl(0, TIOCSCTTY, 0);
171 | pt.close();
172 | for (unsigned int i = 3; i < rl.rlim_cur; ++i)
173 | close(i);
174 | execve(*a, a, environ);
175 | die("pscl: execve");
176 | } else if (pid < 0)
177 | die("pscl: fork");
178 |
179 | psc = new (nothrow) pc_wrap(pt.master(), pt.master());
180 | if (!psc)
181 | die("new pc_wrap OOM");
182 |
183 | if (psc->init(PSC_WRITE_KEY, PSC_READ_KEY, 0) < 0)
184 | die(psc->why());
185 | close(pt.slave());
186 |
187 | struct pollfd *pfds = new (nothrow) pollfd[rl.rlim_cur];
188 | struct state *fd2state = new (nothrow) state[rl.rlim_cur];
189 |
190 | fd2state[0].fd = 0;
191 | fd2state[0].state = STATE_STDIN;
192 |
193 | fd2state[1].fd = 1;
194 | fd2state[1].state = STATE_STDOUT;
195 |
196 | fd2state[pt.master()].fd = pt.master();
197 | fd2state[pt.master()].state = STATE_PTY;
198 |
199 | for (unsigned int i = 0; i < rl.rlim_cur; ++i) {
200 | pfds[i].fd = -1;
201 | pfds[i].events = pfds[i].revents = 0;
202 | }
203 |
204 | pfds[0].fd = 0;
205 | pfds[0].events = POLLIN;
206 | pfds[1].fd = 1;
207 | pfds[pt.master()].fd = pt.master();
208 | pfds[pt.master()].events = POLLIN;
209 |
210 | for (auto it = config::tcp_listens.begin(); it != config::tcp_listens.end(); ++it) {
211 | if ((r = tcp_listen(config::local_proxy_ip, it->first)) < 0)
212 | continue;
213 | pfds[r].fd = r;
214 | pfds[r].events = POLLIN;
215 |
216 | fd2state[r].fd = r;
217 | fd2state[r].rnode = it->second;
218 | fd2state[r].state = STATE_ACCEPT;
219 | }
220 |
221 | for (auto it = config::udp_listens.begin(); it != config::udp_listens.end(); ++it) {
222 | if ((r = udp_listen(config::local_proxy_ip, it->first)) < 0)
223 | continue;
224 | pfds[r].fd = r;
225 | pfds[r].events = POLLIN;
226 |
227 | fd2state[r].fd = r;
228 | fd2state[r].rnode = it->second;
229 | fd2state[r].state = STATE_UDPSERVER;
230 | }
231 |
232 | // bouncing via bounce command
233 | for (auto it = config::bcmd_tcp_listens.begin(); it != config::bcmd_tcp_listens.end(); ++it) {
234 | if ((r = tcp_listen(config::local_proxy_ip, it->first)) < 0)
235 | continue;
236 | pfds[r].fd = r;
237 | pfds[r].events = POLLIN;
238 |
239 | fd2state[r].fd = r;
240 | fd2state[r].rnode = it->second; // actually the cmd e.g. `stty -echo raw;nc example.com 22`
241 | fd2state[r].state = STATE_BCMD_ACCEPT;
242 | }
243 |
244 | if (config::socks5_fd != -1) {
245 | pfds[config::socks5_fd].fd = config::socks5_fd;
246 | pfds[config::socks5_fd].events = POLLIN;
247 |
248 | fd2state[config::socks5_fd].fd = config::socks5_fd;
249 | fd2state[config::socks5_fd].rnode = "";
250 | fd2state[config::socks5_fd].state = STATE_SOCKS5_ACCEPT;
251 | }
252 |
253 | if (config::socks4_fd != -1) {
254 | pfds[config::socks4_fd].fd = config::socks4_fd;
255 | pfds[config::socks4_fd].events = POLLIN;
256 |
257 | fd2state[config::socks4_fd].fd = config::socks4_fd;
258 | fd2state[config::socks4_fd].rnode = "";
259 | fd2state[config::socks4_fd].state = STATE_SOCKS4_ACCEPT;
260 | }
261 |
262 | if (config::script_sock != -1) {
263 | pfds[config::script_sock].fd = config::script_sock;
264 | pfds[config::script_sock].events = POLLIN;
265 |
266 | fd2state[config::script_sock].fd = config::script_sock;
267 | fd2state[config::script_sock].rnode = "";
268 | fd2state[config::script_sock].state = STATE_SCRIPT_ACCEPT;
269 | }
270 |
271 | // Since we have no fd per "connection" for UDP, we need to keep track of
272 | // local sockaddrs <-> id by ourself in order to know to which dst to send replies
273 | // we receive from remote
274 | udp_node2id udp_nodes2id;
275 |
276 | int max_fd = rl.rlim_cur - 1, script_fd = -1;
277 | #ifdef RESPECT_UART_BUFSIZE
278 | uint32_t tx_rate_cnt = 0;
279 | #endif
280 |
281 | string ext_cmd = "", tbuf = "";
282 | string bcmd = "", bcmd_in_log_str = "", bcmd_out_log_str = "", bcmd_buffered_output = "";
283 | const string BCMD_GO_CMD = "GO\n";
284 |
285 | enum { CHUNK_SIZE = 8192 };
286 |
287 | timeval last_tv;
288 | memset(&last_tv, 0, sizeof(last_tv));
289 |
290 | for (;;) {
291 |
292 | memset(sbuf, 0, sizeof(sbuf));
293 |
294 | if (winsize_changed && psc->is_crypted()) {
295 | fd2state[pt.master()].obuf += psc->wsize_cmd();
296 | pfds[pt.master()].events |= POLLOUT;
297 | winsize_changed = 0;
298 | }
299 |
300 | for (i = rl.rlim_cur - 1; i > 0; --i) {
301 | if (fd2state[i].state != STATE_INVALID && fd2state[i].fd != -1) {
302 | max_fd = i;
303 | break;
304 | }
305 | }
306 |
307 | if ((r = poll(pfds, max_fd + 1, 1000)) < 0) {
308 | if (errno == EINTR)
309 | continue;
310 | else
311 | die("pscl: poll");
312 | }
313 |
314 | time_t now = time(nullptr);
315 |
316 | for (i = 0; i <= max_fd; ++i) {
317 |
318 | if (fd2state[i].state == STATE_INVALID)
319 | continue;
320 |
321 | if ((fd2state[i].state == STATE_CLOSING && (now - fd2state[i].time) > CLOSING_TIME) ||
322 | (fd2state[i].state == STATE_CONNECT && (now - fd2state[i].time) > CONNECT_TIME)) {
323 |
324 | if (fd2state[i].state == STATE_CONNECT) {
325 | pfds[pt.master()].events |= POLLOUT;
326 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal interrupted connection to remote
327 | tcp_nodes2sock.erase(fd2state[i].rnode);
328 | }
329 |
330 | close(i);
331 | fd2state[i].fd = -1;
332 | fd2state[i].state = STATE_INVALID;
333 | pfds[i].fd = -1;
334 | pfds[i].events = 0;
335 | continue;
336 | }
337 |
338 | if (pfds[i].revents & (POLLERR|POLLHUP|POLLNVAL)) {
339 | if (fd2state[i].state == STATE_STDIN || fd2state[i].state == STATE_PTY)
340 | die("pscl: TTY hangup");
341 | if (fd2state[i].state == STATE_CONNECTED || fd2state[i].state == STATE_CONNECT) {
342 | pfds[pt.master()].events |= POLLOUT;
343 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection to remote
344 | tcp_nodes2sock.erase(fd2state[i].rnode);
345 | }
346 |
347 | if (fd2state[i].state == STATE_SCRIPT_IO ||
348 | fd2state[i].state == STATE_BCMD_CONNECT || fd2state[i].state == STATE_BCMD_CONNECTED) {
349 | pfds[0].events |= POLLIN; // reactivate stdin
350 | if (config::script_sock >= 0)
351 | pfds[config::script_sock].events |= POLLIN;
352 | if (i == bcmd_accept_fd)
353 | bcmd_accept_fd = -1;
354 | }
355 |
356 | close(i);
357 | fd2state[i].fd = -1;
358 | fd2state[i].state = STATE_INVALID;
359 | fd2state[i].obuf.clear();
360 | pfds[i].fd = -1;
361 | pfds[i].events = 0;
362 | continue;
363 | }
364 |
365 | errno = 0;
366 | ext_cmd.clear();
367 |
368 | if (pfds[i].revents & POLLIN) {
369 | pfds[i].revents = 0;
370 | if (fd2state[i].state == STATE_STDIN) {
371 |
372 | if ((r = read(0, sbuf, sizeof(sbuf))) <= 0) {
373 | if (errno == EINTR)
374 | continue;
375 | else
376 | die("pscl: read");
377 | }
378 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("D:0:", string(sbuf, r));
379 | pfds[pt.master()].events |= POLLOUT;
380 |
381 | } else if (fd2state[i].state == STATE_PTY) {
382 | int starttls = 0;
383 | bool nosys = 0;
384 | do {
385 | tbuf = ext_cmd = "";
386 | if ((r = psc->read(nosys, tbuf, ext_cmd, starttls)) < 0)
387 | die(psc->why());
388 |
389 | nosys = 1;
390 |
391 | // STARTTLS/end-sequence seen
392 | if (starttls == 1) {
393 | winsize_changed = 1;
394 | continue;
395 | }
396 |
397 | // ext_cmd can have been filled by psc->read() only when already STARTTLS happened
398 | if (ext_cmd.size() > 0) {
399 | cmd_handler(ext_cmd, fd2state, pfds);
400 | } else if (tbuf.size() > 0) {
401 |
402 | // Are we running in a bounce command session?
403 | if (bcmd_accept_fd >= 0) {
404 |
405 | // do not echo back bcmd inject that happens just after STATE_BCMD_CONNECT
406 | if (fd2state[bcmd_accept_fd].state == STATE_BCMD_CONNECT) {
407 | bcmd_buffered_output += tbuf;
408 |
409 | // If we see the desired string "GO" echoed back, strip off until newline and only forward real potential data that follows "GO\n".
410 | // Otherwise the echo was just partial and we need to wait until full echo
411 | string::size_type nl = string::npos;
412 | if ((nl = bcmd_buffered_output.find(BCMD_GO_CMD)) != string::npos) {
413 | fd2state[bcmd_accept_fd].obuf += bcmd_buffered_output.substr(nl + BCMD_GO_CMD.size());
414 | fd2state[bcmd_accept_fd].state = STATE_BCMD_CONNECTED;
415 | pfds[bcmd_accept_fd].events |= POLLIN;
416 | if (!fd2state[bcmd_accept_fd].obuf.empty())
417 | pfds[bcmd_accept_fd].events |= POLLOUT;
418 | bcmd_in_log_str += bcmd_buffered_output;
419 | bcmd_buffered_output.clear();
420 |
421 | sleep(3); // We need to sleep since between "echo GO" and "nc 127.0.0.1 22" commands on remote, we want nc to have the
422 | // stdin/stdout assigned and not dumping data to it before nc started
423 | }
424 | } else { // must be STATE_BCMD_CONNECTED, forward as is
425 | fd2state[bcmd_accept_fd].obuf += tbuf;
426 | pfds[bcmd_accept_fd].events |= POLLOUT;
427 |
428 | bcmd_in_log_str += tbuf;
429 |
430 | if (bcmd_in_log_str.size() >= 75) {
431 | fd2state[1].obuf += "\r\n< " + trim_bcmd_output(bcmd_in_log_str);
432 | bcmd_in_log_str.clear();
433 | pfds[1].events |= POLLOUT;
434 | }
435 | }
436 |
437 | // No -> only forward to stdout as is
438 | } else {
439 | fd2state[1].obuf += tbuf;
440 | pfds[1].events |= POLLOUT;
441 | }
442 |
443 | // mirror to script sock if opened
444 | if (script_fd >= 0) {
445 | fd2state[script_fd].time = now;
446 | fd2state[script_fd].obuf += tbuf;
447 | pfds[script_fd].events |= POLLOUT;
448 | }
449 | }
450 | } while (r == 1);
451 |
452 | } else if (fd2state[i].state == STATE_ACCEPT) {
453 | if ((afd = accept(i, nullptr, nullptr)) < 0)
454 | continue;
455 |
456 | maybe_set_rcvbuf(afd, config::rate_limit_bytes);
457 |
458 | // append ID part of host/port/id/ header. We use the accepted sock fd
459 | // as ID, as this is unique and identifies the TCP connection
460 | char id[16] = {0};
461 | snprintf(id, sizeof(id) - 1, "%04hx/", (uint16_t)afd);
462 |
463 | pfds[afd].fd = afd;
464 | pfds[afd].events = 0; // dont accept data until remote peer established proxy conn
465 |
466 | fd2state[afd].fd = afd;
467 | fd2state[afd].rnode = fd2state[i].rnode + id;
468 | fd2state[afd].state = STATE_CONNECT;
469 | fd2state[afd].time = now;
470 | fd2state[afd].obuf.clear();
471 |
472 | tcp_nodes2sock[fd2state[afd].rnode] = afd;
473 |
474 | pfds[pt.master()].events |= POLLOUT;
475 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:N:", fd2state[afd].rnode); // trigger tcp_connect() on remote side
476 |
477 | } else if (fd2state[i].state == STATE_BCMD_ACCEPT) {
478 | if ((afd = accept(i, nullptr, nullptr)) < 0)
479 | continue;
480 |
481 | // can only handle one bcmd bounce at once
482 | if (bcmd_accept_fd >= 0) {
483 | close(afd);
484 | continue;
485 | }
486 |
487 | maybe_set_rcvbuf(afd, config::rate_limit_bytes);
488 |
489 | pfds[afd].fd = afd;
490 | pfds[afd].events = 0; // dont accept data until bcmd has chance to connect
491 |
492 | fd2state[afd].fd = afd;
493 | fd2state[afd].rnode = fd2state[i].rnode;
494 | fd2state[afd].state = STATE_BCMD_CONNECT; // will change to STATE_BCMD_CONNECTED when reading echo back from PTY
495 | fd2state[afd].time = now;
496 | fd2state[afd].obuf.clear();
497 |
498 | pfds[pt.master()].events |= POLLOUT;
499 | fd2state[pt.master()].obuf += fd2state[i].rnode + "\n"; // trigger connect via command (e.g. `...nc example.com 22`) on remote side
500 |
501 | bcmd_accept_fd = afd;
502 | bcmd = fd2state[i].rnode;
503 |
504 | fd2state[1].obuf += "\r\n> " + bcmd;
505 | pfds[1].events |= POLLOUT;
506 | pfds[0].events = 0; // block stdin typing while bouncing
507 |
508 | } else if (fd2state[i].state == STATE_SOCKS5_ACCEPT) {
509 | if ((afd = accept(i, nullptr, nullptr)) < 0)
510 | continue;
511 |
512 | maybe_set_rcvbuf(afd, config::rate_limit_bytes);
513 |
514 | pfds[afd].fd = afd;
515 | pfds[afd].events = POLLIN; // wait for SOCKS5 proto requests
516 | fd2state[afd].fd = afd;
517 | fd2state[afd].rnode = ""; // filled in by later state
518 | fd2state[afd].state = STATE_SOCKS5_AUTH1;
519 | fd2state[afd].time = now;
520 | fd2state[afd].obuf.clear();
521 |
522 | } else if (fd2state[i].state == STATE_SOCKS4_ACCEPT) {
523 | if ((afd = accept(i, nullptr, nullptr)) < 0)
524 | continue;
525 |
526 | maybe_set_rcvbuf(afd, config::rate_limit_bytes);
527 |
528 | pfds[afd].fd = afd;
529 | pfds[afd].events = POLLIN; // wait for SOCKS4 proto requests
530 | fd2state[afd].fd = afd;
531 | fd2state[afd].rnode = ""; // filled in by later state
532 | fd2state[afd].state = STATE_SOCKS4_AUTH;
533 | fd2state[afd].time = now;
534 | fd2state[afd].obuf.clear();
535 |
536 | } else if (fd2state[i].state == STATE_CONNECTED) {
537 | if (config::rate_limit_bytes && fd2state[pt.master()].obuf.size() > MAX_RX_ON_LIMITS)
538 | continue;
539 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0) {
540 | close(i);
541 | pfds[i].fd = -1;
542 | pfds[i].events = 0;
543 | fd2state[i].state = STATE_INVALID;
544 | fd2state[i].fd = -1;
545 | fd2state[i].obuf.clear();
546 | tcp_nodes2sock.erase(fd2state[i].rnode);
547 |
548 | pfds[pt.master()].events |= POLLOUT;
549 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection via PTY to remote
550 | continue;
551 | }
552 | pfds[pt.master()].events |= POLLOUT;
553 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:S:", fd2state[i].rnode + string(sbuf, r));
554 | fd2state[i].time = now;
555 |
556 | } else if (fd2state[i].state == STATE_BCMD_CONNECTED) {
557 | if (config::rate_limit_bytes && fd2state[pt.master()].obuf.size() > MAX_RX_ON_LIMITS)
558 | continue;
559 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0) {
560 | close(i);
561 | pfds[i].fd = -1;
562 | pfds[i].events = 0;
563 | fd2state[i].state = STATE_INVALID;
564 | fd2state[i].fd = -1;
565 | fd2state[i].obuf.clear();
566 |
567 | pfds[0].events |= POLLIN; // reactivate stdin
568 | pfds[1].events |= POLLOUT;
569 | fd2state[1].obuf += "\r\n> Bounce cmd finished, type `reset`.\r\n";
570 | bcmd = "";
571 | bcmd_accept_fd = -1;
572 | continue;
573 | }
574 | pfds[pt.master()].events |= POLLOUT;
575 | fd2state[pt.master()].obuf += string(sbuf, r);
576 | fd2state[i].time = now;
577 |
578 | } else if (fd2state[i].state == STATE_SOCKS4_AUTH) {
579 |
580 | socks4_req *s4r = reinterpret_cast(sbuf);
581 |
582 | // expect SOCKS4 request and send positive response
583 | memset(sbuf, 0, sizeof(sbuf));
584 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0 || sbuf[0] != 4) {
585 | close(i);
586 | pfds[i].fd = -1;
587 | pfds[i].events = 0;
588 | fd2state[i].state = STATE_INVALID;
589 | fd2state[i].fd = -1;
590 | fd2state[i].obuf.clear();
591 | continue;
592 | }
593 |
594 | s4r->ver = 0;
595 | s4r->cmd = 0x5a; // request granted
596 | fd2state[i].obuf += string(sbuf, 8); // orig req w/o ID
597 |
598 | char dst[128] = {0};
599 | uint16_t rport = 0;
600 |
601 | inet_ntop(AF_INET, &s4r->dst, dst, sizeof(dst) - 1);
602 | rport = ntohs(s4r->dport);
603 |
604 | // Now that we know where connection is going to, we can build
605 | // IP/port/ID header
606 | char hdr[256] = {0};
607 | snprintf(hdr, sizeof(hdr) - 1, "%s/%04hx/%04hx/", dst, rport, (uint16_t)i);
608 |
609 | fd2state[i].rnode = hdr;
610 | fd2state[i].state = STATE_CONNECT;
611 | fd2state[i].time = now;
612 |
613 | tcp_nodes2sock[fd2state[i].rnode] = i;
614 |
615 | pfds[pt.master()].events |= POLLOUT;
616 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:N:", fd2state[i].rnode); // trigger tcp_connect() on remote side
617 |
618 | pfds[i].events = POLLOUT; // don't take data until remote site established connection, so *only* POLLOUT
619 |
620 | } else if (fd2state[i].state == STATE_SOCKS5_AUTH1) {
621 |
622 | // expect SOCKS5 auth request (none) and send positive response
623 | memset(sbuf, 0, sizeof(sbuf));
624 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) <= 0 || sbuf[0] != 5) {
625 | close(i);
626 | pfds[i].fd = -1;
627 | pfds[i].events = 0;
628 | fd2state[i].state = STATE_INVALID;
629 | fd2state[i].fd = -1;
630 | fd2state[i].obuf.clear();
631 | continue;
632 | }
633 | pfds[i].events |= POLLOUT;
634 | fd2state[i].state = STATE_SOCKS5_AUTH2;
635 | fd2state[i].obuf += string("\x05\x00", 2);
636 | fd2state[i].time = now;
637 | } else if (fd2state[i].state == STATE_SOCKS5_AUTH2) {
638 |
639 | memset(sbuf, 0, sizeof(sbuf));
640 | socks5_req *s5r = reinterpret_cast(sbuf);
641 |
642 | // expect SOCKS5 connect request
643 | if ((r = recv(i, sbuf, sizeof(sbuf), 0)) < 10 ||
644 | s5r->vers != 5 || // wrong version?
645 | (s5r->atype != 1 && s5r->atype != 3 && s5r->atype != 4) || // not or DNS name or IPv4 or IPv6?
646 | s5r->cmd != 1 || // not a TCP-connect?
647 | (s5r->atype == 3 && s5r->name.nlen > MAX_NAME_LEN) || // DNS name too long?
648 | (s5r->atype == 3 && !config::socks5_dns)) { // SOCKS5 resolving not enabled?
649 | s5r->cmd = 0x08; // atype not supported
650 | writen(i, sbuf, 2);
651 | close(i);
652 | pfds[i].fd = -1;
653 | pfds[i].events = 0;
654 | fd2state[i].state = STATE_INVALID;
655 | fd2state[i].fd = -1;
656 | fd2state[i].obuf.clear();
657 | continue;
658 | }
659 |
660 | char dst[128] = {0};
661 | uint16_t rport = 0;
662 |
663 | // IPv4
664 | if (s5r->atype == 1) {
665 | inet_ntop(AF_INET, &s5r->v4.dst, dst, sizeof(dst) - 1);
666 | rport = ntohs(s5r->v4.dport);
667 |
668 | // IPv6
669 | } else if (s5r->atype == 4) {
670 | inet_ntop(AF_INET6, &s5r->v6.dst, dst, sizeof(dst) - 1);
671 | rport = ntohs(s5r->v6.dport);
672 |
673 | // DNS name
674 | } else {
675 | memcpy(dst, s5r->name.name, s5r->name.nlen);
676 | uint16_t tmp;
677 | memcpy(&tmp, s5r->name.name + s5r->name.nlen, sizeof(tmp));
678 | rport = ntohs(tmp);
679 | }
680 |
681 | // Now that we know where connection is going to, we can build
682 | // IP/port/ID header
683 | char hdr[256] = {0};
684 | snprintf(hdr, sizeof(hdr) - 1, "%s/%04hx/%04hx/", dst, rport, (uint16_t)i);
685 |
686 | fd2state[i].rnode = hdr;
687 | fd2state[i].state = STATE_CONNECT;
688 | fd2state[i].time = now;
689 |
690 | tcp_nodes2sock[fd2state[i].rnode] = i;
691 |
692 | pfds[pt.master()].events |= POLLOUT;
693 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:N:", fd2state[i].rnode); // trigger tcp_connect() on remote side
694 |
695 | s5r->cmd = 0; // response status to socks5 client
696 | fd2state[i].obuf += string(sbuf, r);
697 |
698 | pfds[i].events = POLLOUT; // don't take data until remote site established connection, so *only* POLLOUT
699 |
700 | } else if (fd2state[i].state == STATE_UDPSERVER) {
701 |
702 | char sin[sizeof(sockaddr_in) + sizeof(sockaddr_in6)] = {0};
703 | socklen_t sinlen = sizeof(sin);
704 | if ((r = recvfrom(i, sbuf, sizeof(sbuf), 0, reinterpret_cast(&sin), &sinlen)) <= 0)
705 | continue;
706 |
707 | // in UDP case, we need to generate a unique ID based on dgram origin. If the origin was already
708 | // given an unique ID, put() will find and return it transparently
709 | char id[16] = {0};
710 | snprintf(id, sizeof(id) - 1, "%04hx/", udp_nodes2id.put(string(sin, sinlen)));
711 |
712 | // Note here that ID needs to be appended, unlike with TCP.
713 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:U:S:", fd2state[i].rnode + id + string(sbuf, r));
714 | pfds[pt.master()].events |= POLLOUT;
715 |
716 | fd2state[i].time = now;
717 |
718 | udp_nodes2sock[fd2state[i].rnode + id] = i;
719 |
720 | } else if (fd2state[i].state == STATE_SCRIPT_ACCEPT) {
721 |
722 | if ((script_fd = accept(i, nullptr, nullptr)) < 0)
723 | continue;
724 |
725 | pfds[script_fd].fd = script_fd;
726 |
727 | pfds[script_fd].events = POLLIN;
728 | pfds[i].events = 0; // block further connects to UNIX script socket
729 | pfds[0].events = 0; // block stdin typing during script processing
730 |
731 | fd2state[script_fd].fd = script_fd;
732 | fd2state[script_fd].rnode = "";
733 | fd2state[script_fd].state = STATE_SCRIPT_IO;
734 | fd2state[script_fd].time = now;
735 | fd2state[script_fd].obuf.clear();
736 |
737 | } else if (fd2state[i].state == STATE_SCRIPT_IO) { // very similar to STDIN read
738 |
739 | if ((r = read(i, sbuf, sizeof(sbuf))) <= 0) {
740 | if (errno == EINTR)
741 | continue;
742 | close(i);
743 | script_fd = -1;
744 | pfds[i].fd = -1;
745 | pfds[i].events = 0;
746 | fd2state[i].state = STATE_INVALID;
747 | fd2state[i].fd = -1;
748 | fd2state[i].obuf.clear();
749 | pfds[0].events |= POLLIN; // reactivate stdin
750 | pfds[config::script_sock].events |= POLLIN;
751 | continue;
752 | }
753 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("D:0:", string(sbuf, r));
754 | pfds[pt.master()].events |= POLLOUT;
755 | }
756 |
757 | } else if (pfds[i].revents & POLLOUT) {
758 | pfds[i].revents = 0;
759 | size_t n = fd2state[i].obuf.size() > CHUNK_SIZE ? CHUNK_SIZE : fd2state[i].obuf.size();
760 |
761 | if (fd2state[i].state == STATE_STDOUT) {
762 |
763 | if ((r = write(1, fd2state[i].obuf.c_str(), n)) <= 0) {
764 | if (errno == EINTR)
765 | continue;
766 | else
767 | die("pscl: write");
768 | }
769 |
770 | fd2state[i].obuf.erase(0, r);
771 | } else if (fd2state[i].state == STATE_PTY) {
772 |
773 | // We need to throttle amount of data/usec in cases where bounce command was
774 | // given since remote pty is in raw mode w/o flow control or we run across a serial
775 | // line that has a baud rate set.
776 | if (config::rate_limit_bytes) {
777 | n = 1;
778 |
779 | timeval now_tv;
780 | gettimeofday(&now_tv, nullptr);
781 |
782 | // In usec.
783 | uint64_t tdiff_usec = now_tv.tv_sec*1000000 + now_tv.tv_usec - (last_tv.tv_sec*1000000 + last_tv.tv_usec);
784 |
785 | // This formula only works for rates < 1.000.000 Byte/sec which is guaranteed by the baudrates that we accept.
786 | if (tdiff_usec < (1000000*1.0/config::rate_limit_bytes))
787 | continue;
788 | #if RESPECT_UART_BUFSIZE
789 | // Try to give UART buffers time to get flushed.
790 | if (++tx_rate_cnt >= RESPECT_UART_BUFSIZE) {
791 | if (tdiff_usec < 1000000)
792 | continue;
793 | else
794 | tx_rate_cnt = 0;
795 | }
796 | #endif
797 |
798 | last_tv.tv_sec = now_tv.tv_sec;
799 | last_tv.tv_usec = now_tv.tv_usec;
800 |
801 | // It fits into limit, go ahead with writing.
802 | }
803 |
804 | if ((r = write(psc->w_fileno(), fd2state[i].obuf.c_str(), n)) <= 0) {
805 | if (errno == EAGAIN || errno == EWOULDBLOCK)
806 | continue;
807 | else
808 | die(psc->why());
809 | }
810 |
811 |
812 | // If we are bouncing binary data to a remote cmd, make some nice local progress output.
813 | if (bcmd_accept_fd >= 0 && fd2state[bcmd_accept_fd].state == STATE_BCMD_CONNECTED) {
814 | bcmd_out_log_str += fd2state[i].obuf.substr(0, r);
815 |
816 | if (bcmd_out_log_str.size() >= 75) {
817 | fd2state[1].obuf += "\r\n> " + trim_bcmd_output(bcmd_out_log_str);
818 | bcmd_out_log_str.clear();
819 | pfds[1].events |= POLLOUT;
820 | }
821 | }
822 |
823 | fd2state[i].time = now;
824 | fd2state[i].obuf.erase(0, r);
825 | } else if (fd2state[i].state == STATE_CONNECT || // for the SOCKS4/5 case: reply with conn success
826 | fd2state[i].state == STATE_SOCKS5_AUTH2 || // for the SOCKS5 case: reply for auth success
827 | fd2state[i].state == STATE_CONNECTED) {
828 | if ((r = write(i, fd2state[i].obuf.c_str(), n)) <= 0) {
829 | close(i);
830 | pfds[i].fd = -1;
831 | pfds[i].events = 0;
832 | fd2state[i].state = STATE_INVALID;
833 | fd2state[i].fd = -1;
834 | fd2state[i].obuf.clear();
835 | tcp_nodes2sock.erase(fd2state[i].rnode);
836 |
837 | pfds[pt.master()].events |= POLLOUT;
838 | fd2state[pt.master()].obuf += psc->possibly_b64encrypt("C:T:F:", fd2state[i].rnode); // signal finished connection via PTY to remote
839 | continue;
840 | }
841 |
842 | fd2state[i].time = now;
843 | fd2state[i].obuf.erase(0, r);
844 | } else if (fd2state[i].state == STATE_BCMD_CONNECTED) {
845 | if ((r = write(i, fd2state[i].obuf.c_str(), n)) <= 0) {
846 | close(i);
847 | pfds[i].fd = -1;
848 | pfds[i].events = 0;
849 | fd2state[i].state = STATE_INVALID;
850 | fd2state[i].fd = -1;
851 | fd2state[i].obuf.clear();
852 |
853 | pfds[pt.master()].events |= POLLOUT;
854 | pfds[0].events |= POLLIN; // reactivate stdin
855 | continue;
856 | }
857 |
858 | fd2state[i].time = now;
859 | fd2state[i].obuf.erase(0, r);
860 | } else if (fd2state[i].state == STATE_UDPSERVER) {
861 | string &dgram = fd2state[i].odgrams.front().second;
862 | string sin = udp_nodes2id.get(fd2state[i].odgrams.front().first); // map id back to originating sockaddr
863 | if ((r = sendto(i, dgram.c_str(), dgram.size(), 0, reinterpret_cast(sin.c_str()), sin.size())) <= 0)
864 | continue;
865 |
866 | fd2state[i].odgrams.pop_front();
867 | fd2state[i].time = now;
868 |
869 | } else if (fd2state[i].state == STATE_SCRIPT_IO) {
870 |
871 | if ((r = write(i, fd2state[i].obuf.c_str(), n)) <= 0) {
872 | if (errno == EINTR)
873 | continue;
874 |
875 | close(i);
876 | script_fd = -1;
877 | pfds[i].fd = -1;
878 | pfds[i].events = 0;
879 | fd2state[i].state = STATE_INVALID;
880 | fd2state[i].fd = -1;
881 | fd2state[i].obuf.clear();
882 | pfds[0].events |= POLLIN; // reactivate stdin
883 | pfds[config::script_sock].events |= POLLIN;
884 | continue;
885 | }
886 |
887 | fd2state[i].time = now;
888 | fd2state[i].obuf.erase(0, r);
889 | }
890 |
891 | if (fd2state[i].obuf.empty() && fd2state[i].odgrams.empty())
892 | pfds[i].events &= ~POLLOUT;
893 | }
894 | }
895 | }
896 |
897 | return 0;
898 | }
899 |
900 |
901 | int main(int argc, char **argv)
902 | {
903 | printf("\nPortShellCrypter [pscl] v0.69 (C) 2006-2024 stealth -- github.com/stealth/psc\n\n");
904 |
905 | if (!getenv("SHELL")) {
906 | printf("pscl: No $SHELL set in environment. Exiting.\n");
907 | exit(1);
908 | }
909 |
910 | struct sigaction sa;
911 | memset(&sa, 0, sizeof(sa));
912 | sa.sa_handler = sig_chld;
913 | sa.sa_flags = SA_RESTART;
914 |
915 | if (sigaction(SIGCHLD, &sa, nullptr) < 0)
916 | die("pscl: sigaction");
917 |
918 | memset(&sa, 0, sizeof(sa));
919 | sa.sa_flags = SA_RESTART;
920 | sa.sa_handler = sig_usr1;
921 | if (sigaction(SIGUSR1, &sa, nullptr) < 0)
922 | die("pscl: sigaction");
923 |
924 | memset(&sa, 0, sizeof(sa));
925 | sa.sa_flags = SA_RESTART;
926 | sa.sa_handler = sig_usr2;
927 | if (sigaction(SIGUSR2, &sa, nullptr) < 0)
928 | die("pscl: sigaction");
929 |
930 | memset(&sa, 0, sizeof(sa));
931 | sa.sa_handler = sig_win;
932 | sa.sa_flags = SA_RESTART;
933 | if (sigaction(SIGWINCH, &sa, nullptr) < 0)
934 | die("pscl: sigaction");
935 |
936 | sa.sa_handler = SIG_IGN;
937 | if (sigaction(SIGINT, &sa, nullptr) < 0 || sigaction(SIGQUIT, &sa, nullptr) ||
938 | sigaction(SIGPIPE, &sa, nullptr))
939 | die("pscl: sigaction");
940 |
941 | int c = -1;
942 | char lport[16] = {0}, ip[128] = {0}, port_hex[16] = {0};
943 | char bounce_cmd[128] = {0};
944 | uint16_t rport = 0;
945 | string bauds = "";
946 | bool rate_limit_set = 0;
947 |
948 | while ((c = getopt(argc, argv, "T:U:X:5:4:S:B:l:hN")) != -1) {
949 | switch (c) {
950 | case 'N':
951 | config::socks5_dns = 1;
952 | break;
953 | case 'T':
954 | if (sscanf(optarg, "%15[0-9]:[%127[^]]]:%hu", lport, ip, &rport) == 3) {
955 | snprintf(port_hex, sizeof(port_hex), "%04hx", rport);
956 | config::tcp_listens[lport] = string(ip) + "/" + string(port_hex) + "/";
957 | printf("pscl: set up local TCP port %s to proxy to %s:%hu @ remote.\n", lport, ip, rport);
958 | }
959 | break;
960 | case 'U':
961 | if (sscanf(optarg, "%15[0-9]:[%127[^]]]:%hu", lport, ip, &rport) == 3) {
962 | snprintf(port_hex, sizeof(port_hex), "%04hx", rport);
963 | config::udp_listens[lport] = string(ip) + "/" + string(port_hex) + "/";
964 | printf("pscl: set up local UDP port %s to proxy to %s:%hu @ remote.\n", lport, ip, rport);
965 | }
966 | break;
967 | case 'X':
968 | config::local_proxy_ip = optarg;
969 | break;
970 | case '4':
971 | if (config::socks4_fd == -1) {
972 | config::socks4_port = strtoul(optarg, nullptr, 10);
973 | if ((config::socks4_fd = tcp_listen(config::local_proxy_ip, optarg)) > 0)
974 | printf("pscl: set up SOCKS4 port on %s\n", optarg);
975 | }
976 | break;
977 | case '5':
978 | if (config::socks5_fd == -1) {
979 | config::socks5_port = strtoul(optarg, nullptr, 10);
980 | if ((config::socks5_fd = tcp_listen(config::local_proxy_ip, optarg)) > 0)
981 | printf("pscl: set up SOCKS5 port on %s\n", optarg);
982 | }
983 | break;
984 | case 'S':
985 | if (config::script_sock == -1) {
986 | if ((config::script_sock = unix_listen(optarg)) > 0)
987 | printf("pscl: set up script socket on %s\n", optarg);;
988 | }
989 | break;
990 | case 'B':
991 | if (sscanf(optarg, "%15[0-9]:[%127[^]]]", lport, bounce_cmd) == 2) {
992 | config::bcmd_tcp_listens[lport] = bounce_cmd;
993 | printf("pscl: set up local TCP port %s to bounce via `%s` @ remote.\n", lport, bounce_cmd);
994 | }
995 | break;
996 | case 'l':
997 | if (config_set_baud_limit(optarg) < 0)
998 | printf("pscl: Invalid baud rate. Must be one of 576000, 230400, 115200, 57600,\n"
999 | "pscl: 38400, 9600 or 0.\n");
1000 | else
1001 | rate_limit_set = 1;
1002 | break;
1003 | case 'h':
1004 | default:
1005 | usage(argv[0]);
1006 | exit(0);
1007 | }
1008 | }
1009 |
1010 | // If using bounce cmds, set a default baud rate if none was given
1011 | if (config::bcmd_tcp_listens.size() > 0 && !rate_limit_set)
1012 | config::rate_limit_bytes = 115200/8;
1013 |
1014 | printf("\npscl: Waiting for [pscr] session to appear ...\n");
1015 |
1016 | proxy_loop();
1017 |
1018 | return 0;
1019 | }
1020 |
1021 |
--------------------------------------------------------------------------------
/src/external/aes.cc:
--------------------------------------------------------------------------------
1 | // Taken from https://github.com/kokke/tiny-AES-C which is under
2 | // public domain and heavily tailored to my needs
3 |
4 | #include
5 | #include "aes.h"
6 |
7 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4
8 | #define Nb 4
9 |
10 | #if defined(AES256) && (AES256 == 1)
11 | #define Nk 8
12 | #define Nr 14
13 | #elif defined(AES192) && (AES192 == 1)
14 | #define Nk 6
15 | #define Nr 12
16 | #else
17 | #define Nk 4 // The number of 32 bit words in a key.
18 | #define Nr 10 // The number of rounds in AES Cipher.
19 | #endif
20 |
21 |
22 | // state - array holding the intermediate results during decryption.
23 | typedef uint8_t state_t[4][4];
24 |
25 |
26 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
27 | // The numbers below can be computed dynamically trading ROM for RAM -
28 | // This can be useful in (embedded) bootloader applications, where ROM is often limited.
29 | static const uint8_t sbox[256] = {
30 | //0 1 2 3 4 5 6 7 8 9 A B C D E F
31 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
32 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
33 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
34 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
35 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
36 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
37 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
38 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
39 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
40 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
41 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
42 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
43 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
44 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
45 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
46 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
47 | };
48 |
49 | // The round constant word array, Rcon[i], contains the values given by
50 | // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
51 | static const uint8_t Rcon[11] = {
52 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
53 | };
54 |
55 | /*
56 | * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
57 | * that you can remove most of the elements in the Rcon array, because they are unused.
58 | *
59 | * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
60 | *
61 | * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed),
62 | * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
63 | */
64 |
65 | #define getSBoxValue(num) (sbox[(num)])
66 |
67 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
68 | static void
69 | KeyExpansion(uint8_t *RoundKey, const uint8_t *Key)
70 | {
71 | unsigned i, j, k;
72 | uint8_t tempa[4] = {0}; // Used for the column/row operations
73 |
74 | // The first round key is the key itself.
75 | for (i = 0; i < Nk; ++i) {
76 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
77 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
78 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
79 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
80 | }
81 |
82 | // All other round keys are found from the previous round keys.
83 | for (i = Nk; i < Nb * (Nr + 1); ++i) {
84 | {
85 | k = (i - 1) * 4;
86 | tempa[0] = RoundKey[k + 0];
87 | tempa[1] = RoundKey[k + 1];
88 | tempa[2] = RoundKey[k + 2];
89 | tempa[3] = RoundKey[k + 3];
90 |
91 | }
92 |
93 | if (i % Nk == 0) {
94 | // This function shifts the 4 bytes in a word to the left once.
95 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
96 |
97 | // Function RotWord()
98 | {
99 | const uint8_t u8tmp = tempa[0];
100 | tempa[0] = tempa[1];
101 | tempa[1] = tempa[2];
102 | tempa[2] = tempa[3];
103 | tempa[3] = u8tmp;
104 | }
105 |
106 | // SubWord() is a function that takes a four-byte input word and
107 | // applies the S-box to each of the four bytes to produce an output word.
108 |
109 | // Function Subword()
110 | {
111 | tempa[0] = getSBoxValue(tempa[0]);
112 | tempa[1] = getSBoxValue(tempa[1]);
113 | tempa[2] = getSBoxValue(tempa[2]);
114 | tempa[3] = getSBoxValue(tempa[3]);
115 | }
116 |
117 | tempa[0] = tempa[0] ^ Rcon[i / Nk];
118 | }
119 | #if defined(AES256) && (AES256 == 1)
120 | if (i % Nk == 4) {
121 | // Function Subword()
122 | {
123 | tempa[0] = getSBoxValue(tempa[0]);
124 | tempa[1] = getSBoxValue(tempa[1]);
125 | tempa[2] = getSBoxValue(tempa[2]);
126 | tempa[3] = getSBoxValue(tempa[3]);
127 | }
128 | }
129 | #endif
130 | j = i * 4;
131 | k = (i - Nk) * 4;
132 | RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
133 | RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
134 | RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
135 | RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
136 | }
137 | }
138 |
139 |
140 | void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key)
141 | {
142 | memset(ctx, 0, sizeof(*ctx));
143 | KeyExpansion(ctx->RoundKey, key);
144 | }
145 |
146 |
147 | void AES_init_ctx_iv(struct AES_ctx *ctx, const uint8_t *key, const uint8_t *iv)
148 | {
149 | memset(ctx, 0, sizeof(*ctx));
150 | KeyExpansion(ctx->RoundKey, key);
151 | memcpy(&ctx->iv, iv, AES_BLOCKLEN);
152 |
153 | //only if 32bit counter is used within IV
154 | //ctx->iv.ctr32 = 0;
155 | }
156 |
157 |
158 | void AES_ctx_set_iv(struct AES_ctx *ctx, const uint8_t *iv)
159 | {
160 | memcpy(&ctx->iv, iv, AES_BLOCKLEN);
161 |
162 | //only if 32bit counter is used within IV
163 | //ctx->iv.ctr32 = 0;
164 | }
165 |
166 |
167 | // This function adds the round key to state.
168 | // The round key is added to the state by an XOR function.
169 | static void AddRoundKey(uint8_t round, state_t *state, const uint8_t *RoundKey)
170 | {
171 | uint8_t i, j;
172 | for (i = 0; i < 4; ++i) {
173 | for (j = 0; j < 4; ++j) {
174 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
175 | }
176 | }
177 | }
178 |
179 | // The SubBytes Function Substitutes the values in the
180 | // state matrix with values in an S-box.
181 | static void SubBytes(state_t *state)
182 | {
183 | uint8_t i, j;
184 | for (i = 0; i < 4; ++i) {
185 | for (j = 0; j < 4; ++j) {
186 | (*state)[j][i] = getSBoxValue((*state)[j][i]);
187 | }
188 | }
189 | }
190 |
191 | // The ShiftRows() function shifts the rows in the state to the left.
192 | // Each row is shifted with different offset.
193 | // Offset = Row number. So the first row is not shifted.
194 | static void ShiftRows(state_t *state)
195 | {
196 | uint8_t temp;
197 |
198 | // Rotate first row 1 columns to left
199 | temp = (*state)[0][1];
200 | (*state)[0][1] = (*state)[1][1];
201 | (*state)[1][1] = (*state)[2][1];
202 | (*state)[2][1] = (*state)[3][1];
203 | (*state)[3][1] = temp;
204 |
205 | // Rotate second row 2 columns to left
206 | temp = (*state)[0][2];
207 | (*state)[0][2] = (*state)[2][2];
208 | (*state)[2][2] = temp;
209 |
210 | temp = (*state)[1][2];
211 | (*state)[1][2] = (*state)[3][2];
212 | (*state)[3][2] = temp;
213 |
214 | // Rotate third row 3 columns to left
215 | temp = (*state)[0][3];
216 | (*state)[0][3] = (*state)[3][3];
217 | (*state)[3][3] = (*state)[2][3];
218 | (*state)[2][3] = (*state)[1][3];
219 | (*state)[1][3] = temp;
220 | }
221 |
222 | static uint8_t xtime(uint8_t x)
223 | {
224 | return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
225 | }
226 |
227 | // MixColumns function mixes the columns of the state matrix
228 | static void MixColumns(state_t *state)
229 | {
230 | uint8_t i;
231 | uint8_t Tmp, Tm, t;
232 | for (i = 0; i < 4; ++i) {
233 | t = (*state)[i][0];
234 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^
235 | (*state)[i][3];
236 | Tm = (*state)[i][0] ^ (*state)[i][1];
237 | Tm = xtime(Tm);
238 | (*state)[i][0] ^= Tm ^ Tmp;
239 | Tm = (*state)[i][1] ^ (*state)[i][2];
240 | Tm = xtime(Tm);
241 | (*state)[i][1] ^= Tm ^ Tmp;
242 | Tm = (*state)[i][2] ^ (*state)[i][3];
243 | Tm = xtime(Tm);
244 | (*state)[i][2] ^= Tm ^ Tmp;
245 | Tm = (*state)[i][3] ^ t;
246 | Tm = xtime(Tm);
247 | (*state)[i][3] ^= Tm ^ Tmp;
248 | }
249 | }
250 |
251 | // Multiply is used to multiply numbers in the field GF(2^8)
252 | #define Multiply(x, y) \
253 | ( ((y & 1) * x) ^ \
254 | ((y>>1 & 1) * xtime(x)) ^ \
255 | ((y>>2 & 1) * xtime(xtime(x))) ^ \
256 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
257 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))
258 |
259 |
260 |
261 | // Cipher is the main function that encrypts the PlainText.
262 | static void Cipher(state_t *state, const uint8_t *RoundKey)
263 | {
264 | uint8_t round = 0;
265 |
266 | // Add the First round key to the state before starting the rounds.
267 | AddRoundKey(0, state, RoundKey);
268 |
269 | // There will be Nr rounds.
270 | // The first Nr-1 rounds are identical.
271 | // These Nr rounds are executed in the loop below.
272 | // Last one without MixColumns()
273 | for (round = 1;; ++round) {
274 | SubBytes(state);
275 | ShiftRows(state);
276 | if (round == Nr) {
277 | break;
278 | }
279 | MixColumns(state);
280 | AddRoundKey(round, state, RoundKey);
281 | }
282 | // Add round key to last round
283 | AddRoundKey(Nr, state, RoundKey);
284 | }
285 |
286 |
287 | // This function was taken from cifra public domain code.
288 | // Increments the integer stored at v (of non-zero length len)
289 | // with the most significant byte last.
290 | static inline void incr_be(uint8_t *v, size_t len)
291 | {
292 | len--;
293 | while (1) {
294 | if (++v[len] != 0)
295 | return;
296 | if (len == 0)
297 | return;
298 | len--;
299 | }
300 | }
301 |
302 |
303 | void AES_CTR_xcrypt(struct AES_ctx *ctx, const uint8_t *in, size_t inlen, uint8_t *out)
304 | {
305 | size_t i = 0;
306 |
307 | // remaining XOR from possibly hang-over from last call
308 | for (; ctx->xidx != 0 && i < inlen;) {
309 | out[i] = in[i] ^ ctx->xor_block[ctx->xidx];
310 | ctx->xidx = (ctx->xidx + 1) % AES_BLOCKLEN;
311 | ++i;
312 |
313 | // increase ctr for next block
314 | if (ctx->xidx == 0)
315 | incr_be(ctx->iv.iv, AES_BLOCKLEN);
316 | }
317 |
318 | for (; i < inlen;) {
319 |
320 | memcpy(ctx->xor_block, &ctx->iv, AES_BLOCKLEN);
321 | Cipher(reinterpret_cast(ctx->xor_block), ctx->RoundKey);
322 |
323 | for (;i < inlen;) {
324 | out[i] = in[i] ^ ctx->xor_block[ctx->xidx];
325 | ctx->xidx = (ctx->xidx + 1) % AES_BLOCKLEN;
326 | ++i;
327 |
328 | // increase ctr for next block
329 | if (ctx->xidx == 0) {
330 | incr_be(ctx->iv.iv, AES_BLOCKLEN);
331 | break;
332 | }
333 | }
334 | }
335 | }
336 |
337 |
--------------------------------------------------------------------------------
/src/external/aes.h:
--------------------------------------------------------------------------------
1 | #ifndef _AES_H_
2 | #define _AES_H_
3 |
4 | #include
5 |
6 | #define AES256 1
7 |
8 | #define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
9 |
10 | #if defined(AES256) && (AES256 == 1)
11 | #define AES_KEYLEN 32
12 | #define AES_keyExpSize 240
13 | #elif defined(AES192) && (AES192 == 1)
14 | #define AES_KEYLEN 24
15 | #define AES_keyExpSize 208
16 | #else
17 | #define AES_KEYLEN 16 // Key length in bytes
18 | #define AES_keyExpSize 176
19 | #endif
20 |
21 | struct AES_ctx {
22 | uint8_t RoundKey[AES_keyExpSize];
23 | struct {
24 | uint8_t iv[AES_BLOCKLEN];
25 | //uint32_t ctr32;
26 | } iv;
27 | uint8_t xor_block[AES_BLOCKLEN];
28 | uint8_t xidx;
29 | };
30 |
31 | void AES_init_ctx(struct AES_ctx *, const uint8_t *);
32 |
33 | void AES_init_ctx_iv(struct AES_ctx *, const uint8_t *, const uint8_t *);
34 |
35 | void AES_ctx_set_iv(struct AES_ctx *, const uint8_t *);
36 |
37 | void AES_CTR_xcrypt(struct AES_ctx *, const uint8_t *, size_t, uint8_t *);
38 |
39 |
40 | #endif // _AES_H_
41 |
--------------------------------------------------------------------------------
/src/external/sha512.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * BIRD Library -- SHA-512 and SHA-384 Hash Functions
3 | *
4 | * (c) 2015 CZ.NIC z.s.p.o.
5 | *
6 | * Based on the code from libgcrypt-1.6.0, which is
7 | * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
8 | *
9 | * Can be freely distributed and used under the terms of the GNU GPL.
10 | */
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include "sha512.h"
18 |
19 | static inline uint64_t
20 | get_u64(const uint8_t * p)
21 | {
22 | uint32_t xh, xl;
23 | memcpy(&xh, p, 4);
24 | memcpy(&xl, p + 4, 4);
25 | return (((uint64_t) ntohl(xh)) << 32) | ntohl(xl);
26 | }
27 |
28 | static inline void
29 | put_u64(uint8_t * p, uint64_t x)
30 | {
31 | uint32_t xh, xl;
32 | xh = htonl(x >> 32);
33 | xl = htonl((uint32_t) x);
34 | memcpy(p, &xh, 4);
35 | memcpy(p + 4, &xl, 4);
36 | }
37 |
38 |
39 | // #define SHA512_UNROLLED
40 |
41 | void
42 | sha512_init(struct sha512_context *ctx)
43 | {
44 | ctx->h0 = UINT64_C(0x6a09e667f3bcc908);
45 | ctx->h1 = UINT64_C(0xbb67ae8584caa73b);
46 | ctx->h2 = UINT64_C(0x3c6ef372fe94f82b);
47 | ctx->h3 = UINT64_C(0xa54ff53a5f1d36f1);
48 | ctx->h4 = UINT64_C(0x510e527fade682d1);
49 | ctx->h5 = UINT64_C(0x9b05688c2b3e6c1f);
50 | ctx->h6 = UINT64_C(0x1f83d9abfb41bd6b);
51 | ctx->h7 = UINT64_C(0x5be0cd19137e2179);
52 |
53 | ctx->nblocks = 0;
54 | ctx->count = 0;
55 | }
56 |
57 | static inline uint64_t
58 | ROTR(uint64_t x, uint64_t n)
59 | {
60 | return ((x >> n) | (x << (64 - n)));
61 | }
62 |
63 | static inline uint64_t
64 | Ch(uint64_t x, uint64_t y, uint64_t z)
65 | {
66 | return ((x & y) ^ (~x & z));
67 | }
68 |
69 | static inline uint64_t
70 | Maj(uint64_t x, uint64_t y, uint64_t z)
71 | {
72 | return ((x & y) ^ (x & z) ^ (y & z));
73 | }
74 |
75 | static inline uint64_t
76 | sum0(uint64_t x)
77 | {
78 | return (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39));
79 | }
80 |
81 | static inline uint64_t
82 | sum1(uint64_t x)
83 | {
84 | return (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41));
85 | }
86 |
87 | static const uint64_t k[] = {
88 | UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd),
89 | UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc),
90 | UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
91 | UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118),
92 | UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe),
93 | UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
94 | UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1),
95 | UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694),
96 | UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
97 | UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65),
98 | UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483),
99 | UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
100 | UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210),
101 | UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4),
102 | UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
103 | UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70),
104 | UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926),
105 | UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
106 | UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8),
107 | UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b),
108 | UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
109 | UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30),
110 | UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910),
111 | UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
112 | UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53),
113 | UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8),
114 | UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
115 | UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3),
116 | UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60),
117 | UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
118 | UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9),
119 | UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b),
120 | UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
121 | UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178),
122 | UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6),
123 | UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
124 | UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493),
125 | UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c),
126 | UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
127 | UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817)
128 | };
129 |
130 | /*
131 | * Transform the message W which consists of 16 64-bit-words
132 | */
133 | static uint32_t
134 | sha512_transform(struct sha512_context *ctx, const uint8_t * data)
135 | {
136 | uint64_t a, b, c, d, e, f, g, h;
137 | uint64_t w[16];
138 | uint32_t t;
139 |
140 | /* get values from the chaining vars */
141 | a = ctx->h0;
142 | b = ctx->h1;
143 | c = ctx->h2;
144 | d = ctx->h3;
145 | e = ctx->h4;
146 | f = ctx->h5;
147 | g = ctx->h6;
148 | h = ctx->h7;
149 |
150 | for (t = 0; t < 16; t++)
151 | w[t] = get_u64(data + t * 8);
152 |
153 | #define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7))
154 | #define S1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
155 |
156 | for (t = 0; t < 80 - 16;) {
157 | uint64_t t1, t2;
158 |
159 | /* Performance on a AMD Athlon(tm) Dual Core Processor 4050e
160 | with gcc 4.3.3 using gcry_md_hash_buffer of each 10000 uint8_ts
161 | initialized to 0,1,2,3...255,0,... and 1000 iterations:
162 |
163 | Not unrolled with macros: 440ms
164 | Unrolled with macros: 350ms
165 | Unrolled with inline: 330ms
166 | */
167 | #ifndef SHA512_UNROLLED
168 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t % 16];
169 | w[t % 16] +=
170 | S1(w[(t - 2) % 16]) + w[(t - 7) % 16] +
171 | S0(w[(t - 15) % 16]);
172 | t2 = sum0(a) + Maj(a, b, c);
173 | h = g;
174 | g = f;
175 | f = e;
176 | e = d + t1;
177 | d = c;
178 | c = b;
179 | b = a;
180 | a = t1 + t2;
181 | t++;
182 | #else /* Unrolled */
183 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0];
184 | w[0] += S1(w[14]) + w[9] + S0(w[1]);
185 | t2 = sum0(a) + Maj(a, b, c);
186 | d += t1;
187 | h = t1 + t2;
188 |
189 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 1] + w[1];
190 | w[1] += S1(w[15]) + w[10] + S0(w[2]);
191 | t2 = sum0(h) + Maj(h, a, b);
192 | c += t1;
193 | g = t1 + t2;
194 |
195 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 2] + w[2];
196 | w[2] += S1(w[0]) + w[11] + S0(w[3]);
197 | t2 = sum0(g) + Maj(g, h, a);
198 | b += t1;
199 | f = t1 + t2;
200 |
201 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 3] + w[3];
202 | w[3] += S1(w[1]) + w[12] + S0(w[4]);
203 | t2 = sum0(f) + Maj(f, g, h);
204 | a += t1;
205 | e = t1 + t2;
206 |
207 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 4] + w[4];
208 | w[4] += S1(w[2]) + w[13] + S0(w[5]);
209 | t2 = sum0(e) + Maj(e, f, g);
210 | h += t1;
211 | d = t1 + t2;
212 |
213 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 5] + w[5];
214 | w[5] += S1(w[3]) + w[14] + S0(w[6]);
215 | t2 = sum0(d) + Maj(d, e, f);
216 | g += t1;
217 | c = t1 + t2;
218 |
219 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 6] + w[6];
220 | w[6] += S1(w[4]) + w[15] + S0(w[7]);
221 | t2 = sum0(c) + Maj(c, d, e);
222 | f += t1;
223 | b = t1 + t2;
224 |
225 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 7] + w[7];
226 | w[7] += S1(w[5]) + w[0] + S0(w[8]);
227 | t2 = sum0(b) + Maj(b, c, d);
228 | e += t1;
229 | a = t1 + t2;
230 |
231 | t1 = h + sum1(e) + Ch(e, f, g) + k[t + 8] + w[8];
232 | w[8] += S1(w[6]) + w[1] + S0(w[9]);
233 | t2 = sum0(a) + Maj(a, b, c);
234 | d += t1;
235 | h = t1 + t2;
236 |
237 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 9] + w[9];
238 | w[9] += S1(w[7]) + w[2] + S0(w[10]);
239 | t2 = sum0(h) + Maj(h, a, b);
240 | c += t1;
241 | g = t1 + t2;
242 |
243 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 10] + w[10];
244 | w[10] += S1(w[8]) + w[3] + S0(w[11]);
245 | t2 = sum0(g) + Maj(g, h, a);
246 | b += t1;
247 | f = t1 + t2;
248 |
249 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 11] + w[11];
250 | w[11] += S1(w[9]) + w[4] + S0(w[12]);
251 | t2 = sum0(f) + Maj(f, g, h);
252 | a += t1;
253 | e = t1 + t2;
254 |
255 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 12] + w[12];
256 | w[12] += S1(w[10]) + w[5] + S0(w[13]);
257 | t2 = sum0(e) + Maj(e, f, g);
258 | h += t1;
259 | d = t1 + t2;
260 |
261 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 13] + w[13];
262 | w[13] += S1(w[11]) + w[6] + S0(w[14]);
263 | t2 = sum0(d) + Maj(d, e, f);
264 | g += t1;
265 | c = t1 + t2;
266 |
267 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 14] + w[14];
268 | w[14] += S1(w[12]) + w[7] + S0(w[15]);
269 | t2 = sum0(c) + Maj(c, d, e);
270 | f += t1;
271 | b = t1 + t2;
272 |
273 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 15] + w[15];
274 | w[15] += S1(w[13]) + w[8] + S0(w[0]);
275 | t2 = sum0(b) + Maj(b, c, d);
276 | e += t1;
277 | a = t1 + t2;
278 |
279 | t += 16;
280 | #endif
281 | }
282 |
283 | for (; t < 80;) {
284 | uint64_t t1, t2;
285 |
286 | #ifndef SHA512_UNROLLED
287 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t % 16];
288 | t2 = sum0(a) + Maj(a, b, c);
289 | h = g;
290 | g = f;
291 | f = e;
292 | e = d + t1;
293 | d = c;
294 | c = b;
295 | b = a;
296 | a = t1 + t2;
297 | t++;
298 | #else /* Unrolled */
299 | t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0];
300 | t2 = sum0(a) + Maj(a, b, c);
301 | d += t1;
302 | h = t1 + t2;
303 |
304 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 1] + w[1];
305 | t2 = sum0(h) + Maj(h, a, b);
306 | c += t1;
307 | g = t1 + t2;
308 |
309 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 2] + w[2];
310 | t2 = sum0(g) + Maj(g, h, a);
311 | b += t1;
312 | f = t1 + t2;
313 |
314 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 3] + w[3];
315 | t2 = sum0(f) + Maj(f, g, h);
316 | a += t1;
317 | e = t1 + t2;
318 |
319 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 4] + w[4];
320 | t2 = sum0(e) + Maj(e, f, g);
321 | h += t1;
322 | d = t1 + t2;
323 |
324 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 5] + w[5];
325 | t2 = sum0(d) + Maj(d, e, f);
326 | g += t1;
327 | c = t1 + t2;
328 |
329 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 6] + w[6];
330 | t2 = sum0(c) + Maj(c, d, e);
331 | f += t1;
332 | b = t1 + t2;
333 |
334 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 7] + w[7];
335 | t2 = sum0(b) + Maj(b, c, d);
336 | e += t1;
337 | a = t1 + t2;
338 |
339 | t1 = h + sum1(e) + Ch(e, f, g) + k[t + 8] + w[8];
340 | t2 = sum0(a) + Maj(a, b, c);
341 | d += t1;
342 | h = t1 + t2;
343 |
344 | t1 = g + sum1(d) + Ch(d, e, f) + k[t + 9] + w[9];
345 | t2 = sum0(h) + Maj(h, a, b);
346 | c += t1;
347 | g = t1 + t2;
348 |
349 | t1 = f + sum1(c) + Ch(c, d, e) + k[t + 10] + w[10];
350 | t2 = sum0(g) + Maj(g, h, a);
351 | b += t1;
352 | f = t1 + t2;
353 |
354 | t1 = e + sum1(b) + Ch(b, c, d) + k[t + 11] + w[11];
355 | t2 = sum0(f) + Maj(f, g, h);
356 | a += t1;
357 | e = t1 + t2;
358 |
359 | t1 = d + sum1(a) + Ch(a, b, c) + k[t + 12] + w[12];
360 | t2 = sum0(e) + Maj(e, f, g);
361 | h += t1;
362 | d = t1 + t2;
363 |
364 | t1 = c + sum1(h) + Ch(h, a, b) + k[t + 13] + w[13];
365 | t2 = sum0(d) + Maj(d, e, f);
366 | g += t1;
367 | c = t1 + t2;
368 |
369 | t1 = b + sum1(g) + Ch(g, h, a) + k[t + 14] + w[14];
370 | t2 = sum0(c) + Maj(c, d, e);
371 | f += t1;
372 | b = t1 + t2;
373 |
374 | t1 = a + sum1(f) + Ch(f, g, h) + k[t + 15] + w[15];
375 | t2 = sum0(b) + Maj(b, c, d);
376 | e += t1;
377 | a = t1 + t2;
378 |
379 | t += 16;
380 | #endif
381 | }
382 |
383 | /* Update chaining vars. */
384 | ctx->h0 += a;
385 | ctx->h1 += b;
386 | ctx->h2 += c;
387 | ctx->h3 += d;
388 | ctx->h4 += e;
389 | ctx->h5 += f;
390 | ctx->h6 += g;
391 | ctx->h7 += h;
392 |
393 | return /* burn_stack */ (8 + 16) * sizeof(uint64_t) + sizeof(uint32_t) +
394 | 3 * sizeof(void *);
395 | }
396 |
397 | void
398 | sha512_update(struct sha512_context *ctx, const uint8_t * buf, uint32_t len)
399 | {
400 |
401 | if (ctx->count) {
402 | /* Fill rest of internal buffer */
403 | for (; len && ctx->count < SHA512_BLOCK_SIZE; len--)
404 | ctx->buf[ctx->count++] = *buf++;
405 |
406 | if (ctx->count < SHA512_BLOCK_SIZE)
407 | return;
408 |
409 | /* Process data from internal buffer */
410 | sha512_transform(ctx, ctx->buf);
411 | ctx->nblocks++;
412 | ctx->count = 0;
413 | }
414 |
415 | if (!len)
416 | return;
417 |
418 | /* Process data from input buffer */
419 | while (len >= SHA512_BLOCK_SIZE) {
420 | sha512_transform(ctx, buf);
421 | ctx->nblocks++;
422 | buf += SHA512_BLOCK_SIZE;
423 | len -= SHA512_BLOCK_SIZE;
424 | }
425 |
426 | /* Copy remaining data to internal buffer */
427 | memcpy(ctx->buf, buf, len);
428 | ctx->count = len;
429 | }
430 |
431 | /*
432 | * The routine final terminates the computation and returns the digest. The
433 | * handle is prepared for a new cycle, but adding uint8_ts to the handle will the
434 | * destroy the returned buffer.
435 | *
436 | * Returns: 64 uint8_ts representing the digest. When used for sha384, we take the
437 | * first 48 of those uint8_ts.
438 | */
439 | uint8_t *
440 | sha512_final(struct sha512_context *ctx)
441 | {
442 | uint64_t t, th, msb, lsb;
443 |
444 | sha512_update(ctx, nullptr, 0); /* flush */
445 |
446 | t = ctx->nblocks;
447 | th = 0;
448 |
449 | /* multiply by 128 to make a uint8_t count */
450 | lsb = t << 7;
451 | msb = (th << 7) | (t >> 57);
452 | /* add the count */
453 | t = lsb;
454 | if ((lsb += ctx->count) < t)
455 | msb++;
456 | /* multiply by 8 to make a bit count */
457 | t = lsb;
458 | lsb <<= 3;
459 | msb <<= 3;
460 | msb |= t >> 61;
461 |
462 | if (ctx->count < 112) {
463 | /* enough room */
464 | ctx->buf[ctx->count++] = 0x80; /* pad */
465 | while (ctx->count < 112)
466 | ctx->buf[ctx->count++] = 0; /* pad */
467 | } else {
468 | /* need one extra block */
469 | ctx->buf[ctx->count++] = 0x80; /* pad character */
470 | while (ctx->count < 128)
471 | ctx->buf[ctx->count++] = 0;
472 | sha512_update(ctx, nullptr, 0); /* flush */
473 | memset(ctx->buf, 0, 112); /* fill next block with zeroes */
474 | }
475 |
476 | /* append the 128 bit count */
477 | put_u64(ctx->buf + 112, msb);
478 | put_u64(ctx->buf + 120, lsb);
479 | sha512_transform(ctx, ctx->buf);
480 |
481 | uint8_t *p = ctx->buf;
482 | #define X(a) do { put_u64(p, ctx->h##a); p += 8; } while(0)
483 | X(0);
484 | X(1);
485 | X(2);
486 | X(3);
487 | X(4);
488 | X(5);
489 | X(6);
490 | X(7);
491 | #undef X
492 |
493 | return ctx->buf;
494 | }
495 |
--------------------------------------------------------------------------------
/src/external/sha512.h:
--------------------------------------------------------------------------------
1 | /*
2 | * BIRD Library -- SHA-512 and SHA-384 Hash Functions
3 | *
4 | * (c) 2015 CZ.NIC z.s.p.o.
5 | *
6 | * Based on the code from libgcrypt-1.6.0, which is
7 | * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc.
8 | *
9 | * Can be freely distributed and used under the terms of the GNU GPL.
10 | */
11 |
12 | #ifndef _BIRD_SHA512_H_
13 | #define _BIRD_SHA512_H_
14 |
15 | #define SHA512_SIZE 64
16 | #define SHA512_HEX_SIZE 129
17 | #define SHA512_BLOCK_SIZE 128
18 |
19 | #include
20 |
21 |
22 | struct sha512_context {
23 | uint64_t h0, h1, h2, h3, h4, h5, h6, h7;
24 | uint8_t buf[SHA512_BLOCK_SIZE];
25 | uint32_t nblocks;
26 | uint32_t count;
27 | };
28 |
29 |
30 | void sha512_init(struct sha512_context *);
31 |
32 | void sha512_update(struct sha512_context *, const uint8_t *, uint32_t);
33 |
34 | uint8_t *sha512_final(struct sha512_context *);
35 |
36 |
37 | #endif /* _BIRD_SHA512_H_ */
38 |
--------------------------------------------------------------------------------
/src/misc.cc:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of port shell crypter (psc).
3 | *
4 | * (C) 2006-2022 by Sebastian Krahmer,
5 | * sebastian [dot] krahmer [at] gmail [dot] com
6 | *
7 | * psc is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * psc is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with psc. If not, see .
19 | */
20 |
21 |
22 | #include
23 | #include
24 | #include
25 | #include