Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using SSH authentification with libgit2

I am trying to authenticate against a git server with libgit2 using SSH keys. So far, this is working for URLs like ssh://[email protected]:1234/dirs/repo.git, where my application accepts the URL as an argument.

However, if I remove the username from the URL (i.e. ssh://host.domain:1234/dirs/repo.git) the connection fails, even if I set the user name programmatically (see below).

Consider the following MCVE for a program that checks whether a certain repository is reachable (no error checks except for the necessary):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <git2.h>
#include <git2/sys/repository.h>

int my_cred_cb(git_cred **out, const char *url,
    const char *username_from_url, unsigned int allowed_types, void *payload)
{
  uid_t uid = geteuid();             // Get effective user ID
  struct passwd* pw = getpwuid(uid); // Get password file entry

  char privkeypath[strlen(pw->pw_dir) + 13];
  strcpy(privkeypath, pw->pw_dir);
  strcat(privkeypath, "/.ssh/id_rsa"); // "~user/.ssh/id_rsa"

  char publkeypath[strlen(privkeypath) + 5]; 
  strcpy(publkeypath, privkeypath);
  strcat(publkeypath, ".pub"); // "~user/.ssh/id_rsa.pub"

  const char* username = (username_from_url != NULL ? username_from_url : pw->pw_name);

  printf("Using username: %s, priv key %s and publ key %s\n", username, privkeypath, publkeypath);
  return git_cred_ssh_key_new(out, username, publkeypath, privkeypath, ""); // No passphrase for keys
}

int main(int argc, char** argv)
{
  git_remote* remote = NULL;
  git_repository* repo = NULL;
  git_remote_callbacks cbs;
  const git_error* err;

  if (argc != 2) return EXIT_FAILURE;

  git_libgit2_init();
  git_repository_new(&repo);
  git_remote_create_anonymous(&remote, repo, argv[1]);
  git_remote_init_callbacks(&cbs, GIT_REMOTE_CALLBACKS_VERSION);
  cbs.credentials = my_cred_cb;

  if (git_remote_connect(remote, GIT_DIRECTION_FETCH, &cbs, NULL, NULL)) {
    err = giterr_last();
    printf ("Error %d: %s\n", err->klass, err->message);
    return EXIT_FAILURE;
  }

  git_libgit2_shutdown();
  printf("git repo exists and is reachable.\n");
}

This can be compiled with gcc -Wall -pedantic -std=c99 -o myapp main.c -lssh2 -lgit2, assuming the include and library paths are set properly.

Now consider the output for different URLs:

$ ./myapp ssh://[email protected]:1234/dirs/repo.git
Using username: myuser, priv key /home/myuser/.ssh/id_rsa and publ key /home/myuser/.ssh/id_rsa.pub
git repo exists and is reachable.

And now the failing case:

$ ./myapp ssh://host.domain:1234/dirs/repo.git # Note the missing user name
Using username: myuser, priv key /home/myuser/.ssh/id_rsa and publ key /home/myuser/.ssh/id_rsa.pub
Error 23: callback returned unsupported credentials type

I do not understand why I receive this error, since I pass the exact same information to the library (why is it "unsupported credentials type" in the first place?).

Even worse, I usually use Host entries in my ~/.ssh/config, such that instead of putting host.domain:1234 I can simply use myhost (e.g. git clone ssh://myhost/dirs/repo.git works just fine). For my application, this is the output:

$ ./myapp ssh://myhost/dirs/repo.git
Error 12: failed to resolve address for myhost: Name or service not known

It looks like libgit2 does not configure libssh2 to read in the ssh-config. That's a related, yet probably a different problem. Still I feel these two problems belong together.

Summarizing my questions:

  1. How do I pass the username to the git credentials programmatically (vs. in the URL)?
  2. How do I tell libgit2 to retrieve information from my ssh-config before attempting to connect?
like image 943
andreee Avatar asked Jul 26 '18 15:07

andreee


1 Answers

These really are two separate unrelated questions.

  1. You should be checking the allowed_types parameter in your callback and only return a git_cred_ssh_key_new credential if it contains GIT_CREDTYPE_SSH_KEY. The backend is probably requesting a credential of type GIT_CREDTYPE_USERNAME because the URL doesn't have one. In that case you should be returning a credential created by git_cred_username_new. Your callback will be called twice, once to get the username, and a second time to create the ssh credential.

  2. Reading config settings from your OpenSSH config file at ~/.ssh/config isn't supported by libgit2 because it isn't support by libssh2. If you want to read settings from there, you have to do it yourself.

like image 155
Jason Haslam Avatar answered Oct 18 '22 02:10

Jason Haslam