Commit Graph
11 Commits
Author SHA1 Message Date
Andy Dixon 9805681c2c license: release Nexus Terminal under the GNU AGPL v3
- LICENSE: the GNU Affero General Public License, version 3 (verbatim
  from gnu.org).
- SPDX-License-Identifier: AGPL-3.0-only plus a copyright line on every
  first-party source file (Rust, shell, Python, PHP); vendored crates keep
  their own licenses.
- Cargo manifests carry license/authors/repository; the macOS bundle
  copyright and the About page state the license and link the source.
- Release zips (and the macOS app bundle) include LICENSE.
- README: License section.
2026-10-13 09:46:31 +01:00
Andy Dixon 76bd664ed1 fix: clip sync timestamps, exclusive temp files, zeroized secret reads
- sync: timestamps and tombstones in the shared file are clipped to the
  local clock, and an edit wins a tie with a deletion, so a forged
  future-dated record can no longer override every later local change
  (or keep deleting a re-created profile) for as long as the forged date.
- write_private: the temp file is created O_EXCL under a random name, so a
  symlink pre-placed in a shared folder can't be followed or overwritten.
- Vault/keychain reads return Zeroizing<String>; has_sync_password is
  cached for a few seconds instead of fetching the real value every frame.
- generate_ssh_key creates a missing ~/.ssh as 0700.
- Honest wording: encrypted exports and the sync file carry login-script
  values; the audit entry, the Settings hint and the docs now say so.
2026-10-13 00:36:06 +01:00
Andy Dixon 32890c6415 feat(irc): SASL PLAIN login and IRCv3 server-time
Registration now opens with CAP LS 302 (servers without IRCv3 ignore it
and register as before). From the offered capabilities (multi-line LS
lists included) the client requests server-time and, when the profile
enables it, sasl; then AUTHENTICATE PLAIN with the account (defaulting
to the nick) and a stored SASL password (irc-sasl-password), base64 in
400-byte chunks, and CAP END on success or failure — failures are shown
on the Server tab and the connection carries on (NickServ still works).
server-time tags stamp events with the server's clock, so replayed
history shows real times.

Tested against the fake IRC server: multi-line LS, REQ/ACK, the PLAIN
exchange, 903, CAP END, and a tagged message's timestamp.
2026-10-12 19:11:16 +01:00
Andy Dixon 0fc9451583 feat: SSH certificates and known_hosts import/export
- Host certificates: certificate authorities are trusted per OpenSSH host
  pattern (stored as @cert-authority known-hosts rows, matched against
  'host' or '[host]:port' as OpenSSH does, with *, ? and !). For a host a
  CA covers, the client asks for a certificate; one signed by a matching
  CA, of host type, naming the host and in date is trusted without a
  first-use prompt, and anything else is refused. Hosts no CA covers keep
  the existing key checks. Applies to jump hosts and tunnels too.
- User certificates: <key>-cert.pub next to the configured private key is
  used for publickey login (falling back to the plain key).
- Settings → Known Hosts: import ~/.ssh/known_hosts (hashed names and
  @revoked lines skipped and counted), export in OpenSSH format, and a
  Trust a CA form.

