1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
%%% Copyright (c) 2014-2015, NORDUnet A/S.
%%% See LICENSE for licensing information.
%%%
-module(plop_httputil).
-export([request/4]).
-include_lib("hackney/include/hackney_lib.hrl").
-include_lib("public_key/include/public_key.hrl").
get_auth_header(Headers) ->
case hackney_headers:get_value("X-Catlfish-Auth", Headers) of
undefined ->
undefined;
Result when is_binary(Result) ->
lager:debug("received auth header: ~p", [Result]),
binary_to_list(Result)
end.
add_auth(Method, Path, Headers, Data) ->
AuthHeader = http_auth:create_auth(Method, Path, Data),
lager:debug("sent auth header: ~p", [AuthHeader]),
[{"X-Catlfish-Auth", AuthHeader} | Headers].
get_cert_hostname(Cert) ->
TBSCert = Cert#'OTPCertificate'.tbsCertificate,
Subject = TBSCert#'OTPTBSCertificate'.subject,
{rdnSequence, RDNs} = Subject,
CNs = lists:map(fun([#'AttributeTypeAndValue'{type=?'id-at-commonName',
value=Value}]) ->
Value;
(_) ->
none
end, RDNs),
case [CN || CN <- CNs, CN /= none] of
[{utf8String, BinCN} | _] when is_binary(BinCN) ->
binary_to_list(BinCN);
[CN | _] ->
CN;
[] ->
""
end.
verify_fun(_,{bad_cert, _} = Reason, _) ->
{fail, Reason};
verify_fun(_,{extension, _}, UserState) ->
{unknown, UserState};
verify_fun(_, valid, UserState) ->
{valid, UserState};
verify_fun(Cert, valid_peer, UserState) ->
CertHostname = get_cert_hostname(Cert),
case lists:keyfind(check_hostname, 1, UserState) of
{check_hostname, CertHostname} ->
{valid, UserState};
{check_hostname, ExpectedHostname} ->
lager:info("Expected hostname ~p but certificate has hostname ~p",
[ExpectedHostname, CertHostname]),
{fail, "Hostname does not match"};
false ->
{valid, UserState}
end.
request(DebugTag, URL, Headers, RequestBody) ->
Starttime = os:timestamp(),
ParsedURL = hackney_url:parse_url(URL),
CACertFile = application:get_env(catlfish, https_cacertfile, none),
#hackney_url{path = Path, host = Host} = ParsedURL,
lager:debug("~s: sending http request to ~p",
[DebugTag, URL]),
case hackney:connect(ParsedURL,
[{ssl_options, [{cacertfile, CACertFile},
{verify, verify_peer},
{verify_fun, {fun verify_fun/3,
[{check_hostname, Host}]}}
]}]) of
{ok, ConnRef} ->
lager:debug("~s: connected to ~p",
[DebugTag, URL]),
{ok, StatusCode, RespHeaders, ClientRef} =
hackney:send_request(ConnRef,
{post, Path,
add_auth("POST", Path, Headers,
RequestBody),
RequestBody}),
lager:debug("~s: received headers for ~p: ~p",
[DebugTag, URL, RespHeaders]),
{ok, Body} = hackney:body(ClientRef),
Stoptime = os:timestamp(),
hackney:close(ClientRef),
lager:debug("~s: received body for ~p: time ~p",
[DebugTag, URL, timer:now_diff(Stoptime, Starttime)]),
StatusLine = {none, StatusCode, none},
AuthHeader = get_auth_header(hackney_headers:new(RespHeaders)),
{http_auth:verify_auth(AuthHeader, "REPLY",
binary_to_list(Path), Body),
StatusLine, RespHeaders, Body};
{error, Error} ->
{error, Error}
end.
|