The unified diff between revisions [5de976de..] and [24fb0b25..] is displayed below. It can also be downloaded as a raw diff.

#
#
# patch "cmd_netsync.cc"
#  from [1c9f724505164da0026f77e4e81010fddc81faad]
#    to [92564b21090c2ebc23734588bcaad5a6740b1c2f]
#
# patch "key_store.cc"
#  from [510eb02eab491fd9e79c49fcfcf5e6efa145872f]
#    to [5c423e17442795e5cd6ca41ddc2c041e5ac5e553]
#
# patch "key_store.hh"
#  from [a2cba22c7534941db07838f8f7b9e3abede66cce]
#    to [86b0cfb3002761e38f91aa1b2b696d604a6bc674]
#
# patch "options_list.hh"
#  from [78fbe7c5728513ee672b75d6dcefbc2f508ba915]
#    to [5ea48e4362ae53c3cf69b6422f98eb5a33e92086]
#
============================================================
--- cmd_netsync.cc	1c9f724505164da0026f77e4e81010fddc81faad
+++ cmd_netsync.cc	92564b21090c2ebc23734588bcaad5a6740b1c2f
@@ -237,6 +237,39 @@ CMD(push, "push", "", CMD_REF(network),
                        client_voice, source_role, info);
 }

+CMD_AUTOMATE(push, N_("[ADDRESS[:PORTNUMBER] [PATTERN ...]]"),
+             N_("Pushes branches to a netsync server"),
+             "",
+             options::opts::set_default | options::opts::exclude |
+             options::opts::key_to_push | options::opts::do_not_enforce_ssh_agent)
+{
+  database db(app);
+  key_store keys(app);
+  project_t project(db);
+
+  netsync_connection_info info;
+  extract_client_connection_info(app.opts, app.lua, db, keys, args, info);
+
+  if (app.opts.signing_key() != "" && !app.opts.do_not_enforce_ssh_agent)
+    {
+      // enforce that any command which needs to encrypt a private key later on
+      // uses the ssh_agent to do the task, because we cannot handle password
+      // prompts over stdio. basically this is the same as giving --ssh-sign=only
+      // as option to stdio, but it is more explicit. however, this also leaves
+      // the opportunity of giving a possible passphrase via the get_passphrase
+      // lua hook, because the ssh agent is _always_ used, but its a reasonable
+      // trade-off
+      keys.enforce_ssh_agent();
+
+      N(keys.agent_knows_key(app.opts.signing_key),
+        F("key '%s' is unknown to the running ssh-agent instance")
+            % app.opts.signing_key);
+    }
+
+  run_netsync_protocol(app.opts, app.lua, project, keys,
+                       client_voice, source_role, info);
+}
+
 CMD(pull, "pull", "", CMD_REF(network),
     N_("[ADDRESS[:PORTNUMBER] [PATTERN ...]]"),
     N_("Pulls branches from a netsync server"),
@@ -262,7 +295,8 @@ CMD_AUTOMATE(pull, N_("[ADDRESS[:PORTNUM
 CMD_AUTOMATE(pull, N_("[ADDRESS[:PORTNUMBER] [PATTERN ...]]"),
              N_("Pulls branches from a netsync server"),
              "",
-             options::opts::set_default | options::opts::exclude)
+             options::opts::set_default | options::opts::exclude |
+             options::opts::do_not_enforce_ssh_agent)
 {
   database db(app);
   key_store keys(app);
@@ -272,9 +306,22 @@ CMD_AUTOMATE(pull, N_("[ADDRESS[:PORTNUM
   extract_client_connection_info(app.opts, app.lua, db, keys,
                                  args, info, false);

-  if (app.opts.signing_key() == "")
-    P(F("doing anonymous pull; use -kKEYNAME if you need authentication"));
+  if (app.opts.signing_key() != "" && !app.opts.do_not_enforce_ssh_agent)
+    {
+      // enforce that any command which needs to encrypt a private key later on
+      // uses the ssh_agent to do the task, because we cannot handle password
+      // prompts over stdio. basically this is the same as giving --ssh-sign=only
+      // as option to stdio, but it is more explicit. however, this also leaves
+      // the opportunity of giving a possible passphrase via the get_passphrase
+      // lua hook, because the ssh agent is _always_ used, but its a reasonable
+      // trade-off
+      keys.enforce_ssh_agent();

+      N(keys.agent_knows_key(app.opts.signing_key),
+        F("key '%s' is unknown to the running ssh-agent instance")
+            % app.opts.signing_key);
+    }
+
   run_netsync_protocol(app.opts, app.lua, project, keys,
                        client_voice, sink_role, info);
 }
@@ -285,7 +332,7 @@ CMD(sync, "sync", "", CMD_REF(network),
     N_("This synchronizes branches that match the pattern given in PATTERN "
        "with the netsync server at the address ADDRESS."),
     options::opts::set_default | options::opts::exclude |
-    options::opts::key_to_push)
+    options::opts::key_to_push | options::opts::do_not_enforce_ssh_agent)
 {
   database db(app);
   key_store keys(app);
@@ -305,6 +352,39 @@ CMD(sync, "sync", "", CMD_REF(network),
                        client_voice, source_and_sink_role, info);
 }

+CMD_AUTOMATE(sync, N_("[ADDRESS[:PORTNUMBER] [PATTERN ...]]"),
+             N_("Synchronizes branches with a netsync server"),
+             "",
+             options::opts::set_default | options::opts::exclude |
+             options::opts::key_to_push)
+{
+  database db(app);
+  key_store keys(app);
+  project_t project(db);
+
+  netsync_connection_info info;
+  extract_client_connection_info(app.opts, app.lua, db, keys, args, info);
+
+  if (app.opts.signing_key() != "" && !app.opts.do_not_enforce_ssh_agent)
+    {
+      // enforce that any command which needs to encrypt a private key later on
+      // uses the ssh_agent to do the task, because we cannot handle password
+      // prompts over stdio. basically this is the same as giving --ssh-sign=only
+      // as option to stdio, but it is more explicit. however, this also leaves
+      // the opportunity of giving a possible passphrase via the get_passphrase
+      // lua hook, because the ssh agent is _always_ used, but its a reasonable
+      // trade-off
+      keys.enforce_ssh_agent();
+
+      N(keys.agent_knows_key(app.opts.signing_key),
+        F("key '%s' is unknown to the running ssh-agent instance")
+            % app.opts.signing_key);
+    }
+
+  run_netsync_protocol(app.opts, app.lua, project, keys,
+                       client_voice, source_and_sink_role, info);
+}
+
 class dir_cleanup_helper
 {
 public:
============================================================
--- key_store.cc	510eb02eab491fd9e79c49fcfcf5e6efa145872f
+++ key_store.cc	5c423e17442795e5cd6ca41ddc2c041e5ac5e553
@@ -710,7 +710,31 @@ key_store::export_key_for_agent(rsa_keyp
   p.end_msg();
 }

+void
+key_store::enforce_ssh_agent()
+{
+  ssh_agent & agent = s->get_agent();
+  N(agent.connected(),
+    F("this command enforces the only usage of ssh_agent, "
+      "but no ssh-agent is available."));
+  if (s->ssh_sign_mode != "only")
+    {
+      W(F("enforcing the only usage of ssh_agent"));
+      const_cast<std::string &>(s->ssh_sign_mode) = "only";
+    }
+}

+bool
+key_store::agent_knows_key(rsa_keypair_id const & ident)
+{
+  keypair keyp;
+  N(maybe_get_key_pair(ident, keyp),
+    F("no keypair for '%s' in keyring") % ident);
+
+  ssh_agent & agent = s->get_agent();
+  return agent.has_key(keyp);
+}
+
 //
 // Migration from old databases
 //
============================================================
--- key_store.hh	a2cba22c7534941db07838f8f7b9e3abede66cce
+++ key_store.hh	86b0cfb3002761e38f91aa1b2b696d604a6bc674
@@ -82,6 +82,8 @@ public:
   void add_key_to_agent(rsa_keypair_id const & id);
   void export_key_for_agent(rsa_keypair_id const & id,
                             std::ostream & os);
+  void enforce_ssh_agent();
+  bool agent_knows_key(rsa_keypair_id const & ident);

   // Migration from old databases

============================================================
--- options_list.hh	78fbe7c5728513ee672b75d6dcefbc2f508ba915
+++ options_list.hh	5ea48e4362ae53c3cf69b6422f98eb5a33e92086
@@ -80,6 +80,17 @@ OPT(automate_stdio_size, "automate-stdio
 }
 #endif

+OPT(do_not_enforce_ssh_agent, "do-not-enforce-ssh-agent", bool, false,
+     gettext_noop("do not enforce the usage of ssh-agent. enabling this is "
+                  "highly discouraged if you run this command over stdio "
+                  "as it may prompt for a key password which cannot be "
+                  "handled."))
+#ifdef option_bodies
+{
+  do_not_enforce_ssh_agent = true;
+}
+#endif
+
 OPTSET(bind_opts)
 OPTVAR(bind_opts, std::list<utf8>, bind_uris, )
 OPTVAR(bind_opts, bool, bind_stdio, false)