From 3485f08a03fa8ff70c94decbd445f7b64982873c Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sat, 18 Jul 2015 17:39:25 -0500 Subject: add ssl cert validation, unless OTP version is too old --- src/rebar_pkg_resource.erl | 62 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) (limited to 'src/rebar_pkg_resource.erl') diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 450ff1e..62208a6 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -10,6 +10,7 @@ ,make_vsn/1]). -include("rebar.hrl"). +-include_lib("public_key/include/OTP-PUB-KEY.hrl"). lock(_AppDir, Source) -> Source. @@ -94,7 +95,7 @@ make_vsn(_) -> request(Url, ETag) -> case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]}, - [{relaxed, true}], + [{ssl, [ssl_opts(Url)]}, {relaxed, true}], [{body_format, binary}], rebar) of {ok, {{_Version, 200, _Reason}, Headers, Body}} -> @@ -120,3 +121,62 @@ etag(Path) -> {error, _} -> false end. + +ssl_opts(Url) -> + case check_ssl_version() of + true -> + {ok, {_, _, Hostname, _, _, _}} = http_uri:parse(ec_cnv:to_list(Url)), + VerifyFun = {fun ssl_verify_hostname:verify_fun/3, [{check_hostname, Hostname}]}, + CACerts = cacerts(), + [{verify, verify_peer}, {depth, 2}, {cacerts, CACerts} + ,{partial_chain, fun partial_chain/1}, {verify_fun, VerifyFun}]; + false -> + ?WARN("Insecure HTTPS request (peer verification disabled), please update to OTP 17.4 or later", []), + [{verify, verify_none}] + end. + +partial_chain(Certs) -> + Certs1 = [{Cert, public_key:pkix_decode_cert(Cert, otp)} || Cert <- Certs], + CACerts = cacerts(), + CACerts1 = [public_key:pkix_decode_cert(Cert, otp) || Cert <- CACerts], + + case ec_lists:find(fun({_, Cert}) -> + check_cert(CACerts1, Cert) + end, Certs1) of + {ok, Trusted} -> + {trusted_ca, element(1, Trusted)}; + _ -> + unknown_ca + end. + +extract_public_key_info(Cert) -> + ((Cert#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subjectPublicKeyInfo). + +cacerts() -> + Pems = public_key:pem_decode(rebar_cacerts:cacerts()), + [Der || {'Certificate', Der, _} <- Pems]. + +check_cert(CACerts, Cert) -> + lists:any(fun(CACert) -> + extract_public_key_info(CACert) == extract_public_key_info(Cert) + end, CACerts). + +check_ssl_version() -> + case application:get_key(ssl, vsn) of + {ok, Vsn} -> + parse_vsn(Vsn) >= {5, 3, 6}; + _ -> + false + end. + +parse_vsn(Vsn) -> + version_pad(string:tokens(Vsn, ".")). + +version_pad([Major]) -> + {list_to_integer(Major), 0, 0}; +version_pad([Major, Minor]) -> + {list_to_integer(Major), list_to_integer(Minor), 0}; +version_pad([Major, Minor, Patch]) -> + {list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)}; +version_pad([Major, Minor, Patch | _]) -> + {list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)}. -- cgit v1.1