Wednesday October 20 2021

SSH Askpass and dmenu – dmenu_askpass

Why? I run dwm almost exclusively and I wanted something that integrated well.

Also, because I’ve deployed modern ecdsa-sk and ed25519-sk SSH keys to all of my infrastructure askpass becomes a lot more important. Before It’d just be used to unlock a key and load it into my SSH agent, now it’s much more important than that since any time I log into a machine it will require me to tap my authenticator–it’s not always obvious that I should and without askpass with a forwarded agent I may not get prompted.

It’s easy enough to use, assuming you’re already starting an ssh agent on login, simply set the environment variables:

SSH_ASKPASS=dmenu_askpass
SSH_ASKPASS_REQUIRE=force

The SSH_ASKPASS_REQUIRE is optional, though it’s my preference.

You clone it from my git server:

git clone -b dmenu_askpass https://git.riedstra.dev/x/dmenu

Alternatively a fresh checkout of dmenu from their git server and the following patch will apply: ( Or a direct download )

From 34ffa30edc8ae60e83c538c5d8809d1981e5a9e5 Mon Sep 17 00:00:00 2001
From: Mitchell Riedstra <mitch@riedstra.dev>
Date: Wed, 20 Oct 2021 20:45:53 -0400
Subject: SSH askpass suppport

dmenu_askpass has been added and can be used to read in SSH key
passwords, useful for if your agent is forwarded. Full support for
prompting from security keys, often requiring you to tap the
authenticator. This unlike some other askpass variants does not stick
around after you tap the button.
---
 Makefile      |  3 ++-
 dmenu.c       | 13 +++++++++++--
 dmenu_askpass |  3 +++
 3 files changed, 16 insertions(+), 3 deletions(-)
 create mode 100755 dmenu_askpass

diff --git a/Makefile b/Makefile
index a03a95c..2a4cd96 100644
--- a/Makefile
+++ b/Makefile
@@ -42,10 +42,11 @@ dist: clean
 
 install: all
 	mkdir -p $(DESTDIR)$(PREFIX)/bin
-	cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin
+	cp -f dmenu dmenu_path dmenu_run dmenu_askpass stest $(DESTDIR)$(PREFIX)/bin
 	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu
 	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path
 	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run
+	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_askpass
 	chmod 755 $(DESTDIR)$(PREFIX)/bin/stest
 	mkdir -p $(DESTDIR)$(MANPREFIX)/man1
 	sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1
diff --git a/dmenu.c b/dmenu.c
index 98507d9..8c12e4b 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -35,6 +35,7 @@ struct item {
 };
 
 static char text[BUFSIZ] = "";
+static int echo = 1;
 static char *embed;
 static int bh, mw, mh;
 static int inputw = 0, promptw;
@@ -143,7 +144,11 @@ drawmenu(void)
 	/* draw input field */
 	w = (lines > 0 || !matches) ? mw - x : inputw;
 	drw_setscheme(drw, scheme[SchemeNorm]);
-	drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
+
+	if (echo)
+		drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
+	else
+		drw_text(drw, x, 0, w, bh, lrpad / 2, "(no echo)", 0);
 
 	curpos = TEXTW(text) - TEXTW(&text[cursor]);
 	if ((curpos += lrpad / 2 - 1) < w) {
@@ -262,6 +267,7 @@ match(void)
 	}
 	curr = sel = matches;
 	calcoffsets();
+	memset(buf, '\0', sizeof(text));
 }
 
 static void
@@ -474,6 +480,7 @@ insert:
 	case XK_Return:
 	case XK_KP_Enter:
 		puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
+		memset(text, '\0', sizeof text);
 		if (!(ev->state & ControlMask)) {
 			cleanup();
 			exit(0);
@@ -700,7 +707,7 @@ setup(void)
 static void
 usage(void)
 {
-	fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+	fputs("usage: dmenu [-bfivE] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
 	      "             [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr);
 	exit(1);
 }
@@ -720,6 +727,8 @@ main(int argc, char *argv[])
 			topbar = 0;
 		else if (!strcmp(argv[i], "-f"))   /* grabs keyboard before reading stdin */
 			fast = 1;
+		else if (!strcmp(argv[i], "-E"))   /* no Echo, for use with passphrases */
+			echo = 0;
 		else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
 			fstrncmp = strncasecmp;
 			fstrstr = cistrstr;
diff --git a/dmenu_askpass b/dmenu_askpass
new file mode 100755
index 0000000..9f61844
--- /dev/null
+++ b/dmenu_askpass
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec <&-
+exec dmenu -E -p "${1:-OpenSSH Passphrase: }"
-- 
2.33.0

In the next few days I’ll be seeing if I can get this listed as one of the patches on the Suckless website. In the mean time I figure my readers will enjoy the patch and for those glancing over my work it’s easier to see it on my blog than scattered across the internet.