Skip to content

Commit

Permalink
Merge branch 'stevengj-securezero'
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanKarpinski committed Jul 25, 2016
2 parents 67d33b7 + c389aa3 commit aa7acff
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 182 deletions.
65 changes: 48 additions & 17 deletions base/c.jl
Original file line number Diff line number Diff line change
Expand Up @@ -331,38 +331,69 @@ function transcode(::Type{UInt16}, src::Vector{UInt8})
end

function transcode(::Type{UInt8}, src::Vector{UInt16})
dst = UInt8[]
i, n = 1, length(src)
n > 0 || return dst
sizehint!(dst, n)
n = length(src)
n == 0 && return UInt8[]

# Precompute m = sizeof(dst). This involves annoying duplication
# of the loop over the src array. However, this is not just an
# optimization: it is problematic for security reasons to grow
# dst dynamically, because Base.winprompt uses this function to
# convert passwords to UTF-8 and we don't want to make unintentional
# copies of the password data.
a = src[1]
i, m = 1, 0
while true
if a < 0x80
m += 1
elseif a < 0x800 # 2-byte UTF-8
m += 2
elseif a & 0xfc00 == 0xd800 && i < length(src)
b = src[i += 1]
if (b & 0xfc00) == 0xdc00 # 2-unit UTF-16 sequence => 4-byte UTF-8
m += 4
else
m += 3
a = b; continue
end
else
# 1-unit high UTF-16 or unpaired high surrogate
# either way, encode as 3-byte UTF-8 code point
m += 3
end
i < n || break
a = src[i += 1]
end

dst = Array{UInt8}(m)
a = src[1]
i, j = 1, 0
while true
if a < 0x80 # ASCII
push!(dst, a % UInt8)
dst[j += 1] = a % UInt8
elseif a < 0x800 # 2-byte UTF-8
push!(dst, 0xc0 | ((a >> 6) % UInt8),
0x80 | ((a % UInt8) & 0x3f))
dst[j += 1] = 0xc0 | ((a >> 6) % UInt8)
dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f)
elseif a & 0xfc00 == 0xd800 && i < n
b = src[i += 1]
if (b & 0xfc00) == 0xdc00
# 2-unit UTF-16 sequence => 4-byte UTF-8
a += 0x2840
push!(dst, 0xf0 | ((a >> 8) % UInt8),
0x80 | ((a % UInt8) >> 2),
0xf0 $ ((((a % UInt8) << 4) & 0x3f) $ (b >> 6) % UInt8),
0x80 | ((b % UInt8) & 0x3f))
dst[j += 1] = 0xf0 | ((a >> 8) % UInt8)
dst[j += 1] = 0x80 | ((a % UInt8) >> 2)
dst[j += 1] = 0xf0 $ ((((a % UInt8) << 4) & 0x3f) $ (b >> 6) % UInt8)
dst[j += 1] = 0x80 | ((b % UInt8) & 0x3f)
else
push!(dst, 0xe0 | ((a >> 12) % UInt8),
0x80 | (((a >> 6) % UInt8) & 0x3f),
0x80 | ((a % UInt8) & 0x3f))
dst[j += 1] = 0xe0 | ((a >> 12) % UInt8)
dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f)
dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f)
a = b; continue
end
else
# 1-unit high UTF-16 or unpaired high surrogate
# either way, encode as 3-byte UTF-8 code point
push!(dst, 0xe0 | ((a >> 12) % UInt8),
0x80 | (((a >> 6) % UInt8) & 0x3f),
0x80 | ((a % UInt8) & 0x3f))
dst[j += 1] = 0xe0 | ((a >> 12) % UInt8)
dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f)
dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f)
end
i < n || break
a = src[i += 1]
Expand Down
223 changes: 116 additions & 107 deletions base/libgit2/callbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,143 +64,152 @@ function credentials_callback(cred::Ptr{Ptr{Void}}, url_ptr::Cstring,

# get credentials object from payload pointer
creds = nothing
creds_are_temp = true
if payload_ptr != C_NULL
tmpobj = unsafe_pointer_to_objref(payload_ptr)
if isa(tmpobj, AbstractCredentials)
creds = tmpobj
creds_are_temp = false
end
end
isusedcreds = checkused!(creds)

# use ssh key or ssh-agent
if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY))
creds == nothing && (creds = SSHCredentials())
credid = "ssh:https://$host"
try
# use ssh key or ssh-agent
if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY))
creds == nothing && (creds = SSHCredentials())
credid = "ssh:https://$host"

