Line Flow Count Block(s) Source
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 <libssh2.h>
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 ##### 2 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 ##### 2,3 assert(out);
871 ##### 4 *out = NULL;
872 -
873 ##### 4 git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
874 ##### 5 return -1;
875 - #endif
876 - }
877 -
878 ##### 2 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 ##### 2,3 assert(out);
915 ##### 4 *out = NULL;
916 -
917 ##### 4 git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
918 ##### 5 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 9 2 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 9 2 return 0;
944 -
945 - #endif
946 - }