Verified against OpenSSH sshd with ssh-keygen-issued certificates: host
cert accepted without prompting and user-cert login with only
TrustedUserCAKeys; a CA for other hosts falls back to the key check; a
different CA is refused.
2026-10-12 19:02:59 +01:00
Andy Dixon 455d8fa6da feat(rdp): Kerberos logon through a configured KDC
An RDP profile can name a Kerberos KDC; CredSSP then authenticates with
Kerberos instead of NTLM, reaching the KDC through a small TCP client
(RFC 4120 length-prefixed framing, 5 s connect / 10 s read timeouts,
replies capped at 1 MiB so a hostile KDC can't force a huge allocation).
UDP KDCs are refused with a clear message. Tested against a fake KDC
(round trip, oversized reply, UDP); not against a real realm.

Smart-card logon is not implemented: it needs PC/SC hardware and a card
to build and test against.
2026-10-12 18:30:30 +01:00
Andy Dixon ae2b07db4b feat(rdp): RD Gateway transport (experimental)
RDP profiles can go through a Remote Desktop Gateway (MS-TSGU over an
HTTPS WebSocket) with its own username and password, stored like other
credentials (rdp-gateway-password). The gateway stream is bridged to a
loopback socket so the blocking RDP client uses it like any transport.

IronRDP's gateway crate (0.0.1) is vendored with fixes: it used
ironrdp-tls, which accepts any certificate, before sending Basic
credentials — the gateway is now verified against the web PKI; its
asserts on gateway-supplied lengths are errors instead of panics; and
the server port is passed through. Checked against a self-signed
openssl s_server: the connection is refused ('invalid peer certificate')
before any request or credential is sent. Not tested against a real
gateway; Basic-auth gateways only (no NTLM).
2026-10-12 18:26:45 +01:00
Andy Dixon 2fa56e0723 feat(rdp): printer redirection, saved as PostScript
An optional 'Nexus Terminal' printer (IronRDP's PostScript driver
announce) appears on the remote desktop; each job is written to
'Nexus print <time>.ps' in Downloads, with a toast saying where. Jobs
are capped at 256 MiB and empty jobs leave no file. The shared drive and
the printer now share one RDPDR backend (rdp_devices); the printer
works on every platform, the drive on Linux and macOS.

Tested with a unit test driving create/write/close; xrdp has no printer
redirection, so not exercised against a server.
2026-10-12 18:14:31 +01:00
Andy Dixon b798422735 feat(rdp): remote audio and a shared folder redirected as a drive
- Audio: the RDPSND channel with IronRDP's cpal playback backend (PCM;
  Opus left out to avoid another native library). On by default for new
  RDP profiles. Building now needs the ALSA headers on Linux
  (libasound2-dev), added to CI, release and fuzz jobs and the build docs.
- Shared folder (Linux/macOS): one local folder appears on the remote
  desktop as a drive. IronRDP's native backend joins server paths onto
  the share unchecked, so it is wrapped in ConfinedDrive: create, list
  and rename paths with '..' components, NUL bytes, or that resolve
  through a symlink outside the share are refused (ACCESS_DENIED);
  tests cover the escapes.

Verified against xrdp (with FUSE): the remote read the shared file, and
writes, mkdir and rename landed in the local folder. Not verified:
audio playback (the test container has no sound module), and xrdp lists
the drive as 'ignored' and can't list its directories (an IronRDP/xrdp
naming and listing mismatch, fixed upstream for the name).
2026-10-12 18:07:49 +01:00
Andy Dixon 6476ccff28 feat(rdp): text clipboard sharing in both directions
A CLIPRDR channel with a text-only backend (CF_UNICODETEXT): text copied
on the remote desktop is fetched and goes through the app's remote-
clipboard preference like VNC and OSC 52; local text is offered to the
remote when the desktop gains focus, on Ctrl+V and with Paste Clipboard
(which no longer types the text). Backend callbacks run inside the
active stage, so they queue requests that the session loop sends.

Verified against xrdp: the remote X clipboard read back the offered
text, and a remote copy arrived through on_clipboard.
2026-10-12 17:47:22 +01:00
Andy Dixon fda804e3f5 fix: wipe credentials in the connect path and keep release builds out of dumps and debuggers
- Credential callbacks (SecretFn) now return Zeroizing<String>, as do the
  session-only/pending caches they read and the RDP client's password,
  so Nexus's own copies are wiped; a plain String copy is made only
  where a library API needs one (SSH password auth, IRC, VNC, CredSSP).
- Release builds disable core dumps (RLIMIT_CORE 0) and, on Linux, mark
  the process non-dumpable (PR_SET_DUMPABLE 0), which also blocks ptrace
  attach and /proc/<pid>/mem reads by other processes of the same user.
  Debug builds are unchanged so they stay debuggable. Verified on the
  release binary: core limit 0, /proc/<pid>/{environ,mem} root-owned and
  unreadable, local shells (including cwd capture) still work.

Closes the remaining parts of SEC-20261012-007 that the app controls;
egui's internal text buffers remain outside its reach.
2026-10-12 17:19:27 +01:00
Andy Dixon b149fb3b38 refactor: split the session manager into modules
sessions.rs (1 730 lines) becomes sessions/ with mod.rs (types, session
lifecycle, I/O, recording, tests), credentials.rs (secret resolution,
prompts, host-key checks), protocols.rs (SSH, GitHub, IRC, VNC, RDP
connects and SSH tunnels), local.rs (local shells, login scripts) and
extras.rs (per-protocol accessors). Code moves unchanged; helpers that
crossed a module boundary became pub(super).
2026-10-12 16:57:56 +01:00