# first try ssh-agent if credentials support its usage
if creds[:usesshagent, credid] === nothing || creds[:usesshagent, credid] == "Y"
err = ccall((:git_cred_ssh_key_from_agent, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring), cred, username_ptr)
creds[:usesshagent, credid] = "U" # used ssh-agent only one time
err == 0 && return Cint(0)
end

# first try ssh-agent if credentials support its usage
if creds[:usesshagent, credid] === nothing || creds[:usesshagent, credid] == "Y"
err = ccall((:git_cred_ssh_key_from_agent, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring), cred, username_ptr)
creds[:usesshagent, credid] = "U" # used ssh-agent only one time
err == 0 && return Cint(0)
end
errcls, errmsg = Error.last_error()
if errcls != Error.None
# Check if we used ssh-agent
if creds[:usesshagent, credid] == "U"
println("ERROR: $errmsg ssh-agent")
creds[:usesshagent, credid] = "E" # reported ssh-agent error
else
println("ERROR: $errmsg")
end
flush(STDOUT)
end

errcls, errmsg = Error.last_error()
if errcls != Error.None
# Check if we used ssh-agent
if creds[:usesshagent, credid] == "U"
println("ERROR: $errmsg ssh-agent")
creds[:usesshagent, credid] = "E" # reported ssh-agent error
# if username is not provided, then prompt for it
username = if username_ptr == Cstring(C_NULL)
uname = creds[:user, credid] # check if credentials were already used
uname !== nothing && !isusedcreds ? uname : prompt("Username for '$schema$host'")
else
println("ERROR: $errmsg")
unsafe_string(username_ptr)
end
flush(STDOUT)
end
creds[:user, credid] = username # save credentials

# if username is not provided, then prompt for it
username = if username_ptr == Cstring(C_NULL)
uname = creds[:user, credid] # check if credentials were already used
uname !== nothing && !isusedcreds ? uname : prompt("Username for '$schema$host'")
else
unsafe_string(username_ptr)
end
creds[:user, credid] = username # save credentials

# For SSH we need a private key location
privatekey = if haskey(ENV,"SSH_KEY_PATH")
ENV["SSH_KEY_PATH"]
else
keydefpath = creds[:prvkey, credid] # check if credentials were already used
if keydefpath !== nothing && !isusedcreds
keydefpath # use cached value
# For SSH we need a private key location
privatekey = if haskey(ENV,"SSH_KEY_PATH")
ENV["SSH_KEY_PATH"]
else
if keydefpath === nothing || isempty(keydefpath)
keydefpath = joinpath(homedir(),".ssh","id_rsa")
keydefpath = creds[:prvkey, credid] # check if credentials were already used
if keydefpath !== nothing && !isusedcreds
keydefpath # use cached value
else
if keydefpath === nothing || isempty(keydefpath)
keydefpath = joinpath(homedir(),".ssh","id_rsa")
end
prompt("Private key location for '$schema$username@$host'", default=keydefpath)
end
prompt("Private key location for '$schema$username@$host'", default=keydefpath)
end
end
creds[:prvkey, credid] = privatekey # save credentials

# For SSH we need a public key location, look for environment vars SSH_* as well
publickey = if haskey(ENV,"SSH_PUB_KEY_PATH")
ENV["SSH_PUB_KEY_PATH"]
else
keydefpath = creds[:pubkey, credid] # check if credentials were already used
if keydefpath !== nothing && !isusedcreds
keydefpath # use cached value
creds[:prvkey, credid] = privatekey # save credentials

# For SSH we need a public key location, look for environment vars SSH_* as well
publickey = if haskey(ENV,"SSH_PUB_KEY_PATH")
ENV["SSH_PUB_KEY_PATH"]
else
if keydefpath === nothing || isempty(keydefpath)
keydefpath = privatekey*".pub"
end
if isfile(keydefpath)
keydefpath
keydefpath = creds[:pubkey, credid] # check if credentials were already used
if keydefpath !== nothing && !isusedcreds
keydefpath # use cached value
else
prompt("Public key location for '$schema$username@$host'", default=keydefpath)
if keydefpath === nothing || isempty(keydefpath)
keydefpath = privatekey*".pub"
end
if isfile(keydefpath)
keydefpath
else
prompt("Public key location for '$schema$username@$host'", default=keydefpath)
end
end
end
end
creds[:pubkey, credid] = publickey # save credentials

