ssh_file.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 517 行
ERL
517 行
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%%%% Description: SSH file handling-module(ssh_file).-include("ssh.hrl").-include("PKCS-1.hrl").-include("DSS.hrl").-export([public_host_dsa_key/2,private_host_dsa_key/2, public_host_rsa_key/2,private_host_rsa_key/2, public_host_key/2,private_host_key/2, lookup_host_key/3, add_host_key/3, % del_host_key/2, lookup_user_key/3, ssh_dir/2, file_name/3]).-export([private_identity_key/2]).%% , public_identity_key/2,%% identity_keys/2]).-export([encode_public_key/1, decode_public_key_v2/2]).-import(lists, [reverse/1, append/1]).-define(DBG_PATHS, true).%% APIpublic_host_dsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_dsa_key.pub", Opts), read_public_key_v2(File, "ssh-dss").private_host_dsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_dsa_key", Opts), read_private_key_v2(File, "ssh-dss").public_host_rsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_rsa_key.pub", Opts), read_public_key_v2(File, "ssh-rsa").private_host_rsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_rsa_key", Opts), read_private_key_v2(File, "ssh-rsa").public_host_key(Type, Opts) -> File = file_name(Type, "ssh_host_key", Opts), case read_private_key_v1(File,public) of {error, enoent} -> read_public_key_v1(File++".pub"); Result -> Result end. private_host_key(Type, Opts) -> File = file_name(Type, "ssh_host_key", Opts), read_private_key_v1(File,private).%% in: "host" out: "host,1.2.3.4.add_ip(Host) -> case inet:getaddr(Host, inet) of {ok, Addr} -> case ssh_cm:encode_ip(Addr) of false -> Host; IPString -> Host ++ "," ++ IPString end; _ -> Host end. replace_localhost("localhost") -> {ok, Hostname} = inet:gethostname(), Hostname;replace_localhost(Host) -> Host.%% lookup_host_key%% return {ok, Key(s)} or {error, not_found}%%lookup_host_key(Host, Alg, Opts) -> Host1 = replace_localhost(Host), do_lookup_host_key(Host1, Alg, Opts). do_lookup_host_key(Host, Alg, Opts) -> case file:open(file_name(user, "known_hosts", Opts), [read]) of {ok, Fd} -> Res = lookup_host_key_fd(Fd, Host, Alg), file:close(Fd), Res; {error, enoent} -> {error, not_found}; Error -> Error end.add_host_key(Host, Key, Opts) -> Host1 = add_ip(replace_localhost(Host)), case file:open(file_name(user, "known_hosts", Opts),[write,append]) of {ok, Fd} -> Res = add_key_fd(Fd, Host1, Key), file:close(Fd), Res; Error -> Error end.%% del_host_key(Host, Opts) ->%% Host1 = replace_localhost(Host),%% case file:open(file_name(user, "known_hosts", Opts),[write,read]) of%% {ok, Fd} ->%% Res = del_key_fd(Fd, Host1),%% file:close(Fd),%% Res;%% Error ->%% Error%% end.identity_key_filename("ssh-dss") -> "id_dsa";identity_key_filename("ssh-rsa") -> "id_rsa".private_identity_key(Alg, Opts) -> Path = file_name(user, identity_key_filename(Alg), Opts), read_private_key_v2(Path, Alg).read_public_key_v2(File, Type) -> case file:read_file(File) of {ok,Bin} -> List = binary_to_list(Bin), case lists:prefix(Type, List) of true -> List1 = lists:nthtail(length(Type), List), K_S = ssh_bits:b64_decode(List1), decode_public_key_v2(K_S, Type); false -> {error, bad_format} end; Error -> Error end.decode_public_key_v2(K_S, "ssh-rsa") -> case ssh_bits:decode(K_S,[string,mpint,mpint]) of ["ssh-rsa", E, N] -> {ok, #ssh_key { type = rsa, public = {N,E}, comment=""}}; _ -> {error, bad_format} end;decode_public_key_v2(K_S, "ssh-dss") -> case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of ["ssh-dss",P,Q,G,Y] -> {ok,#ssh_key { type = dsa, public = {P,Q,G,Y} }}; _A -> {error, bad_format} end;decode_public_key_v2(_, _) -> {error, bad_format}. read_public_key_v1(File) -> case file:read_file(File) of {ok,Bin} -> List = binary_to_list(Bin), case io_lib:fread("~d ~d ~d ~s", List) of {ok,[_Sz,E,N,Comment],_} -> {ok,#ssh_key { type = rsa, public ={N,E}, comment = Comment }}; _Error -> {error, bad_format} end; Error -> Error end.pem_type("ssh-dss") -> "DSA";pem_type("ssh-rsa") -> "RSA".read_private_key_v2(File, Type) -> case file:read_file(File) of {ok,Bin} -> case read_pem(binary_to_list(Bin), pem_type(Type)) of {ok,Bin1} -> decode_private_key_v2(Bin1, Type); Error -> Error end; Error -> Error end.decode_private_key_v2(Private,"ssh-rsa") -> case 'PKCS-1':decode( 'RSAPrivateKey', Private) of {ok,RSA} -> %% FIXME Check for two-prime version {ok, #ssh_key { type = rsa, public = {RSA#'RSAPrivateKey'.modulus, RSA#'RSAPrivateKey'.publicExponent}, private = {RSA#'RSAPrivateKey'.modulus, RSA#'RSAPrivateKey'.privateExponent} }}; Error -> Error end;decode_private_key_v2(Private, "ssh-dss") -> case 'DSS':decode('DSAPrivateKey', Private) of {ok,DSA} -> %% FIXME Check for two-prime version {ok, #ssh_key { type = dsa, public = {DSA#'DSAPrivateKey'.p, DSA#'DSAPrivateKey'.q, DSA#'DSAPrivateKey'.g, DSA#'DSAPrivateKey'.y}, private= {DSA#'DSAPrivateKey'.p, DSA#'DSAPrivateKey'.q, DSA#'DSAPrivateKey'.g, DSA#'DSAPrivateKey'.x} }}; _ -> {error,bad_format} end.%% SSH1 private key format%% <<"SSH PRIVATE KEY FILE FORMATE 1.1\n" 0:8 %% CipherNum:8, Reserved:32,%% NSz/uint32, N/bignum, E/bignum, Comment/string,%%%% [ R0:8 R1:8 R0:8 R1:8, D/bignum, IQMP/bignum, Q/bignum, P/bignum, Pad(8)]>>%%%% where [ ] is encrypted using des3 (ssh1 version) and%% a posssibly empty pass phrase using md5(passphase) as key%% read_private_key_v1(File, Type) -> case file:read_file(File) of {ok,<<"SSH PRIVATE KEY FILE FORMAT 1.1\n",0, CipherNum,_Resereved:32,Bin/binary>>} -> decode_private_key_v1(Bin, CipherNum,Type); {ok,_} -> {error, bad_format}; Error -> Error end.decode_private_key_v1(Bin, CipherNum, Type) -> case ssh_bits:decode(Bin,0,[uint32, bignum, bignum, string]) of {Offset,[_NSz,N,E,Comment]} -> if Type == public -> {ok,#ssh_key { type=rsa, public={N,E}, comment=Comment}}; Type == private -> <<_:Offset/binary, Encrypted/binary>> = Bin, case ssh_bits:decode(decrypt1(Encrypted, CipherNum),0, [uint32, bignum, bignum, bignum, bignum,{pad,8}]) of {_,[_,D,IQMP,Q,P]} -> {ok,#ssh_key { type=rsa, public={N,E}, private={D,IQMP,Q,P}, comment=Comment}}; _ -> {error,bad_format} end end; _ -> {error,bad_format} end.decrypt1(Bin, CipherNum) -> decrypt1(Bin, CipherNum,"").decrypt1(Bin, CipherNum, Phrase) -> if CipherNum == ?SSH_CIPHER_NONE; Phrase == "" -> Bin; CipherNum == ?SSH_CIPHER_3DES -> <<K1:8/binary, K2:8/binary>> = erlang:md5(Phrase), K3 = K1, IV = <<0,0,0,0,0,0,0,0>>, Bin1 = crypto:des_cbc_decrypt(K3,IV,Bin), Bin2 = crypto:des_cbc_encrypt(K2,IV,Bin1), crypto:des_cbc_decrypt(K1,IV,Bin2) end.%% encrypt1(Bin, CipherNum) ->%% encrypt1(Bin, CipherNum,"").%% encrypt1(Bin, CipherNum, Phrase) ->%% if CipherNum == ?SSH_CIPHER_NONE; Phrase == "" ->%% Bin;%% CipherNum == ?SSH_CIPHER_3DES ->%% <<K1:8/binary, K2:8/binary>> = erlang:md5(Phrase),%% K3 = K1,%% IV = <<0,0,0,0,0,0,0,0>>,%% Bin1 = crypto:des_cbc_encrypt(K1,IV,Bin),%% Bin2 = crypto:des_cbc_decrypt(K2,IV,Bin1),%% crypto:des_cbc_encrypt(K3,IV,Bin2)%% end.lookup_host_key_fd(Fd, Host, Alg) -> case io:get_line(Fd, '') of eof -> {error, not_found}; Line -> case string:tokens(Line, " ") of [HostList, Alg, KeyData] -> %% io:format("lookup_host_key_fd: HostList ~p Alg ~p KeyData ~p\n", %% [HostList, Alg, KeyData]), case lists:member(Host, string:tokens(HostList, ",")) of true -> decode_public_key_v2(ssh_bits:b64_decode(KeyData), Alg); false -> lookup_host_key_fd(Fd, Host, Alg) end; _ -> lookup_host_key_fd(Fd, Host, Alg) end end.%% del_key_fd(Fd, Host) ->%% del_key_fd(Fd, Host, 0, 0).%% del_key_fd(Fd, Host, ReadPos0, WritePos0) ->%% case io:get_line(Fd, '') of%% eof ->%% if ReadPos0 == WritePos0 ->%% ok;%% true ->%% file:truncate(Fd)%% end;%% Line ->%% {ok,ReadPos1} = file:position(Fd, cur),%% case string:tokens(Line, " ") of%% [HostList, _Type, _KeyData] ->%% case lists:member(Host, string:tokens(HostList, ",")) of%% true ->%% del_key_fd(Fd, Host, ReadPos1, WritePos0);%% false ->%% if ReadPos0 == WritePos0 ->%% del_key_fd(Fd, Host, ReadPos1, ReadPos1);%% true ->%% file:position(Fd, WritePos0),%% file:write(Fd, Line),%% {ok,WritePos1} = file:position(Fd,cur),%% del_key_fd(Fd, Host, ReadPos1, WritePos1)%% end%% end;%% _ ->%% if ReadPos0 == WritePos0 ->%% del_key_fd(Fd, Host, ReadPos1, ReadPos1);%% true ->%% file:position(Fd, WritePos0),%% file:write(Fd, Line),%% {ok,WritePos1} = file:position(Fd,cur),%% del_key_fd(Fd, Host, ReadPos1, WritePos1)%% end %% end%% end.add_key_fd(Fd, Host, Key) -> case Key#ssh_key.type of rsa -> {N,E} = Key#ssh_key.public, DK = ssh_bits:b64_encode( ssh_bits:encode(["ssh-rsa",E,N], [string,mpint,mpint])), file:write(Fd, [Host, " ssh-rsa ", DK, "\n"]); dsa -> {P,Q,G,Y} = Key#ssh_key.public, DK = ssh_bits:b64_encode( ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint])), file:write(Fd, [Host, " ssh-dss ", DK, "\n"]) end.read_pem(Cs, Type) -> case read_line(Cs) of {"-----BEGIN "++Rest,Cs1} -> case string:tokens(Rest, " ") of [Type, "PRIVATE", "KEY-----"] -> read_pem64(Cs1, [], Type); _ -> {error, bad_format} end; {"",Cs1} when Cs1 =/= "" -> read_pem(Cs1,Type); {_,""} -> {error, bad_format} end.read_pem64(Cs, Acc, Type) -> case read_line(Cs) of {"-----END "++Rest,_Cs1} -> case string:tokens(Rest, " ") of [Type, "PRIVATE", "KEY-----"] -> {ok,ssh_bits:b64_decode(append(reverse(Acc)))}; Toks -> error_logger:format("ssh: TOKENS=~p\n", [Toks]), {error, bad_format} end; {B64, Cs1} when Cs1 =/= "" -> read_pem64(Cs1, [B64|Acc], Type); _What -> {error, bad_format} end.read_line(Cs) -> read_line(Cs,[]).read_line([$\r,$\n|T], Acc) -> {reverse(Acc), T};read_line([$\n|T], Acc) -> {reverse(Acc), T};read_line([C|T], Acc) -> read_line(T,[C|Acc]);read_line([], Acc) -> {reverse(Acc),[]}.lookup_user_key(User, Alg, Opts) -> SshDir = ssh_dir({remoteuser,User}, Opts), case lookup_user_key_f(User, SshDir, Alg, "authorized_keys", Opts) of {ok, Key} -> {ok, Key}; _ -> lookup_user_key_f(User, SshDir, Alg, "authorized_keys2", Opts) end.lookup_user_key_f(_User, [], _Alg, _F, _Opts) -> {error, nouserdir};lookup_user_key_f(_User, nouserdir, _Alg, _F, _Opts) -> {error, nouserdir};lookup_user_key_f(_User, Dir, Alg, F, _Opts) -> FileName = filename:join(Dir, F), case file:open(FileName, [read]) of {ok, Fd} -> Res = lookup_user_key_fd(Fd, Alg), file:close(Fd), Res; {error, Reason} -> {error, {{openerr, Reason}, {file, FileName}}} end.lookup_user_key_fd(Fd, Alg) -> case io:get_line(Fd, '') of eof -> {error, not_found}; Line -> case string:tokens(Line, " ") of [Alg, KeyData, _] -> %% io:format("lookup_user_key_fd: HostList ~p Alg ~p KeyData ~p\n", %% [HostList, Alg, KeyData]), decode_public_key_v2(ssh_bits:b64_decode(KeyData), Alg); _Other -> ?dbg(false, "key_fd Other: ~w ~w\n", [Alg, _Other]), lookup_user_key_fd(Fd, Alg) end end.encode_public_key(#ssh_key{type = rsa, public = {N, E}}) -> ssh_bits:encode(["ssh-rsa",E,N], [string,mpint,mpint]);encode_public_key(#ssh_key{type = dsa, public = {P,Q,G,Y}}) -> ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint]).%%%% Utils%%%% server use this to find individual keys for%% an individual user when user tries to login%% with publickeyssh_dir({remoteuser, User}, Opts) -> case proplists:get_value(user_dir_fun, Opts) of undefined -> case proplists:get_value(user_dir, Opts) of undefined -> filename:join(["/", "home",User,".ssh"]); Dir -> Dir end; FUN -> FUN(User) end;%% client use this to find client ssh keysssh_dir(user, Opts) -> case proplists:get_value(user_dir, Opts, false) of false -> filename:join(os:getenv("HOME"), ".ssh"); D -> D end;%% server use this to find server host keysssh_dir(system, Opts) -> proplists:get_value(system_dir, Opts, "/etc/ssh").file_name(Type, Name, Opts) -> FN = filename:join(ssh_dir(Type, Opts), Name), ?dbg(?DBG_PATHS, "file_name: ~p\n", [FN]), FN.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?