static int _git_ssh_setup_conn()

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;
}