File Coverage

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


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