Compare commits

...
33 Commits
Author SHA1 Message Date
RandyTheSilly 1f5a2c8f3c Update documentation for release v1.5.1
Former-commit-id: d9721cfcc36cc53f2719f0c57c3c5c58578e3eb1
Former-commit-id: 9e3bba69e4d4e8d074b3882b56f2221aa7edd6ad
2023-10-23 23:19:03 -04:00
RandyTheSilly 386044d081 Fix colons and backslashes in gpg key names being interpreted as their hexadecimal equivalents
Former-commit-id: 98a19896d872423ff771e4b7bb5014397b63e889
Former-commit-id: 940178d578742aa457b985df4d7b231324aba0d9
2023-10-23 22:32:56 -04:00
RandyTheSilly c651c41a41 Disable ConfigParser interpolation to allow for "%" to be stored in configuration files
Former-commit-id: 4ac84f3ceef3407bd9e8e2c2f2191f7ea2ee2a55
Former-commit-id: 2fb4bf9d2630520702e99fa0913a3db5e1a0d0bb
2023-10-23 21:08:01 -04:00
RandyTheSilly 7cb245edde Fix CLIPCLEAR port-job for generic Linux (Wayland/X11)
Former-commit-id: 3690a41cbfce5eff13388fa7c9fe9f39d6e36865
Former-commit-id: a9a991dc141d1eb7d7973f8ecb08188dda27953b
2023-10-23 20:37:41 -04:00
RandyTheSilly edf31f9a7c Add port-job for making clipclear.py platform-specific
Former-commit-id: 6aafd4dfcddefc31bf61d3018b63ef48c156d8a8
Former-commit-id: c016e6872d71bc4bb347d85cf964dd9e7b7fe149
2023-10-23 13:51:11 -04:00
RandyTheSilly 362b1aa025 Remove unused variable from RMEXTMAN.py
Former-commit-id: ea907d093c1ffdccffc9db55f573d08650cfb7e2
Former-commit-id: e2178912cec4b4327c576af625fe8d1e6e498665
2023-10-23 13:05:46 -04:00
RandyTheSilly 5633520d82 Fix clipboard clearing on non-Wayland platforms; drop dos2unix as a WSL dependency; suggest openssh-server on Debian and Fedora
Former-commit-id: 046786460edc669fa9b962314953164c1cce078e
Former-commit-id: d9f0eeb57d73a0255c9dc713533d08d30413bf90
2023-10-23 10:35:59 -04:00
RandyTheSilly a487bd561a Fix clipboard contents sometimes failing to clear
Former-commit-id: 8ecbbb6199864fda973e47605a10d737318f3993
Former-commit-id: eb625eded3f9518967ee9fa06201311e8571e394
2023-10-20 00:32:29 -04:00
RandyTheSilly cbc0538e82 Call printf with %b to avoid interpreting directives
Former-commit-id: f1e812634d538b8e8c412b0c3aa1c43aab1c4f1b
Former-commit-id: f95072b8742e752be18dc3fe9cba17c8e82eb41b
2023-10-19 20:52:35 -04:00
RandyTheSilly 5c589de4f2 Switch to v1.5.1 extension pointer
Former-commit-id: 3997d7c4f7a05a8992b93a9b8d73c3d44e4c6026
Former-commit-id: 2a56f062345d177023c9a6f1f5ed5415450a9155
2023-10-19 18:47:06 -04:00
RandyTheSilly 44392467e5 Require identity to be passed to whitelist_verify() (for extension support with custom SSH keys)
Former-commit-id: 9db42722585f1fb707d5144e828cefddfcbf5137
Former-commit-id: 1e79c49459b49eb422788eba315a6d9c6e09f9a1
2023-10-18 15:14:20 -04:00
RandyTheSilly 18aacf648b Add pop-up explaining optional configuration menu
Former-commit-id: ed0d42781420080646ec6051e9f9c56ba8eaba99
Former-commit-id: 48b7d5de29ee9ad88b0aec222954769473ddda8a
2023-10-17 00:52:54 -04:00
RandyTheSilly ac621a8a73 Add "TROUBLESHOOTING" section to man page
Former-commit-id: 352412410256035906c586975b9cc5460f6b19be
Former-commit-id: 2d1092037448f1d2aaf9c137b751efcbe984995e
2023-10-17 00:40:06 -04:00
RandyTheSilly 98f76ac1ab Exclude "known_hosts.old" in ssh key listing
Former-commit-id: fc088270e9865b35ae4b10f020fec916e5093a8a
Former-commit-id: 8988481a7d3f37636dfa534312890760cd24ca81
2023-10-16 23:51:20 -04:00
RandyTheSilly bdff85f024 Fix copy_id_check resulting in errors when called from another module
Former-commit-id: 31bb5415499ca5f06413eba61eaafd284d2d5da1
Former-commit-id: 9cd0e6852e09874a1a06c8cde22e539f1cb58345
2023-10-16 23:46:19 -04:00
RandyTheSilly 2bcabe8597 Update README.md to specify Python 3.12 support
Former-commit-id: cfc8548cfb03f839d3cec89be68f839e8863de89
Former-commit-id: 1c0495fb2421d5f1b035a468fa15175b2a031e43
2023-10-02 20:05:10 -04:00
RandyTheSilly 7259a8732c (MacOS) Port new clipboard behavior to MacOS
Former-commit-id: bd887c5ee6a75130b8c994034708ae7893764251
Former-commit-id: e1621ed1a986f731c8217d741fd84a42196cb4e9
2023-09-26 20:29:51 -04:00
RandyTheSilly 20cdc27b88 (Haiku) Port new clipboard behavior to Haiku
Former-commit-id: 3a40559cf39475a0291187a4853c1e5f335b96f0
Former-commit-id: 95bfec78129d9d22afb16a33775df8d3abb24fa1
2023-09-26 19:51:05 -04:00
RandyTheSilly 2f6bdb8d2e (Termux) Port new clipboard clear behavior to Termux
Former-commit-id: bdda7fae8c2305e1f64c468d45c205d12645f669
Former-commit-id: e6fea38cc85a03b8970f0cdf61390825cc048c20
2023-09-26 19:32:56 -04:00
RandyTheSilly eb4fae3e9e Fixed new clipboard clear behavior on FreeBSD
Former-commit-id: e276182b11c518bc7b880eaebb7d9c8fd528761b
Former-commit-id: fc1f067c5c0df86109795feca05527be5322d829
2023-09-26 19:03:07 -04:00
RandyTheSilly 30afbafcbf Fixed xclip throwing errors on FreeBSD
Former-commit-id: 65b466c0598f2194d452c0897ffa9eeb7ec3b281
Former-commit-id: 51ad92728be141113047bb25fb8841d141d545e6
2023-09-26 18:37:42 -04:00
RandyTheSilly e010180219 (X11) Port new clipboard clear behavior to X11
Former-commit-id: 11edd45f8ccaa6de548d6656ef50cd0c1bdff0b4
Former-commit-id: 1fb779b6d27937d83802f50fb6857f24ec7765ed
2023-09-25 13:53:59 -04:00
RandyTheSilly 1f4e7d5cae (WSL) Marked "dos2unix" as a dependency for the WSL package
Former-commit-id: e1adc5ac923a07d87ccb2ee3c687b9e935200617
Former-commit-id: a906b07e1cc19921d1ec94cec99644657b963ee1
2023-09-25 13:11:01 -04:00
RandyTheSilly 67983da22e (WSL) Port new clipboard clear behavior to WSL
Former-commit-id: e1d96418abae79f6af75d72eeb6279e661196bd5
Former-commit-id: 0b345de72d9dc13de2f0db7c26264e0deb685965
2023-09-25 12:54:21 -04:00
RandyTheSilly aa0bd017c9 Fixed CLIPBOARD.py port-job overriding the new clipboard clear behavior
Former-commit-id: 13f705945bf0bbe95b5699f2bd64b9c790324048
Former-commit-id: 54e8c6f8d54e592bdda885de296f56f1d5ecdd24
2023-09-24 22:04:00 -04:00
RandyTheSilly 5a4e952d4d (Wayland) Only clear clipboard contents if they have not been changed since altered by sshyp
Former-commit-id: 2b6be8d327419bccc51430f732fb03ff9bba0439
Former-commit-id: 09406c55e394e97c90d984067bff9b4677b0496a
2023-09-24 21:50:14 -04:00
RandyTheSilly db05e9b05e Fixed offline-mode sync warning displaying at inappropriate times
Former-commit-id: 37c0fe00ec30a903ec7ba40525f01a2963b24d72
Former-commit-id: e6cea4d212e6f9b231dc0d0743b667b9f2eb376c
2023-09-24 19:35:48 -04:00
RandyTheSilly 397ad7d409 Update Haiku package to use Python 3.11
Former-commit-id: cc3ae472b67be63d960b4993c355f9d5acf7d899
Former-commit-id: 8d758713964c081c6dbb56899f2b84e51d25eac0
2023-09-24 19:27:20 -04:00
RandyTheSilly 5af02866b4 Fixed clipboard contents leaking through process list on Wayland
Former-commit-id: bb1485fec2d891af9f49b4d8e81df72b0e6160d9
Former-commit-id: 2eff4ace8b82735a890bf1e669d92d7abbb59dfa
2023-09-24 18:56:42 -04:00
RandyTheSilly e8267f838a Read some fields from config file as boolean values
Former-commit-id: ba2f082fc4d043d55ba5d25875ac9b9600bbbd3a
Former-commit-id: 4e31560e91047d035eb87e312fd17f3e8ad1935f
2023-09-24 16:38:30 -04:00
RandyTheSilly e4fbcd6825 Added an error for when the configured text editor cannot be found on the system
Former-commit-id: f41356854b88cf6a53604ef37025667d17041da7
Former-commit-id: 627cb9298583b7fbb6358edead75c65e671619d0
2023-09-24 15:04:36 -04:00
RandyTheSilly 9ba4b8d724 Added a warning when the user manually attempts to sync in offline mode
Former-commit-id: f54591cc6997078a248c5d8dc3dcd533824c04e4
Former-commit-id: fb405077f39e22840e98bca6a854496bd583c49c
2023-09-24 14:35:01 -04:00
RandyTheSilly 0b73d4016f Always use the user-specified ssh keyfile in place of the previously hard-coded ~/.ssh/sshyp
Former-commit-id: 2ec48a889401a5da39c162f966b2e1eaa4c90aad
Former-commit-id: e0071948993af51c3de731eaf49c16d5fdab3632
2023-09-24 14:03:44 -04:00
12 changed files with 233 additions and 97 deletions
+4 -3
View File
@@ -1,7 +1,7 @@
![sshyp](https://github.com/rwinkhart/sshyp-labs/blob/main/extra/artwork/sshyp-banner.png)
[![release](https://img.shields.io/github/v/release/rwinkhart/sshyp)](https://github.com/rwinkhart/sshyp/releases)
![python](https://img.shields.io/badge/python-3.7--3.11-yellow)
![python](https://img.shields.io/badge/python-3.7--3.12-yellow)
[![downloads](https://img.shields.io/github/downloads/rwinkhart/sshyp/total)](https://github.com/rwinkhart/sshyp/releases)
[![CodeQL](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/rwinkhart/sshyp/actions/workflows/codeql-analysis.yml)
@@ -77,9 +77,10 @@ All available options can be found with `sshyp help`, or alternatively, in the m
# Roadmap
Short-term Goals:
- migrate from gpg to a better-suited utility focused on symmetric cipher encryption
- implement a method for tracking how long it has been since a password has been changed (password aging)
- find and fix more bugs
Long-term Goals:
- improve OS compatibility
- migrate from gpg to a better-suited utility focused on symmetric cipher encryption
- seize the thrones, shear the humans
+1 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh
git add -f extra lib/sshyp.py lib/sshync.py lib/stweak.py port-jobs share LICENSE README.md package.sh commit.sh .gitignore
git add -f extra lib/sshyp.py lib/sshync.py lib/stweak.py lib/clipclear.py port-jobs share LICENSE README.md package.sh commit.sh .gitignore
git commit -m "$1"
git push
+31
View File
@@ -1,3 +1,34 @@
sshyp v1.5.1
10/24/2023
the fortified flock update - patch one
this release fixes both long and short-standing bugs that managed to escape discovery in testing;
it also addresses some issues that first-time users may be put-off by upon encountering
user-facing features:
- the clipboard is now only cleared if the contents of the clipboard still match what was placed onto it by sshyp
^ this means that sshyp will no longer remove data from the clipboard that it did not place there
- a warning/explanation is now provided when the user attempts to sync in offline mode
- a readable error is now provided if the user attempts to edit a note and the specified text editor cannot be found
- the optional configuration menu is now explained when encountered during "sshyp init"
- the man page now includes common troubleshooting information
fixes/optimizations:
- custom, user-provided ssh keyfiles are now properly utilized in all cases
^ previously, using custom ssh keyfiles would break various features
- clipboard contents are no longer leaked via the process list on Wayland
- interpolation has been disabled for ConfigParser, allowing "%" to be stored in configuration files
- gpg keys with ":" or "\" in their names are now properly written to the configuration file
- copy_id_check() and whitelist_verify() are now safely callable from extensions
- the ssh key selection will no longer see "known_hosts.old" as a valid keyfile
- when possible, data from the configuration file is now read as boolean values
- the Haiku package has been updated to use Python 3.11
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
sshyp v1.5.0
07/12/2023
+27 -1
View File
@@ -1,4 +1,4 @@
.TH sshyp 1 "12 July 2023" "v1.5.0" "sshyp man page"
.TH sshyp 1 "24 October 2023" "v1.5.1" "sshyp man page"
.SH NAME
\fBsshyp\fR - Simple, self-hosted, synchronized password management for UNIX(-like) systems. Alternative to (and compatible with) pass/password-store.
.SH SYNOPSIS
@@ -99,6 +99,32 @@ Client setup:
Done!
Please note that the server setup intentionally does not allow the reading of entries (it does not allow adding a gpg decryption key). For security purposes, only clients can read entries.
.SH TROUBLESHOOTING
.B Clipboard not clearing:
The intended way for sshyp to interact with the system clipboard is for it to clear it 30 seconds after copying a field. Unfortunately, this does not work by default on all systems due to the prevalence of clipboard managers.
Clipboard managers save a history of what has been copied to the clipboard, which is already a big enough issue on its own for people who copy sensitive information to their clipboard. Some clipboard managers simply will not allow the clipboard to be empty and will replace its contents with the last copied item if you attempt to clear it. One such naughty clipboard manager is KDE Klipper, which comes packaged into KDE Plasma and is typically enabled by default on most distributions. Due to this behavior, KDE Klipper breaks sshyp's clipboard clearing functionality and should not be left enabled.
It is likely other popular clipboard managers exhibit this behavior as well (Klipper is just the one I first noticed it with). Clipboard managers should not be enabled by default in any environment or distribution due to their potential security implications.
.B Shell completions not working:
ZSH:
Make sure your ~/.zshrc contains the following:
autoload -Uz compinit && compinit
Restart your shell
Bash:
Install your distribution's 'bash-completion' package and restart your shell
OR
Source the completion script manually by adding the following to your ~/.bashrc (then restart your shell):
Linux:
source /usr/share/bash-completion/completions/sshyp
FreeBSD:
source /usr/local/share/bash-completion/completions/sshyp
Haiku:
source /system/data/bash-completion/completions/sshyp
.SH EXIT CODES
0 - no error
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env python3
from hashlib import sha512
from subprocess import PIPE, run
from sys import argv
from time import sleep
# set period of time to wait before attempting to clear clipboard
sleep(30)
# enable the storing of clipboard contents hash for later comparison
hash_paste = sha512()
# PORT START CLIPCLEAR
if argv[2] == 'wsl':
hash_paste.update(run(('powershell.exe', '-c', 'Get-Clipboard'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('powershell.exe', '-c', 'Set-Clipboard'))
elif argv[2] == 'wayland':
hash_paste.update(run('wl-paste', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('wl-copy', '-c'))
elif argv[2] == 'haiku':
hash_paste.update(run(('clipboard', '-p'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('clipboard', '-r'))
elif argv[2] == 'mac':
hash_paste.update(run('pbpaste', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run('pbcopy', input=b'')
elif argv[2] == 'termux':
hash_paste.update(run('termux-clipboard-get', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(("termux-clipboard-set", "''"))
elif argv[2] == 'x11':
hash_paste.update(run(('xclip', '-o', '-sel', 'c'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('xclip', '-i', '/dev/null', '-sel', 'c'))
# PORT END CLIPCLEAR
+1 -1
View File
@@ -124,7 +124,7 @@ def sort_titles_mods(_list_1, _list_2):
# returns a list of data read from a sshync job profile
def get_profile(_profile_dir):
from configparser import ConfigParser
_profile_data = ConfigParser()
_profile_data = ConfigParser(interpolation=None)
if isfile(_profile_dir):
_profile_data.read(_profile_dir)
else:
+51 -37
View File
@@ -123,7 +123,11 @@ def edit_note(_note_lines, _exit_on_match=False):
with NamedTemporaryFile(mode='w+') as _tmp:
_tmp.write(_joined_note_lines)
_tmp.seek(0)
run((editor, _tmp.name))
try:
run((editor, _tmp.name))
except FileNotFoundError:
print(f"\n\u001b[38;5;9merror: the configured text editor ({editor}) cannot be found on this system\n\n"
f"please either install the editor or re-configure the active editor using 'sshyp tweak'\u001b[0m\n")
_tmp.seek(0)
_new_note = _tmp.read().rstrip()
if _exit_on_match and _joined_note_lines == _new_note:
@@ -143,8 +147,8 @@ def decrypt(_entry_dir, _quick_verify=None, _quick_pass=None):
_contents = None
# check quick-unlock status, fetch passphrase
if _quick_verify == 'true':
_quick_pass = whitelist_verify(port, username_ssh, ip, client_device_id)
if _quick_verify:
_quick_pass = whitelist_verify(port, username_ssh, ip, client_device_id, identity)
else:
if _quick_pass is None:
_quick_pass = False
@@ -180,14 +184,14 @@ def decrypt(_entry_dir, _quick_verify=None, _quick_pass=None):
# checks the user's whitelist status and fetches the full gpg key password if possible
def whitelist_verify(_port, _username_ssh, _ip, _client_device_id):
def whitelist_verify(_port, _username_ssh, _ip, _client_device_id, _identity):
try:
run(('gpg', '--pinentry-mode', 'cancel', '-qd', '--output', '/dev/null',
f"{home}/.config/sshyp/lock.gpg"), stderr=DEVNULL, check=True)
return False
except CalledProcessError:
_i, _full_password = 0, ''
_server_whitelist = run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', _port, f"{_username_ssh}@{_ip}",
_server_whitelist = run(('ssh', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
f'python3 -c \'from os import listdir; print(*listdir("/home/{_username_ssh}'
f'/.config/sshyp/whitelist"))\''), stdout=PIPE, text=True).stdout.rstrip().split()
for _device_id in _server_whitelist:
@@ -195,7 +199,7 @@ def whitelist_verify(_port, _username_ssh, _ip, _client_device_id):
from getpass import getpass
_quick_unlock_password = getpass(prompt='\nquick-unlock pin: ')
_quick_unlock_password_excluded = \
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', _port, f"{_username_ssh}@{_ip}",
run(('ssh', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
f"gpg --pinentry-mode loopback --passphrase '{_quick_unlock_password}' "
f"-qd ~/.config/sshyp/excluded.gpg"), stdout=PIPE, text=True).stdout.rstrip()
while _i < len(_quick_unlock_password_excluded):
@@ -256,22 +260,22 @@ def line_edit(_lines, _edit_data, _edit_line):
# attempts to connect to the user's server via ssh to register the device for syncing
def copy_id_check(_port, _username_ssh, _ip, _client_device_id, _sshyp_data):
def copy_id_check(_port, _username_ssh, _ip, _client_device_id, _identity, _sshyp_data):
from stweak import write_config
if not _sshyp_data.has_section('CLIENT-ONLINE'):
_sshyp_data.add_section('CLIENT-ONLINE')
try:
run(('ssh', '-o', 'ConnectTimeout=3', '-i', f"{home}/.ssh/sshyp", '-p', _port, f"{_username_ssh}@{_ip}",
run(('ssh', '-o', 'ConnectTimeout=3', '-i', _identity, '-p', _port, f"{_username_ssh}@{_ip}",
f'python3 -c \'from pathlib import Path; Path("/home/{_username_ssh}/.config/sshyp/devices/'
f'{_client_device_id}").touch(mode=0o400, exist_ok=True)\''), stderr=DEVNULL, check=True)
except CalledProcessError:
print('\n\u001b[38;5;9mwarning: ssh connection could not be made - ensure the public key (~/.ssh/sshyp.pub) is '
print(f'\n\u001b[38;5;9mwarning: ssh connection could not be made - ensure the public key ({_identity}) is '
'registered on the remote server and that the entered ip, port, and username are correct\n\nsyncing '
'functionality will be disabled until this is addressed\u001b[0m\n')
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '1')
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', 'true')
write_config(_sshyp_data)
return True
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '0')
_sshyp_data.set('CLIENT-ONLINE', 'ssh_error', 'false')
write_config(_sshyp_data)
return False
@@ -290,7 +294,7 @@ def print_info():
}}-}}-*]{4*' '}`..'..'{9*' '}\u001b[0m♥♥♥\u001b[0m{9*' '}`..'..'{6*' '}|
{4*' '}◎-◎{4*' '}//{3*' '}\\\\{10*' '}\u001b[38;5;9m♥\u001b[0m{10*' '}//{3*' '}\\\\{5*' '}/|\\""")
print(f"{_border}{_blank}\n\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;15;48;5;8msshyp "
f"version 1.5.0\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;7;48;5;8m/\u001b[0m")
f"version 1.5.1\u001b[38;5;15;48;5;15m{18*' '}\u001b[38;5;7;48;5;8m/\u001b[0m")
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{14*' '}\u001b[38;5;15;48;5;8mthe fortified flock"
f" update\u001b[38;5;15;48;5;15m{15*' '}\u001b[38;5;7;48;5;8m/\u001b[0m\n{_blank}")
print(f"\u001b[38;5;7;48;5;8m\\\u001b[38;5;15;48;5;15m{9*' '}\u001b[38;5;15;48;5;8mcopyright 2021-2023 ",
@@ -424,7 +428,7 @@ def add_entry():
def add_folder():
Path(directory + entry_name).mkdir(mode=0o700, parents=True, exist_ok=True)
if not ssh_error:
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
f'.mkdir(mode=0o700, parents=True, exist_ok=True)\''))
@@ -453,13 +457,13 @@ def rename():
# if renaming a folder
if not ssh_error:
Path(f"{directory}{_new_name}").mkdir(mode=0o700, parents=True, exist_ok=True)
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
f'python3 -c \'from pathlib import Path; Path("{directory_ssh}{entry_name}")'
f'.rename(Path("{directory_ssh}{_new_name}"))\''))
else:
move(f"{directory}{entry_name}", f"{directory}{_new_name}")
if not ssh_error:
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
@@ -517,6 +521,7 @@ def gen():
# copies a specified field of an entry to the clipboard
def copy_data():
from hashlib import sha512
from subprocess import Popen
# ensure the copy target is an entry
target_type_check(entry_name, True, True)
@@ -533,33 +538,38 @@ def copy_data():
# ensure field is not blank
if _copy_subject == '':
raise IndexError
# store hashed _copy_subject for later comparison
_hash = sha512()
_hash.update(_copy_subject.encode('utf-8'))
# PORT START CLIPBOARD
# WSL clipboard detection
if 'WSL_DISTRO_NAME' in environ:
run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
Popen("sleep 30; powershell.exe -c Set-Clipboard ''", shell=True, stdout=DEVNULL, stderr=DEVNULL)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wsl'), stdout=DEVNULL,
stderr=DEVNULL)
# Wayland clipboard detection
elif 'WAYLAND_DISPLAY' in environ:
run(('wl-copy', _copy_subject))
Popen('sleep 30; wl-copy -c', shell=True)
run('wl-copy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\', '\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wayland'))
# Haiku clipboard detection
elif uname()[0] == 'Haiku':
run(('clipboard', '-c', _copy_subject))
Popen('sleep 30; clipboard -r', shell=True)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'haiku'))
# MacOS clipboard detection
elif uname()[0] == 'Darwin':
run('pbcopy', stdin=Popen(('printf', _copy_subject.replace('\\', '\\\\').replace('%', '%%')), stdout=PIPE)
.stdout)
Popen("sleep 30; printf '' | pbcopy", shell=True)
run('pbcopy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\', '\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'mac'))
# Termux (Android) clipboard detection
elif isdir("/data/data/com.termux"):
run(('termux-clipboard-set', _copy_subject))
Popen("sleep 30; termux-clipboard-set ''", shell=True)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'termux'))
# X11 clipboard detection
elif 'DISPLAY' in environ:
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _copy_subject.replace('\\', '\\\\')
.replace('%', '%%')), stdout=PIPE).stdout)
Popen("sleep 30; printf '' | xclip -sel c", shell=True)
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', '%b', _copy_subject.replace('\\', '\\\\')), stdout=PIPE)
.stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'x11'))
else:
print('\n\u001b[38;5;9merror: clipboard tool could not be determined\n\nnote that the clipboard does not '
'function in a raw tty\u001b[0m\n')
@@ -570,7 +580,7 @@ def copy_data():
def remove_data():
decrypt(None, _quick_verify=quick_unlock_enabled)
if not ssh_error:
run(('ssh', '-i', f"{home}/.ssh/sshyp", '-p', port, f"{username_ssh}@{ip}",
run(('ssh', '-i', identity, '-p', port, f"{username_ssh}@{ip}",
f'cd /usr/lib/sshyp; python3 -c \'from sshync import delete; delete("{entry_name}", "remotely", True)\''))
else:
offline_delete(entry_name, 'locally', silent_sync)
@@ -581,7 +591,7 @@ def extension_runner():
_output_com, _extension_dir = None, realpath(__file__).rsplit('/', 1)[0] + '/extensions/'
if isdir(_extension_dir):
for _extension in listdir(_extension_dir):
_extension_config = ConfigParser()
_extension_config = ConfigParser(interpolation=None)
_extension_config.read(_extension_dir + _extension)
_input_com = _extension_config.get('config', 'input').split()
if _input_com == arguments[arg_start:]:
@@ -603,7 +613,7 @@ if __name__ == "__main__":
ssh_error, success_flag, sync_flag, silent_sync, pass_show = False, False, False, False, False
# set to avoid PEP8 warnings
arg_start, device_type = None, None
arg_start, device_type, offline_mode_enabled = None, None, None
# retrieve typed argument
arguments = argv[1:]
@@ -624,27 +634,28 @@ if __name__ == "__main__":
# import saved userdata
try:
sshyp_data = ConfigParser()
sshyp_data = ConfigParser(interpolation=None)
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
device_type = sshyp_data.get('GENERAL', 'device_type')
if device_type == 'client':
directory = f"{home}/.local/share/sshyp/"
gpg_id = sshyp_data.get('CLIENT-GENERAL', 'gpg_id')
editor = sshyp_data.get('CLIENT-GENERAL', 'text_editor')
offline_mode_enabled = sshyp_data.get('CLIENT-GENERAL', 'offline_mode_enabled')
if offline_mode_enabled == 'true':
offline_mode_enabled = sshyp_data.getboolean('CLIENT-GENERAL', 'offline_mode_enabled')
if offline_mode_enabled:
ssh_error = True
quick_unlock_enabled = 'false'
quick_unlock_enabled = False
else:
quick_unlock_enabled = sshyp_data.get('CLIENT-ONLINE', 'quick_unlock_enabled')
quick_unlock_enabled = sshyp_data.getboolean('CLIENT-ONLINE', 'quick_unlock_enabled')
username_ssh = sshyp_data.get('SSHYNC', 'user')
ip = sshyp_data.get('SSHYNC', 'ip')
port = sshyp_data.get('SSHYNC', 'port')
directory_ssh = sshyp_data.get('SSHYNC', 'remote_dir')
identity = sshyp_data.get('SSHYNC', 'identity_file')
client_device_id = listdir(f"{home}/.config/sshyp/devices")[0]
ssh_error = int(sshyp_data.get('CLIENT-ONLINE', 'ssh_error'))
if ssh_error == 1:
ssh_error = copy_id_check(port, username_ssh, ip, client_device_id, sshyp_data)
ssh_error = sshyp_data.getboolean('CLIENT-ONLINE', 'ssh_error')
if ssh_error:
ssh_error = copy_id_check(port, username_ssh, ip, client_device_id, identity, sshyp_data)
except (FileNotFoundError, NoSectionError, NoOptionError):
print(f"\n{73*'!'}")
print("not all necessary configurations have been made - please run 'sshyp init'")
@@ -714,6 +725,9 @@ if __name__ == "__main__":
sync()
elif (arg_count > 0 and arguments[0] == 'sync') or (arg_count > 1 and arguments[1] == 'shear'):
sync('\n')
elif arg_count > 0 and arguments[0] == 'sync' and offline_mode_enabled:
print("\n\u001b[38;5;9mwarning: sshyp is currently configured in offline mode - ssh synchronization is "
"disabled\u001b[0m\n")
# PORT START ARGS-SERVER
# server arguments
+17 -15
View File
@@ -10,7 +10,7 @@ from subprocess import PIPE, run
# PORT START UNAME-IMPORT-STWEAK
from os import uname
# PORT END UNAME-IMPORT-STWEAK
home, sshyp_data, stdscr = expanduser('~'), ConfigParser(), None
home, sshyp_data, stdscr = expanduser('~'), ConfigParser(interpolation=None), None
if isfile(f"{home}/.config/sshyp/sshyp.ini"):
_exists_flag = True
sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini")
@@ -99,7 +99,7 @@ def gpg_config():
stdout=PIPE, text=True).stdout.splitlines() if _item.startswith('uid')]
_named_uid_list = []
for _uid in _uid_list:
_named_uid_list.append(_uid.split(':')[9])
_named_uid_list.append(_uid.split(':')[9].replace('\\x3a', ':').replace('\\x5c', '\\'))
_named_uid_list.append('auto-generate')
_gpg_id_sel = curses_radio(_named_uid_list, 'gpg key selection')
if _gpg_id_sel == len(_named_uid_list)-1:
@@ -151,7 +151,7 @@ def ssh_config():
# ensure ~/.ssh directory exists
Path(f"{home}/.ssh").mkdir(mode=0o700, exist_ok=True)
for _file in listdir(f"{home}/.ssh"):
if not _file.startswith('.') and _file not in ('known_hosts', 'authorized_keys') \
if not _file.startswith('.') and _file not in ('known_hosts', 'known_hosts.old', 'authorized_keys') \
and not _file.endswith('.pub') and isfile(f"{home}/.ssh/{_file}"):
_keys.append(f"{home}/.ssh/{_file}")
_keys.extend(['auto-generate', 'other (type the location)'])
@@ -184,11 +184,11 @@ def ssh_config():
sshyp_data.set('SSHYNC', 'remote_dir', f"/home/{_username_ssh}/.local/share/sshyp/")
sshyp_data.set('SSHYNC', 'identity_file', _ssh_key)
write_config()
return _iport[1], _username_ssh, _iport[0]
return _iport[1], _username_ssh, _iport[0], _ssh_key
# device id configuration
def dev_id_config(_ip, _username_ssh, _port):
def dev_id_config(_ip, _username_ssh, _port, _identity):
from sshyp import copy_id_check, string_gen
_device_id_prefix = curses_text('name this device:\n\n\n\n\n(ctrl+g/enter to confirm)\n\nimportant: this '
'id must be unique amongst your client devices\n\nthis is used to keep track of '
@@ -200,7 +200,7 @@ def dev_id_config(_ip, _username_ssh, _port):
remove(f"{home}/.config/sshyp/devices/{_id}")
open(f"{home}/.config/sshyp/devices/{_device_id}", 'w')
# test server connection and attempt to register device id
copy_id_check(_ip, _username_ssh, _port, _device_id, sshyp_data)
copy_id_check(_ip, _username_ssh, _port, _device_id, _identity, sshyp_data)
# quick-unlock configuration
@@ -360,8 +360,8 @@ def extension_downloader():
from os import chmod
from tempfile import gettempdir
from urllib.request import urlopen, urlretrieve
_file_data = urlopen("https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/pointers/v1.5.0").read()
_pointer = ConfigParser()
_file_data = urlopen("https://raw.githubusercontent.com/rwinkhart/sshyp-labs/main/pointers/v1.5.1").read()
_pointer = ConfigParser(interpolation=None)
_pointer.read_string(_file_data.decode('utf-8'))
_extensions = _pointer.sections()
_extensions.append('CANCEL')
@@ -464,15 +464,15 @@ def global_menu(_scr, _device_type, _top_message):
if not sshyp_data.has_option('CLIENT-ONLINE', 'quick_unlock_enabled'):
quick_unlock_config(True)
# set to None to check if modified later
_ip, _username_ssh, _port = None, None, None
_ip, _username_ssh, _port, _identity = None, None, None, None
# ...and there is no sshync config present
if not sshyp_data.has_section('SSHYNC'):
_ip, _username_ssh, _port = ssh_config()
_ip, _username_ssh, _port, _identity = ssh_config()
# ...and there is no device ID present
if not listdir(f"{home}/.config/sshyp/devices"):
if None in (_ip, _username_ssh, _port):
_ip, _username_ssh, _port = ssh_config()
dev_id_config(_ip, _username_ssh, _port)
_ip, _username_ssh, _port, _identity = ssh_config()
dev_id_config(_ip, _username_ssh, _port, _identity)
# ...or ssh_error is missing
elif not sshyp_data.has_option('CLIENT-ONLINE', 'ssh_error'):
sshyp_data.set('CLIENT-ONLINE', 'ssh_error', '1')
@@ -492,7 +492,7 @@ def global_menu(_scr, _device_type, _top_message):
if not sshyp_data.has_section('SSHYNC'):
ssh_config()
dev_id_config(sshyp_data.get('SSHYNC', 'ip'), sshyp_data.get('SSHYNC', 'user'),
sshyp_data.get('SSHYNC', 'port'))
sshyp_data.get('SSHYNC', 'port'), sshyp_data.get('SSHYNC', 'identity_file'))
elif _choice == 4:
editor_config(False)
elif _choice == 5:
@@ -555,10 +555,10 @@ def initial_setup(_scr):
if _dev_sync_types[1] != 'true':
# ssh+sshync configuration
_ip, _username_ssh, _port = ssh_config()
_ip, _username_ssh, _port, _identity = ssh_config()
# device id configuration
dev_id_config(_ip, _username_ssh, _port)
dev_id_config(_ip, _username_ssh, _port, _identity)
# PORT START CLIPTOOL
# check for clipboard tool and display warning if missing
@@ -576,6 +576,8 @@ def initial_setup(_scr):
# PORT END CLIPTOOL
# run optional configuration menu
curses_radio(['okay'], 'required configuration complete\n\na menu for additional (optional) configuration will be '
'displayed\n\nthis menu can be safely exited at any time')
wrapped_entry(_dev_sync_types[0], 'additional configuration options:')
+11 -5
View File
@@ -144,7 +144,7 @@ provides {
requires {
gnupg
openssh
python310
python3.11
}
urls {
\"https://github.com/rwinkhart/sshyp\"
@@ -220,11 +220,17 @@ Section: utils
Architecture: all
Maintainer: Randall Winkhart <idgr at tutanota dot com>
Description: A light-weight, self-hosted, synchronized password manager
Depends: python3, gnupg, openssh-client
Suggests: wl-clipboard, xclip, bash-completion
Priority: optional
Installed-Size: $size
Depends: python3, gnupg, openssh-client
" > output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
if [ "$1" = 'Debian' ]; then
printf "Suggests: wl-clipboard, xclip, bash-completion, openssh-server
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
else
printf "Suggests: bash-completion, openssh-server
" >> output/debiantemp/sshyp_"$version"-"$revision"_all/DEBIAN/control
fi
dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/debiantemp/sshyp_"$version"-"$revision"_all/
mv output/debiantemp/sshyp_"$version"-"$revision"_all.deb output/"$special"-sshyp_"$version"-"$revision"_all.deb
rm -rf output/debiantemp
@@ -296,7 +302,7 @@ License: GPL-3.0-only
URL: https://github.com/rwinkhart/sshyp
Source0: GENERIC-FEDORA-sshyp-"$version".tar.xz
Requires: python gnupg openssh-clients
Recommends: wl-clipboard xclip bash-completion
Recommends: wl-clipboard xclip bash-completion openssh-server
%%description
sshyp is a password-store compatible CLI password manager available for UNIX(-like) systems - its primary goal is to make syncing passwords and notes across devices as easy as possible via CLI.
%%install
@@ -381,7 +387,7 @@ printf "/usr/bin/sshyp
cp lib/* port-jobs/working/
cd port-jobs
./CLIPTOOL.py LINUX
./CLIPBOARD.py LINUX
./CLIPBOARD.py BSD
./UNAME.py TMP
./COMMENTS.py ALL
./BLANKS.py
+45 -29
View File
@@ -3,44 +3,60 @@ import re
from sys import argv, exit as s_exit
# read arguments
arguments, replacement = argv[1:], None
arguments, clip_replacement, clear_replacement = argv[1:], None, None
# define replacement text depending on arguments
if len(arguments) > 0:
if arguments[0] == 'WSL':
replacement = """run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
Popen("sleep 30; powershell.exe -c Set-Clipboard ''", shell=True, stdout=DEVNULL, stderr=DEVNULL)"""
clip_replacement = """run(('powershell.exe', '-c', "Set-Clipboard '" + _copy_subject.replace("'", "''") + "'"))
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wsl'), stdout=DEVNULL, stderr=DEVNULL)"""
clear_replacement = """hash_paste.update(run(('powershell.exe', '-c', 'Get-Clipboard'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('powershell.exe', '-c', 'Set-Clipboard'))"""
elif arguments[0] == 'MAC':
replacement = """run('pbcopy', stdin=Popen(('printf', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')
.replace('%', '%%')), stdout=PIPE).stdout)
Popen("sleep 30; printf '' | pbcopy", shell=True)"""
clip_replacement = """run('pbcopy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'mac'))"""
clear_replacement = """hash_paste.update(run('pbpaste', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run('pbcopy', input=b'')"""
elif arguments[0] == 'HAIKU':
replacement = """run(('clipboard', '-c', _copy_subject))
Popen('sleep 30; clipboard -r', shell=True)"""
clip_replacement = """run(('clipboard', '-c', _copy_subject))
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'haiku'))"""
clear_replacement = """hash_paste.update(run(('clipboard', '-p'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('clipboard', '-r'))"""
elif arguments[0] == 'TERMUX':
replacement = """run(('termux-clipboard-set', _copy_subject))
Popen("sleep 30; termux-clipboard-set ''", shell=True)"""
elif arguments[0] == 'LINUX':
replacement = """if 'WAYLAND_DISPLAY' in environ:
run(('wl-copy', _copy_subject))
Popen('sleep 30; wl-copy -c', shell=True)
clip_replacement = """run(('termux-clipboard-set', _copy_subject))
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'termux'))"""
clear_replacement = """hash_paste.update(run('termux-clipboard-get', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(("termux-clipboard-set", "''"))"""
elif arguments[0] in ('LINUX', 'BSD'):
clip_replacement = """if 'WAYLAND_DISPLAY' in environ:
run('wl-copy', stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'wayland'))
else:
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _copy_subject
.replace('\\\\\\', '\\\\\\\\\\\\\\').replace('%', '%%')), stdout=PIPE).stdout)
Popen("sleep 30; printf '' | xclip -sel c", shell=True)"""
run(('xclip', '-sel', 'c'), stdin=Popen(('printf', '%b', _copy_subject.replace('\\\\\\', '\\\\\\\\\\\\\\')), stdout=PIPE).stdout)
Popen((realpath(__file__).rsplit('/', 1)[0] + "/clipclear.py", _hash.hexdigest(), 'x11'))"""
clear_replacement = """if argv[2] == 'wayland':
hash_paste.update(run('wl-paste', stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('wl-copy', '-c'))
else:
hash_paste.update(run(('xclip', '-o', '-sel', 'c'), stdout=PIPE).stdout.strip())
if argv[1] == hash_paste.hexdigest():
run(('xclip', '-i', '/dev/null', '-sel', 'c'))"""
else:
s_exit()
# define PORT target
string1 = '# PORT START CLIPBOARD'
string2 = '# PORT END CLIPBOARD'
targets = (('CLIPBOARD', 'sshyp.py', '', '', clip_replacement),
('CLIPCLEAR', 'clipclear.py', '\n', '', clear_replacement))
# read input file
text = open('working/sshyp.py', 'r').read()
# find and replace the defined PORT target
regex = re.compile(f"{string1}.*?{string2}", re.DOTALL)
new_text = re.sub(regex, replacement, text)
# write updated text
open('working/sshyp.py', 'w').write(new_text)
for target in targets:
# read text from target file
text = open(f"working/{target[1]}", 'r').read()
# compile regex and modify text
regex = re.compile(f"{target[2]}# PORT START {target[0]}.*?# PORT END {target[0]}{target[3]}", re.DOTALL)
new_text = re.sub(regex, target[4], text)
# write updated text to target file
open(f"working/{target[1]}", 'w').write(new_text)
+1 -3
View File
@@ -1,14 +1,12 @@
#!/usr/bin/env python3
import re
devtype_replacement = """_install_type = curses_radio(('client (ssh-synchronized)', 'client (offline)'),
'device + sync type configuration')"""
targets = (('TWEAK-EXTEND-FUNCTIONS', 'stweak.py', '\n', '\n\n', ''),
('TWEAK-EXTEND-OPTION', 'stweak.py', '', '', "curses_radio(['okay'], "
"'extension management is not supported on this platform\\\\n\\\\ninstead, you may install and manage "
"extensions through your system package manager\\\\n\\\\nofficial extension packages are available "
"at https://github.com/rwinkhart/sshyp-labs/releases')"))
for target in targets:
# read text from target file
text = open(f"working/{target[1]}", 'r').read()
+2 -2
View File
@@ -1,6 +1,6 @@
#!/bin/sh
if [ "$1" = 'PREP' ]; then
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./*.py
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./*.py
else
sed -i '1 s/.*/#!\/bin\/env\ python3.10/' ./working/sshyp.py
sed -i '1 s/.*/#!\/bin\/env\ python3.11/' ./working/sshyp.py ./working/clipclear.py
fi