Skip to content

Commit 6b4a8bc

Browse files
committed
added digest authentication support
1 parent 61a3c5b commit 6b4a8bc

File tree

2 files changed

+97
-24
lines changed

2 files changed

+97
-24
lines changed

src/exo_http.erl

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
%% parse interface
4545
-export([convert_uri/1]).
4646
-export([tokens/1]).
47+
-export([get_authenticate/1]).
4748

4849
-export([set_chdr/3,
4950
set_shdr/3]).
@@ -60,13 +61,18 @@
6061
make_response/4,
6162
auth_basic_encode/2,
6263
url_encode/1,
63-
make_headers/2
64+
make_headers/2,
65+
make_basic_request/2,
66+
make_digest_request/2
6467
]).
6568
-export([url_decode/1,
6669
parse_query/1]).
70+
-export([make_digest_response/3]).
71+
6772

6873
-import(lists, [reverse/1]).
6974

75+
-define(Q, $\").
7076
%%
7177
%% Perform a HTTP/1.1 GET
7278
%%
@@ -909,10 +915,39 @@ auth_basic_encode(User,undefined) ->
909915
auth_basic_encode(User,Pass) ->
910916
base64:encode_to_string(to_list(User)++":"++to_list(Pass)).
911917

912-
make_headers(undefined, _Pass) -> [];
913-
make_headers(User, Pass) ->
918+
make_headers(User, Pass) -> %% bad name should go
919+
make_basic_request(User, Pass).
920+
921+
make_basic_request(undefined, _Pass) -> [];
922+
make_basic_request(User, Pass) ->
914923
[{"Authorization", "Basic "++auth_basic_encode(User, Pass)}].
915924

925+
make_digest_request(undefined, _Params) -> [];
926+
make_digest_request(User, Params) ->
927+
[{"Authorization", "Digest " ++
928+
make_param(<<"username">>,User) ++
929+
lookup_param(<<"realm">>, Params) ++
930+
lookup_param(<<"nonce">>, Params) ++
931+
lookup_param(<<"uri">>, Params) ++
932+
lookup_param(<<"response">>, Params)}].
933+
934+
make_param(Key, Value) ->
935+
to_key(Key)++"="++to_value(Value).
936+
937+
lookup_param(Key, List) ->
938+
case proplists:get_value(Key, List) of
939+
undefined -> [];
940+
Value -> ", "++make_param(Key, Value)
941+
end.
942+
943+
to_key(Bin) when is_binary(Bin) -> binary_to_list(Bin);
944+
to_key(List) when is_list(List) -> List.
945+
946+
to_value(Bin) when is_binary(Bin) -> [?Q]++binary_to_list(Bin)++[?Q];
947+
to_value(List) when is_list(List) -> [?Q]++List++[?Q];
948+
to_value(Atom) when is_atom(Atom) -> atom_to_list(Atom);
949+
to_value(Int) when is_integer(Int) -> integer_to_list(Int).
950+
916951
%%
917952
%% Url encode a string
918953
%%
@@ -1138,3 +1173,57 @@ tokens(undefined) ->
11381173
tokens(Line) ->
11391174
string:tokens(string:to_lower(Line), ";").
11401175

1176+
1177+
%% Read and parse WWW-Authenticate header value
1178+
get_authenticate(undefined) ->
1179+
{none,[]};
1180+
get_authenticate(<<>>) ->
1181+
{none,[]};
1182+
get_authenticate(<<$\s,Cs/binary>>) ->
1183+
get_authenticate(Cs);
1184+
get_authenticate(<<"Basic ",Cs/binary>>) ->
1185+
{basic, get_params(Cs)};
1186+
get_authenticate(<<"Digest ",Cs/binary>>) ->
1187+
{digest, get_params(Cs)};
1188+
get_authenticate(List) when is_list(List) ->
1189+
get_authenticate(list_to_binary(List)).
1190+
1191+
get_params(Bin) ->
1192+
Ps = binary:split(Bin, <<" ">>, [global]),
1193+
[ case binary:split(P, <<"=">>) of
1194+
[K,V] -> {K,unq(V)};
1195+
[K] -> {K,true}
1196+
end || P <- Ps, P =/= <<>> ].
1197+
1198+
%% "unquote" a string or a binary
1199+
unq(String) when is_binary(String) -> unq(binary_to_list(String));
1200+
unq([$\s|Cs]) -> unq(Cs);
1201+
unq([?Q|Cs]) -> unq_(Cs);
1202+
unq(Cs) -> Cs.
1203+
1204+
unq_([?Q|_]) -> [];
1205+
unq_([C|Cs]) -> [C|unq_(Cs)];
1206+
unq_([]) -> [].
1207+
1208+
make_digest_response(Cred, Method, AuthParams) ->
1209+
Nonce = proplists:get_value(<<"nonce">>,AuthParams,""),
1210+
DigestUriValue = proplists:get_value(<<"uri">>,AuthParams,""),
1211+
%% FIXME! Verify Nonce!!!
1212+
A1 = a1(Cred),
1213+
HA1 = hex(crypto:md5(A1)),
1214+
A2 = a2(Method, DigestUriValue),
1215+
HA2 = hex(crypto:md5(A2)),
1216+
hex(kd(HA1, Nonce++":"++HA2)).
1217+
1218+
a1({digest,_Path,User,Password,Realm}) ->
1219+
iolist_to_binary([User,":",Realm,":",Password]).
1220+
1221+
a2(Method, Uri) ->
1222+
iolist_to_binary([atom_to_list(Method),":",Uri]).
1223+
1224+
kd(Secret, Data) ->
1225+
crypto:md5([Secret,":",Data]).
1226+
1227+
hex(Bin) ->
1228+
[ element(X+1, {$0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$a,$b,$c,$d,$e,$f}) ||
1229+
<<X:4>> <= Bin ].

src/exo_http_server.erl

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -378,17 +378,9 @@ handle_basic_auth(_Socket, _Request, _Body, _,
378378
handle_digest_auth(_Socket, Request, _Body, {digest,AuthParams},
379379
Cred={digest,_Path,_User,_Password,_Realm}, State) ->
380380
Response = proplists:get_value(<<"response">>,AuthParams,""),
381-
Nonce = proplists:get_value(<<"nonce">>,AuthParams,""),
382-
DigestUriValue = proplists:get_value(<<"uri">>,AuthParams,""),
383-
%% FIXME! Verify Nonce!!!
384-
A1 = a1(Cred),
385-
%% lager:debug("A1 = \"~s\"", [A1]),
386-
HA1 = hex(crypto:md5(A1)),
387-
A2 = a2(Request#http_request.method, DigestUriValue),
388-
%% lager:debug("A2 = \"~s\"", [A2]),
389-
HA2 = hex(crypto:md5(A2)),
390-
Digest = hex(kd(HA1, Nonce++":"++HA2)),
391-
%% lager:debug("Digest = \"~s\"", [Digest]),
381+
Method = Request#http_request.method,
382+
Digest = exo_http:make_digest_response(Cred, Method, AuthParams),
383+
%% io:format("response=~p, digest=~p\n", [Response,Digest]),
392384
if Digest =:= Response ->
393385
ok;
394386
true ->
@@ -397,10 +389,10 @@ handle_digest_auth(_Socket, Request, _Body, {digest,AuthParams},
397389
handle_digest_auth(_Socket, Request, _Body, _, Cred, State) ->
398390
digest_required(Request, Cred, State).
399391

400-
digest_required(Request,_Cred={digest,Path,_User,_Password,Realm},State) ->
392+
digest_required(Request,_Cred={digest,_Path,_User,_Password,Realm},State) ->
401393
Nonce = nonce_value(Request, State),
402394
{required, ["Digest realm=",?Q,Realm,?Q," ",
403-
"url=",?Q,Path,?Q," ",
395+
%% "url=",?Q,Path,?Q," ",
404396
"nonce=",?Q,Nonce,?Q], State}.
405397

406398
nonce_value(Request, State) ->
@@ -410,14 +402,6 @@ nonce_value(Request, State) ->
410402
TimeStamp = hex(<<T:64>>),
411403
hex(crypto:md5([TimeStamp,":",ETag,":",State#state.private_key])).
412404

413-
a1({_,_Path,User,Password,Realm}) ->
414-
iolist_to_binary([User,":",Realm,":",Password]).
415-
416-
a2(Method, Uri) ->
417-
iolist_to_binary([atom_to_list(Method),":",Uri]).
418-
419-
kd(Secret, Data) ->
420-
crypto:md5([Secret,":",Data]).
421405

422406
%% convert binary to ASCII hex
423407
hex(Bin) ->

0 commit comments

Comments
 (0)