Skip to content

Commit 904876b

Browse files
LibGit2: improve error when CA root cert can't be set
This also fixes an insecure behavior: even if `set_ssl_cert_locations` failed, `REFCOUNT` was still incremented, so subsequent calls to `ensure_initialized` didn't call `initialize` and so there is never a successful call to `set_ssl_cert_locations`. Without this libgit2 defaults to not verifying host identities, which is insecure. To prevent this, this patch locks on `ensure_initialized` and decrements `REFCOUNT` if initialize throws an error, ensuring that `initialize` succeeds at least once, including the call to `set_ssl_cert_locations`.
1 parent ab35e37 commit 904876b

File tree

1 file changed

+34
-12
lines changed

1 file changed

+34
-12
lines changed

stdlib/LibGit2/src/LibGit2.jl

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -961,13 +961,19 @@ end
961961

962962
## lazy libgit2 initialization
963963

964+
const ENSURE_INITIALIZED_LOCK = ReentrantLock()
965+
964966
function ensure_initialized()
965-
x = Threads.atomic_cas!(REFCOUNT, 0, 1)
966-
if x < 0
967-
negative_refcount_error(x)::Union{}
968-
end
969-
if x == 0
970-
initialize()
967+
lock(ENSURE_INITIALIZED_LOCK) do
968+
x = Threads.atomic_cas!(REFCOUNT, 0, 1)
969+
x > 0 && return
970+
x < 0 && negative_refcount_error(x)::Union{}
971+
try initialize()
972+
catch
973+
Threads.atomic_sub!(REFCOUNT, 1)
974+
@assert REFCOUNT[] == 0
975+
rethrow()
976+
end
971977
end
972978
return nothing
973979
end
@@ -991,12 +997,28 @@ end
991997
end
992998

993999
function set_ssl_cert_locations(cert_loc)
994-
cert_file = isfile(cert_loc) ? cert_loc : Cstring(C_NULL)
995-
cert_dir = isdir(cert_loc) ? cert_loc : Cstring(C_NULL)
996-
cert_file == C_NULL && cert_dir == C_NULL && return
997-
@check ccall((:git_libgit2_opts, :libgit2), Cint,
998-
(Cint, Cstring...),
999-
Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
1000+
cert_file = cert_dir = Cstring(C_NULL)
1001+
if isdir(cert_loc) # directories
1002+
cert_dir = cert_loc
1003+
else # files, /dev/null, non-existent paths, etc.
1004+
cert_file = cert_loc
1005+
end
1006+
ret = ccall((:git_libgit2_opts, :libgit2), Cint, (Cint, Cstring...),
1007+
Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
1008+
ret >= 0 && return ret
1009+
err = Error.GitError(ret)
1010+
err.class == Error.SSL &&
1011+
err.msg == "TLS backend doesn't support certificate locations" ||
1012+
throw(err)
1013+
var = nothing
1014+
for v in NetworkOptions.CA_ROOTS_VARS
1015+
haskey(ENV, v) && (var = v)
1016+
end
1017+
@assert var !== nothing # otherwise we should be here
1018+
msg = """
1019+
Your Julia is built with a SSL/TLS engine that libgit2 doesn't know how to configure to use a file or directory of certificate authority roots, but your environment specifies one via the $var variable. If you believe your system's root certificates are safe to use, you can `export JULIA_SSL_CA_ROOTS_PATH=""` in your environment to use those instead.
1020+
"""
1021+
throw(Error.GitError(err.class, err.code, chomp(msg)))
10001022
end
10011023

10021024
end # module

0 commit comments

Comments
 (0)