From 662ea802f40062d6f095bdeea61e69d7b665de25 Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Thu, 26 Feb 2015 16:50:41 +0100 Subject: Added authentication --- src/http_auth.erl | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/http_auth.erl (limited to 'src/http_auth.erl') 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. -- cgit v1.1