in src/transports/ssh.c [513:690]
static int _git_ssh_setup_conn(
ssh_subtransport *t,
const char *url,
const char *cmd,
git_smart_subtransport_stream **stream)
{
git_net_url urldata = GIT_NET_URL_INIT;
int auth_methods, error = 0;
size_t i;
ssh_stream *s;
git_credential *cred = NULL;
LIBSSH2_SESSION* session=NULL;
LIBSSH2_CHANNEL* channel=NULL;
t->current_stream = NULL;
*stream = NULL;
if (ssh_stream_alloc(t, url, cmd, stream) < 0)
return -1;
s = (ssh_stream *)*stream;
s->session = NULL;
s->channel = NULL;
for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
const char *p = ssh_prefixes[i];
if (!git__prefixcmp(url, p)) {
if ((error = git_net_url_parse(&urldata, url)) < 0)
goto done;
goto post_extract;
}
}
if ((error = git_ssh_extract_url_parts(&urldata, url)) < 0)
goto done;
if (urldata.port == NULL)
urldata.port = git__strdup(SSH_DEFAULT_PORT);
GIT_ERROR_CHECK_ALLOC(urldata.port);
post_extract:
if ((error = git_socket_stream_new(&s->io, urldata.host, urldata.port)) < 0 ||
(error = git_stream_connect(s->io)) < 0)
goto done;
if ((error = _git_ssh_session_create(&session, s->io)) < 0)
goto done;
if (t->owner->certificate_check_cb != NULL) {
git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
const char *key;
cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
if (key != NULL) {
cert.type |= GIT_CERT_SSH_SHA256;
memcpy(&cert.hash_sha256, key, 32);
}
#endif
key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
if (key != NULL) {
cert.type |= GIT_CERT_SSH_SHA1;
memcpy(&cert.hash_sha1, key, 20);
}
key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
if (key != NULL) {
cert.type |= GIT_CERT_SSH_MD5;
memcpy(&cert.hash_md5, key, 16);
}
if (cert.type == 0) {
git_error_set(GIT_ERROR_SSH, "unable to get the host key");
error = -1;
goto done;
}
/* We don't currently trust any hostkeys */
git_error_clear();
cert_ptr = &cert;
error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, urldata.host, t->owner->message_cb_payload);
if (error < 0 && error != GIT_PASSTHROUGH) {
if (!git_error_last())
git_error_set(GIT_ERROR_NET, "user cancelled hostkey check");
goto done;
}
}
/* we need the username to ask for auth methods */
if (!urldata.username) {
if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0)
goto done;
urldata.username = git__strdup(((git_credential_username *) cred)->username);
cred->free(cred);
cred = NULL;
if (!urldata.username)
goto done;
} else if (urldata.username && urldata.password) {
if ((error = git_credential_userpass_plaintext_new(&cred, urldata.username, urldata.password)) < 0)
goto done;
}
if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
goto done;
error = GIT_EAUTH;
/* if we already have something to try */
if (cred && auth_methods & cred->credtype)
error = _git_ssh_authenticate_session(session, cred);
while (error == GIT_EAUTH) {
if (cred) {
cred->free(cred);
cred = NULL;
}
if ((error = request_creds(&cred, t, urldata.username, auth_methods)) < 0)
goto done;
if (strcmp(urldata.username, git_credential_get_username(cred))) {
git_error_set(GIT_ERROR_SSH, "username does not match previous request");
error = -1;
goto done;
}
error = _git_ssh_authenticate_session(session, cred);
if (error == GIT_EAUTH) {
/* refresh auth methods */
if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
goto done;
else
error = GIT_EAUTH;
}
}
if (error < 0)
goto done;
channel = libssh2_channel_open_session(session);
if (!channel) {
error = -1;
ssh_error(session, "Failed to open SSH channel");
goto done;
}
libssh2_channel_set_blocking(channel, 1);
s->session = session;
s->channel = channel;
t->current_stream = s;
done:
if (error < 0) {
ssh_stream_free(*stream);
if (session)
libssh2_session_free(session);
}
if (cred)
cred->free(cred);
git_net_url_dispose(&urldata);
return error;
}