passphrase = if haskey(ENV,"SSH_KEY_PASS")
ENV["SSH_KEY_PASS"]
else
passdef = creds[:pass, credid] # check if credentials were already used
if passdef === nothing || isusedcreds
if is_windows()
passdef = Base.winprompt(
"Your SSH Key requires a password, please enter it now:",
"Passphrase required", privatekey; prompt_username = false)
isnull(passdef) && return Cint(Error.EAUTH)
passdef = Base.get(passdef)[2]
else
passdef = prompt("Passphrase for $privatekey", password=true)
creds[:pubkey, credid] = publickey # save credentials

passphrase = if haskey(ENV,"SSH_KEY_PASS")
ENV["SSH_KEY_PASS"]
else
passdef = creds[:pass, credid] # check if credentials were already used
if passdef === nothing || isusedcreds
if is_windows()
passdef = Base.winprompt(
"Your SSH Key requires a password, please enter it now:",
"Passphrase required", privatekey; prompt_username = false)
isnull(passdef) && return Cint(Error.EAUTH)
passdef = Base.get(passdef)[2]
else
passdef = prompt("Passphrase for $privatekey", password=true)
end
end
end
end
creds[:pass, credid] = passphrase # save credentials
creds[:pass, credid] = passphrase # save credentials

isempty(username) && return Cint(Error.EAUTH)
isempty(username) && return Cint(Error.EAUTH)

err = ccall((:git_cred_ssh_key_new, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring, Cstring, Cstring, Cstring),
cred, username, publickey, privatekey, passphrase)
err == 0 && return Cint(0)
end
err = ccall((:git_cred_ssh_key_new, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring, Cstring, Cstring, Cstring),
cred, username, publickey, privatekey, passphrase)
err == 0 && return Cint(0)
end

if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT))
creds == nothing && (creds = UserPasswordCredentials())
credid = "$schema$host"

username = creds[:user, credid]
userpass = creds[:pass, credid]
if is_windows()
if username === nothing || userpass === nothing || isusedcreds
res = Base.winprompt("Please enter your credentials for '$schema$host'", "Credentials required",
username === nothing ? "" : username; prompt_username = true)
isnull(res) && return Cint(Error.EAUTH)
username, userpass = Base.get(res)
end
else
if username === nothing || isusedcreds
username = prompt("Username for '$schema$host'")
end
if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT))
creds == nothing && (creds = UserPasswordCredentials())
credid = "$schema$host"

username = creds[:user, credid]
userpass = creds[:pass, credid]
if is_windows()
if username === nothing || userpass === nothing || isusedcreds
res = Base.winprompt("Please enter your credentials for '$schema$host'", "Credentials required",
username === nothing ? "" : username; prompt_username = true)
isnull(res) && return Cint(Error.EAUTH)
username, userpass = Base.get(res)
end
else
if username === nothing || isusedcreds
username = prompt("Username for '$schema$host'")
end

if userpass === nothing || isusedcreds
userpass = prompt("Password for '$schema$username@$host'", password=true)
if userpass === nothing || isusedcreds
userpass = prompt("Password for '$schema$username@$host'", password=true)
end
end
end
creds[:user, credid] = username # save credentials
creds[:pass, credid] = userpass # save credentials
creds[:user, credid] = username # save credentials
creds[:pass, credid] = userpass # save credentials

isempty(username) && isempty(userpass) && return Cint(Error.EAUTH)
isempty(username) && isempty(userpass) && return Cint(Error.EAUTH)

err = ccall((:git_cred_userpass_plaintext_new, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring, Cstring),
cred, username, userpass)
err == 0 && return Cint(0)
err = ccall((:git_cred_userpass_plaintext_new, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring, Cstring),
cred, username, userpass)
err == 0 && return Cint(0)
end
finally
# if credentials are not passed back to caller via payload,
# then zero any passwords immediately.
if creds_are_temp && creds !== nothing
securezero!(creds)
end
end

return Cint(err)
end

Expand Down
Loading

0 comments on commit aa7acff

Please sign in to comment.