From 069afeae2712144145f87eb6179caf01fa3c944e Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 9 Feb 2024 14:03:27 +0100 Subject: [PATCH] Implement pinentry integration for ui --- agent/unixsocketagent.go | 44 ++++++++++++++++++++++++++++++++++ ui/src/services/goldwarden.py | 38 ++++++++++++++++++++++------- ui/src/services/pinentry.py | 28 ++++++++++++++++++++++ ui/src/ui/pinentry.py | 6 ++++- ui/src/ui/pinentry_approval.py | 4 ++-- 5 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 ui/src/services/pinentry.py diff --git a/agent/unixsocketagent.go b/agent/unixsocketagent.go index f474b8c..d1c6033 100644 --- a/agent/unixsocketagent.go +++ b/agent/unixsocketagent.go @@ -208,6 +208,50 @@ func serveAgentSession(c net.Conn, vault *vault.Vault, cfg *config.Config) { err error }{getPasswordResponse.Pin, nil} } + case getApprovalRequest := <-getApprovalChan: + log.Info("Received getApproval request") + payload := messages.PinentryApprovalRequest{ + Message: getApprovalRequest.description, + } + payloadPayload, err := messages.IPCMessageFromPayload(payload) + if err != nil { + writeError(c, err) + continue + } + + payloadBytes, err := json.Marshal(payloadPayload) + if err != nil { + writeError(c, err) + continue + } + + _, err = c.Write(payloadBytes) + if err != nil { + log.Error("Failed writing to socket " + err.Error()) + } + + buf := make([]byte, 1024*1024) + nr, err := c.Read(buf) + if err != nil { + return + } + + data := buf[0:nr] + + var msg messages.IPCMessage + err = json.Unmarshal(data, &msg) + if err != nil { + writeError(c, err) + continue + } + + if msg.Type == messages.MessageTypeForEmptyPayload(messages.PinentryApprovalResponse{}) { + getApprovalResponse := messages.ParsePayload(msg).(messages.PinentryApprovalResponse) + getApprovalReturnChan <- struct { + approved bool + err error + }{getApprovalResponse.Approved, nil} + } } } diff --git a/ui/src/services/goldwarden.py b/ui/src/services/goldwarden.py index 5c4728b..f3e8676 100644 --- a/ui/src/services/goldwarden.py +++ b/ui/src/services/goldwarden.py @@ -10,18 +10,17 @@ BINARY_PATHS = [ ] BINARY_PATH = "" +BINARY_PATH = None +for path in BINARY_PATHS: + if os.path.exists(path): + BINARY_PATH = path + break +if BINARY_PATH == None: + raise Exception("Could not find goldwarden binary") authenticated_connection = None def create_authenticated_connection(token): global authenticated_connection - global BINARY_PATH - BINARY_PATH = None - for path in BINARY_PATHS: - if os.path.exists(path): - BINARY_PATH = path - break - if BINARY_PATH == None: - raise Exception("Could not find goldwarden binary") authenticated_connection = subprocess.Popen([f"{BINARY_PATH}", "session"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) if not token == None: authenticated_connection.stdin.write("authenticate-session " + token + "\n") @@ -141,6 +140,29 @@ def is_daemon_running(): daemon_not_running = ("daemon running" in result) return not daemon_not_running +def listen_for_pinentry(on_pinentry, on_pin_approval): + print("listening for pinentry", BINARY_PATH) + pinentry_process = subprocess.Popen([f"{BINARY_PATH}", "pinentry"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + while True: + line = pinentry_process.stdout.readline() + # starts with pin-request + if line.startswith("pin-request"): + text = line.split(",")[1].strip() + pin = on_pinentry(text) + if pin == None: + pin = "" + pinentry_process.stdin.write(pin + "\n") + pinentry_process.stdin.flush() + if line.startswith("approval-request"): + text = line.split(",")[1].strip() + approval = on_pin_approval(text) + if approval: + pinentry_process.stdin.write("true\n") + pinentry_process.stdin.flush() + else: + pinentry_process.stdin.write("false\n") + pinentry_process.stdin.flush() + # def run_daemon(): # restic_cmd = f"daemonize" # # print while running diff --git a/ui/src/services/pinentry.py b/ui/src/services/pinentry.py new file mode 100644 index 0000000..6cb3a4e --- /dev/null +++ b/ui/src/services/pinentry.py @@ -0,0 +1,28 @@ +import subprocess +import os +from src.services import goldwarden + +root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir)) + +def get_pin(message): + p = subprocess.Popen(["python3", "-m", "src.ui.pinentry"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=root_path, start_new_session=True) + p.stdin.write(f"{message}\n".encode()) + p.stdin.flush() + pin = p.stdout.readline().decode().strip() + if pin == "": + return None + return pin + +def get_approval(message): + p = subprocess.Popen(["python3", "-m", "src.ui.pinentry_approval"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd=root_path, start_new_session=True) + p.stdin.write(f"{message}\n".encode()) + p.stdin.flush() + result = p.stdout.readline().decode().strip() + if result == "true": + return True + return False + +def daemon(): + goldwarden.listen_for_pinentry(get_pin, get_approval) + +daemon() \ No newline at end of file diff --git a/ui/src/ui/pinentry.py b/ui/src/ui/pinentry.py index de2edc5..a1abb0a 100644 --- a/ui/src/ui/pinentry.py +++ b/ui/src/ui/pinentry.py @@ -47,13 +47,17 @@ class MainWindow(Gtk.ApplicationWindow): # Cancel button cancel_button = Gtk.Button(label="Cancel") cancel_button.set_hexpand(True) # Make the button expand horizontally + def on_cancel_button_clicked(button): + print("", flush=True) + os._exit(0) + cancel_button.connect("clicked", on_cancel_button_clicked) button_box.append(cancel_button) # Approve button approve_button = Gtk.Button(label="Approve") approve_button.set_hexpand(True) # Make the button expand horizontally def on_approve_button_clicked(button): - print(self.password_entry.get_text()) + print(self.password_entry.get_text(), flush=True) os._exit(0) approve_button.connect("clicked", on_approve_button_clicked) button_box.append(approve_button) diff --git a/ui/src/ui/pinentry_approval.py b/ui/src/ui/pinentry_approval.py index d1daf52..17bd2e2 100644 --- a/ui/src/ui/pinentry_approval.py +++ b/ui/src/ui/pinentry_approval.py @@ -42,7 +42,7 @@ class MainWindow(Gtk.ApplicationWindow): cancel_button = Gtk.Button(label="Cancel") cancel_button.set_hexpand(True) # Make the button expand horizontally def on_cancel_button_clicked(button): - print("false") + print("false", flush=True) os._exit(0) cancel_button.connect("clicked", on_cancel_button_clicked) button_box.append(cancel_button) @@ -51,7 +51,7 @@ class MainWindow(Gtk.ApplicationWindow): approve_button = Gtk.Button(label="Approve") approve_button.set_hexpand(True) # Make the button expand horizontally def on_approve_button_clicked(button): - print("true") + print("true", flush=True) os._exit(0) approve_button.connect("clicked", on_approve_button_clicked) button_box.append(approve_button)