Skip to content

Commit 276e25d

Browse files
committed
Added support for certificate_check callback
Since libgit2 removed support for GIT_SSL_NO_VERIFY environment variable and "http.sslverify" in favor of certificate check callbacks, we need rugged to support the certificate check callback. One usecase for this is self-signed certificates which can be verified in the callback.
1 parent 37e0c11 commit 276e25d

File tree

4 files changed

+77
-6
lines changed

4 files changed

+77
-6
lines changed

ext/rugged/rugged.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ struct rugged_remote_cb_payload
157157
VALUE transfer_progress;
158158
VALUE update_tips;
159159
VALUE credentials;
160+
VALUE certificate_check;
160161
VALUE result;
161162
int exception;
162163
};

ext/rugged/rugged_remote.c

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extern VALUE rb_cRuggedRepo;
2929
extern VALUE rb_eRuggedError;
3030
VALUE rb_cRuggedRemote;
3131

32-
#define RUGGED_REMOTE_CALLBACKS_INIT {1, progress_cb, NULL, credentials_cb, NULL, transfer_progress_cb, update_tips_cb, NULL, NULL, push_update_reference_cb, NULL}
32+
#define RUGGED_REMOTE_CALLBACKS_INIT {1, progress_cb, NULL, credentials_cb, certificate_check_cb, transfer_progress_cb, update_tips_cb, NULL, NULL, push_update_reference_cb, NULL}
3333

3434
static int progress_cb(const char *str, int len, void *data)
3535
{
@@ -96,6 +96,27 @@ static int update_tips_cb(const char *refname, const git_oid *src, const git_oid
9696
return payload->exception ? GIT_ERROR : GIT_OK;
9797
}
9898

99+
static int certificate_check_cb(git_cert *cert, int valid, const char *host, void *data)
100+
{
101+
struct rugged_remote_cb_payload *payload = data;
102+
VALUE args = rb_ary_new2(3);
103+
VALUE ret;
104+
105+
if (NIL_P(payload->certificate_check))
106+
return valid ? 0 : GIT_ECERTIFICATE;
107+
108+
rb_ary_push(args, payload->certificate_check);
109+
rb_ary_push(args, INT2FIX(valid));
110+
rb_ary_push(args, rb_str_new_utf8(host));
111+
112+
ret = rb_protect(rugged__block_yield_splat, args, &payload->exception);
113+
114+
if (payload->exception)
115+
return GIT_ERROR;
116+
117+
return rugged_parse_bool(ret) ? GIT_OK : GIT_ECERTIFICATE;
118+
}
119+
99120
struct extract_cred_args
100121
{
101122
VALUE rb_callback;
@@ -174,6 +195,7 @@ void rugged_remote_init_callbacks_and_payload_from_options(
174195
memcpy(callbacks, &prefilled, sizeof(git_remote_callbacks));
175196

176197
if (!NIL_P(rb_options)) {
198+
CALLABLE_OR_RAISE(payload->certificate_check, rb_options, "certificate_check");
177199
CALLABLE_OR_RAISE(payload->update_tips, rb_options, "update_tips");
178200
CALLABLE_OR_RAISE(payload->progress, rb_options, "progress");
179201
CALLABLE_OR_RAISE(payload->transfer_progress, rb_options, "transfer_progress");
@@ -274,7 +296,7 @@ static VALUE rb_git_remote_ls(int argc, VALUE *argv, VALUE self)
274296
git_strarray custom_headers = {0};
275297
const git_remote_head **heads;
276298

277-
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };
299+
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };
278300

279301
VALUE rb_options;
280302

@@ -471,7 +493,7 @@ static VALUE rb_git_remote_check_connection(int argc, VALUE *argv, VALUE self)
471493
git_remote *remote;
472494
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
473495
git_strarray custom_headers = {0};
474-
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };
496+
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };
475497
VALUE rb_direction, rb_options;
476498
ID id_direction;
477499
int error, direction;
@@ -537,6 +559,11 @@ static VALUE rb_git_remote_check_connection(int argc, VALUE *argv, VALUE self)
537559
* A callback that will be executed each time a reference is updated locally. It will be
538560
* passed the +refname+, +old_oid+ and +new_oid+.
539561
*
562+
* :certificate_check ::
563+
* A callback that will be executed each time we validate a certificate using https. It
564+
* will be passed the +valid+, +host_name+ and the callback should return a true/false to
565+
* indicate if the certificate has been validated.
566+
*
540567
* :message ::
541568
* The message to insert into the reflogs. Defaults to "fetch".
542569
*
@@ -559,7 +586,7 @@ static VALUE rb_git_remote_fetch(int argc, VALUE *argv, VALUE self)
559586
git_strarray refspecs;
560587
git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
561588
const git_transfer_progress *stats;
562-
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };
589+
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };
563590

564591
char *log_message = NULL;
565592
int error;
@@ -650,7 +677,7 @@ static VALUE rb_git_remote_push(int argc, VALUE *argv, VALUE self)
650677

651678
int error = 0;
652679

653-
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, rb_hash_new(), 0 };
680+
struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, rb_hash_new(), 0 };
654681

655682
rb_scan_args(argc, argv, "01:", &rb_refspecs, &rb_options);
656683

ext/rugged/rugged_repo.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ static VALUE rb_git_repo_clone_at(int argc, VALUE *argv, VALUE klass)
485485
{
486486
VALUE url, local_path, rb_options_hash;
487487
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
488-
struct rugged_remote_cb_payload remote_payload = { Qnil, Qnil, Qnil, Qnil, 0 };
488+
struct rugged_remote_cb_payload remote_payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };
489489
git_repository *repo;
490490
int error;
491491

test/online/fetch_test.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,49 @@ def test_fetch_over_https
2828
"refs/tags/commit_tree"
2929
], @repo.refs.map(&:name).sort
3030
end
31+
32+
def test_fetch_over_https_with_certificate_callback
33+
result = {}
34+
@repo.remotes.create("origin", "https://github.com/libgit2/TestGitRepository.git")
35+
36+
@repo.fetch("origin", {
37+
certificate_check: lambda { |valid, host|
38+
result[:valid] = valid
39+
true
40+
}
41+
})
42+
assert_equal result[:valid], 1
43+
end
44+
45+
def test_fetch_over_https_with_certificate_callback_fail
46+
result = {}
47+
@repo.remotes.create("origin", "https://github.com/libgit2/TestGitRepository.git")
48+
49+
exception = assert_raises Rugged::NetworkError do
50+
@repo.fetch("origin", {
51+
certificate_check: lambda { |valid, host|
52+
result[:valid] = valid
53+
false
54+
}
55+
})
56+
end
57+
assert_equal "user cancelled certificate check", exception.message
58+
end
59+
60+
def test_fetch_over_https_with_certificate_callback_exception
61+
result = {}
62+
@repo.remotes.create("origin", "https://github.com/libgit2/TestGitRepository.git")
63+
64+
exception = assert_raises RuntimeError do
65+
@repo.fetch("origin", {
66+
certificate_check: lambda { |valid, host|
67+
result[:valid] = valid
68+
raise "Exception from callback"
69+
}
70+
})
71+
end
72+
assert_equal "Exception from callback", exception.message
73+
end
3174
end
3275

3376
if Rugged.features.include?(:ssh) && ssh_creds?

0 commit comments

Comments
 (0)