File Coverage

deps/libgit2/src/transports/ssh.c
Criterion Covered Total %
statement 2 12 16.6
branch 0 4 0.0
condition n/a
subroutine n/a
pod n/a
total 2 16 12.5


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "ssh.h"
9              
10             #ifdef GIT_SSH
11             #include
12             #endif
13              
14             #include "global.h"
15             #include "git2.h"
16             #include "buffer.h"
17             #include "net.h"
18             #include "netops.h"
19             #include "smart.h"
20             #include "streams/socket.h"
21              
22             #include "git2/credential.h"
23             #include "git2/sys/credential.h"
24              
25             #ifdef GIT_SSH
26              
27             #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
28              
29             static const char *ssh_prefixes[] = { "ssh://", "ssh+git://", "git+ssh://" };
30              
31             static const char cmd_uploadpack[] = "git-upload-pack";
32             static const char cmd_receivepack[] = "git-receive-pack";
33              
34             typedef struct {
35             git_smart_subtransport_stream parent;
36             git_stream *io;
37             LIBSSH2_SESSION *session;
38             LIBSSH2_CHANNEL *channel;
39             const char *cmd;
40             char *url;
41             unsigned sent_command : 1;
42             } ssh_stream;
43              
44             typedef struct {
45             git_smart_subtransport parent;
46             transport_smart *owner;
47             ssh_stream *current_stream;
48             git_credential *cred;
49             char *cmd_uploadpack;
50             char *cmd_receivepack;
51             } ssh_subtransport;
52              
53             static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
54              
55             static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
56             {
57             char *ssherr;
58             libssh2_session_last_error(session, &ssherr, NULL, 0);
59              
60             git_error_set(GIT_ERROR_SSH, "%s: %s", errmsg, ssherr);
61             }
62              
63             /*
64             * Create a git protocol request.
65             *
66             * For example: git-upload-pack '/libgit2/libgit2'
67             */
68             static int gen_proto(git_buf *request, const char *cmd, const char *url)
69             {
70             const char *repo;
71             int len;
72             size_t i;
73              
74             for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
75             const char *p = ssh_prefixes[i];
76              
77             if (!git__prefixcmp(url, p)) {
78             url = url + strlen(p);
79             repo = strchr(url, '/');
80             if (repo && repo[1] == '~')
81             ++repo;
82              
83             goto done;
84             }
85             }
86             repo = strchr(url, ':');
87             if (repo) repo++;
88              
89             done:
90             if (!repo) {
91             git_error_set(GIT_ERROR_NET, "malformed git protocol URL");
92             return -1;
93             }
94              
95             len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
96              
97             git_buf_grow(request, len);
98             git_buf_puts(request, cmd);
99             git_buf_puts(request, " '");
100             git_buf_decode_percent(request, repo, strlen(repo));
101             git_buf_puts(request, "'");
102              
103             if (git_buf_oom(request))
104             return -1;
105              
106             return 0;
107             }
108              
109             static int send_command(ssh_stream *s)
110             {
111             int error;
112             git_buf request = GIT_BUF_INIT;
113              
114             error = gen_proto(&request, s->cmd, s->url);
115             if (error < 0)
116             goto cleanup;
117              
118             error = libssh2_channel_exec(s->channel, request.ptr);
119             if (error < LIBSSH2_ERROR_NONE) {
120             ssh_error(s->session, "SSH could not execute request");
121             goto cleanup;
122             }
123              
124             s->sent_command = 1;
125              
126             cleanup:
127             git_buf_dispose(&request);
128             return error;
129             }
130              
131             static int ssh_stream_read(
132             git_smart_subtransport_stream *stream,
133             char *buffer,
134             size_t buf_size,
135             size_t *bytes_read)
136             {
137             int rc;
138             ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
139              
140             *bytes_read = 0;
141              
142             if (!s->sent_command && send_command(s) < 0)
143             return -1;
144              
145             if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
146             ssh_error(s->session, "SSH could not read data");
147             return -1;
148             }
149              
150             /*
151             * If we can't get anything out of stdout, it's typically a
152             * not-found error, so read from stderr and signal EOF on
153             * stderr.
154             */
155             if (rc == 0) {
156             if ((rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) {
157             git_error_set(GIT_ERROR_SSH, "%*s", rc, buffer);
158             return GIT_EEOF;
159             } else if (rc < LIBSSH2_ERROR_NONE) {
160             ssh_error(s->session, "SSH could not read stderr");
161             return -1;
162             }
163             }
164              
165              
166             *bytes_read = rc;
167              
168             return 0;
169             }
170              
171             static int ssh_stream_write(
172             git_smart_subtransport_stream *stream,
173             const char *buffer,
174             size_t len)
175             {
176             ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
177             size_t off = 0;
178             ssize_t ret = 0;
179              
180             if (!s->sent_command && send_command(s) < 0)
181             return -1;
182              
183             do {
184             ret = libssh2_channel_write(s->channel, buffer + off, len - off);
185             if (ret < 0)
186             break;
187              
188             off += ret;
189              
190             } while (off < len);
191              
192             if (ret < 0) {
193             ssh_error(s->session, "SSH could not write data");
194             return -1;
195             }
196              
197             return 0;
198             }
199              
200             static void ssh_stream_free(git_smart_subtransport_stream *stream)
201             {
202             ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
203             ssh_subtransport *t;
204              
205             if (!stream)
206             return;
207              
208             t = OWNING_SUBTRANSPORT(s);
209             t->current_stream = NULL;
210              
211             if (s->channel) {
212             libssh2_channel_close(s->channel);
213             libssh2_channel_free(s->channel);
214             s->channel = NULL;
215             }
216              
217             if (s->session) {
218             libssh2_session_disconnect(s->session, "closing transport");
219             libssh2_session_free(s->session);
220             s->session = NULL;
221             }
222              
223             if (s->io) {
224             git_stream_close(s->io);
225             git_stream_free(s->io);
226             s->io = NULL;
227             }
228              
229             git__free(s->url);
230             git__free(s);
231             }
232              
233             static int ssh_stream_alloc(
234             ssh_subtransport *t,
235             const char *url,
236             const char *cmd,
237             git_smart_subtransport_stream **stream)
238             {
239             ssh_stream *s;
240              
241             assert(stream);
242              
243             s = git__calloc(sizeof(ssh_stream), 1);
244             GIT_ERROR_CHECK_ALLOC(s);
245              
246             s->parent.subtransport = &t->parent;
247             s->parent.read = ssh_stream_read;
248             s->parent.write = ssh_stream_write;
249             s->parent.free = ssh_stream_free;
250              
251             s->cmd = cmd;
252              
253             s->url = git__strdup(url);
254             if (!s->url) {
255             git__free(s);
256             return -1;
257             }
258              
259             *stream = &s->parent;
260             return 0;
261             }
262              
263             static int git_ssh_extract_url_parts(
264             git_net_url *urldata,
265             const char *url)
266             {
267             char *colon, *at;
268             const char *start;
269              
270             colon = strchr(url, ':');
271              
272              
273             at = strchr(url, '@');
274             if (at) {
275             start = at + 1;
276             urldata->username = git__substrdup(url, at - url);
277             GIT_ERROR_CHECK_ALLOC(urldata->username);
278             } else {
279             start = url;
280             urldata->username = NULL;
281             }
282              
283             if (colon == NULL || (colon < start)) {
284             git_error_set(GIT_ERROR_NET, "malformed URL");
285             return -1;
286             }
287              
288             urldata->host = git__substrdup(start, colon - start);
289             GIT_ERROR_CHECK_ALLOC(urldata->host);
290              
291             return 0;
292             }
293              
294             static int ssh_agent_auth(LIBSSH2_SESSION *session, git_credential_ssh_key *c) {
295             int rc = LIBSSH2_ERROR_NONE;
296              
297             struct libssh2_agent_publickey *curr, *prev = NULL;
298              
299             LIBSSH2_AGENT *agent = libssh2_agent_init(session);
300              
301             if (agent == NULL)
302             return -1;
303              
304             rc = libssh2_agent_connect(agent);
305              
306             if (rc != LIBSSH2_ERROR_NONE)
307             goto shutdown;
308              
309             rc = libssh2_agent_list_identities(agent);
310              
311             if (rc != LIBSSH2_ERROR_NONE)
312             goto shutdown;
313              
314             while (1) {
315             rc = libssh2_agent_get_identity(agent, &curr, prev);
316              
317             if (rc < 0)
318             goto shutdown;
319              
320             /* rc is set to 1 whenever the ssh agent ran out of keys to check.
321             * Set the error code to authentication failure rather than erroring
322             * out with an untranslatable error code.
323             */
324             if (rc == 1) {
325             rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
326             goto shutdown;
327             }
328              
329             rc = libssh2_agent_userauth(agent, c->username, curr);
330              
331             if (rc == 0)
332             break;
333              
334             prev = curr;
335             }
336              
337             shutdown:
338              
339             if (rc != LIBSSH2_ERROR_NONE)
340             ssh_error(session, "error authenticating");
341              
342             libssh2_agent_disconnect(agent);
343             libssh2_agent_free(agent);
344              
345             return rc;
346             }
347              
348             static int _git_ssh_authenticate_session(
349             LIBSSH2_SESSION *session,
350             git_credential *cred)
351             {
352             int rc;
353              
354             do {
355             git_error_clear();
356             switch (cred->credtype) {
357             case GIT_CREDENTIAL_USERPASS_PLAINTEXT: {
358             git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred;
359             rc = libssh2_userauth_password(session, c->username, c->password);
360             break;
361             }
362             case GIT_CREDENTIAL_SSH_KEY: {
363             git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
364              
365             if (c->privatekey)
366             rc = libssh2_userauth_publickey_fromfile(
367             session, c->username, c->publickey,
368             c->privatekey, c->passphrase);
369             else
370             rc = ssh_agent_auth(session, c);
371              
372             break;
373             }
374             case GIT_CREDENTIAL_SSH_CUSTOM: {
375             git_credential_ssh_custom *c = (git_credential_ssh_custom *)cred;
376              
377             rc = libssh2_userauth_publickey(
378             session, c->username, (const unsigned char *)c->publickey,
379             c->publickey_len, c->sign_callback, &c->payload);
380             break;
381             }
382             case GIT_CREDENTIAL_SSH_INTERACTIVE: {
383             void **abstract = libssh2_session_abstract(session);
384             git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred;
385              
386             /* ideally, we should be able to set this by calling
387             * libssh2_session_init_ex() instead of libssh2_session_init().
388             * libssh2's API is inconsistent here i.e. libssh2_userauth_publickey()
389             * allows you to pass the `abstract` as part of the call, whereas
390             * libssh2_userauth_keyboard_interactive() does not!
391             *
392             * The only way to set the `abstract` pointer is by calling
393             * libssh2_session_abstract(), which will replace the existing
394             * pointer as is done below. This is safe for now (at time of writing),
395             * but may not be valid in future.
396             */
397             *abstract = c->payload;
398              
399             rc = libssh2_userauth_keyboard_interactive(
400             session, c->username, c->prompt_callback);
401             break;
402             }
403             #ifdef GIT_SSH_MEMORY_CREDENTIALS
404             case GIT_CREDENTIAL_SSH_MEMORY: {
405             git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
406              
407             assert(c->username);
408             assert(c->privatekey);
409              
410             rc = libssh2_userauth_publickey_frommemory(
411             session,
412             c->username,
413             strlen(c->username),
414             c->publickey,
415             c->publickey ? strlen(c->publickey) : 0,
416             c->privatekey,
417             strlen(c->privatekey),
418             c->passphrase);
419             break;
420             }
421             #endif
422             default:
423             rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
424             }
425             } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
426              
427             if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED ||
428             rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED ||
429             rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
430             return GIT_EAUTH;
431              
432             if (rc != LIBSSH2_ERROR_NONE) {
433             if (!git_error_last())
434             ssh_error(session, "Failed to authenticate SSH session");
435             return -1;
436             }
437              
438             return 0;
439             }
440              
441             static int request_creds(git_credential **out, ssh_subtransport *t, const char *user, int auth_methods)
442             {
443             int error, no_callback = 0;
444             git_credential *cred = NULL;
445              
446             if (!t->owner->cred_acquire_cb) {
447             no_callback = 1;
448             } else {
449             error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods,
450             t->owner->cred_acquire_payload);
451              
452             if (error == GIT_PASSTHROUGH) {
453             no_callback = 1;
454             } else if (error < 0) {
455             return error;
456             } else if (!cred) {
457             git_error_set(GIT_ERROR_SSH, "callback failed to initialize SSH credentials");
458             return -1;
459             }
460             }
461              
462             if (no_callback) {
463             git_error_set(GIT_ERROR_SSH, "authentication required but no callback set");
464             return -1;
465             }
466              
467             if (!(cred->credtype & auth_methods)) {
468             cred->free(cred);
469             git_error_set(GIT_ERROR_SSH, "callback returned unsupported credentials type");
470             return -1;
471             }
472              
473             *out = cred;
474              
475             return 0;
476             }
477              
478             static int _git_ssh_session_create(
479             LIBSSH2_SESSION** session,
480             git_stream *io)
481             {
482             int rc = 0;
483             LIBSSH2_SESSION* s;
484             git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent);
485              
486             assert(session);
487              
488             s = libssh2_session_init();
489             if (!s) {
490             git_error_set(GIT_ERROR_NET, "failed to initialize SSH session");
491             return -1;
492             }
493              
494             do {
495             rc = libssh2_session_handshake(s, socket->s);
496             } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
497              
498             if (rc != LIBSSH2_ERROR_NONE) {
499             ssh_error(s, "failed to start SSH session");
500             libssh2_session_free(s);
501             return -1;
502             }
503              
504             libssh2_session_set_blocking(s, 1);
505              
506             *session = s;
507              
508             return 0;
509             }
510              
511             #define SSH_DEFAULT_PORT "22"
512              
513             static int _git_ssh_setup_conn(
514             ssh_subtransport *t,
515             const char *url,
516             const char *cmd,
517             git_smart_subtransport_stream **stream)
518             {
519             git_net_url urldata = GIT_NET_URL_INIT;
520             int auth_methods, error = 0;
521             size_t i;
522             ssh_stream *s;
523             git_credential *cred = NULL;
524             LIBSSH2_SESSION* session=NULL;
525             LIBSSH2_CHANNEL* channel=NULL;
526              
527             t->current_stream = NULL;
528              
529             *stream = NULL;
530             if (ssh_stream_alloc(t, url, cmd, stream) < 0)
531             return -1;
532              
533             s = (ssh_stream *)*stream;
534             s->session = NULL;
535             s->channel = NULL;
536              
537             for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
538             const char *p = ssh_prefixes[i];
539              
540             if (!git__prefixcmp(url, p)) {
541             if ((error = git_net_url_parse(&urldata, url)) < 0)
542             goto done;
543              
544             goto post_extract;
545             }
546             }
547             if ((error = git_ssh_extract_url_parts(&urldata, url)) < 0)
548             goto done;
549              
550             if (urldata.port == NULL)
551             urldata.port = git__strdup(SSH_DEFAULT_PORT);
552              
553             GIT_ERROR_CHECK_ALLOC(urldata.port);
554              
555             post_extract:
556             if ((error = git_socket_stream_new(&s->io, urldata.host, urldata.port)) < 0 ||
557             (error = git_stream_connect(s->io)) < 0)
558             goto done;
559              
560             if ((error = _git_ssh_session_create(&session, s->io)) < 0)
561             goto done;
562              
563             if (t->owner->certificate_check_cb != NULL) {
564             git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
565             const char *key;
566              
567             cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
568              
569             #ifdef LIBSSH2_HOSTKEY_HASH_SHA256
570             key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
571             if (key != NULL) {
572             cert.type |= GIT_CERT_SSH_SHA256;
573             memcpy(&cert.hash_sha256, key, 32);
574             }
575             #endif
576              
577             key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
578             if (key != NULL) {
579             cert.type |= GIT_CERT_SSH_SHA1;
580             memcpy(&cert.hash_sha1, key, 20);
581             }
582              
583             key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
584             if (key != NULL) {
585             cert.type |= GIT_CERT_SSH_MD5;
586             memcpy(&cert.hash_md5, key, 16);
587             }
588              
589             if (cert.type == 0) {
590             git_error_set(GIT_ERROR_SSH, "unable to get the host key");
591             error = -1;
592             goto done;
593             }
594              
595             /* We don't currently trust any hostkeys */
596             git_error_clear();
597              
598             cert_ptr = &cert;
599              
600             error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, urldata.host, t->owner->message_cb_payload);
601              
602             if (error < 0 && error != GIT_PASSTHROUGH) {
603             if (!git_error_last())
604             git_error_set(GIT_ERROR_NET, "user cancelled hostkey check");
605              
606             goto done;
607             }
608             }
609              
610             /* we need the username to ask for auth methods */
611             if (!urldata.username) {
612             if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0)
613             goto done;
614              
615             urldata.username = git__strdup(((git_credential_username *) cred)->username);
616             cred->free(cred);
617             cred = NULL;
618             if (!urldata.username)
619             goto done;
620             } else if (urldata.username && urldata.password) {
621             if ((error = git_credential_userpass_plaintext_new(&cred, urldata.username, urldata.password)) < 0)
622             goto done;
623             }
624              
625             if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
626             goto done;
627              
628             error = GIT_EAUTH;
629             /* if we already have something to try */
630             if (cred && auth_methods & cred->credtype)
631             error = _git_ssh_authenticate_session(session, cred);
632              
633             while (error == GIT_EAUTH) {
634             if (cred) {
635             cred->free(cred);
636             cred = NULL;
637             }
638              
639             if ((error = request_creds(&cred, t, urldata.username, auth_methods)) < 0)
640             goto done;
641              
642             if (strcmp(urldata.username, git_credential_get_username(cred))) {
643             git_error_set(GIT_ERROR_SSH, "username does not match previous request");
644             error = -1;
645             goto done;
646             }
647              
648             error = _git_ssh_authenticate_session(session, cred);
649              
650             if (error == GIT_EAUTH) {
651             /* refresh auth methods */
652             if ((error = list_auth_methods(&auth_methods, session, urldata.username)) < 0)
653             goto done;
654             else
655             error = GIT_EAUTH;
656             }
657             }
658              
659             if (error < 0)
660             goto done;
661              
662             channel = libssh2_channel_open_session(session);
663             if (!channel) {
664             error = -1;
665             ssh_error(session, "Failed to open SSH channel");
666             goto done;
667             }
668              
669             libssh2_channel_set_blocking(channel, 1);
670              
671             s->session = session;
672             s->channel = channel;
673              
674             t->current_stream = s;
675              
676             done:
677             if (error < 0) {
678             ssh_stream_free(*stream);
679              
680             if (session)
681             libssh2_session_free(session);
682             }
683              
684             if (cred)
685             cred->free(cred);
686              
687             git_net_url_dispose(&urldata);
688              
689             return error;
690             }
691              
692             static int ssh_uploadpack_ls(
693             ssh_subtransport *t,
694             const char *url,
695             git_smart_subtransport_stream **stream)
696             {
697             const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
698              
699             return _git_ssh_setup_conn(t, url, cmd, stream);
700             }
701              
702             static int ssh_uploadpack(
703             ssh_subtransport *t,
704             const char *url,
705             git_smart_subtransport_stream **stream)
706             {
707             GIT_UNUSED(url);
708              
709             if (t->current_stream) {
710             *stream = &t->current_stream->parent;
711             return 0;
712             }
713              
714             git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
715             return -1;
716             }
717              
718             static int ssh_receivepack_ls(
719             ssh_subtransport *t,
720             const char *url,
721             git_smart_subtransport_stream **stream)
722             {
723             const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack;
724              
725              
726             return _git_ssh_setup_conn(t, url, cmd, stream);
727             }
728              
729             static int ssh_receivepack(
730             ssh_subtransport *t,
731             const char *url,
732             git_smart_subtransport_stream **stream)
733             {
734             GIT_UNUSED(url);
735              
736             if (t->current_stream) {
737             *stream = &t->current_stream->parent;
738             return 0;
739             }
740              
741             git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
742             return -1;
743             }
744              
745             static int _ssh_action(
746             git_smart_subtransport_stream **stream,
747             git_smart_subtransport *subtransport,
748             const char *url,
749             git_smart_service_t action)
750             {
751             ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
752              
753             switch (action) {
754             case GIT_SERVICE_UPLOADPACK_LS:
755             return ssh_uploadpack_ls(t, url, stream);
756              
757             case GIT_SERVICE_UPLOADPACK:
758             return ssh_uploadpack(t, url, stream);
759              
760             case GIT_SERVICE_RECEIVEPACK_LS:
761             return ssh_receivepack_ls(t, url, stream);
762              
763             case GIT_SERVICE_RECEIVEPACK:
764             return ssh_receivepack(t, url, stream);
765             }
766              
767             *stream = NULL;
768             return -1;
769             }
770              
771             static int _ssh_close(git_smart_subtransport *subtransport)
772             {
773             ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
774              
775             assert(!t->current_stream);
776              
777             GIT_UNUSED(t);
778              
779             return 0;
780             }
781              
782             static void _ssh_free(git_smart_subtransport *subtransport)
783             {
784             ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
785              
786             assert(!t->current_stream);
787              
788             git__free(t->cmd_uploadpack);
789             git__free(t->cmd_receivepack);
790             git__free(t);
791             }
792              
793             #define SSH_AUTH_PUBLICKEY "publickey"
794             #define SSH_AUTH_PASSWORD "password"
795             #define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive"
796              
797             static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username)
798             {
799             const char *list, *ptr;
800              
801             *out = 0;
802              
803             list = libssh2_userauth_list(session, username, strlen(username));
804              
805             /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
806             if (list == NULL && !libssh2_userauth_authenticated(session)) {
807             ssh_error(session, "Failed to retrieve list of SSH authentication methods");
808             return -1;
809             }
810              
811             ptr = list;
812             while (ptr) {
813             if (*ptr == ',')
814             ptr++;
815              
816             if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
817             *out |= GIT_CREDENTIAL_SSH_KEY;
818             *out |= GIT_CREDENTIAL_SSH_CUSTOM;
819             #ifdef GIT_SSH_MEMORY_CREDENTIALS
820             *out |= GIT_CREDENTIAL_SSH_MEMORY;
821             #endif
822             ptr += strlen(SSH_AUTH_PUBLICKEY);
823             continue;
824             }
825              
826             if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) {
827             *out |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
828             ptr += strlen(SSH_AUTH_PASSWORD);
829             continue;
830             }
831              
832             if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) {
833             *out |= GIT_CREDENTIAL_SSH_INTERACTIVE;
834             ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE);
835             continue;
836             }
837              
838             /* Skipt it if we don't know it */
839             ptr = strchr(ptr, ',');
840             }
841              
842             return 0;
843             }
844             #endif
845              
846 0           int git_smart_subtransport_ssh(
847             git_smart_subtransport **out, git_transport *owner, void *param)
848             {
849             #ifdef GIT_SSH
850             ssh_subtransport *t;
851              
852             assert(out);
853              
854             GIT_UNUSED(param);
855              
856             t = git__calloc(sizeof(ssh_subtransport), 1);
857             GIT_ERROR_CHECK_ALLOC(t);
858              
859             t->owner = (transport_smart *)owner;
860             t->parent.action = _ssh_action;
861             t->parent.close = _ssh_close;
862             t->parent.free = _ssh_free;
863              
864             *out = (git_smart_subtransport *) t;
865             return 0;
866             #else
867             GIT_UNUSED(owner);
868             GIT_UNUSED(param);
869              
870 0 0         assert(out);
871 0           *out = NULL;
872              
873 0           git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
874 0           return -1;
875             #endif
876             }
877              
878 0           int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload)
879             {
880             #ifdef GIT_SSH
881             git_strarray *paths = (git_strarray *) payload;
882             git_transport *transport;
883             transport_smart *smart;
884             ssh_subtransport *t;
885             int error;
886             git_smart_subtransport_definition ssh_definition = {
887             git_smart_subtransport_ssh,
888             0, /* no RPC */
889             NULL,
890             };
891              
892             if (paths->count != 2) {
893             git_error_set(GIT_ERROR_SSH, "invalid ssh paths, must be two strings");
894             return GIT_EINVALIDSPEC;
895             }
896              
897             if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0)
898             return error;
899              
900             smart = (transport_smart *) transport;
901             t = (ssh_subtransport *) smart->wrapped;
902              
903             t->cmd_uploadpack = git__strdup(paths->strings[0]);
904             GIT_ERROR_CHECK_ALLOC(t->cmd_uploadpack);
905             t->cmd_receivepack = git__strdup(paths->strings[1]);
906             GIT_ERROR_CHECK_ALLOC(t->cmd_receivepack);
907              
908             *out = transport;
909             return 0;
910             #else
911             GIT_UNUSED(owner);
912             GIT_UNUSED(payload);
913              
914 0 0         assert(out);
915 0           *out = NULL;
916              
917 0           git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
918 0           return -1;
919             #endif
920             }
921              
922             #ifdef GIT_SSH
923             static void shutdown_ssh(void)
924             {
925             libssh2_exit();
926             }
927             #endif
928              
929 86           int git_transport_ssh_global_init(void)
930             {
931             #ifdef GIT_SSH
932             if (libssh2_init(0) < 0) {
933             git_error_set(GIT_ERROR_SSH, "unable to initialize libssh2");
934             return -1;
935             }
936              
937             git__on_shutdown(shutdown_ssh);
938             return 0;
939              
940             #else
941              
942             /* Nothing to initialize */
943 86           return 0;
944              
945             #endif
946             }