Skip to content

Commit 563cb90

Browse files
committed
Bring back the certificate check callback
This was implemented for clone, but not for fetch or push, so it was deleted during the conversion, which shows why we need to unify these callback structures.
1 parent ac2e363 commit 563cb90

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

pygit2/remote.py

+54
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,25 @@ def credentials(self, url, username_from_url, allowed_types):
122122
"""
123123
raise Passthrough
124124

125+
def certificate_check(self, certificate, valid, host):
126+
"""Certificate callback
127+
128+
Override with your own function to determine whether the accept
129+
the server's certificate.
130+
131+
:param None certificate: The certificate. It is currently always None
132+
while we figure out how to represent it cross-platform
133+
134+
:param bool valid: Whether the TLS/SSH library thinks the certificate
135+
is valid
136+
137+
:param str host: The hostname we want to connect to
138+
139+
Return value: True to connect, False to abort
140+
"""
141+
142+
raise Passthrough
143+
125144
def transfer_progress(self, stats):
126145
"""Transfer progress callback
127146
@@ -156,6 +175,7 @@ def _fill_fetch_options(self, fetch_opts):
156175
fetch_opts.callbacks.transfer_progress = self._transfer_progress_cb
157176
fetch_opts.callbacks.update_tips = self._update_tips_cb
158177
fetch_opts.callbacks.credentials = self._credentials_cb
178+
fetch_opts.callbacks.certificate_check = self._certificate_cb
159179
# We need to make sure that this handle stays alive
160180
self._self_handle = ffi.new_handle(self)
161181
fetch_opts.callbacks.payload = self._self_handle
@@ -167,6 +187,7 @@ def _fill_push_options(self, push_opts):
167187
push_opts.callbacks.transfer_progress = self._transfer_progress_cb
168188
push_opts.callbacks.update_tips = self._update_tips_cb
169189
push_opts.callbacks.credentials = self._credentials_cb
190+
push_opts.callbacks.certificate_check = self._certificate_cb
170191
push_opts.callbacks.push_update_reference = self._push_update_reference_cb
171192
# We need to make sure that this handle stays alive
172193
self._self_handle = ffi.new_handle(self)
@@ -266,6 +287,39 @@ def _credentials_cb(cred_out, url, username, allowed, data):
266287

267288
return 0
268289

290+
@ffi.callback('int (*git_transport_certificate_check_cb)'
291+
'(git_cert *cert, int valid, const char *host, void *payload)')
292+
def _certificate_cb(cert_i, valid, host, data):
293+
self = ffi.from_handle(data)
294+
295+
# We want to simulate what should happen if libgit2 supported pass-through for
296+
# this callback. For SSH, 'valid' is always False, because it doesn't look
297+
# at known_hosts, but we do want to let it through in order to do what libgit2 would
298+
# if the callback were not set.
299+
try:
300+
is_ssh = cert_i.cert_type == C.GIT_CERT_HOSTKEY_LIBSSH2
301+
302+
if not hasattr(self, 'certificate_check') or not self.certificate_check:
303+
raise Passthrough
304+
305+
# python's parsing is deep in the libraries and assumes an OpenSSL-owned cert
306+
val = self.certificate_check(None, bool(valid), ffi.string(host))
307+
if not val:
308+
return C.GIT_ECERTIFICATE
309+
except Exception as e:
310+
if e is Passthrough:
311+
if is_ssh:
312+
return 0
313+
elif valid:
314+
return 0
315+
else:
316+
return C.GIT_ECERTIFICATE
317+
318+
self._stored_exception = e
319+
return C.GIT_EUSER
320+
321+
return 0
322+
269323
class Remote(object):
270324
def __init__(self, repo, ptr):
271325
"""The constructor is for internal use only"""

0 commit comments

Comments
 (0)