]> git.neil.brown.name Git - edlib.git/commitdiff
Add lib-askpass
authorNeilBrown <neil@brown.name>
Wed, 12 Jul 2023 06:37:03 +0000 (16:37 +1000)
committerNeilBrown <neil@brown.name>
Wed, 12 Jul 2023 22:18:34 +0000 (08:18 +1000)
AskPass will as for a password and not display what is entered.
A new script "el-askpass" is provided that ssh can use to ask for a
password through edlib.
lib-shellcmd sets the environment so this script is used.

Net result is that if a command run from edlib needs ssh to ask for a
password, it can.

Signed-off-by: NeilBrown <neil@brown.name>
DOC/TODO.md
Makefile
lib-askpass.c [new file with mode: 0644]
modules.ini
python/lib-server.py
python/lib-shellcmd.py

index 4a670a541e9c1a179605d6478fc5ed5e6f842354..7c0e070db81a529d7c24c713d076089f220cd6ec 100644 (file)
@@ -178,9 +178,13 @@ Module features
 
 ### lib-askpass
 
-- [ ] New module which server can use to ask for a password.
+- [X] New module which server can use to ask for a password.
       When an external program is run, we pass SSH_ASKPASS=client
       and SSH_ASKPASS_REQUIRE=force.
+- [ ] have askpass tell shellmode (and others) about the SSH_ASKPASS*
+      environment, so they don't need to know it themselves
+- [ ] start-agent command which runs ssh-agent can records the
+      environment reported for later shells.
 
 ### lib-linecount
 
index f00fdf55321a13eb2be7524a24906f70d61af2f0..a794a2eefb51d20cf1af9d198419ab9ac24dd597 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -86,17 +86,18 @@ SHOBJ = O/doc-text.o O/doc-dir.o O/doc-docs.o \
        O/lib-renderline.o O/lib-x11selection-gtk.o O/lib-autosave.o \
        O/lib-x11selection-xcb.o O/display-x11-xcb.o \
        O/lib-linefilter.o O/lib-wiggle.o O/lib-aspell.o O/lib-calc.o \
-       O/lib-menu.o O/lib-unicode-names.o \
+       O/lib-menu.o O/lib-unicode-names.o O/lib-askpass.o \
        O/lang-python.o \
        O/mode-emacs.o O/emacs-search.o \
        O/display-ncurses.o
 XOBJ = O/rexel.o
 WOBJ = O/libwiggle.a
 
-BIN = edlib elc
+BIN = edlib elc el-askpass
 
 bin/edlib : edlib
 bin/elc : python/lib-server.py
+bin/el-askpass : python/lib-server.py
 
 # From python 3.8 on we need python3-embed to get the right libraries
 pypkg=$(shell pkg-config --atleast-version=3.8 python3 && echo python3-embed || echo python3)
