summaryrefslogtreecommitdiff
path: root/src/http_auth.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/http_auth.erl')
-rw-r--r--src/http_auth.erl138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/http_auth.erl b/src/http_auth.erl
new file mode 100644
index 0000000..6a076fa
--- /dev/null
+++ b/src/http_auth.erl
@@ -0,0 +1,138 @@
+%%% Copyright (c) 2014, NORDUnet A/S.
+%%% See LICENSE for licensing information.
+
+-module(http_auth).
+-export([verify_auth/4, create_auth/3, init_key_table/0]).
+
+-define(KEY_TABLE, http_auth_keys).
+
+init_key_table() ->
+ case ets:info(?KEY_TABLE) of
+ undefined ->
+ ok;
+ _ ->
+ ets:delete(?KEY_TABLE)
+ end,
+ ets:new(?KEY_TABLE, [set, public, named_table]),
+ read_key_table().
+
+read_key_table() ->
+ PublickeyDir = application:get_env(plop, publickey_path, none),
+ ServersACL = application:get_env(plop, allowed_servers, []),
+ ClientsACL = application:get_env(plop, allowed_clients, []),
+ Keys = sets:from_list(
+ lists:flatmap(fun ({_, Keys}) ->
+ case Keys of
+ noauth ->
+ [];
+ _ when is_list(Keys) ->
+ Keys
+ end
+ end, ServersACL ++ ClientsACL)),
+ lists:foreach(
+ fun (KeyName) ->
+ Key = sign:read_keyfile_ec(PublickeyDir ++ "/" ++
+ KeyName ++ ".pem"),
+ true = ets:insert(?KEY_TABLE, {KeyName, Key})
+ end, sets:to_list(Keys)),
+ {_OwnKeyName, OwnKeyFile} = application:get_env(plop, own_key, none),
+ OwnKey = sign:read_keyfile_ec(OwnKeyFile),
+ true = ets:insert(?KEY_TABLE, {own_key, OwnKey}).
+
+
+own_key() ->
+ {KeyName, _KeyFile} = application:get_env(plop, own_key, none),
+ [{_, Key}] = ets:lookup(?KEY_TABLE, own_key),
+ {Key, KeyName}.
+
+lookup_publickey(nokey) ->
+ nokey;
+lookup_publickey(KeyName) ->
+ case ets:lookup(?KEY_TABLE, KeyName) of
+ [{_, Key}] ->
+ Key;
+ [] ->
+ failure
+ end.
+
+parse_option(S) ->
+ parse_option(S, []).
+parse_option([], Key) ->
+ {lists:reverse(Key), []};
+parse_option([$= | Rest], Key) ->
+ {lists:reverse(Key), Rest};
+parse_option([C | Rest], Key) ->
+ parse_option(Rest, [C | Key]).
+
+
+sign(PrivKey, Method, Path, Data) ->
+ public_key:sign(iolist_to_binary([Method, 0, Path, 0, Data]), sha256, PrivKey).
+
+verify(Signature, PublicKey, Method, Path, Data) ->
+ public_key:verify(iolist_to_binary([Method, 0, Path, 0, Data]), sha256, Signature, PublicKey).
+
+check_acl(Method, KeyName, Path) ->
+ EnvVarName = case Method of
+ "REPLY" ->
+ allowed_servers;
+ _ ->
+ allowed_clients
+ end,
+ ACL = application:get_env(plop, EnvVarName, []),
+ lager:debug("ACL: ~p", [ACL]),
+ case lists:keyfind(Path, 1, ACL) of
+ {_, noauth} ->
+ lager:debug("Anonymous access allowed"),
+ success;
+ {_, AllowedKeys} when is_list(AllowedKeys) ->
+ lager:debug("Checking key ~p, allowed keys: ~p", [KeyName, AllowedKeys]),
+ case lists:member(KeyName, AllowedKeys) of
+ true ->
+ success;
+ false ->
+ failure
+ end;
+ false ->
+ lager:debug("No allowed keys found for: ~p", [Path]),
+ failure
+ end.
+
+verify_auth(undefined, Method, Path, _Data) ->
+ case check_acl(Method, noauth, Path) of
+ success ->
+ noauth;
+ Error ->
+ lager:info("anonymous access not allowed for path ~p", [Path]),
+ Error
+ end;
+verify_auth(AuthHeader, Method, Path, Data) ->
+ [AuthTokenBase64 | OptionsRaw] = string:tokens(AuthHeader, ";"),
+ AuthToken = base64:decode(AuthTokenBase64),
+ Options = [parse_option(E) || E <- OptionsRaw],
+ KeyName = case lists:keyfind("key", 1, Options) of
+ {_, Value} ->
+ Value;
+ false ->
+ nokey
+ end,
+ AuthSuccess = case lookup_publickey(KeyName) of
+ nokey ->
+ false;
+ failure ->
+ lager:info("key name ~p could not be found", [KeyName]),
+ false;
+ Key ->
+ verify(AuthToken, Key, Method, Path, Data)
+ end,
+ case AuthSuccess of
+ true ->
+ check_acl(Method, KeyName, Path);
+ _ ->
+ lager:info("authentication token ~p was not valid for key name ~p: ~p ~p ~p", [mochihex:to_hex(AuthToken), KeyName, Method, Path, Data]),
+ failure
+ end.
+
+create_auth(Method, Path, Data) ->
+ {Key, KeyName} = own_key(),
+ AuthToken = sign(Key, Method, Path, Data),
+ base64:encode_to_string(AuthToken) ++ ";key=" ++ KeyName.