Skip to content

Commit

Permalink
LibGit2: improve error when CA root cert can't be set
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
StefanKarpinski committed Dec 11, 2020
1 parent ab35e37 commit 904876b
Showing 1 changed file with 34 additions and 12 deletions.
46 changes: 34 additions & 12 deletions stdlib/LibGit2/src/LibGit2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -961,13 +961,19 @@ end

## lazy libgit2 initialization

const ENSURE_INITIALIZED_LOCK = ReentrantLock()

function ensure_initialized()
x = Threads.atomic_cas!(REFCOUNT, 0, 1)
if x < 0
negative_refcount_error(x)::Union{}
end
if x == 0
initialize()
lock(ENSURE_INITIALIZED_LOCK) do
x = Threads.atomic_cas!(REFCOUNT, 0, 1)
x > 0 && return
x < 0 && negative_refcount_error(x)::Union{}
try initialize()
catch
Threads.atomic_sub!(REFCOUNT, 1)
@assert REFCOUNT[] == 0
rethrow()
end
end
return nothing
end
Expand All @@ -991,12 +997,28 @@ end
end

function set_ssl_cert_locations(cert_loc)
cert_file = isfile(cert_loc) ? cert_loc : Cstring(C_NULL)
cert_dir = isdir(cert_loc) ? cert_loc : Cstring(C_NULL)
cert_file == C_NULL && cert_dir == C_NULL && return
@check ccall((:git_libgit2_opts, :libgit2), Cint,
(Cint, Cstring...),
Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
cert_file = cert_dir = Cstring(C_NULL)
if isdir(cert_loc) # directories
cert_dir = cert_loc
else # files, /dev/null, non-existent paths, etc.
cert_file = cert_loc
end
ret = ccall((:git_libgit2_opts, :libgit2), Cint, (Cint, Cstring...),
Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
ret >= 0 && return ret
err = Error.GitError(ret)
err.class == Error.SSL &&
err.msg == "TLS backend doesn't support certificate locations" ||
throw(err)
var = nothing
for v in NetworkOptions.CA_ROOTS_VARS
haskey(ENV, v) && (var = v)
end
@assert var !== nothing # otherwise we should be here
msg = """
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.
"""
throw(Error.GitError(err.class, err.code, chomp(msg)))
end

end # module

0 comments on commit 904876b

Please sign in to comment.