diff --git a/zomp/lib/otpr/zx/0.3.0/src/zx.erl b/zomp/lib/otpr/zx/0.3.0/src/zx.erl index ebe6d2b..861169f 100644 --- a/zomp/lib/otpr/zx/0.3.0/src/zx.erl +++ b/zomp/lib/otpr/zx/0.3.0/src/zx.erl @@ -39,7 +39,7 @@ -export_type([serial/0, package_id/0, package/0, realm/0, name/0, version/0, identifier/0, host/0, - key_data/0, key_bin/0, key_id/0, key_name/0, + key/0, key_data/0, key_bin/0, key_id/0, key_name/0, user_id/0, user_name/0, contact_info/0, user_data/0, lower0_9/0, label/0, ss_tag/0, search_tag/0, description/0, package_type/0, @@ -60,11 +60,12 @@ Minor :: non_neg_integer() | z, Patch :: non_neg_integer() | z}. -type host() :: {string() | inet:ip_address(), inet:port_number()}. +-type key() :: term(). % Srsly. This is what public_key:der_decode/2 returns. -type key_data() :: {Name :: key_name(), Public :: none | key_bin(), Private :: none | key_bin()}. -type key_bin() :: {Sig :: none | {key_name(), binary()}, - Der :: binary()}. + DER :: binary()}. -type key_id() :: {realm(), key_name()}. -type key_name() :: key_hash(). -type key_hash() :: binary(). @@ -225,12 +226,12 @@ do(["sync", "keys"]) -> done(zx_auth:sync_keys()); do(["create", "user"]) -> done(zx_local:create_user()); -do(["create", "userfile"]) -> - done(zx_local:create_userfile()); do(["create", "keypair"]) -> done(zx_local:grow_a_pair()); do(["export", "user"]) -> - done(zx_local:export_user()); + done(zx_local:export_user(zpuf)); +do(["export", "user", "dangerous"]) -> + done(zx_local:export_user(zduf)); do(["import", "user", ZdufFile]) -> done(zx_local:import_user(ZdufFile)); do(["list", "users", Realm]) -> @@ -888,9 +889,8 @@ usage_dev() -> " zx reject PackageID~n" " zx sync keys~n" " zx create user~n" - " zx create userfile~n" " zx create keypair~n" - " zx export user~n" + " zx export user [dangerous]~n" " zx import user ZDUF~n" " zx list users Realm~n" " zx list packagers PackageName~n" diff --git a/zomp/lib/otpr/zx/0.3.0/src/zx_auth.erl b/zomp/lib/otpr/zx/0.3.0/src/zx_auth.erl index 93c98d1..57054a7 100644 --- a/zomp/lib/otpr/zx/0.3.0/src/zx_auth.erl +++ b/zomp/lib/otpr/zx/0.3.0/src/zx_auth.erl @@ -307,6 +307,7 @@ add_user(ZPUF) -> add_user2(Bin) -> case zx_lib:b_to_t(Bin) of {ok, {UserInfo, KeyData}} -> add_user3(UserInfo, KeyData); + {ok, _} -> {error, "Malformed user file.", 1}; error -> {error, "Bad user file.", 1} end. diff --git a/zomp/lib/otpr/zx/0.3.0/src/zx_daemon.erl b/zomp/lib/otpr/zx/0.3.0/src/zx_daemon.erl index d9108e7..542d20c 100644 --- a/zomp/lib/otpr/zx/0.3.0/src/zx_daemon.erl +++ b/zomp/lib/otpr/zx/0.3.0/src/zx_daemon.erl @@ -152,8 +152,8 @@ list_sysops/1, fetch/1, install/1, build/1, wait_result/1, wait_results/1]). --export([register_key/2, get_key/2, keybin/2, - find_keypair/1, have_key/2, list_keys/2]). +-export([register_key/2, get_key/2, get_keybin/2, + have_key/2, list_keys/2]). -export([report/1, result/2, notify/2]). -export([connect/0, disconnect/0]). -export([conf/1, conf/2, hosts/0, @@ -187,8 +187,7 @@ dropped = maps:new() :: requests(), timer = none :: none | reference(), mx = mx_new() :: monitor_index(), - cx = new_cx() :: conn_index(), - kx = load_kx() :: key_index()}). + cx = new_cx() :: conn_index()}). -record(conf, {realms = zx_lib:list_realms() :: [zx:realm()], @@ -213,7 +212,7 @@ -record(rmeta, {serial = 0 :: non_neg_integer(), prime = {"zomp.tsuriai.jp", 11311} :: zx:host(), - key = [] :: zx:key_name(), + key = none :: none | zx:key_name(), sysop = none :: zx:user_name(), assigned = none :: none | managed | pid(), available = [] :: [pid()]}). @@ -224,12 +223,6 @@ requests = [] :: [id()], subs = [] :: [{pid(), zx:package()}]}). --record(key, - {pubhash = none :: none | binary(), - pub = none :: none | public_key:public_key(), - keyhash = none :: none | binary(), - key = none :: none | public_key:private_key()}). - %% State Types -type state() :: #s{}. @@ -239,11 +232,6 @@ | {unsubscribe, pid(), zx:package()} | {request, pid(), id(), action()}. -type requests() :: #{id() := {pid(), action()}}. --type key_index() :: {rk_index(), pair_index(), key_owners()}. --type rk_index() :: #{zx:realm() := key_registry()}. --type pair_index() :: #{zx:key_hash() := zx:key_id()}. --type key_registry() :: #{zx:key_hash() := #key{}}. --type key_owners() :: #{zx:user_id() := {[zx:key_hash()], [zx:key_hash()]}}. -type monitor_index() :: #{pid() := {reference(), category()}}. -type conn_index() :: #cx{} | zomp | proxy. -type realm_meta() :: #rmeta{}. @@ -606,8 +594,8 @@ drop_mirror(Host) -> when Owner :: zx:realm() | zx:user_id(), KeyData :: zx:key_data(). -register_key(Owner, Data) -> - gen_server:call(?MODULE, {register_key, Owner, Data}). +register_key(Owner, KeyData) -> + gen_server:call(?MODULE, {register_key, Owner, KeyData}). -spec get_key(Type, KeyID) -> Result @@ -624,23 +612,25 @@ get_key(Type, KeyID) -> gen_server:call(?MODULE, {get_key, Type, KeyID}). --spec keybin(Type, KeyID) -> Result +-spec get_keybin(Type, KeyID) -> Result when Type :: public | private, KeyID :: zx:key_id(), Result :: {ok, binary()} | {error, file:posix()}. -keybin(Type, KeyID) -> - gen_server:call(?MODULE, {keybin, Type, KeyID}). +get_keybin(Type, KeyID) -> + gen_server:call(?MODULE, {get_keybin, Type, KeyID}). --spec find_keypair(KeyName) -> Result - when KeyName :: zx:key_name(), - Result :: {ok, zx:key_id()} - | error. - -find_keypair(KeyName) -> - gen_server:call(?MODULE, {find_keypair, KeyName}). +% TODO: This should be an external request to a Zomp node. +% FIXME: Determine how this should work. +%-spec find_keypair(KeyName) -> Result +% when KeyName :: zx:key_name(), +% Result :: {ok, zx:key_id()} +% | error. +% +%find_keypair(KeyName) -> +% gen_server:call(?MODULE, {find_keypair, KeyName}). -spec have_key(Type, KeyID) -> boolean() @@ -814,29 +804,25 @@ handle_call({build, PackageID}, _, State) -> Result = do_build(PackageID), NewState = eval_queue(State), {reply, Result, NewState}; -handle_call({get_key, Type, KeyID}, _, State = #s{kx = KX}) -> - Result = do_get_key(Type, KeyID, element(1, KX)), +handle_call({get_key, Type, KeyID}, _, State) -> + Result = do_get_key(Type, KeyID), NewState = eval_queue(State), {reply, Result, NewState}; -handle_call({keybin, Type, KeyID}, _, State = #s{kx = KX}) -> - Result = do_keybin(Type, KeyID, element(1, KX)), +handle_call({get_keybin, Type, KeyID}, _, State) -> + Result = do_get_keybin(Type, KeyID), NewState = eval_queue(State), {reply, Result, NewState}; -handle_call({find_keypair, KeyName}, _, State = #s{kx = KX}) -> - Result = maps:find(KeyName, element(2, KX)), +handle_call({have_key, Type, KeyID}, _, State) -> + Result = do_have_key(Type, KeyID), NewState = eval_queue(State), {reply, Result, NewState}; -handle_call({have_key, Type, KeyID}, _, State = #s{kx = KX}) -> - Result = do_have_key(Type, KeyID, element(1, KX)), - NewState = eval_queue(State), - {reply, Result, NewState}; -handle_call({list_keys, Type, Owner}, _, State = #s{kx = KX}) -> - Result = do_list_keys(Type, Owner, KX), +handle_call({list_keys, Type, Owner}, _, State) -> + Result = do_list_keys(Type, Owner), NewState = eval_queue(State), {reply, Result, NewState}; handle_call({register_key, Owner, Data}, _, State) -> - {Result, NextState} = do_register_key(Owner, Data, State), - NewState = eval_queue(NextState), + Result = do_register_key(Owner, Data), + NewState = eval_queue(State), {reply, Result, NewState}; handle_call({takeover, Realm}, _, State = #s{conf = Conf}) -> {Result, NewConf} = do_takeover(Realm, Conf), @@ -1868,11 +1854,9 @@ do_takeover(Realm, C = #conf{realms = Realms, managed = Managed}) -> end. --spec do_abdicate(Realm, State) -> {Result, NewState} +-spec do_abdicate(Realm, State) -> NewState when Realm :: zx:realm(), State :: state(), - Result :: ok - | {error, unmanaged}, NewState :: state(). do_abdicate(Realm, State = #s{conf = C = #conf{managed = Managed}}) -> @@ -1882,271 +1866,121 @@ do_abdicate(Realm, State = #s{conf = C = #conf{managed = Managed}}) -> NewC = C#conf{managed = NewManaged}, ok = save_conf(NewC), ok = log(info, "No longer managing realm: ~160tp", [Realm]), - {ok, State#s{conf = NewC}}; + State#s{conf = NewC}; false -> ok = tell(error, "Cannot abdicate an unmanaged realm."), - {{error, unmanaged}, State} + State end. %%% Key Functions --spec load_kx() -> key_index(). - -load_kx() -> - case file:read_file(key_stash_path()) of - {ok, Bin} -> binary_to_term(Bin); - {error, enoent} -> {maps:new(), maps:new(), maps:new()} - end. - -key_stash_path() -> - filename:join(zx_lib:path(key), "key.stash"). - - --spec do_register_key(Owner, KeyData, State) -> {ok, NewState} +-spec do_register_key(Owner, KeyData) -> Result when Owner :: zx:realm() | zx:user_id(), KeyData :: zx:key_data(), - State :: state(), - NewState :: state(). + Result :: ok + | {error, Reason}, + Reason :: bad_user + | bad_realm + | file:posix(). -do_register_key(UserID = {Realm, _}, Data, State = #s{kx = {RX, PX, UK}}) -> - {NewRX, NewPX} = do_register_realm_key(Realm, Data, RX, PX), - NewUK = - case Data of - {PairHash, none, {_, K}} when is_binary(K) -> - Update = fun({Ps, Ks}) -> {Ps, [PairHash | Ks]} end, - maps:update_with(UserID, Update, {[], [PairHash]}, UK); - {PairHash, {_, P}, none} when is_binary(P) -> - Update = fun({Ps, Ks}) -> {[PairHash | Ps], Ks} end, - maps:update_with(UserID, Update, {[PairHash], []}, UK); - {PairHash, {_, P}, {_, K}} when is_binary(P), is_binary(K) -> - Update = fun({Ps, Ks}) -> {[PairHash | Ps], [PairHash | Ks]} end, - maps:update_with(UserID, Update, {[PairHash], [PairHash]}, UK) +do_register_key(Owner = {Realm, UserName}, KeyData) -> + case zx_userconf:load(Owner) of + {ok, UC} -> + do_register_key2(Realm, UC, KeyData); + {error, bad_user} -> + UC = zx_userconf:new(), + NewUC = UC#{realm => Realm, username => UserName}, + do_register_key2(Realm, NewUC, KeyData); + Error -> + Error + end. + + +do_register_key2(Realm, UC = #{keys := Keys}, KeyData = {KeyHash, _, _}) -> + NewUC = + case lists:member(KeyHash, Keys) of + false -> UC#{keys => [KeyHash | Keys]}; + true -> UC end, - NewKX = {NewRX, NewPX, NewUK}, - ok = stash_keys(NewKX), - {ok, State#s{kx = NewKX}}; -do_register_key(Realm, Data, State = #s{kx = {RX, PX, UK}}) -> - {NewRX, NewPX} = do_register_realm_key(Realm, Data, RX, PX), - NewKX = {NewRX, NewPX, UK}, - ok = stash_keys(NewKX), - {ok, State#s{kx = NewKX}}. + ok = zx_userconf:save(NewUC), + do_register_key3(Realm, KeyData). -do_register_realm_key(Realm, Data, RealmIndex, PairIndex) -> - PairHash = element(1, Data), - {K, Pairs} = do_register_realm_key2(Realm, Data), - KeyRegistry = maps:get(Realm, RealmIndex, #{}), - NewKeyRegistry = update_registry(PairHash, K, KeyRegistry), - NewRealmIndex = maps:put(Realm, NewKeyRegistry, RealmIndex), - NewPairIndex = maps:merge(PairIndex, Pairs), - {NewRealmIndex, NewPairIndex}. - -do_register_realm_key2(Realm, {PairHash, {_, PubBin}, none}) -> - PubHash = crypto:hash(sha512, PubBin), - ok = store_key(public, {Realm, PairHash}, PubBin), - Pub = public_key:der_decode('RSAPublicKey', PubBin), - K = #key{pubhash = PubHash, pub = Pub}, - Pairs = #{PubHash => PairHash}, - {K, Pairs}; -do_register_realm_key2(Realm, {PairHash, none, {_, KeyBin}}) -> - KeyHash = crypto:hash(sha512, KeyBin), - ok = store_key(private, {Realm, PairHash}, KeyBin), - Key = public_key:der_decode('RSAPrivateKey', KeyBin), - K = #key{keyhash = KeyHash, key = Key}, - Pairs = #{KeyHash => PairHash}, - {K, Pairs}; -do_register_realm_key2(Realm, {PairHash, {_, PubBin}, {_, KeyBin}}) -> - PubHash = crypto:hash(sha512, PubBin), - KeyHash = crypto:hash(sha512, KeyBin), - ok = store_key(public, {Realm, PairHash}, PubBin), - ok = store_key(private, {Realm, PairHash}, KeyBin), - PairHash = crypto:hash(sha512, <>), - Pub = public_key:der_decode('RSAPublicKey', PubBin), - Key = public_key:der_decode('RSAPrivateKey', KeyBin), - K = #key{pubhash = PubHash, pub = Pub, keyhash = KeyHash, key = Key}, - Pairs = #{PubHash => PairHash, KeyHash => PairHash}, - {K, Pairs}. - -store_key(Type, KeyID, Bin) -> - Path = zx_key:path(Type, KeyID), - ok = filelib:ensure_dir(Path), - file:write_file(Path, Bin). - -update_registry(PairHash, - K = #key{pubhash = none, keyhash = KeyHash, key = Key}, - Registry) -> - case maps:find(PairHash, Registry) of - error -> - maps:put(PairHash, K, Registry); - {ok, #key{pubhash = none, keyhash = none}} -> - maps:put(PairHash, K, Registry); - {ok, OldK = #key{keyhash = none}} -> - maps:put(PairHash, OldK#key{keyhash = KeyHash, key = Key}, Registry); - {ok, #key{keyhash = KeyHash}} -> - Registry - end; -update_registry(PairHash, - K = #key{keyhash = none, pubhash = PubHash, pub = Pub}, - Registry) -> - case maps:find(PairHash, Registry) of - error -> - maps:put(PairHash, K, Registry); - {ok, #key{pubhash = none, keyhash = none}} -> - maps:put(PairHash, K, Registry); - {ok, OldK = #key{pubhash = none}} -> - maps:put(PairHash, OldK#key{pubhash = PubHash, pub = Pub}, Registry); - {ok, #key{pubhash = PubHash}} -> - Registry - end; -update_registry(PairHash, K, Registry) -> - maps:put(PairHash, K, Registry). - -stash_keys(KX) -> - Bin = term_to_binary(KX), - file:write_file(key_stash_path(), Bin). +do_register_key3(Realm, {KeyHash, none, {_, Key}}) -> + zx_key:save_bin(private, {Realm, KeyHash}, Key); +do_register_key3(Realm, {KeyHash, {_, Pub}, none}) -> + zx_key:save_bin(public, {Realm, KeyHash}, Pub); +do_register_key3(Realm, {KeyHash, {_, Pub}, {_, Key}}) -> + ok = zx_key:save_bin(public, {Realm, KeyHash}, Pub), + zx_key:save(private, {Realm, KeyHash}, Key); +do_register_key3(_, {_, none, none}) -> + ok. --spec do_get_key(Type, KeyID, RK) -> Result +-spec do_get_key(Type, KeyID) -> Result when Type :: public | private, KeyID :: zx:key_id(), - RK :: rk_index(), Result :: {ok, public_key:rsa_public_key() | public_key:rsa_private_key()} | {error, Reason}, Reason :: bad_realm | no_pub | no_key - | bad_key. + | file:posix(). -do_get_key(Type, {Realm, KeyName}, RK) -> - case maps:find(Realm, RK) of - {ok, Registry} -> do_get_key2(Type, KeyName, Registry); - error -> {error, bad_realm} - end. - -do_get_key2(public, KeyName, Registry) -> - case maps:find(KeyName, Registry) of - {ok, #key{pub = none}} -> {error, no_pub}; - {ok, #key{pub = Pub}} -> {ok, Pub}; - error -> {error, bad_key} - end; -do_get_key2(private, KeyName, Registry) -> - case maps:find(KeyName, Registry) of - {ok, #key{key = none}} -> {error, no_key}; - {ok, #key{key = Key}} -> {ok, Key}; - error -> {error, bad_key} - end. +do_get_key(Type, KeyID) -> + zx_key:load(Type, KeyID). --spec do_keybin(Type, KeyID, RK) -> Result +-spec do_get_keybin(Type, KeyID) -> Result when Type :: public | private, KeyID :: zx:key_id(), - RK :: rk_index(), Result :: {ok, binary()} - | {error, bad_realm | bad_key | no_key | file:posix()}. + | {error, bad_realm | no_key | no_pub | file:posix()}. -do_keybin(Type, KeyID = {Realm, _}, RK) -> - case maps:find(Realm, RK) of - {ok, Registry} -> do_keybin2(Type, KeyID, Registry); - error -> {error, bad_realm} - end. - -do_keybin2(public, KeyID = {_, KeyName}, Registry) -> - case maps:find(KeyName, Registry) of - {ok, #key{pubhash = none}} -> {error, no_key}; - {ok, #key{}} -> file:read_file(zx_key:path(public, KeyID)); - error -> {error, bad_key} - end; -do_keybin2(private, KeyID, Registry) -> - KeyName = element(2, KeyID), - case maps:find(KeyName, Registry) of - {ok, #key{keyhash = none}} -> {error, no_key}; - {ok, #key{}} -> file:read_file(zx_key:path(private, KeyID)); - error -> {error, bad_key} - end. +do_get_keybin(Type, KeyID) -> + zx_key:load_bin(Type, KeyID). --spec do_have_key(Type, KeyID, RK) -> boolean() +-spec do_have_key(Type, KeyID) -> boolean() when Type :: public | private, - KeyID :: zx:key_id(), - RK :: rk_index(). + KeyID :: zx:key_id(). -do_have_key(Type, {Realm, KeyName}, RK) -> - case maps:find(Realm, RK) of - {ok, Registry} -> - case maps:find(KeyName, Registry) of - {ok, K} -> do_have_key2(Type, K); - error -> false - end; - error -> - false - end. - -do_have_key2(public, #key{pub = none}) -> false; -do_have_key2(private, #key{key = none}) -> false; -do_have_key2(_, #key{}) -> true. +do_have_key(Type, KeyID) -> + zx_key:exists(Type, KeyID). --spec do_list_keys(Type, Owner, KX) -> Result +-spec do_list_keys(Type, Owner) -> Result when Type :: public | private, Owner :: zx:realm() | zx:user_id(), - KX :: key_index(), Result :: {ok, [zx:key_hash()]} | {error, bad_realm | bad_user}. -do_list_keys(public, UserID, {_, _, UK}) when is_tuple(UserID) -> - case maps:find(UserID, UK) of - {ok, {Keys, _}} -> {ok, Keys}; - error -> {error, bad_user} +do_list_keys(Type, UserID = {Realm, _}) -> + case zx_userconf:load(UserID) of + {ok, #{keys := Keys}} -> do_list_keys2(Type, [{Realm, K} || K <- Keys]); + Error -> Error end; -do_list_keys(private, UserID, {_, _, UK}) when is_tuple(UserID) -> - case maps:find(UserID, UK) of - {ok, {_, Keys}} -> {ok, Keys}; - error -> {error, bad_user} - end; -do_list_keys(public, Realm, {RK, _, _}) -> - case maps:find(Realm, RK) of - {ok, Registry} -> - List = maps:to_list(Registry), - {ok, [ID || {ID, #key{pubhash = PH}} <- List, is_binary(PH)]}; - error -> - {error, bad_realm} - end; -do_list_keys(private, Realm, {RK, _, _}) -> - case maps:find(Realm, RK) of - {ok, Registry} -> - List = maps:to_list(Registry), - {ok, [ID || {ID, #key{keyhash = KH}} <- List, is_binary(KH)]}; - error -> - {error, bad_realm} +do_list_keys(Type, Realm) -> + case zx_lib:load_realm_conf(Realm) of + {ok, #{key := Key}} -> do_list_keys2(Type, [{Realm, Key}]); + Error -> Error end. +do_list_keys2(Type, KeyIDs) -> + Exists = fun(ID) -> zx_key:exists(Type, ID) end, + {ok, lists:filter(Exists, KeyIDs)}. + -spec do_drop_realm(Realm, State) -> NewState when Realm :: zx:realm(), State :: state(), NewState :: state(). -do_drop_realm(Realm, State = #s{kx = {RealmIndex, Pairs, Owners}}) -> - NewKX = - case maps:take(Realm, RealmIndex) of - {KeyIndex, NextRealmIndex} -> - NewPairs = scrub_pairs(maps:values(KeyIndex), Pairs), - Screen = fun({{R, _}, _}) -> R == Realm end, - NewOwners = maps:filter(Screen, Owners), - {NextRealmIndex, NewPairs, NewOwners}; - error -> - {RealmIndex, Pairs, Owners} - end, +do_drop_realm(Realm, State) -> Dirs = [etc, var, tmp, log, key, zsp, lib], RM = fun(D) -> ok = zx_lib:rm_rf(zx_lib:path(D, Realm)) end, ok = lists:foreach(RM, Dirs), - {_, NewState} = do_abdicate(Realm, State), - NewState#s{kx = NewKX}. - -scrub_pairs([#key{pubhash = PubHash, keyhash = KeyHash} | Rest], Pairs) -> - scrub_pairs(Rest, maps:without([PubHash, KeyHash], Pairs)); -scrub_pairs([], Pairs) -> - Pairs. + do_abdicate(Realm, State). -spec become_proxy(State) -> NewState diff --git a/zomp/lib/otpr/zx/0.3.0/src/zx_key.erl b/zomp/lib/otpr/zx/0.3.0/src/zx_key.erl index bcd4998..43a5e59 100644 --- a/zomp/lib/otpr/zx/0.3.0/src/zx_key.erl +++ b/zomp/lib/otpr/zx/0.3.0/src/zx_key.erl @@ -13,33 +13,15 @@ -copyright("Craig Everett "). -license("GPL-3.0"). --export([path/2, - generate_rsa/1, - load/2, sign/2, verify/3]). +-export([generate_rsa/1, + save/3, load/2, + save_bin/3, load_bin/2, + exists/2, + sign/2, verify/3]). -include("zx_logger.hrl"). -%%% Functions - --spec path(public | private, zx:key_id()) -> file:filename(). - -path(Type, {Realm, KeyHash}) -> - Size = byte_size(KeyHash) * 8, - <> = KeyHash, - String = integer_to_list(N, 36), - Name = - case Type of - public -> String ++ ".pub.der"; - private -> String ++ ".key.der" - end, - zx_lib:path(key, Realm, Name). - - - -%%% Key generation - - -spec generate_rsa(Owner) -> Result when Owner :: zx:realm() | zx:user_id(), Result :: {ok, zx:key_hash()} @@ -170,27 +152,93 @@ openssl() -> end. +-spec save(Type, KeyID, Key) -> Result + when Type :: private | public, + KeyID :: zx:key_id(), + Key :: zx:key(), + Result :: ok + | {error, file:posix()}. +%% @private +%% Encode a key to DER and store it as a file. + +save(Type, KeyID, Key) -> + KeyDER = public_key:der_encode(der_label(Type), Key), + save_bin(Type, KeyID, KeyDER). + + -spec load(Type, KeyID) -> Result when Type :: private | public, KeyID :: zx:key_id(), - Result :: {ok, DecodedKey :: term()} - | {error, Reason :: file:posix()}. + Result :: {ok, DecodedKey :: zx:key()} + | {error, file:posix()}. %% @private -%% Hide the details behind reading and loading DER encoded RSA key files. +%% Load and decode a DER encoded key from file. load(Type, KeyID) -> - DerType = - case Type of - private -> 'RSAPrivateKey'; - public -> 'RSAPublicKey' - end, - Path = path(Type, KeyID), - case file:read_file(Path) of - {ok, Bin} -> {ok, public_key:der_decode(DerType, Bin)}; - Error -> Error + case load_bin(Type, KeyID) of + {ok, KeyDER} -> {ok, public_key:der_decode(der_label(Type), KeyDER)}; + Error -> Error end. +-spec save_bin(Type, KeyID, Bin) -> Result + when Type :: private | public, + KeyID :: zx:key_id(), + Bin :: binary(), + Result :: ok + | {error, bad_realm | file:posix()}. +%% @private +%% Save a key's binary representation directly. + +save_bin(Type, KeyID = {Realm, _}, Bin) -> + case zx_lib:realm_exists(Realm) of + true -> save_bin2(Type, KeyID, Bin); + false -> {error, bad_realm} + end. + +save_bin2(Type, KeyID, Bin) -> + Path = path(Type, KeyID), + ok = filelib:ensure_dir(Path), + ok = log(info, "Saving ~p key to ~p.", [Type, Path]), + file:write_file(Path, Bin). + + +-spec load_bin(Type, KeyID) -> Result + when Type :: private | public, + KeyID :: zx:key_id(), + Result :: {ok, binary()} + | {error, bad_realm | no_key | no_pub | file:posix()}. +%% @private +%% Load a binary key's representation directly without decoding it. + +load_bin(Type, KeyID = {Realm, _}) -> + case zx_lib:realm_exists(Realm) of + true -> load_bin2(Type, KeyID); + false -> {error, bad_realm} + end. + +load_bin2(Type, KeyID) -> + Path = path(Type, KeyID), + ok = log(info, "Loading ~p key from ~p.", [Type, Path]), + case file:read_file(Path) of + {ok, Bin} -> + {ok, Bin}; + {error, enoent} -> + Reason = case Type of private -> no_key; public -> no_pub end, + {error, Reason}; + Error -> + Error + end. + + +-spec exists(Type, KeyID) -> boolean() + when Type :: private | public, + KeyID :: zx:key_id(). + +exists(Type, KeyID) -> + filelib:is_regular(path(Type, KeyID)). + + -spec sign(Data, Key) -> Signature when Data :: binary(), Key :: public_key:rsa_private_key(), @@ -210,3 +258,28 @@ sign(Data, Key) -> verify(Data, Signature, PubKey) -> public_key:verify(Data, sha512, Signature, PubKey). + + +der_label(private) -> 'RSAPrivateKey'; +der_label(public) -> 'RSAPublicKey'. + + +-spec path(public | private, zx:key_id()) -> file:filename(). + +path(Type, {Realm, KeyHash}) -> + Name = name(Type, KeyHash), + zx_lib:path(key, Realm, Name). + + +string(KeyHash) -> + Size = byte_size(KeyHash) * 8, + <> = KeyHash, + integer_to_list(N, 36). + + +name(Type, KeyHash) -> + String = string(KeyHash), + case Type of + public -> String ++ ".pub.der"; + private -> String ++ ".key.der" + end. diff --git a/zomp/lib/otpr/zx/0.3.0/src/zx_lib.erl b/zomp/lib/otpr/zx/0.3.0/src/zx_lib.erl index 2ef6ceb..96d4486 100644 --- a/zomp/lib/otpr/zx/0.3.0/src/zx_lib.erl +++ b/zomp/lib/otpr/zx/0.3.0/src/zx_lib.erl @@ -17,10 +17,10 @@ -export([zomp_dir/0, find_zomp_dir/0, path/1, path/2, path/3, path/4, ppath/2, - new_logpath/1, userconf/1, + new_logpath/1, force_dir/1, mktemp_dir/1, random_string/0, list_realms/0, realm_exists/1, - get_prime/1, realm_meta/1, + get_prime/1, read_project_meta/0, read_project_meta/1, read_package_meta/1, write_project_meta/1, write_project_meta/2, write_terms/2, exec_shell/1, @@ -141,12 +141,6 @@ new_logpath(PackageID = {Realm, Name, _}) -> filename:join(Dir, FileName). --spec userconf(zx:user_id()) -> file:filename(). - -userconf({Realm, UserName}) -> - filename:join(path(etc, Realm), UserName ++ ".user"). - - -spec force_dir(Path) -> Result when Path :: file:filename(), Result :: ok @@ -205,31 +199,12 @@ realm_exists(Realm) -> %% Check the given Realm's config file for the current prime node and return it. get_prime(Realm) -> - case realm_meta(Realm) of - {ok, RealmMeta} -> - {prime, Prime} = lists:keyfind(prime, 1, RealmMeta), - {ok, Prime}; - Error -> - Error + case load_realm_conf(Realm) of + {ok, RealmMeta} -> maps:find(prime, RealmMeta); + Error -> Error end. --spec realm_meta(Realm) -> Result - when Realm :: string(), - Result :: {ok, Meta} - | {error, Reason}, - Meta :: [{atom(), term()}], - Reason :: file:posix(). -%% @private -%% Given a realm name, try to locate and read the realm's configuration file if it -%% exists, exiting with an appropriate error message if there is a problem reading -%% the file. - -realm_meta(Realm) -> - RealmFile = filename:join(path(etc, Realm), "realm.conf"), - file:consult(RealmFile). - - -spec read_project_meta() -> Result when Result :: {ok, zx_zsp:meta()} | {error, file:posix()}. @@ -666,7 +641,8 @@ realm_conf(Realm) -> Result :: {ok, RealmConf} | {error, Reason}, RealmConf :: map(), - Reason :: badarg + Reason :: bad_realm + | badarg | terminated | system_limit | file:posix() @@ -679,6 +655,8 @@ load_realm_conf(Realm) -> case file:consult(Path) of {ok, C} -> {ok, maps:from_list(C)}; + {error, enoent} -> + {error, bad_realm}; Error -> ok = log(warning, "Loading realm conf ~ts failed with: ~tw", [Path, Error]), Error diff --git a/zomp/lib/otpr/zx/0.3.0/src/zx_local.erl b/zomp/lib/otpr/zx/0.3.0/src/zx_local.erl index 9a9e740..e36ab71 100644 --- a/zomp/lib/otpr/zx/0.3.0/src/zx_local.erl +++ b/zomp/lib/otpr/zx/0.3.0/src/zx_local.erl @@ -14,7 +14,7 @@ -export([initialize/0, set_version/1, list_realms/0, list_packages/1, list_versions/1, latest/1, describe/1, provides/1, - list_keys/0, list_sysops/1, + list_sysops/1, set_dep/1, list_deps/0, list_deps/1, drop_dep/1, verup/1, package/1, update_meta/0, update_app_file/0, import_realm/1, drop_realm/1, logpath/2, @@ -23,7 +23,7 @@ drop_mirror/0, drop_mirror/1, drop_mirror/2, create_project/0, grow_a_pair/0, grow_a_pair/2, drop_key/1, - create_user/0, create_userfile/0, export_user/0, import_user/1, + create_user/0, export_user/1, import_user/1, create_realm/0, create_realmfile/0, create_realmfile/1]). -export([create_user/1, select_private_key/1]). @@ -836,21 +836,6 @@ print_packages(PackageID) -> io:format("~ts~n", [PackageString]). --spec list_keys() -> zx:outcome(). - -list_keys() -> - {ok, Realm} = pick_realm(), - UserName = select_user(Realm), - {ok, ID} = zx_daemon:list_keys({Realm, UserName}), - case zx_daemon:wait_result(ID) of - {ok, RemoteKeys} -> - Print = fun(KN) -> io:format("~ts~n", [KN]) end, - lists:foreach(Print, RemoteKeys); - Error -> - Error - end. - - -spec list_sysops(zx:realm()) -> zx:outcome(). list_sysops(Realm) -> @@ -1734,8 +1719,8 @@ package_exists({Realm, Package, _}) -> grow_a_pair() -> case pick_realm() of - {error, no_realms} -> {error, "No realms configured.", 61}; - Realm -> grow_a_pair(Realm) + {ok, Realm} -> grow_a_pair(Realm); + {error, no_realms} -> {error, "No realms configured.", 61} end. grow_a_pair(Realm) -> @@ -1744,13 +1729,11 @@ grow_a_pair(Realm) -> grow_a_pair(Realm, UserName) -> case zx_key:generate_rsa(Realm) of - {ok, PairHash} -> - UserConf = zx_lib:userconf({Realm, UserName}), - {ok, UserData} = file:consult(UserConf), - Keys = proplists:get_value(keys, UserData), - NewKeys = [PairHash | Keys], - NewUserData = lists:keystor(keys, 1, {keys, NewKeys}, UserData), - zx_lib:write_terms(UserConf, NewUserData); + {ok, KeyHash} -> + UserID = {Realm, UserName}, + {ok, UserConf = #{keys := Keys}} = zx_userconf:load(UserID), + NewUserConf = UserConf#{keys => [KeyHash | Keys]}, + zx_userconf:save(NewUserConf); Error -> Error end. @@ -1836,7 +1819,7 @@ store_realm(#realm_init{realm = Realm, RealmConfPath = filename:join(zx_lib:path(etc, Realm), "realm.conf"), ok = zx_lib:write_terms(RealmConfPath, RealmConf), ok = create_realmfile(Realm), - ok = create_userfile(Realm, UserName), + ok = export_user(zpuf, Realm, UserName), ZPUF = Realm ++ "-" ++ UserName ++ ".zpuf", ZRF = Realm ++ ".zrf", Message = @@ -2091,38 +2074,69 @@ store_user(#user_data{realm = Realm, realname = RealName, contact_info = ContactInfo, keys = [KeyHash]}) -> - UserConf = - [{realm, Realm}, - {username, UserName}, - {realname, RealName}, - {contact_info, ContactInfo}, - {keys, [KeyHash]}], - Path = zx_lib:userconf({Realm, UserName}), - ok = filelib:ensure_dir(Path), - zx_lib:write_terms(Path, UserConf). + UC = zx_userconf:new(), + NewUC = + UC#{realm := Realm, + username := UserName, + realname := RealName, + contact_info := ContactInfo, + keys := [KeyHash]}, + zx_userconf:save(NewUC). --spec create_userfile() -> ok. +-spec export_user(Type) -> ok + when Type :: zpuf | zduf. -create_userfile() -> +export_user(Type) -> case pick_realm() of - {error, no_realms} -> {error, "No realms configured.", 61}; - Realm -> create_userfile(Realm) + {ok, Realm} -> export_user(Type, Realm); + {error, no_realms} -> {error, "No realms configured.", 61} end. -create_userfile(Realm) -> +export_user(Type, Realm) -> UserName = select_user(Realm), - create_userfile(Realm, UserName). + export_user(Type, Realm, UserName). -create_userfile(Realm, UserName) -> - UserConf = zx_lib:userconf({Realm, UserName}), - {ok, UserData} = file:consult(UserConf), - Keys = proplists:get_value(keys, UserData), +export_user(Type, Realm, UserName) -> + {ok, UserConf} = zx_userconf:load({Realm, UserName}), + Keys = maps:get(keys, UserConf), + {Load, Ext, Message} = exporter(Type, Realm), + KeyData = lists:foldl(Load, [], Keys), + UserFile = Realm ++ "-" ++ UserName ++ Ext, + UserData = maps:to_list(UserConf), + ok = tell(info, "UserData: ~p", [UserData]), + Bin = term_to_binary({UserData, KeyData}), + ok = file:write_file(UserFile, Bin), + io:format(Message, [UserFile, Realm]). + +exporter(zduf, Realm) -> Load = fun(KeyName, Acc) -> - case zx_daemon:keybin(public, {Realm, KeyName}) of + Key = + case zx_daemon:get_keybin(private, {Realm, KeyName}) of + {ok, KDer} -> {none, KDer}; + _ -> none + end, + Pub = + case zx_daemon:get_keybin(public, {Realm, KeyName}) of + {ok, PDer} -> {none, PDer}; + _ -> none + end, + [{KeyName, Pub, Key} | Acc] + end, + Ext = ".zduf", + Message = + "Wrote Zomp DANGEROUS user file to ~ts.~n" + "WARNING: This file contains your PRIVATE KEYS and should NEVER be shared.~n" + "Its only use is for the `import user [.zduf]` command!~n" + "Importing the user will only work if you have first imported realm ~ts.~n", + {Load, Ext, Message}; +exporter(zpuf, Realm) -> + Load = + fun(KeyName, Acc) -> + case zx_daemon:get_keybin(public, {Realm, KeyName}) of {ok, Der} -> Public = {none, Der}, [{KeyName, Public, none} | Acc]; @@ -2130,54 +2144,12 @@ create_userfile(Realm, UserName) -> Acc end end, - PubKeyData = lists:foldl(Load, [], Keys), - UserFile = Realm ++ "-" ++ UserName ++ ".zpuf", - Bin = term_to_binary({UserData, PubKeyData}), - ok = file:write_file(UserFile, Bin), + Ext = ".zpuf", Message = "Wrote Zomp public user file to ~ts.~n" "This file can be given to a sysop from ~ts and added to the realm.~n" "It ONLY contains PUBLIC KEY data.~n", - io:format(Message, [UserFile, Realm]). - - --spec export_user() -> ok. - -export_user() -> - case pick_realm() of - {error, no_realms} -> {error, "No realms configured.", 61}; - Realm -> export_user(Realm) - end. - - -export_user(Realm) -> - UserName = select_user(Realm), - UserConf = zx_lib:userconf({Realm, UserName}), - {ok, UserData} = file:consult(UserConf), - Keys = proplists:get_value(keys, UserData), - Load = - fun(KeyName, Acc) -> - Key = - case zx_daemon:keybin(private, {Realm, KeyName}) of - {ok, KDer} -> {none, KDer}; - _ -> none - end, - Pub = - case zx_daemon:keybin(public, {Realm, KeyName}) of - {ok, PDer} -> {none, PDer}; - _ -> none - end, - [{KeyName, Pub, Key} | Acc] - end, - KeyData = lists:foldl(Load, [], Keys), - UserFile = Realm ++ "-" ++ UserName ++ ".zduf", - Bin = term_to_binary({UserData, KeyData}), - ok = file:write_file(UserFile, Bin), - Message = - "Wrote Zomp DANGEROUS user file to ~ts.~n" - "WARNING: This file contains your PRIVATE KEYS and should NEVER be shared with " - "anyone. Its only use is for the `import user [.zduf]` command!~n", - io:format(Message, [UserFile]). + {Load, Ext, Message}. -spec import_user(file:filename()) -> zx:outcome(). @@ -2202,24 +2174,22 @@ import_user2(Bin) -> {error, "Bad data. Is this really a legitimate .zduf or .zpuf?", 1} end. -import_user3(NewUserData, KeyData) -> - Realm = proplists:get_value(realm, NewUserData), - UserName = proplists:get_value(username, NewUserData), - UserConf = zx_lib:userconf({Realm, UserName}), - case file:consult(UserConf) of - {ok, OldUserData} -> - UpdatedUserData = merge_userconf(NewUserData, OldUserData, KeyData), - ok = zx_lib:write_terms(UserConf, UpdatedUserData), +import_user3(UserData, KeyData) -> + UserConf = #{realm := Realm, username := UserName} = maps:from_list(UserData), + UserID = {Realm, UserName}, + case zx_userconf:load(UserID) of + {ok, OldUserConf} -> + NewUserConf = zx_userconf:merge(UserConf, OldUserConf, KeyData), + ok = zx_userconf:save(NewUserConf), import_user4(Realm, UserName, KeyData); - {error, enoent} -> - case filelib:is_dir(filename:dirname(UserConf)) of - true -> - ok = zx_lib:write_terms(UserConf, NewUserData), - import_user4(Realm, UserName, KeyData); - false -> - ok = tell(error, "Realm ~tp is not configured.", [Realm]), - {error, bad_realm} - end + {error, bad_user} -> + ok = zx_userconf:save(UserConf), + import_user4(Realm, UserName, KeyData); + {error, bad_realm} -> + ok = tell(error, "Realm ~tp is not configured.", [Realm]), + {error, bad_realm}; + Error -> + Error end. import_user4(Realm, UserName, Keys) -> @@ -2227,16 +2197,6 @@ import_user4(Realm, UserName, Keys) -> ok = lists:foreach(Register, Keys), tell(info, "Imported user ~ts to realm ~ts.", [UserName, Realm]). -merge_userconf(NewUserData, OldUserData, KeyData) -> - NewMap = maps:from_list(NewUserData), - OldMap = maps:from_list(OldUserData), - KeySet = sets:from_list([element(1, K) || K <- KeyData]), - NewKeySet = sets:from_list(maps:get(keys, NewMap)), - OldKeySet = sets:from_list(maps:get(keys, OldMap)), - UltimateKeySet = sets:intersection([KeySet, NewKeySet, OldKeySet]), - Final = maps:put(keys, sets:to_list(UltimateKeySet), maps:merge(OldMap, NewMap)), - maps:to_list(Final). - -spec pick_realm() -> Result when Result :: {ok, zx:realm()} @@ -2534,7 +2494,7 @@ create_realmfile(Realm) -> {ok, RealmConf} = zx_lib:load_realm_conf(Realm), ok = tell("Realm found, creating realm file..."), KeyName = maps:get(key, RealmConf), - {ok, PubDER} = zx_daemon:keybin(public, {Realm, KeyName}), + {ok, PubDER} = zx_daemon:get_keybin(public, {Realm, KeyName}), Blob = term_to_binary({RealmConf, PubDER}), ZRF = Realm ++ ".zrf", ok = file:write_file(ZRF, Blob), @@ -2716,9 +2676,7 @@ select_user(Realm) -> | error. select_private_key(UserID) -> - ConfPath = zx_lib:userconf(UserID), - {ok, UserConf} = file:consult(ConfPath), - Keys = proplists:get_value(keys, UserConf), + {ok, #{keys := Keys}} = zx_userconf:load(UserID), Realm = element(1, UserID), select_private_key2(Realm, Keys). diff --git a/zomp/lib/otpr/zx/0.3.0/src/zx_userconf.erl b/zomp/lib/otpr/zx/0.3.0/src/zx_userconf.erl new file mode 100644 index 0000000..aee95c8 --- /dev/null +++ b/zomp/lib/otpr/zx/0.3.0/src/zx_userconf.erl @@ -0,0 +1,87 @@ +%%% @doc +%%% ZX UserConf +%%% +%%% Handling user configuration data. +%%% @end + +-module(zx_userconf). +-vsn("0.3.0"). +-author("Craig Everett "). +-copyright("Craig Everett "). +-license("GPL-3.0"). + +-export([new/0, save/1, load/1, merge/2, merge/3, path/1]). + +-type data() :: #{realm := string(), + username := string(), + realname := string(), + contact_info := [zx:contact_info()], + keys := [binary()]}. + + +new() -> + #{realm => "", + username => "", + realname => "", + contact_info => "", + keys => []}. + + +-spec save(data()) -> ok | {error, file:posix()}. + +save(Data = #{realm := Realm, username := UserName}) -> + Path = path({Realm, UserName}), + ok = filelib:ensure_dir(Path), + Terms = maps:to_list(Data), + zx_lib:write_terms(Path, Terms). + + +-spec load(UserID) -> Result + when UserID :: zx:user_id(), + Result :: {ok, zx:userconf()} + | {error, Reason}, + Reason :: bad_realm + | bad_user + | file:posix(). + +load(UserID = {Realm, _}) -> + case zx_lib:realm_exists(Realm) of + true -> load2(UserID); + false -> {error, bad_realm} + end. + +load2(UserID) -> + case file:consult(path(UserID)) of + {ok, Terms} -> {ok, maps:from_list(Terms)}; + {error, enoent} -> {error, bad_user}; + Error -> Error + end. + + +-spec merge(New, Old) -> Merged + when New :: data(), + Old :: data(), + Merged :: data(). + +merge(New, Old) -> + merge(New, Old, []). + + +-spec merge(New, Old, Keys) -> Merged + when New :: data(), + Old :: data(), + Keys :: [zx:key_data()], + Merged :: data(). + +merge(New, Old, KeyData) -> + KeySet = sets:from_list([element(1, K) || K <- KeyData]), + NewKeySet = sets:from_list(maps:get(keys, New)), + OldKeySet = sets:from_list(maps:get(keys, Old)), + UltimateKeySet = sets:intersection([KeySet, NewKeySet, OldKeySet]), + maps:put(keys, sets:to_list(UltimateKeySet), maps:merge(Old, New)). + + +-spec path(zx:user_id()) -> file:filename(). + +path({Realm, UserName}) -> + filename:join(zx_lib:path(etc, Realm), UserName ++ ".user").