From ac035db5977320aa7a2e5b59d19b00d76a7a1607 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Tue, 27 Jun 2023 14:49:03 -0400 Subject: [PATCH 1/8] Re-organized repo for upcoming v1.5.0 extension management changes --- README.md | 21 +- commit.sh | 2 +- extensions/password-pasture/CMakeLists.txt | 16 + extensions/password-pasture/build.sh | 5 + extensions/password-pasture/main.c | 380 +++++++++++++++++++ extensions/password-pasture/requirements.txt | 3 + extensions/sshyp-mfa/sshyp-mfa.ext | 3 + extensions/sshyp-mfa/sshyp-mfa.py | 90 +++++ pointers/v1.5.0 | 6 + 9 files changed, 513 insertions(+), 13 deletions(-) create mode 100644 extensions/password-pasture/CMakeLists.txt create mode 100755 extensions/password-pasture/build.sh create mode 100644 extensions/password-pasture/main.c create mode 100644 extensions/password-pasture/requirements.txt create mode 100644 extensions/sshyp-mfa/sshyp-mfa.ext create mode 100755 extensions/sshyp-mfa/sshyp-mfa.py create mode 100644 pointers/v1.5.0 diff --git a/README.md b/README.md index dab2498..a436b93 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,17 @@ # sshyp-labs -Experimental extensions for the sshyp password manager. +Extensions for the [sshyp password manager](https://github.com/rwinkhart/sshyp). -sshyp-labs is currently the home of sshyp-mfa and password-pasture (sshyp-gui). +When new functionality is desired that goes outside of sshyp's primary goals or requires venturing outside of the Python standard library, said functionality is implemented as an extension. + +# Installation +For sshyp v1.5.0+, sshyp extensions should be installed from the `sshyp tweak` menu's "extension management" option. + +**Do NOT use the packages from the releases page! These are only for older, unsupported releases of sshyp!** # Available Extensions -sshyp-mfa - [installation and usage instructions](https://github.com/rwinkhart/sshyp-labs/wiki/sshyp-mfa) +[sshyp-mfa](https://github.com/rwinkhart/sshyp-labs/wiki/sshyp-mfa): read mfa data from sshyp entries to generate and copy totp keys to the clipboard (optional Steam support) -sshyp-mfa is a unique approach to generating multi-factor authentication keys. Upon running `sshyp copy -m`, -an MFA key will be generated and copied to your clipboard. sshyp-mfa will continue to run in the background and copy a -new key to your clipboard every time the actively copied one expires. Never worry about a TOTP timer expiring again! -At any time, sshyp-mfa can be closed with ctrl+c to stop this process. - -password-pasture - [installation and usage instructions](https://github.com/rwinkhart/sshyp-labs/wiki/password-pasture) - -password-pasture is an incredibly experimental GTK4 GUI interface for sshyp. It is currently not in a usable state -and has not been updated since sshyp v1.1.X. Development is set to resume after sshyp is more feature-complete. +[password-pasture](https://github.com/rwinkhart/sshyp-labs/wiki/password-pasture): a HIGHLY experimental GTK4 sshyp GUI - very incomplete (last updated for sshyp v1.1.x) # Acknowledgements sshyp-mfa relies on [ValvePython/steam](https://github.com/ValvePython/steam) for Steam support. diff --git a/commit.sh b/commit.sh index 1b0cfa2..fb850d2 100755 --- a/commit.sh +++ b/commit.sh @@ -1,4 +1,4 @@ #!/bin/sh -git add -f extra password-pasture sshyp-mfa LICENSE README.md version commit.sh .gitignore +git add -f LICENSE README.md commit.sh extensions extra pointers git commit -m "$1" git push diff --git a/extensions/password-pasture/CMakeLists.txt b/extensions/password-pasture/CMakeLists.txt new file mode 100644 index 0000000..bd166ba --- /dev/null +++ b/extensions/password-pasture/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.23) +project(password_pasture C) + +set(CMAKE_C_STANDARD 17) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK4 REQUIRED gtk4) + +include_directories(${GTK4_INCLUDE_DIRS}) +link_directories(${GTK4_LIBRARY_DIRS}) + +add_definitions(${GTK4_CFLAGS_OTHER}) + +add_executable(password_pasture main.c) + +target_link_libraries(password_pasture ${GTK4_LIBRARIES}) \ No newline at end of file diff --git a/extensions/password-pasture/build.sh b/extensions/password-pasture/build.sh new file mode 100755 index 0000000..3fece79 --- /dev/null +++ b/extensions/password-pasture/build.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +rm -rf CMakeFiles cmake_install.cmake CMakeCache.txt Makefile +cmake ./ +sleep 10 # prevents clock skew +make -j$(nproc) diff --git a/extensions/password-pasture/main.c b/extensions/password-pasture/main.c new file mode 100644 index 0000000..3772d26 --- /dev/null +++ b/extensions/password-pasture/main.c @@ -0,0 +1,380 @@ +#include +#include +#include +#include +#include + +GtkWidget *label_selected; GtkListBox *list_box; +char entries[1024][1024]; int entry_array_count = 0; + +void str_remove(char *str, const char *sub) { + char *p, *q, *r; + if (*sub && (q = r = strstr(str, sub)) != 0) { + size_t len = strlen(sub); + while ((r = strstr(p = r + len, sub)) != 0) { + while (p < r) + *q++ = *p++; + } + while ((*q++ = *p++) != '\0') + continue; + } +} + +void gen_entry_array(char *path, size_t size, const char *path_orig) { + DIR *dir; + struct dirent *entry; + size_t len = strlen(path); + + if (!(dir = opendir(path))) { + return; } + + while ((entry = readdir(dir)) != 0) { + char *name = entry->d_name; + if (entry->d_type == DT_DIR) { + if (!strcmp(name, ".") || !strcmp(name, "..")) { + continue; } + path[len] = '/'; + strcpy(path + len + 1, name); + gen_entry_array(path, size, path_orig); + path[len] = '\0'; + } else { + char path_filtered[1024]; + strcpy(path_filtered, path); + str_remove(path_filtered, path_orig); + size_t name_len = strlen(name); + name[name_len-4] = 0; + char sshyp_path[1024]; strcpy(sshyp_path, path_filtered); strcat(sshyp_path, "/"); strcat(sshyp_path, name); + strcpy(entries[entry_array_count], sshyp_path); + entry_array_count++; + } + } + closedir(dir); +} + +void password_convert(GtkEntryBuffer *buffer) { + const char *password = gtk_entry_buffer_get_text(buffer); + char *sub1 = "gpg --pinentry-mode loopback --batch --passphrase-fd 0 --armor -qd --output /dev/null ~/.config/sshyp/lock.gpg <<< '", *sub2 = "'", *command = (char *) malloc(2048); + strcpy(command, sub1); strcat(command, password); strcat(command, sub2); + system(command); + free(command); +} + +void gpg_lock() { + system("gpgconf --reload gpg-agent"); +} + +void password_prompt(GtkWindow *window) { + GtkWidget *dialog, *dialog_header, *box_header; // main window and header bar + GtkWidget *button_unlock, *button_cancel, *button_lock; // header bar buttons + GtkWidget *content_area, *password_entry; // content area and widgets + + dialog = gtk_dialog_new(); + password_entry = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(password_entry), FALSE); + gtk_entry_set_placeholder_text(GTK_ENTRY(password_entry), "passphrase"); + dialog_header = gtk_header_bar_new(); + gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE); + box_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_halign(box_header, GTK_ALIGN_CENTER); + button_unlock = gtk_button_new_with_label("unlock"); + button_cancel = gtk_button_new_with_label("cancel"); + button_lock = gtk_button_new_from_icon_name("changes-prevent"); + gtk_widget_set_size_request(button_lock, 20, -1); + gtk_box_append(GTK_BOX(box_header), button_unlock); + gtk_box_append(GTK_BOX(box_header), button_cancel); + gtk_box_append(GTK_BOX(box_header), button_lock); + gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), box_header); + gtk_window_set_title(GTK_WINDOW(dialog), ""); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window)); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50); + + // add password entry to dialog content area + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_append(GTK_BOX(content_area), password_entry); + + gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header); + gtk_widget_show(dialog); + + // button actions + g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(password_convert), gtk_entry_get_buffer(GTK_ENTRY(password_entry))); + g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect(button_lock, "clicked", G_CALLBACK(gpg_lock), 0); + g_signal_connect_swapped(button_lock, "clicked", G_CALLBACK(gtk_window_close), dialog); +} + +void copy_item(GtkWidget *button) { + char *command = malloc(2048); const char *button_label = gtk_button_get_label(GTK_BUTTON(button)); + strcpy(command, "sshyp copy "); + strcat(command, button_label); strcat(command, " "); + strcat(command, gtk_label_get_text((GtkLabel *) label_selected)); + system(command); + free(command); +} + +void copy_edit_field_prompt(GtkWidget *button, gpointer *window) { + GtkWidget *dialog, *dialog_header; // main window and header bar + GtkWidget *button_cancel; // header bar buttons + GtkWidget *content_area, *button_pwd, *button_usr, *button_url, *button_nte, *button_mfa; // content area and widgets + int mode; // copy/edit mode flag + + // set mode based on button pressed, copy = 1, edit = 2 + const char *button_label = gtk_button_get_icon_name(GTK_BUTTON(button)); + if (strcmp(button_label, "edit-copy") == 0) { + mode = 1; + } else { + mode = 2; + } + + dialog = gtk_dialog_new(); + dialog_header = gtk_header_bar_new(); + gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE); + button_cancel = gtk_button_new_with_label("cancel"); + gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), button_cancel); + gtk_window_set_title(GTK_WINDOW(dialog), ""); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window)); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50); + + // add options to dialog content area + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + button_pwd = gtk_button_new_with_label("password"); + button_usr = gtk_button_new_with_label("username"); + button_url = gtk_button_new_with_label("url"); + button_nte = gtk_button_new_with_label("note"); + button_mfa = gtk_button_new_with_label("mfa"); + gtk_box_append(GTK_BOX(content_area), button_pwd); + gtk_box_append(GTK_BOX(content_area), button_usr); + gtk_box_append(GTK_BOX(content_area), button_url); + gtk_box_append(GTK_BOX(content_area), button_nte); + gtk_box_append(GTK_BOX(content_area), button_mfa); + + gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header); + gtk_widget_show(dialog); + + // button actions + if (mode == 1) { + g_signal_connect(button_pwd, "clicked", G_CALLBACK(copy_item), 0); + g_signal_connect(button_usr, "clicked", G_CALLBACK(copy_item), 0); + g_signal_connect(button_url, "clicked", G_CALLBACK(copy_item), 0); + g_signal_connect(button_nte, "clicked", G_CALLBACK(copy_item), 0); + g_signal_connect(button_mfa, "clicked", G_CALLBACK(copy_item), 0); + } + + g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect_swapped(button_pwd, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect_swapped(button_usr, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect_swapped(button_url, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect_swapped(button_nte, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect_swapped(button_mfa, "clicked", G_CALLBACK(gtk_window_close), dialog); +} + +void set_selection_label(GtkWidget *button, gpointer *label) { + const char *button_label = gtk_button_get_label(GTK_BUTTON(button)); + gtk_label_set_text(GTK_LABEL(label), button_label); +} + +void entry_list_gen() { + GtkWidget *entry_button; + + GtkWidget *iter = gtk_widget_get_first_child ((GtkWidget *) list_box); + while (iter != 0) { + GtkWidget *next = gtk_widget_get_next_sibling (iter); + gtk_list_box_remove (list_box, iter); + iter = next; + } + + char path[1024]; char *user_home = getenv("HOME"); strcpy(path, user_home); strcat(path, "/.local/share/sshyp"); + char path_orig[1024]; strcpy(path_orig, path); strcat(path_orig, "/"); + gen_entry_array(path, sizeof path, path_orig); + + // bubble sort entries array + char *temp = (char *) malloc(1024); + for(int i=0; i 0){ + strcpy(temp, entries[j]); + strcpy(entries[j], entries[j+1]); + strcpy(entries[j+1], temp); + } + } + } + free(temp); + + strcpy(entries[entry_array_count], "\0"); + + for (int i_2 = 0; strcmp(entries[i_2], "\0") != 0; i_2++) { + entry_button = gtk_button_new_with_label(entries[i_2]); + gtk_label_set_xalign(GTK_LABEL(gtk_button_get_child(GTK_BUTTON(entry_button))), GTK_JUSTIFY_LEFT); + gtk_button_set_has_frame(GTK_BUTTON(entry_button), FALSE); + g_signal_connect(entry_button, "clicked", G_CALLBACK(set_selection_label), (gpointer *)label_selected); + gtk_list_box_append(list_box, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); + gtk_list_box_append(list_box, entry_button); + } +} + +void sshyp_sync() { + system("sshyp sync"); + entry_array_count = 0; + entry_list_gen(); +} + +void delete_entry() { + char *command = (char *) malloc(2048); + strcpy(command, "sshyp shear "); strcat(command, gtk_label_get_text(GTK_LABEL(label_selected))); + system(command); + free(command); + entry_array_count = 0; + entry_list_gen(); +} + +void are_they_sure(GtkWindow *window) { + GtkWidget *dialog, *dialog_header, *box_header; // main window and header bar + GtkWidget *button_delete, *button_cancel; // header bar buttons + GtkWidget *content_area, *label_sure; // content area and widgets + + dialog = gtk_dialog_new(); + dialog_header = gtk_header_bar_new(); + gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE); + box_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_halign(box_header, GTK_ALIGN_CENTER); + button_delete = gtk_button_new_with_label("delete"); + button_cancel = gtk_button_new_with_label("cancel"); + gtk_box_append(GTK_BOX(box_header), button_delete); + gtk_box_append(GTK_BOX(box_header), button_cancel); + gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), box_header); + gtk_window_set_title(GTK_WINDOW(dialog), ""); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window)); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50); + + // add label to dialog content area + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_widget_set_halign(content_area, GTK_ALIGN_CENTER); + char *label_text = malloc(2048); + strcpy(label_text, "this will delete:\n"); + strcat(label_text, gtk_label_get_text(GTK_LABEL(label_selected))); + strcat(label_text, "\nare you sure?"); + label_sure = gtk_label_new(label_text); + free(label_text); + gtk_label_set_justify(GTK_LABEL(label_sure), GTK_JUSTIFY_CENTER); + gtk_box_append(GTK_BOX(content_area), label_sure); + + // button actions + g_signal_connect(button_delete, "clicked", G_CALLBACK(delete_entry), 0); + g_signal_connect_swapped(button_delete, "clicked", G_CALLBACK(gtk_window_close), dialog); + g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog); + + gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header); + gtk_widget_show(dialog); +} + +static void activate(GtkApplication* app) { + GtkWidget *window, *header_bar; // main window and header bar + GtkWidget *button_sync, *button_unlock; // header bar buttons + GtkWidget *stack, *stack_switcher; GtkStackPage *page_info; // stack widgets + GtkWidget *box_info, *sshyp_logo, *label_home1; // info page widgets + GtkWidget *box_browse_page, *box_browse_controls; // browse page boxes + GtkWidget *button_shear, *button_read, *button_edit, *button_copy; // browse buttons + GtkWidget *scrollable; // scrollable entry list container + + window = gtk_application_window_new(app); + gtk_window_set_title(GTK_WINDOW(window), ""); + gtk_window_set_default_size(GTK_WINDOW(window), 360, 720); + + header_bar = gtk_header_bar_new(); + gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(header_bar), FALSE); + + // !!start main stack stuff!! + + // create the stack + stack = gtk_stack_new(); + gtk_stack_set_transition_type(GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT); + + // attach the stack to the main window + gtk_window_set_child(GTK_WINDOW(window), stack); + + // browse + button_shear = gtk_button_new_from_icon_name("edit-delete"); + gtk_widget_set_hexpand(button_shear, FALSE); + gtk_widget_set_size_request(button_shear, 50, -1); + g_signal_connect_swapped(button_shear, "clicked", G_CALLBACK(are_they_sure), window); + button_read = gtk_button_new_from_icon_name("mail-read"); + gtk_widget_set_hexpand(button_read, TRUE); + button_edit = gtk_button_new_from_icon_name("document-edit"); + gtk_widget_set_hexpand(button_edit, TRUE); + g_signal_connect(button_edit, "clicked", G_CALLBACK(copy_edit_field_prompt), (gpointer *) window); + button_copy = gtk_button_new_from_icon_name("edit-copy"); + gtk_widget_set_hexpand(button_copy, TRUE); + g_signal_connect(button_copy, "clicked", G_CALLBACK(copy_edit_field_prompt), (gpointer *) window); + box_browse_page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + label_selected = gtk_label_new("no entry selected"); + gtk_widget_set_hexpand(label_selected, TRUE); + gtk_widget_set_halign(label_selected, GTK_ALIGN_CENTER); + box_browse_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_valign(box_browse_controls, GTK_ALIGN_START); + gtk_widget_set_size_request(box_browse_controls, -1, 50); + gtk_box_append(GTK_BOX(box_browse_controls), button_shear); + gtk_box_append(GTK_BOX(box_browse_controls), button_read); + gtk_box_append(GTK_BOX(box_browse_controls), button_edit); + gtk_box_append(GTK_BOX(box_browse_controls), button_copy); + gtk_box_append(GTK_BOX(box_browse_page), label_selected); + gtk_box_append(GTK_BOX(box_browse_page), box_browse_controls); + + // !!start scrollable list!! + scrollable = gtk_scrolled_window_new(); + list_box = (GtkListBox *) gtk_list_box_new(); + gtk_widget_set_vexpand(GTK_WIDGET(list_box), TRUE); + + entry_list_gen(); + + gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollable), GTK_WIDGET(list_box)); + // !!end scrollable list!! + + gtk_box_append(GTK_BOX(box_browse_page), GTK_WIDGET(scrollable)); + + // info + box_info = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + sshyp_logo = gtk_image_new_from_file("PLACEHOLDER"); + gtk_image_set_pixel_size(GTK_IMAGE(sshyp_logo), 300); + label_home1 = gtk_label_new("password pasture\nexperimental gui client for sshyp"); + gtk_label_set_justify(GTK_LABEL(label_home1), GTK_JUSTIFY_CENTER); + gtk_label_set_markup(GTK_LABEL(label_home1), "password pasture\nexperimental gui client for sshyp"); + gtk_box_append(GTK_BOX(box_info), sshyp_logo); + gtk_box_append(GTK_BOX(box_info), label_home1); + + stack_switcher = gtk_stack_switcher_new(); + page_info = gtk_stack_add_titled(GTK_STACK(stack), box_info, "info", "info"); + gtk_stack_page_set_icon_name(GTK_STACK_PAGE(page_info), "help-about"); + gtk_stack_add_titled(GTK_STACK(stack), box_browse_page, "browse", "browse"); + gtk_stack_switcher_set_stack(GTK_STACK_SWITCHER(stack_switcher), GTK_STACK(stack)); + gtk_header_bar_pack_start(GTK_HEADER_BAR(header_bar), stack_switcher); + + // !!end main stack stuff!! + + // header bar buttons + button_sync = gtk_button_new_from_icon_name("view-refresh"); + g_signal_connect(button_sync, "clicked", G_CALLBACK(sshyp_sync), 0); + gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_sync); + + button_unlock = gtk_button_new_from_icon_name("changes-allow"); + g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(password_prompt), window); + gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_unlock); + + //GtkWidget *button_debug = gtk_button_new_from_icon_name("applications-science"); + //g_signal_connect(button_debug, "clicked", G_CALLBACK(0), 0); + //gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_debug); + + gtk_window_set_titlebar(GTK_WINDOW(window), header_bar); + gtk_widget_show(window); +} + +int main(int argc, + char **argv) { + GtkApplication *app = gtk_application_new("org.rwinkhart.sshyp", G_APPLICATION_FLAGS_NONE); + g_signal_connect(app, "activate", G_CALLBACK(activate), 0); + int status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); + return status; +} diff --git a/extensions/password-pasture/requirements.txt b/extensions/password-pasture/requirements.txt new file mode 100644 index 0000000..bcf80ca --- /dev/null +++ b/extensions/password-pasture/requirements.txt @@ -0,0 +1,3 @@ +arch: base-devel gtk4 cmake make gcc + +alpine: build-base gtk4.0 gtk4.0-dev cmake make gcc binutils abuild diff --git a/extensions/sshyp-mfa/sshyp-mfa.ext b/extensions/sshyp-mfa/sshyp-mfa.ext new file mode 100644 index 0000000..0f9a762 --- /dev/null +++ b/extensions/sshyp-mfa/sshyp-mfa.ext @@ -0,0 +1,3 @@ +[config] +input = copy -m +output = sshyp-mfa diff --git a/extensions/sshyp-mfa/sshyp-mfa.py b/extensions/sshyp-mfa/sshyp-mfa.py new file mode 100755 index 0000000..62c625b --- /dev/null +++ b/extensions/sshyp-mfa/sshyp-mfa.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +from base64 import b32decode +from configparser import ConfigParser +from os import environ, listdir, uname +from os.path import expanduser, isdir, isfile +from sshyp import decrypt, whitelist_verify +from subprocess import PIPE, Popen, run +from sys import argv, exit as s_exit +from time import sleep, strftime, time +home = expanduser("~") + + +def totp(_secret, _algo, _digits, _period): # uses provided information to generate a standard totp key + from hmac import new as new_mac + from struct import pack, unpack + _secret = b32decode(_secret.upper() + '=' * ((8 - len(_secret)) % 8)) + _counter = pack('>Q', int(time() / _period)) + _mac = new_mac(_secret, _counter, _algo).digest() + _offset = _mac[-1] & 0x0f + _binary = unpack('>L', _mac[_offset:_offset + 4])[0] & 0x7fffffff + return str(_binary)[-_digits:].zfill(_digits) + + +def mfa_read_shortcut(): # extracts MFA info from the user-specified sshyp entry + if not isfile(f"{directory}{arguments[0]}.gpg"): + print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not exist\u001b[0m\n") + s_exit(1) + if quick_unlock_enabled == 'true': + _mfa_data = decrypt(directory + arguments[0], _quick_pass=whitelist_verify(port, username_ssh, ip, device_id)) + else: + _mfa_data = decrypt(directory + arguments[0]) + try: + _type = _mfa_data[4].split('otpauth://')[1].split('/')[0] + _secret = _mfa_data[4].split('?secret=')[1].split('&issuer=')[0] + _algo = _mfa_data[4].split('&algorithm=')[1].split('&digits=')[0] + _digits = int(_mfa_data[4].split('&digits=')[1].split('&period=')[0]) + _period = int(_mfa_data[4].split('&period=')[1]) + return _type, _secret, _algo, _digits, _period + except IndexError: + print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not contain valid mfa data\u001b[0m\n") + s_exit(1) + + +if __name__ == '__main__': + # argument fetcher + arguments = argv[1:] + if len(arguments) < 1 or not arguments[0].startswith('/'): + print("\nsshyp-mfa extension usage: sshyp copy -m\n\nrun 'man sshyp-mfa' for more information\n") + s_exit(1) + + # user data fetcher + sshyp_data = ConfigParser() + sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini") + quick_unlock_enabled = sshyp_data.get('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 = sshyp_data.get('SSHYNC', 'local_dir') + device_id = listdir(f"{home}/.config/sshyp/devices")[0] + + # main process: runs functions to generate MFA key, then continuously copies up-to-date MFA key to clipboard + try: + mfa_data, copied = mfa_read_shortcut(), None + print('\nmfa key copied to clipboard\n\nuntil this process is closed, your clipboard will be automatically ' + 'updated with the newest mfa key') + while True: + if str(int(strftime('%S'))/mfa_data[4]).endswith('.0') or copied is None: + if copied is None: + copied = 1 + if mfa_data[0] == 'steam': + from steam.guard import generate_twofactor_code as steam_totp + _mfa_key = steam_totp(b32decode(mfa_data[1])) + else: + _mfa_key = totp(mfa_data[1], mfa_data[2], mfa_data[3], mfa_data[4]) + if 'WSL_DISTRO_NAME' in environ: # WSL clipboard detection + run(('powershell.exe', '-c', 'Set-Clipboard', _mfa_key)) + elif 'WAYLAND_DISPLAY' in environ: # Wayland clipboard detection + run(('wl-copy', _mfa_key)) + elif uname()[0] == 'Haiku': # Haiku clipboard detection + run(('clipboard', '-c', _mfa_key)) + elif uname()[0] == 'Darwin': # MacOS clipboard detection + run('pbcopy', stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout) + elif isdir("/data/data/com.termux"): # Termux (Android) clipboard detection + run(('termux-clipboard-set', _mfa_key)) + else: # X11 clipboard detection + run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout) + sleep(1) + except KeyboardInterrupt: + print('\n') + s_exit(0) diff --git a/pointers/v1.5.0 b/pointers/v1.5.0 new file mode 100644 index 0000000..a39c3b0 --- /dev/null +++ b/pointers/v1.5.0 @@ -0,0 +1,6 @@ +[sshyp-mfa] +supported = true +desc = read mfa data from sshyp entries to generate and copy totp keys to the clipboard (optional Steam support) +usage = sshyp / copy -m +py = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.1/sshyp-mfa/sshyp-mfa.py +ext = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.1/sshyp-mfa/sshyp-mfa.ext From 19ff50a5db838ba93856dae80e8bedababd05f8b Mon Sep 17 00:00:00 2001 From: Randall Winkhart <90295779+rwinkhart@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:51:21 -0400 Subject: [PATCH 2/8] Delete version --- version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 version diff --git a/version b/version deleted file mode 100644 index 10be734..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -1.4.0.1 From 0bbcda37f2ae30d546a413fa6f534bfddcd53573 Mon Sep 17 00:00:00 2001 From: Randall Winkhart <90295779+rwinkhart@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:52:35 -0400 Subject: [PATCH 3/8] Delete sshyp-mfa directory --- sshyp-mfa/extra/manpage | 44 --- sshyp-mfa/lib/extensions/sshyp-mfa | 3 - sshyp-mfa/lib/sshyp-mfa.py | 90 ------ sshyp-mfa/package.sh | 311 --------------------- sshyp-mfa/requirements.txt | 1 - sshyp-mfa/share/licenses/sshyp-mfa/license | 14 - 6 files changed, 463 deletions(-) delete mode 100644 sshyp-mfa/extra/manpage delete mode 100644 sshyp-mfa/lib/extensions/sshyp-mfa delete mode 100755 sshyp-mfa/lib/sshyp-mfa.py delete mode 100755 sshyp-mfa/package.sh delete mode 100644 sshyp-mfa/requirements.txt delete mode 100644 sshyp-mfa/share/licenses/sshyp-mfa/license diff --git a/sshyp-mfa/extra/manpage b/sshyp-mfa/extra/manpage deleted file mode 100644 index b82357e..0000000 --- a/sshyp-mfa/extra/manpage +++ /dev/null @@ -1,44 +0,0 @@ -.TH sshyp-mfa 1 "04 March 2023" "v1.4.0.1" "sshyp-mfa man page" -.SH NAME -sshyp-mfa \- An MFA (TOTP/Steam) key generator for the sshyp password manager. -.SH SYNOPSIS -Extension Usage: sshyp copy -m - -Direct Usage: sshyp-mfa -.SH DESCRIPTION -sshyp-mfa is an extension for the sshyp password manager that reads MFA data from sshyp entries and generates generic TOTP and Steam keys. -.SH EXAMPLES -Generating and copying an MFA key for an existing entry saved as ~/.local/share/sshyp-mfa/development/github.gpg - sshyp /development/github copy -m - sshyp-mfa /development/github - -.SH SETUP -sshyp-mfa requires a pre-existing functional sshyp setup. - -MFA keys are generated off of MFA data placed in the second line of a sshyp entry's notes field. This data needs to be added manually, through sshyp, and must be in Authenticator backup format. - -It might be easiest to import all of your MFA keys into Authenticator, export them into plain text, then add all of the exported MFA data into the second notes line in their respective sshyp entries. -.SH FORMAT -Format: otpauth:///?secret=&issuer=&algorithm=&digits=&period= - -Help! What do I put in each of those spaces? - - is almost always 'totp', but in the case of Steam, it needs to be set to 'steam'. - - is a part of the Authenticator backup format, but it is not used in sshyp-mfa. Set to anything. - - refers to the secret used to generate your MFA key. This is usually directly provided by the issuer, but it is sometimes hidden and more easily retrieved by copying it from a QR-compatible MFA app (such as Aegis). - - is a part of the Authenticator backup format, but it is not used in sshyp-mfa. Set to anything. - - refers to the algorithm used to generate your MFA key based on your secret. This is almost always 'sha1'. - - refers to the intended length of your MFA key. This is almost always '6', but in the case of Steam, it needs to be set to '5'. - - refers to the interval at which a new MFA key needs to be generated. This is almost always '30', for 30 seconds. - -Example configuration (for most services, see above for differences regarding Steam): - -otpauth://totp/MyNameIsBob?secret=YUGBSG65SG9SDBSDF56SBFVSC86SBVD6&issuer=Github&algorithm=sha1&digits=6&period=30 -.SH AUTHOR -Randall Winkhart (https://github.com/rwinkhart) diff --git a/sshyp-mfa/lib/extensions/sshyp-mfa b/sshyp-mfa/lib/extensions/sshyp-mfa deleted file mode 100644 index 0f9a762..0000000 --- a/sshyp-mfa/lib/extensions/sshyp-mfa +++ /dev/null @@ -1,3 +0,0 @@ -[config] -input = copy -m -output = sshyp-mfa diff --git a/sshyp-mfa/lib/sshyp-mfa.py b/sshyp-mfa/lib/sshyp-mfa.py deleted file mode 100755 index 62c625b..0000000 --- a/sshyp-mfa/lib/sshyp-mfa.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 -from base64 import b32decode -from configparser import ConfigParser -from os import environ, listdir, uname -from os.path import expanduser, isdir, isfile -from sshyp import decrypt, whitelist_verify -from subprocess import PIPE, Popen, run -from sys import argv, exit as s_exit -from time import sleep, strftime, time -home = expanduser("~") - - -def totp(_secret, _algo, _digits, _period): # uses provided information to generate a standard totp key - from hmac import new as new_mac - from struct import pack, unpack - _secret = b32decode(_secret.upper() + '=' * ((8 - len(_secret)) % 8)) - _counter = pack('>Q', int(time() / _period)) - _mac = new_mac(_secret, _counter, _algo).digest() - _offset = _mac[-1] & 0x0f - _binary = unpack('>L', _mac[_offset:_offset + 4])[0] & 0x7fffffff - return str(_binary)[-_digits:].zfill(_digits) - - -def mfa_read_shortcut(): # extracts MFA info from the user-specified sshyp entry - if not isfile(f"{directory}{arguments[0]}.gpg"): - print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not exist\u001b[0m\n") - s_exit(1) - if quick_unlock_enabled == 'true': - _mfa_data = decrypt(directory + arguments[0], _quick_pass=whitelist_verify(port, username_ssh, ip, device_id)) - else: - _mfa_data = decrypt(directory + arguments[0]) - try: - _type = _mfa_data[4].split('otpauth://')[1].split('/')[0] - _secret = _mfa_data[4].split('?secret=')[1].split('&issuer=')[0] - _algo = _mfa_data[4].split('&algorithm=')[1].split('&digits=')[0] - _digits = int(_mfa_data[4].split('&digits=')[1].split('&period=')[0]) - _period = int(_mfa_data[4].split('&period=')[1]) - return _type, _secret, _algo, _digits, _period - except IndexError: - print(f"\n\u001b[38;5;9merror: entry ({arguments[0]}) does not contain valid mfa data\u001b[0m\n") - s_exit(1) - - -if __name__ == '__main__': - # argument fetcher - arguments = argv[1:] - if len(arguments) < 1 or not arguments[0].startswith('/'): - print("\nsshyp-mfa extension usage: sshyp copy -m\n\nrun 'man sshyp-mfa' for more information\n") - s_exit(1) - - # user data fetcher - sshyp_data = ConfigParser() - sshyp_data.read(f"{home}/.config/sshyp/sshyp.ini") - quick_unlock_enabled = sshyp_data.get('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 = sshyp_data.get('SSHYNC', 'local_dir') - device_id = listdir(f"{home}/.config/sshyp/devices")[0] - - # main process: runs functions to generate MFA key, then continuously copies up-to-date MFA key to clipboard - try: - mfa_data, copied = mfa_read_shortcut(), None - print('\nmfa key copied to clipboard\n\nuntil this process is closed, your clipboard will be automatically ' - 'updated with the newest mfa key') - while True: - if str(int(strftime('%S'))/mfa_data[4]).endswith('.0') or copied is None: - if copied is None: - copied = 1 - if mfa_data[0] == 'steam': - from steam.guard import generate_twofactor_code as steam_totp - _mfa_key = steam_totp(b32decode(mfa_data[1])) - else: - _mfa_key = totp(mfa_data[1], mfa_data[2], mfa_data[3], mfa_data[4]) - if 'WSL_DISTRO_NAME' in environ: # WSL clipboard detection - run(('powershell.exe', '-c', 'Set-Clipboard', _mfa_key)) - elif 'WAYLAND_DISPLAY' in environ: # Wayland clipboard detection - run(('wl-copy', _mfa_key)) - elif uname()[0] == 'Haiku': # Haiku clipboard detection - run(('clipboard', '-c', _mfa_key)) - elif uname()[0] == 'Darwin': # MacOS clipboard detection - run('pbcopy', stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout) - elif isdir("/data/data/com.termux"): # Termux (Android) clipboard detection - run(('termux-clipboard-set', _mfa_key)) - else: # X11 clipboard detection - run(('xclip', '-sel', 'c'), stdin=Popen(('printf', _mfa_key), stdout=PIPE).stdout) - sleep(1) - except KeyboardInterrupt: - print('\n') - s_exit(0) diff --git a/sshyp-mfa/package.sh b/sshyp-mfa/package.sh deleted file mode 100755 index 939d896..0000000 --- a/sshyp-mfa/package.sh +++ /dev/null @@ -1,311 +0,0 @@ -#!/bin/sh - -version=$(sed -n '1{p;q}' ../version) -if [ -z "$2" ]; then - revision=1 -else - revision="$2" -fi - -_create_generic() { - printf '\npackaging as generic...\n' - mkdir -p output/generictemp/usr/bin \ - output/generictemp/usr/lib/sshyp \ - output/generictemp/usr/share/man/man1 - cp -r lib/. output/generictemp/usr/lib/sshyp/ - ln -s /usr/lib/sshyp/sshyp-mfa.py output/generictemp/usr/bin/sshyp-mfa - cp -r share output/generictemp/usr/ - cp extra/manpage output/generictemp/usr/share/man/man1/sshyp-mfa.1 - gzip output/generictemp/usr/share/man/man1/sshyp-mfa.1 - XZ_OPT=-e6 tar -C output/generictemp -cvJf output/GENERIC-sshyp-mfa-"$version".tar.xz usr/ - rm -rf output/generictemp - sha512="$(sha512sum output/GENERIC-sshyp-mfa-"$version".tar.xz | awk '{print $1;}')" - printf '\ngeneric packaging complete\n\n' -} && - -_create_pkgbuild() { - printf '\ngenerating PKGBUILD...\n' - if [ "$1" = 'Deb' ]; then - local source='https://github.com/rwinkhart/sshyp-labs/releases/download/v"$pkgver"/UBUNTU-sshyp-mfa_"$pkgver"-"$pkgrel"_all.deb' - local decomp_target='data.tar.xz' - else - local source='https://github.com/rwinkhart/sshyp-labs/releases/download/v"$pkgver"/GENERIC-sshyp-mfa-"$pkgver".tar.xz' - local decomp_target='GENERIC-sshyp-"$pkgver".tar.xz' - fi - printf "# Maintainer: Randall Winkhart -pkgname=sshyp-mfa -pkgver="$version" -pkgrel="$revision" -pkgdesc='An MFA (TOTP/Steam) key generator for the sshyp password manager' -url='https://github.com/rwinkhart/sshyp-labs' -arch=('any') -license=('GPL-3.0-only') -depends=(sshyp) -source=(\""$source"\") -sha512sums=('"$sha512"') - -package() { - - tar -xf $decomp_target -C "\"\${pkgdir}\"" - -} -" > output/PKGBUILD - printf '\nPKGBUILD generated\n\n' -} && - -_create_apkbuild() { - printf '\ngenerating APKBUILD...\n' - if [ "$1" = 'Deb' ]; then - local source="https://github.com/rwinkhart/sshyp-labs/releases/download/v\"\$pkgver\"/UBUNTU-sshyp-mfa_\"\$pkgver\"-"$revision"_all.deb" - local sumsname="UBUNTU-sshyp-mfa_\"\$pkgver\"-"$revision"_all.deb" - local processing='mkdir -p "$pkgdir" - 7z x "$srcdir"/* -o"$srcdir" - tar -xf "$srcdir"/data.tar -C "$pkgdir" - else - local source='https://github.com/rwinkhart/sshyp-labs/releases/download/v"$pkgver"/GENERIC-sshyp-mfa-"$pkgver".tar.xz' - local sumsname='GENERIC-sshyp-"$pkgver".tar.xz' - local processing='mkdir -p "$pkgdir" - cp -r "$srcdir/usr/" "$pkgdir"' - fi - printf "# Maintainer: Randall Winkhart -pkgname=sshyp-mfa -pkgver="$version" -pkgrel="$((revision-1))" -pkgdesc='An MFA (TOTP/Steam) key generator for the sshyp password manager' -options=!check -url='https://github.com/rwinkhart/sshyp-labs' -arch='noarch' -license='GPL-3.0-only' -depends='sshyp' -source=\""$source"\" - -package() { - $processing -} - -sha512sums=\" -"$sha512" "$sumsname" -\" -" > output/APKBUILD - printf '\nAPKBUILD generated\n\n' -} && - -_create_hpkg() { - printf '\npackaging for Haiku...\n' - mkdir -p output/haikutemp/bin \ - output/haikutemp/lib/sshyp \ - output/haikutemp/documentation/man/man1 \ - output/haikutemp/documentation/packages/sshyp-mfa - printf "name sshypmfa -version "$version"-"$revision" -architecture any -summary \"An MFA (TOTP/Steam) key generator for the sshyp password manager\" -description \"sshyp-mfa is an extension for the sshyp password manager that reads MFA data from sshyp entries and generates generic TOTP and Steam keys.\" -packager \"Randall Winkhart \" -vendor \"Randall Winkhart\" -licenses { - \"GNU GPL v3\" -} -copyrights { - \"2021-2023 Randall Winkhart\" -} -provides { - sshypmfa = "$version" - cmd:sshypmfa -} -requires { - sshyp -} -urls { - \"https://github.com/rwinkhart/sshyp-labs\" -} -" > output/haikutemp/.PackageInfo - cp -r lib/. output/haikutemp/lib/sshyp/ - sed -i '1 s/.*/#!\/bin\/env\ python3.10/' output/haikutemp/lib/sshyp/sshyp-mfa.py - ln -s /system/lib/sshyp/sshyp-mfa.py output/haikutemp/bin/sshyp-mfa - cp -r share/licenses/sshyp-mfa/. output/haikutemp/documentation/packages/sshyp-mfa/ - cp extra/manpage output/haikutemp/documentation/man/man1/sshyp-mfa.1 - gzip output/haikutemp/documentation/man/man1/sshyp-mfa.1 - cd output/haikutemp - package create -b HAIKU-sshyp-mfa-"$version"-"$revision"_all.hpkg - package add HAIKU-sshyp-mfa-"$version"-"$revision"_all.hpkg bin lib documentation - cd ../.. - mv output/haikutemp/HAIKU-sshyp-mfa-"$version"-"$revision"_all.hpkg output/ - rm -rf output/haikutemp - printf "\nHaiku packaging complete\n\n" -} && - -_create_deb() { - printf '\npackaging for Debian/Ubuntu...\n' - mkdir -p output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/DEBIAN \ - output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/lib/sshyp \ - output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/bin \ - output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/share/man/man1 - printf "Package: sshyp-mfa -Version: $version -Section: utils -Architecture: all -Maintainer: Randall Winkhart -Description: An MFA (TOTP/Steam) key generator for the sshyp password manager -Depends: sshyp -Priority: optional -Installed-Size: 100 -" > output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/DEBIAN/control - cp -r lib/. output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/lib/sshyp/ - ln -s /usr/lib/sshyp/sshyp-mfa.py output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/bin/sshyp-mfa - cp -r share output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/ - cp extra/manpage output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/share/man/man1/sshyp-mfa.1 - gzip output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/usr/share/man/man1/sshyp-mfa.1 - dpkg-deb --build --root-owner-group -z6 -Sextreme -Zxz output/debiantemp/sshyp-mfa_"$version"-"$revision"_all/ - mv output/debiantemp/sshyp-mfa_"$version"-"$revision"_all.deb output/UBUNTU-sshyp_"$version"-"$revision"_all.deb - rm -rf output/debiantemp - sha512="$(sha512sum output/UBUNTU-sshyp-mfa_"$version"-"$revision"_all.deb | awk '{print $1;}')" - printf '\nDebian/Ubuntu packaging complete\n\n' -} && - -_create_termux() { - printf '\npackaging for Termux...\n' - mkdir -p output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/DEBIAN \ - output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp \ - output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin \ - output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1 - printf "Package: sshyp-mfa -Version: $version -Section: utils -Architecture: all -Maintainer: Randall Winkhart -Description: An MFA (TOTP/Steam) key generator for the sshyp password manager -Depends: sshyp -Priority: optional -Installed-Size: 100 -" > output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/DEBIAN/control - cp -r lib/. output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/lib/sshyp/ - ln -s /data/data/com.termux/files/usr/lib/sshyp/sshyp-mfa.py output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/bin/sshyp-mfa - cp -r share output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/ - cp extra/manpage output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp-mfa.1 - gzip output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/data/data/com.termux/files/usr/share/man/man1/sshyp-mfa.1 - dpkg-deb --build --root-owner-group output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux/ - mv output/termuxtemp/sshyp-mfa_"$version"-"$revision"_all_termux.deb output/TERMUX-sshyp_"$version"-"$revision"_all.deb - rm -rf output/termuxtemp - printf '\nTermux packaging complete\n\n' -} && - -_create_rpm() { - printf '\npackaging for Fedora...\n' - rm -rf ~/rpmbuild - rpmdev-setuptree - mkdir -p output/fedoratemp/usr/bin \ - output/fedoratemp/usr/lib/sshyp/extensions \ - mkdir -p output/fedoratemp/usr/bin \ - output/fedoratemp/usr/lib/sshyp \ - output/fedoratemp/usr/share/man/man1 - printf "Name: sshyp-mfa -Version: "$version" -Release: "$revision" -Summary: An MFA (TOTP/Steam) key generator for the sshyp password manager -BuildArch: noarch -License: GPL-3.0-only -URL: https://github.com/rwinkhart/sshyp-labs -Source0: GENERIC-FEDORA-sshyp-mfa-"$version".tar.xz -Requires: sshyp -%%description -sshyp-mfa is an extension for the sshyp password manager that reads MFA data from sshyp entries and generates generic TOTP and Steam keys. -%%install -tar xf %%{_sourcedir}/GENERIC-FEDORA-sshyp-mfa-"$version".tar.xz -C %%{_sourcedir} -cp -r %%{_sourcedir}/usr %%{buildroot} -%%files -/usr/bin/sshyp-mfa -/usr/lib/sshyp/sshyp-mfa.py -/usr/lib/sshyp/extensions/sshyp-mfa -%%license /usr/share/licenses/sshyp-mfa/license -%%doc /usr/share/man/man1/sshyp-mfa.1.gz -" > ~/rpmbuild/SPECS/sshyp-mfa.spec -ln -s /usr/lib/sshyp/sshyp-mfa.py output/fedoratemp/usr/bin/sshyp-mfa -cp -r share output/fedoratemp/usr/ -cp extra/manpage output/fedoratemp/usr/share/man/man1/sshyp-mfa.1 -gzip output/fedoratemp/usr/share/man/man1/sshyp-mfa.1 -XZ_OPT=-e6 tar -C output/fedoratemp -cvJf output/GENERIC-FEDORA-sshyp-mfa-"$version".tar.xz usr/ -rm -rf output/fedoratemp -cp output/GENERIC-FEDORA-sshyp-mfa-"$version".tar.xz ~/rpmbuild/SOURCES -rpmbuild -bb ~/rpmbuild/SPECS/sshyp-mfa.spec -mv ~/rpmbuild/RPMS/noarch/sshyp-mfa-"$version"-"$revision".noarch.rpm output/FEDORA-sshyp-mfa-"$version"-"$revision".noarch.rpm -rm -rf ~/rpmbuild -printf '\nFedora packaging complete\n\n' -} && - -_create_freebsd_pkg() { - printf '\npackaging for FreeBSD...\n' - mkdir -p output/freebsdtemp/usr/lib/sshyp \ - output/freebsdtemp/usr/bin \ - output/freebsdtemp/usr/share/man/man1 - printf "name: sshyp-mfa -version: \""$version"\" -abi = \"FreeBSD:13:*\"; -arch = \"freebsd:13:*\"; -origin: security/sshyp-mfa -comment: \"a sshyp extension\" -desc: \"an MFA (TOTP/Steam) key generator for the sshyp password manager\" -maintainer: -www: https://github.com/rwinkhart/sshyp-labs -prefix: / -\"deps\" : { - \"sshyp\" : { - \"origin\" : \"security/sshyp\" - }, - }, -" > output/freebsdtemp/+MANIFEST -printf "/usr/bin/sshyp-mfa -/usr/lib/sshyp/sshyp-mfa.py -/usr/lib/sshyp/extensions/sshyp-mfa -/usr/share/licenses/sshyp-mfa/license -/usr/share/man/man1/sshyp-mfa.1.gz -" > output/freebsdtemp/plist -cp -r lib/. output/freebsdtemp/usr/lib/sshyp/ -ln -s /usr/lib/sshyp/sshyp-mfa.py output/freebsdtemp/usr/bin/sshyp-mfa -cp -r share output/freebsdtemp/usr/ -cp extra/manpage output/freebsdtemp/usr/share/man/man1/sshyp-mfa.1 -gzip output/freebsdtemp/usr/share/man/man1/sshyp-mfa.1 -pkg create -m output/freebsdtemp/ -r output/freebsdtemp/ -p output/freebsdtemp/plist -o output/ -mv output/sshyp-mfa-"$version".pkg output/FREEBSD-sshyp-mfa-"$version"-"$revision".pkg -rm -rf output/freebsdtemp -printf '\nFreeBSD packaging complete\n\n' -} && - -case "$1" in - pkgbuild) - _create_deb - _create_pkgbuild Deb - ;; - apkbuild) - _create_deb - _create_apkbuild Deb - ;; - haiku) - _create_hpkg - ;; - debian) - _create_deb - ;; - termux) - _create_termux - ;; - fedora) - _create_rpm - ;; - freebsd) - _create_freebsd_pkg - ;; - buildable-arch) - _create_deb - _create_pkgbuild Deb - _create_apkbuild Deb - case "$(pacman -Q freebsd-pkg)" in - freebsd-pkg*) - _create_freebsd_pkg - ;; - esac - ;; - *) - printf '\nusage: package.sh [target] \n\ntargets:\n mainline: pkgbuild apkbuild fedora debian haiku freebsd\n experimental: termux\n groups: buildable-arch\n\n' - ;; -esac diff --git a/sshyp-mfa/requirements.txt b/sshyp-mfa/requirements.txt deleted file mode 100644 index 51eb0da..0000000 --- a/sshyp-mfa/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -steam[client] diff --git a/sshyp-mfa/share/licenses/sshyp-mfa/license b/sshyp-mfa/share/licenses/sshyp-mfa/license deleted file mode 100644 index 24d5321..0000000 --- a/sshyp-mfa/share/licenses/sshyp-mfa/license +++ /dev/null @@ -1,14 +0,0 @@ - sshyp-mfa is a FOSS extension for the sshyp password manager. - Copyright (C) 2022-2023 Randall Winkhart idgr@tutanota.com - - This program is free software: you can redistribute it and/or modify - it under the terms of version 3 (only) of the GNU General Public License - as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . From 23be7f8c3018ecb33d112b5c22e0a590169e37ae Mon Sep 17 00:00:00 2001 From: Randall Winkhart <90295779+rwinkhart@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:52:44 -0400 Subject: [PATCH 4/8] Delete password-pasture directory --- password-pasture/CMakeLists.txt | 16 -- password-pasture/build.sh | 5 - password-pasture/main.c | 380 ------------------------------ password-pasture/requirements.txt | 3 - 4 files changed, 404 deletions(-) delete mode 100644 password-pasture/CMakeLists.txt delete mode 100755 password-pasture/build.sh delete mode 100644 password-pasture/main.c delete mode 100644 password-pasture/requirements.txt diff --git a/password-pasture/CMakeLists.txt b/password-pasture/CMakeLists.txt deleted file mode 100644 index bd166ba..0000000 --- a/password-pasture/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.23) -project(password_pasture C) - -set(CMAKE_C_STANDARD 17) - -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK4 REQUIRED gtk4) - -include_directories(${GTK4_INCLUDE_DIRS}) -link_directories(${GTK4_LIBRARY_DIRS}) - -add_definitions(${GTK4_CFLAGS_OTHER}) - -add_executable(password_pasture main.c) - -target_link_libraries(password_pasture ${GTK4_LIBRARIES}) \ No newline at end of file diff --git a/password-pasture/build.sh b/password-pasture/build.sh deleted file mode 100755 index 3fece79..0000000 --- a/password-pasture/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -rm -rf CMakeFiles cmake_install.cmake CMakeCache.txt Makefile -cmake ./ -sleep 10 # prevents clock skew -make -j$(nproc) diff --git a/password-pasture/main.c b/password-pasture/main.c deleted file mode 100644 index 3772d26..0000000 --- a/password-pasture/main.c +++ /dev/null @@ -1,380 +0,0 @@ -#include -#include -#include -#include -#include - -GtkWidget *label_selected; GtkListBox *list_box; -char entries[1024][1024]; int entry_array_count = 0; - -void str_remove(char *str, const char *sub) { - char *p, *q, *r; - if (*sub && (q = r = strstr(str, sub)) != 0) { - size_t len = strlen(sub); - while ((r = strstr(p = r + len, sub)) != 0) { - while (p < r) - *q++ = *p++; - } - while ((*q++ = *p++) != '\0') - continue; - } -} - -void gen_entry_array(char *path, size_t size, const char *path_orig) { - DIR *dir; - struct dirent *entry; - size_t len = strlen(path); - - if (!(dir = opendir(path))) { - return; } - - while ((entry = readdir(dir)) != 0) { - char *name = entry->d_name; - if (entry->d_type == DT_DIR) { - if (!strcmp(name, ".") || !strcmp(name, "..")) { - continue; } - path[len] = '/'; - strcpy(path + len + 1, name); - gen_entry_array(path, size, path_orig); - path[len] = '\0'; - } else { - char path_filtered[1024]; - strcpy(path_filtered, path); - str_remove(path_filtered, path_orig); - size_t name_len = strlen(name); - name[name_len-4] = 0; - char sshyp_path[1024]; strcpy(sshyp_path, path_filtered); strcat(sshyp_path, "/"); strcat(sshyp_path, name); - strcpy(entries[entry_array_count], sshyp_path); - entry_array_count++; - } - } - closedir(dir); -} - -void password_convert(GtkEntryBuffer *buffer) { - const char *password = gtk_entry_buffer_get_text(buffer); - char *sub1 = "gpg --pinentry-mode loopback --batch --passphrase-fd 0 --armor -qd --output /dev/null ~/.config/sshyp/lock.gpg <<< '", *sub2 = "'", *command = (char *) malloc(2048); - strcpy(command, sub1); strcat(command, password); strcat(command, sub2); - system(command); - free(command); -} - -void gpg_lock() { - system("gpgconf --reload gpg-agent"); -} - -void password_prompt(GtkWindow *window) { - GtkWidget *dialog, *dialog_header, *box_header; // main window and header bar - GtkWidget *button_unlock, *button_cancel, *button_lock; // header bar buttons - GtkWidget *content_area, *password_entry; // content area and widgets - - dialog = gtk_dialog_new(); - password_entry = gtk_entry_new(); - gtk_entry_set_visibility(GTK_ENTRY(password_entry), FALSE); - gtk_entry_set_placeholder_text(GTK_ENTRY(password_entry), "passphrase"); - dialog_header = gtk_header_bar_new(); - gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE); - box_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_halign(box_header, GTK_ALIGN_CENTER); - button_unlock = gtk_button_new_with_label("unlock"); - button_cancel = gtk_button_new_with_label("cancel"); - button_lock = gtk_button_new_from_icon_name("changes-prevent"); - gtk_widget_set_size_request(button_lock, 20, -1); - gtk_box_append(GTK_BOX(box_header), button_unlock); - gtk_box_append(GTK_BOX(box_header), button_cancel); - gtk_box_append(GTK_BOX(box_header), button_lock); - gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), box_header); - gtk_window_set_title(GTK_WINDOW(dialog), ""); - gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window)); - gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); - gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50); - - // add password entry to dialog content area - content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - gtk_box_append(GTK_BOX(content_area), password_entry); - - gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header); - gtk_widget_show(dialog); - - // button actions - g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(password_convert), gtk_entry_get_buffer(GTK_ENTRY(password_entry))); - g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect(button_lock, "clicked", G_CALLBACK(gpg_lock), 0); - g_signal_connect_swapped(button_lock, "clicked", G_CALLBACK(gtk_window_close), dialog); -} - -void copy_item(GtkWidget *button) { - char *command = malloc(2048); const char *button_label = gtk_button_get_label(GTK_BUTTON(button)); - strcpy(command, "sshyp copy "); - strcat(command, button_label); strcat(command, " "); - strcat(command, gtk_label_get_text((GtkLabel *) label_selected)); - system(command); - free(command); -} - -void copy_edit_field_prompt(GtkWidget *button, gpointer *window) { - GtkWidget *dialog, *dialog_header; // main window and header bar - GtkWidget *button_cancel; // header bar buttons - GtkWidget *content_area, *button_pwd, *button_usr, *button_url, *button_nte, *button_mfa; // content area and widgets - int mode; // copy/edit mode flag - - // set mode based on button pressed, copy = 1, edit = 2 - const char *button_label = gtk_button_get_icon_name(GTK_BUTTON(button)); - if (strcmp(button_label, "edit-copy") == 0) { - mode = 1; - } else { - mode = 2; - } - - dialog = gtk_dialog_new(); - dialog_header = gtk_header_bar_new(); - gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE); - button_cancel = gtk_button_new_with_label("cancel"); - gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), button_cancel); - gtk_window_set_title(GTK_WINDOW(dialog), ""); - gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window)); - gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); - gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50); - - // add options to dialog content area - content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - button_pwd = gtk_button_new_with_label("password"); - button_usr = gtk_button_new_with_label("username"); - button_url = gtk_button_new_with_label("url"); - button_nte = gtk_button_new_with_label("note"); - button_mfa = gtk_button_new_with_label("mfa"); - gtk_box_append(GTK_BOX(content_area), button_pwd); - gtk_box_append(GTK_BOX(content_area), button_usr); - gtk_box_append(GTK_BOX(content_area), button_url); - gtk_box_append(GTK_BOX(content_area), button_nte); - gtk_box_append(GTK_BOX(content_area), button_mfa); - - gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header); - gtk_widget_show(dialog); - - // button actions - if (mode == 1) { - g_signal_connect(button_pwd, "clicked", G_CALLBACK(copy_item), 0); - g_signal_connect(button_usr, "clicked", G_CALLBACK(copy_item), 0); - g_signal_connect(button_url, "clicked", G_CALLBACK(copy_item), 0); - g_signal_connect(button_nte, "clicked", G_CALLBACK(copy_item), 0); - g_signal_connect(button_mfa, "clicked", G_CALLBACK(copy_item), 0); - } - - g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect_swapped(button_pwd, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect_swapped(button_usr, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect_swapped(button_url, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect_swapped(button_nte, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect_swapped(button_mfa, "clicked", G_CALLBACK(gtk_window_close), dialog); -} - -void set_selection_label(GtkWidget *button, gpointer *label) { - const char *button_label = gtk_button_get_label(GTK_BUTTON(button)); - gtk_label_set_text(GTK_LABEL(label), button_label); -} - -void entry_list_gen() { - GtkWidget *entry_button; - - GtkWidget *iter = gtk_widget_get_first_child ((GtkWidget *) list_box); - while (iter != 0) { - GtkWidget *next = gtk_widget_get_next_sibling (iter); - gtk_list_box_remove (list_box, iter); - iter = next; - } - - char path[1024]; char *user_home = getenv("HOME"); strcpy(path, user_home); strcat(path, "/.local/share/sshyp"); - char path_orig[1024]; strcpy(path_orig, path); strcat(path_orig, "/"); - gen_entry_array(path, sizeof path, path_orig); - - // bubble sort entries array - char *temp = (char *) malloc(1024); - for(int i=0; i 0){ - strcpy(temp, entries[j]); - strcpy(entries[j], entries[j+1]); - strcpy(entries[j+1], temp); - } - } - } - free(temp); - - strcpy(entries[entry_array_count], "\0"); - - for (int i_2 = 0; strcmp(entries[i_2], "\0") != 0; i_2++) { - entry_button = gtk_button_new_with_label(entries[i_2]); - gtk_label_set_xalign(GTK_LABEL(gtk_button_get_child(GTK_BUTTON(entry_button))), GTK_JUSTIFY_LEFT); - gtk_button_set_has_frame(GTK_BUTTON(entry_button), FALSE); - g_signal_connect(entry_button, "clicked", G_CALLBACK(set_selection_label), (gpointer *)label_selected); - gtk_list_box_append(list_box, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); - gtk_list_box_append(list_box, entry_button); - } -} - -void sshyp_sync() { - system("sshyp sync"); - entry_array_count = 0; - entry_list_gen(); -} - -void delete_entry() { - char *command = (char *) malloc(2048); - strcpy(command, "sshyp shear "); strcat(command, gtk_label_get_text(GTK_LABEL(label_selected))); - system(command); - free(command); - entry_array_count = 0; - entry_list_gen(); -} - -void are_they_sure(GtkWindow *window) { - GtkWidget *dialog, *dialog_header, *box_header; // main window and header bar - GtkWidget *button_delete, *button_cancel; // header bar buttons - GtkWidget *content_area, *label_sure; // content area and widgets - - dialog = gtk_dialog_new(); - dialog_header = gtk_header_bar_new(); - gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(dialog_header), FALSE); - box_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_halign(box_header, GTK_ALIGN_CENTER); - button_delete = gtk_button_new_with_label("delete"); - button_cancel = gtk_button_new_with_label("cancel"); - gtk_box_append(GTK_BOX(box_header), button_delete); - gtk_box_append(GTK_BOX(box_header), button_cancel); - gtk_header_bar_set_title_widget(GTK_HEADER_BAR(dialog_header), box_header); - gtk_window_set_title(GTK_WINDOW(dialog), ""); - gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window)); - gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); - gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 50); - - // add label to dialog content area - content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - gtk_widget_set_halign(content_area, GTK_ALIGN_CENTER); - char *label_text = malloc(2048); - strcpy(label_text, "this will delete:\n"); - strcat(label_text, gtk_label_get_text(GTK_LABEL(label_selected))); - strcat(label_text, "\nare you sure?"); - label_sure = gtk_label_new(label_text); - free(label_text); - gtk_label_set_justify(GTK_LABEL(label_sure), GTK_JUSTIFY_CENTER); - gtk_box_append(GTK_BOX(content_area), label_sure); - - // button actions - g_signal_connect(button_delete, "clicked", G_CALLBACK(delete_entry), 0); - g_signal_connect_swapped(button_delete, "clicked", G_CALLBACK(gtk_window_close), dialog); - g_signal_connect_swapped(button_cancel, "clicked", G_CALLBACK(gtk_window_close), dialog); - - gtk_window_set_titlebar(GTK_WINDOW(dialog), dialog_header); - gtk_widget_show(dialog); -} - -static void activate(GtkApplication* app) { - GtkWidget *window, *header_bar; // main window and header bar - GtkWidget *button_sync, *button_unlock; // header bar buttons - GtkWidget *stack, *stack_switcher; GtkStackPage *page_info; // stack widgets - GtkWidget *box_info, *sshyp_logo, *label_home1; // info page widgets - GtkWidget *box_browse_page, *box_browse_controls; // browse page boxes - GtkWidget *button_shear, *button_read, *button_edit, *button_copy; // browse buttons - GtkWidget *scrollable; // scrollable entry list container - - window = gtk_application_window_new(app); - gtk_window_set_title(GTK_WINDOW(window), ""); - gtk_window_set_default_size(GTK_WINDOW(window), 360, 720); - - header_bar = gtk_header_bar_new(); - gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(header_bar), FALSE); - - // !!start main stack stuff!! - - // create the stack - stack = gtk_stack_new(); - gtk_stack_set_transition_type(GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT); - - // attach the stack to the main window - gtk_window_set_child(GTK_WINDOW(window), stack); - - // browse - button_shear = gtk_button_new_from_icon_name("edit-delete"); - gtk_widget_set_hexpand(button_shear, FALSE); - gtk_widget_set_size_request(button_shear, 50, -1); - g_signal_connect_swapped(button_shear, "clicked", G_CALLBACK(are_they_sure), window); - button_read = gtk_button_new_from_icon_name("mail-read"); - gtk_widget_set_hexpand(button_read, TRUE); - button_edit = gtk_button_new_from_icon_name("document-edit"); - gtk_widget_set_hexpand(button_edit, TRUE); - g_signal_connect(button_edit, "clicked", G_CALLBACK(copy_edit_field_prompt), (gpointer *) window); - button_copy = gtk_button_new_from_icon_name("edit-copy"); - gtk_widget_set_hexpand(button_copy, TRUE); - g_signal_connect(button_copy, "clicked", G_CALLBACK(copy_edit_field_prompt), (gpointer *) window); - box_browse_page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - label_selected = gtk_label_new("no entry selected"); - gtk_widget_set_hexpand(label_selected, TRUE); - gtk_widget_set_halign(label_selected, GTK_ALIGN_CENTER); - box_browse_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_valign(box_browse_controls, GTK_ALIGN_START); - gtk_widget_set_size_request(box_browse_controls, -1, 50); - gtk_box_append(GTK_BOX(box_browse_controls), button_shear); - gtk_box_append(GTK_BOX(box_browse_controls), button_read); - gtk_box_append(GTK_BOX(box_browse_controls), button_edit); - gtk_box_append(GTK_BOX(box_browse_controls), button_copy); - gtk_box_append(GTK_BOX(box_browse_page), label_selected); - gtk_box_append(GTK_BOX(box_browse_page), box_browse_controls); - - // !!start scrollable list!! - scrollable = gtk_scrolled_window_new(); - list_box = (GtkListBox *) gtk_list_box_new(); - gtk_widget_set_vexpand(GTK_WIDGET(list_box), TRUE); - - entry_list_gen(); - - gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrollable), GTK_WIDGET(list_box)); - // !!end scrollable list!! - - gtk_box_append(GTK_BOX(box_browse_page), GTK_WIDGET(scrollable)); - - // info - box_info = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); - sshyp_logo = gtk_image_new_from_file("PLACEHOLDER"); - gtk_image_set_pixel_size(GTK_IMAGE(sshyp_logo), 300); - label_home1 = gtk_label_new("password pasture\nexperimental gui client for sshyp"); - gtk_label_set_justify(GTK_LABEL(label_home1), GTK_JUSTIFY_CENTER); - gtk_label_set_markup(GTK_LABEL(label_home1), "password pasture\nexperimental gui client for sshyp"); - gtk_box_append(GTK_BOX(box_info), sshyp_logo); - gtk_box_append(GTK_BOX(box_info), label_home1); - - stack_switcher = gtk_stack_switcher_new(); - page_info = gtk_stack_add_titled(GTK_STACK(stack), box_info, "info", "info"); - gtk_stack_page_set_icon_name(GTK_STACK_PAGE(page_info), "help-about"); - gtk_stack_add_titled(GTK_STACK(stack), box_browse_page, "browse", "browse"); - gtk_stack_switcher_set_stack(GTK_STACK_SWITCHER(stack_switcher), GTK_STACK(stack)); - gtk_header_bar_pack_start(GTK_HEADER_BAR(header_bar), stack_switcher); - - // !!end main stack stuff!! - - // header bar buttons - button_sync = gtk_button_new_from_icon_name("view-refresh"); - g_signal_connect(button_sync, "clicked", G_CALLBACK(sshyp_sync), 0); - gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_sync); - - button_unlock = gtk_button_new_from_icon_name("changes-allow"); - g_signal_connect_swapped(button_unlock, "clicked", G_CALLBACK(password_prompt), window); - gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_unlock); - - //GtkWidget *button_debug = gtk_button_new_from_icon_name("applications-science"); - //g_signal_connect(button_debug, "clicked", G_CALLBACK(0), 0); - //gtk_header_bar_pack_end(GTK_HEADER_BAR(header_bar), button_debug); - - gtk_window_set_titlebar(GTK_WINDOW(window), header_bar); - gtk_widget_show(window); -} - -int main(int argc, - char **argv) { - GtkApplication *app = gtk_application_new("org.rwinkhart.sshyp", G_APPLICATION_FLAGS_NONE); - g_signal_connect(app, "activate", G_CALLBACK(activate), 0); - int status = g_application_run(G_APPLICATION(app), argc, argv); - g_object_unref(app); - return status; -} diff --git a/password-pasture/requirements.txt b/password-pasture/requirements.txt deleted file mode 100644 index bcf80ca..0000000 --- a/password-pasture/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -arch: base-devel gtk4 cmake make gcc - -alpine: build-base gtk4.0 gtk4.0-dev cmake make gcc binutils abuild From 08bc5dec8728c5d6d3fa9d691ddb73196b7be56f Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Tue, 27 Jun 2023 15:17:54 -0400 Subject: [PATCH 5/8] sshyp-mfa - Call extension directly, rather than through a symlink --- commit.sh | 2 +- extensions/sshyp-mfa/sshyp-mfa.ext | 2 +- pointers/v1.5.0 | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/commit.sh b/commit.sh index fb850d2..f079d57 100755 --- a/commit.sh +++ b/commit.sh @@ -1,4 +1,4 @@ #!/bin/sh -git add -f LICENSE README.md commit.sh extensions extra pointers +git add -f LICENSE README.md commit.sh extensions extra pointers .gitignore git commit -m "$1" git push diff --git a/extensions/sshyp-mfa/sshyp-mfa.ext b/extensions/sshyp-mfa/sshyp-mfa.ext index 0f9a762..2179b22 100644 --- a/extensions/sshyp-mfa/sshyp-mfa.ext +++ b/extensions/sshyp-mfa/sshyp-mfa.ext @@ -1,3 +1,3 @@ [config] input = copy -m -output = sshyp-mfa +output = /usr/lib/sshyp/sshyp-mfa.py diff --git a/pointers/v1.5.0 b/pointers/v1.5.0 index a39c3b0..959dbfe 100644 --- a/pointers/v1.5.0 +++ b/pointers/v1.5.0 @@ -1,5 +1,4 @@ [sshyp-mfa] -supported = true desc = read mfa data from sshyp entries to generate and copy totp keys to the clipboard (optional Steam support) usage = sshyp / copy -m py = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.1/sshyp-mfa/sshyp-mfa.py From e9a1728c6f70681a0231b88b166f542fedc2c70e Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Thu, 6 Jul 2023 08:57:47 -0400 Subject: [PATCH 6/8] Update variable names and file extensions to be more clear --- extensions/sshyp-mfa/{sshyp-mfa.ext => sshyp-mfa.ini} | 0 pointers/v1.5.0 | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename extensions/sshyp-mfa/{sshyp-mfa.ext => sshyp-mfa.ini} (100%) diff --git a/extensions/sshyp-mfa/sshyp-mfa.ext b/extensions/sshyp-mfa/sshyp-mfa.ini similarity index 100% rename from extensions/sshyp-mfa/sshyp-mfa.ext rename to extensions/sshyp-mfa/sshyp-mfa.ini diff --git a/pointers/v1.5.0 b/pointers/v1.5.0 index 959dbfe..6fb68c7 100644 --- a/pointers/v1.5.0 +++ b/pointers/v1.5.0 @@ -1,5 +1,5 @@ [sshyp-mfa] desc = read mfa data from sshyp entries to generate and copy totp keys to the clipboard (optional Steam support) usage = sshyp / copy -m -py = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.1/sshyp-mfa/sshyp-mfa.py -ext = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/v1.5.0.1/sshyp-mfa/sshyp-mfa.ext +exe = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/ext-manager/extensions/sshyp-mfa/sshyp-mfa.py +ini = https://raw.githubusercontent.com/rwinkhart/sshyp-labs/ext-manager/extensions/sshyp-mfa/sshyp-mfa.ini From bc09ec5e30b087fe782955598bc1ab49968c7110 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Thu, 6 Jul 2023 10:10:30 -0400 Subject: [PATCH 7/8] sshyp-mfa - Removed file extension expectation --- extensions/sshyp-mfa/sshyp-mfa.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/sshyp-mfa/sshyp-mfa.ini b/extensions/sshyp-mfa/sshyp-mfa.ini index 2179b22..f3aae0f 100644 --- a/extensions/sshyp-mfa/sshyp-mfa.ini +++ b/extensions/sshyp-mfa/sshyp-mfa.ini @@ -1,3 +1,3 @@ [config] input = copy -m -output = /usr/lib/sshyp/sshyp-mfa.py +output = /usr/lib/sshyp/sshyp-mfa From 90884b4cd7a61444cb6c985a68d61208bbea31c2 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Thu, 6 Jul 2023 13:21:40 -0400 Subject: [PATCH 8/8] Added error message when generating Steam TOTP keys without dependency --- extensions/sshyp-mfa/sshyp-mfa.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/sshyp-mfa/sshyp-mfa.py b/extensions/sshyp-mfa/sshyp-mfa.py index 62c625b..c9ae285 100755 --- a/extensions/sshyp-mfa/sshyp-mfa.py +++ b/extensions/sshyp-mfa/sshyp-mfa.py @@ -68,8 +68,12 @@ if __name__ == '__main__': if copied is None: copied = 1 if mfa_data[0] == 'steam': - from steam.guard import generate_twofactor_code as steam_totp - _mfa_key = steam_totp(b32decode(mfa_data[1])) + try: + from steam.guard import generate_twofactor_code as steam_totp + _mfa_key = steam_totp(b32decode(mfa_data[1])) + except ModuleNotFoundError: + print('\n\u001b[38;5;9merror: steam module not found\n\ninstall with "pip install -U \'steam[client]\'"\u001b[0m\n') + s_exit(6) else: _mfa_key = totp(mfa_data[1], mfa_data[2], mfa_data[3], mfa_data[4]) if 'WSL_DISTRO_NAME' in environ: # WSL clipboard detection