diff --git a/lib-askpass.c b/lib-askpass.c
new file mode 100644 (file)
index 0000000..6ab786c
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright Neil Brown ©2023 <neil@brown.name>
+ * May be distributed under terms of GPLv2 - see file:COPYING
+ *
+ * askpass - ask for a password.
+ * This doesn't yet have any focus on protecting the password
+ * from being swapped out.
+ *
+ * We place a popup in mid-display with a message and
+ * "HEAVY BALLOT X" for each character typed.
+ */
+
+#include <unistd.h>
+#include <memory.h>
+
+#define PANE_DATA_TYPE struct apinfo
+#include "core.h"
+
+struct apinfo {
+       char *msg safe;
+       struct buf b;
+       struct command *c;
+};
+#include "core-pane.h"
+
+static struct map *askpass_map;
+DEF_LOOKUP_CMD(askpass_handle, askpass_map);
+
+DEF_CMD(askpass_refresh_view)
+{
+       struct buf b;
+       struct apinfo *ai = &ci->home->data;
+       int shift = 0;
+       int i;
+
+       buf_init(&b);
+       buf_concat(&b, ai->msg);
+       for (i = 0; i < utf8_strlen(buf_final(&ai->b)); i++)
+               buf_append(&b, 0x2718); /* HEAVY BALLOT X */
+       call("render-line:set", ci->focus, b.len, NULL, buf_final(&b));
+       for (i = 0; i < 10; i++) {
+               int cw;
+               attr_set_int(&ci->focus->attrs, "shift_left", shift);
+               call("render-line:measure", ci->focus, b.len);
+               cw = pane_attr_get_int(ci->focus, "curs_width", 1);
+               if (ci->home->parent->cx < ci->home->parent->w - cw)
+                       break;
+               shift += 8 * cw;
+       }
+       free(buf_final(&b));
+       return 1;
+}
+
+DEF_CMD(askpass_key)
+{
+       const char *k = ksuffix(ci, "K-");
+       struct apinfo *ai = &ci->home->data;
+
+       buf_concat(&ai->b, k);
+       pane_damaged(ci->home, DAMAGED_VIEW);
+       return 1;
+}
+
+DEF_CMD(askpass_bs)
+{
+       struct apinfo *ai = &ci->home->data;
+
+       if (ai->b.len > 0)
+               ai->b.len = utf8_round_len(ai->b.b, ai->b.len-1);
+       pane_damaged(ci->home, DAMAGED_VIEW);
+       return 1;
+}
+
+DEF_CMD(askpass_ignore)
+{
+       return 1;
+}
+
+DEF_CMD(askpass_done)
+{
+       struct apinfo *ai = &ci->home->data;
+
+       comm_call(ai->c, "cb", ci->focus, ai->b.len, NULL,
+                 buf_final(&ai->b));
+       memset(ai->b.b, 0, ai->b.size);
+       call("popup:close", ci->focus);
+       return 1;
+}
+
+DEF_CMD(askpass_abort)
+{
+       struct apinfo *ai = &ci->home->data;
+
+       memset(ai->b.b, 0, ai->b.size);
+       comm_call(ai->c, "cb", ci->focus, -1);
+       call("popup:close", ci->focus);
+       return 1;
+}
+
+DEF_CMD(askpass_attach)
+{
+       struct pane *p, *p2;
+
+       if (!ci->str || !ci->comm2)
+               return Enoarg;
+       p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "D2");
+       if (!p)
+               return Efail;
+       p2  = call_ret(pane, "attach-view", p);
+       if (!p2)
+               goto fail;
+       p = p2;
+
+       p2 = call_ret(pane, "attach-renderline", p);
+       if (!p2)
+               goto fail;
+       p = p2;
+
+       p2 = pane_register(p, 0, &askpass_handle.c);
+       if (!p2)
+               goto fail;
+       p = p2;
+
+       attr_set_str(&p->attrs, "pane-title", "Ask Password");
+
+       p->data.msg = strdup(ci->str);
+       p->data.c = command_get(ci->comm2);
+       buf_init(&p->data.b);
+       pane_damaged(p, DAMAGED_VIEW);
+       return 1;
+
+fail:
+       if (ci->focus->focus)
+               pane_close(ci->focus->focus);
+       return Efail;
+}
+
+DEF_CMD(askpass_close)
+{
+       struct apinfo *ai = &ci->home->data;
+
+       free(ai->msg);
+       ai->msg = safe_cast NULL;
+       free(buf_final(&ai->b));
+       ai->b.b = safe_cast NULL;
+       command_put(ai->c);
+       ai->c = NULL;
+       return 1;
+}
+
+void edlib_init(struct pane *ed safe)
+{
+       call_comm("global-set-command", ed, &askpass_attach,
+                 0, NULL, "AskPass");
+
+       askpass_map = key_alloc();
+       key_add(askpass_map, "Close", &askpass_close);
+       key_add_prefix(askpass_map, "K-", &askpass_key);
+       key_add_prefix(askpass_map, "K:", &askpass_ignore);
+       key_add_prefix(askpass_map, "M:", &askpass_ignore);
+       key_add(askpass_map, "K:Enter", &askpass_done);
+       key_add(askpass_map, "K:Backspace", &askpass_bs);
+       key_add(askpass_map, "K:ESC", &askpass_abort);
+       key_add(askpass_map, "K:C-C", &askpass_abort);
+       key_add(askpass_map, "K:C-G", &askpass_abort);
+       key_add_prefix(askpass_map, "Refresh:view", &askpass_refresh_view);
+}
index 6a14178282e8ec05e61c4f2d033bb4cf6cd885ff..e624653600083227de31bd4e03e32e1c7dca6fd1 100644 (file)
@@ -155,3 +155,4 @@ lib-shellcmd = attach-shellcmd
 
 lib-unicode-names = Unicode-names
 lib-menu = attach-menu
+lib-askpass = AskPass
index 786eed1900f1ef762f057ff0ac1350d270f05d2d..ee75cacb44bfd0e862967fc3fea762c4eaa1bc4a 100755 (executable)
@@ -174,6 +174,17 @@ if sys.argv[0] == "":
                     self.sock = None
                     self.close()
                     return edlib.Efalse
+                if cmd == "askpass":
+                    def cb(key, str1, **a):
+                        if str1:
+                            self.sock.send(str1.encode())
+                        self.sock.close()
+                        self.sock = None
+
+                    p = self.choose_pane()
+                    if p:
+                        p.call("AskPass", arg, cb)
+                    return 1
                 self.sock.send(b"Unknown")
                 return 1
 
@@ -317,6 +328,19 @@ if sys.argv[0] == "":
     edlib.editor.call("global-set-command", "interactive-cmd-server-start",
                 server_rebind)
 
+elif 'askpass' in sys.argv[0]:
+    msg = sys.argv[1]
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    try:
+        s.connect(sockpath)
+    except OSError:
+        print("Cannot connect to ",sockpath)
+        sys.exit(1)
+
+    s.send(b"askpass\0" + msg.encode())
+    ret = s.recv(1000)
+    print(ret.decode('utf-8', 'ignore'))
+    sys.exit(0)
 else:
     term = False
     file = None
index 5f87d48129e4f1e84e02b2f9130856cb8303039a..8975fdba200f1badb342449ca90423616e57a4f0 100644 (file)
@@ -59,6 +59,12 @@ class ShellPane(edlib.Pane):
             self.call("doc:replace", "Cmd: %s\nCwd: %s\n\n" % (cmd,cwd))
         env = os.environ.copy()
         env['PWD'] = cwd
+        askp = self.call("xdg-find-edlib-file",
+                         "el-askpass", "bin",  ret='str')
+        if askp:
+            env['SSH_ASKPASS'] = askp
+            env['SSH_ASKPASS_REQUIRE'] = 'force'
+
         try:
             self.pipe = subprocess.Popen(cmd, shell=True, close_fds=True,
                                          cwd=cwd, env=env,