DEV Community

Mika Feiler
Mika Feiler

Posted on • Edited on

Checking remote authorized_keys without entering key passphrase, done more neatly — and against .ssh/config

You have a bunch of client machines with private keys set up to access a bunch of destination hosts. How do you check which of them has them to access which? Maybe you wish for ssh-copy-id to not ask for key password when you can first check that quicker with ssh and ^C?
How does ssh act out

Enter passphrase for key '/path/to/key': 
Enter fullscreen mode Exit fullscreen mode

as opposed to

Permission denied (publickey).
Enter fullscreen mode Exit fullscreen mode

While -vvv (debug3) shows more, let's see just debug1 (-v):

debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: publickey
debug1: Will attempt key: /path/to/key ED25519 SHA256:... explicit
debug1: Offering public key: /path/to/key ED25519 SHA256:... explicit
debug1: Server accepts key: /path/to/key ED25519 SHA256:... explicit
Enter passphrase for key '/path/to/key': 
Enter fullscreen mode Exit fullscreen mode

So we can clearly just scrape that stderr, seek the debug1 accept or the passphrase prompt, regex the path to key out of the thing.

Or we can do it neater.
Cockpit Project happened to make a contribution enabling us to use libssh for that: https://gitlab.com/libssh/libssh-mirror/-/merge_requests/134
— they happened to want to be able to prompt the user for key passphrase only when needed. We've only been having that available in libssh for two years.

The ssh_userauth_publickey_auto_get_current_identity function has to be called in an auth_function callback, and the value it obtains is the key file path.

Besides the auto mode, even without that it was already possible to check a single key — with the result of the ssh_userauth_try_publickey function.

Attached is the quick-and-dirty source code for such a utility:

// LGPL (c) 2024-02-27 Mika Feiler <m@mikf.pl>
// BUILD: cc -lssh

/*
      This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

#include <libssh/libssh.h>
#include <libssh/callbacks.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

ssh_session s;

int leave = -1;

int callback(const char *_prompt, char *_buf, size_t _len,
         int _echo, int _verify, void* _) {
  int rc;
  char *value[500];
  rc = ssh_userauth_publickey_auto_get_current_identity(s, value);
  // Thanks, Cockpit Project!
  printf("%s\n", *value);
  leave = 0;
}

int usage(char* a0) {
    printf("Usage: %s hostname [port [keypath|'' [keyuser [disregardunknownhost]]]]\n\n%s\n%s\n%s\n\n%s\n",
       a0,
       "This little program uses ssh_userauth_publickey_auto_get_current_identity,",
       "thanks to Cockpit, to offer keys (per config) to host and print out path",
       "of one that gets accepted by the host, even if it has a passphrase.",
       "This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.");
    return 0;
}

void handle_host_verification(int pass_unknown) {
  enum ssh_known_hosts_e state = ssh_session_is_known_server(s);
  switch(state) {
  case SSH_KNOWN_HOSTS_UNKNOWN: if(pass_unknown) exit(5);
  case SSH_KNOWN_HOSTS_OK: break;
  default:
    exit(3);
  }
}

int main(int argc, char* argv[]) {
  if(argc < 2)
    return usage(argv[0]);

  s = ssh_new();
  if (s == NULL)
    exit(-1);

  int verbosity = SSH_LOG_WARNING;
  ssh_options_set(s, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);

  ssh_options_set(s, SSH_OPTIONS_HOST, argv[1]);
  ssh_options_set(s, SSH_OPTIONS_PORT_STR, argc > 2 ? argv[2] : "22");

  int rc;
  rc = ssh_connect(s);
  if (rc != SSH_OK)
    exit(rc);

  handle_host_verification(argc < 6); // will allow unknown host with one more arg

  struct ssh_callbacks_struct callbacks = {
    .auth_function = callback
  };
  // As per test example for ssh_userauth_publickey_auto_get_current_identity !

  ssh_set_blocking(s, 1);

  // "User SHOULD be NULL" lol
  char* user = argc > 4 && strlen(argv[4]) > 0 ? argv[4] : NULL;

  if (argc > 3 && strlen(argv[3]) > 0) { // if key path specified
    ssh_key key = ssh_key_new();
    rc = ssh_pki_import_pubkey_file(argv[3], &key);
    if (rc != SSH_OK)
      exit(rc);
    rc = ssh_userauth_try_publickey(s, user, key);
    leave = rc;
  } else { // the cool behavior doing .ssh/config :3
    ssh_callbacks_init(&callbacks);
    ssh_set_callbacks(s, &callbacks);

    rc = ssh_userauth_publickey_auto(s, user, NULL);
  }
  leave = leave == -1 ? rc : leave;

  ssh_disconnect(s); ssh_free(s);
  exit(leave);
}
Enter fullscreen mode Exit fullscreen mode

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Billboard image

Try REST API Generation for Snowflake

DevOps for Private APIs. Automate the building, securing, and documenting of internal/private REST APIs with built-in enterprise security on bare-metal, VMs, or containers.

  • Auto-generated live APIs mapped from Snowflake database schema
  • Interactive Swagger API documentation
  • Scripting engine to customize your API
  • Built-in role-based access control

Learn more

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay