ssh_transport.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,781 行 · 第 1/4 页
ERL
1,781 行
server_hello(S, User, SSH, Timeout) after Timeout -> ?dbg(true, "server_hello timeout ~p\n", [Timeout]), gen_tcp:close(S), {error, timeout} end.client_hello(S, User, SSH, Timeout) -> receive {tcp, S, V = "SSH-"++_} -> Version = trim_tail(V), ?dbg(true, "server version: ~p\n",[Version]), case string:tokens(Version, "-") of [_, "2.0" | _] -> negotiate(S, User, SSH#ssh { s_vsn = {2,0}, s_version = Version }, true); [_, "1.99" | _] -> %% compatible server negotiate(S, User, SSH#ssh { s_vsn = {2,0}, s_version = Version }, true); [_, "1.3" | _] -> negotiate(S, User, SSH#ssh { s_vsn = {1,3}, s_version = Version }, true); [_, "1.5" | _] -> negotiate(S, User, SSH#ssh { s_vsn = {1,5}, s_version = Version }, true); _ -> exit(unknown_version) end; {tcp, S, _Line} -> ?dbg(true, "info: ~p\n", [_Line]), inet:setopts(S, [{active, once}]), client_hello(S, User, SSH, Timeout); {tcp_error, S, Reason} -> ?dbg(true, "client_hello got tcp error ~p\n", [Reason]), {error, {tcp_error, Reason}}; {tcp_closed, S} -> ?dbg(true, "client_hello: tcp_closed\n", []), {error, tcp_closed}; Other -> io:format("Other ~p\n", [Other]) after Timeout -> ?dbg(true, "client_hello timeout ~p\n", [Timeout]), gen_tcp:close(S), {error, timeout} end.%% Determine the version and algorithmsnegotiate(S, User, SSH, UserAck) -> inet:setopts(S, [{packet,0},{mode,binary}]), send_negotiate(S, User, SSH, UserAck).%% We start re-negotiatesend_negotiate(S, User, SSH, UserAck) -> SendAlg = kex_init(SSH), {ok, SendPdu} = send_algorithms(S, SSH, SendAlg), {ok, {RecvPdu,RecvAlg}} = recv_algorithms(S, SSH), kex_negotiate(S, User, SSH, UserAck, SendAlg, SendPdu, RecvAlg, RecvPdu).%% Other side started re-negotiaterecv_negotiate(S, User, SSH, RecvAlg, UserAck) -> RecvPdu = ssh_bits:encode(RecvAlg), SendAlg = kex_init(SSH), {ok, SendPdu} = send_algorithms(S, SSH, SendAlg), send_msg(S, SSH, SendAlg), kex_negotiate(S, User, SSH, UserAck, SendAlg, SendPdu, RecvAlg, RecvPdu).%% Select algorithmskex_negotiate(S, User, SSH, UserAck, SendAlg, SendPdu, RecvAlg, RecvPdu) -> case SSH#ssh.role of client -> SSH1 = SSH#ssh { c_keyinit = SendPdu, s_keyinit = RecvPdu }, {ok, SSH2} = select_algorithm(SSH1, #alg {}, SendAlg, RecvAlg), ALG = SSH2#ssh.algorithms, case client_kex(S, SSH2, ALG#alg.kex) of {ok, SSH3} -> newkeys(S, User, SSH3, UserAck); Error -> kexfailed(S, User, UserAck, Error) end; server -> SSH1 = SSH#ssh { c_keyinit = RecvPdu, s_keyinit = SendPdu }, {ok,SSH2} = select_algorithm(SSH1, #alg {}, RecvAlg, SendAlg), ALG = SSH2#ssh.algorithms, case server_kex(S, SSH2, ALG#alg.kex) of {ok, SSH3} -> newkeys(S, User, SSH3, UserAck); Error -> kexfailed(S, User, UserAck, Error) end end. newkeys(S, User, SSH, UserAck) -> %% Send new keys and wait for newkeys send_msg(S, SSH, #ssh_msg_newkeys {}), case recv_msg(S, SSH) of {ok, M} when record(M, ssh_msg_newkeys) -> SSH1 = install_alg(SSH), if UserAck == true -> User ! {self(), {ok, self()}}, inet:setopts(S, [{active, once}]), ssh_main(S, User, SSH1); true -> inet:setopts(S, [{active, once}]), ssh_main(S, User, SSH1) end; {ok,_} -> {error, bad_message}; Error -> Error end.client_kex(S, SSH, 'diffie-hellman-group1-sha1') -> ssh_bits:install_messages(kexdh_messages()), {G,P} = dh_group1(), {Private, Public} = dh_gen_key(G,P,1024), ?dbg(?DBG_KEX, "public: ~.16B\n", [Public]), send_msg(S, SSH, #ssh_msg_kexdh_init { e = Public }), case recv_msg(S, SSH) of {ok, R} when record(R, ssh_msg_kexdh_reply) -> K_S = R#ssh_msg_kexdh_reply.public_host_key, F = R#ssh_msg_kexdh_reply.f, K = ssh_math:ipow(F, Private, P), H = kex_h(SSH, K_S, Public, F, K), H_SIG = R#ssh_msg_kexdh_reply.h_sig, ?dbg(?DBG_KEX, "shared_secret: ~s\n", [fmt_binary(K, 16, 4)]), ?dbg(?DBG_KEX, "hash: ~s\n", [fmt_binary(H, 16, 4)]), case verify_host_key(S, SSH, K_S, H, H_SIG) of ok -> {ok, SSH#ssh { shared_secret = K, exchanged_hash = H, session_id = H }}; Error -> Error end; {ok,_} -> {error, bad_message}; Error -> Error end;client_kex(S, SSH, 'diffie-hellman-group-exchange-sha1') -> ssh_bits:install_messages(kex_dh_gex_messages()), Min = 512, NBits = 1024, Max = 4096, send_msg(S, SSH, #ssh_msg_kex_dh_gex_request { min = Min, n = NBits, max = Max }), case recv_msg(S, SSH) of {ok, RG} when record(RG, ssh_msg_kex_dh_gex_group) -> P = RG#ssh_msg_kex_dh_gex_group.p, G = RG#ssh_msg_kex_dh_gex_group.g, {Private, Public} = dh_gen_key(G,P, 1024), ?dbg(?DBG_KEX, "public: ~.16B\n", [Public]), send_msg(S, SSH, #ssh_msg_kex_dh_gex_init { e = Public }), case recv_msg(S, SSH) of {ok, R} when record(R, ssh_msg_kex_dh_gex_reply) -> K_S = R#ssh_msg_kex_dh_gex_reply.public_host_key, F = R#ssh_msg_kex_dh_gex_reply.f, K = ssh_math:ipow(F, Private, P), H = kex_h(SSH, K_S, Min, NBits, Max, P, G, Public, F, K), H_SIG = R#ssh_msg_kex_dh_gex_reply.h_sig, ?dbg(?DBG_KEX, "shared_secret: ~s\n", [fmt_binary(K, 16, 4)]), ?dbg(?DBG_KEX, "hash: ~s\n", [fmt_binary(H, 16, 4)]), case verify_host_key(S, SSH, K_S, H, H_SIG) of ok -> {ok, SSH#ssh { shared_secret = K, exchanged_hash = H, session_id = H }}; Error -> Error end; {ok,_} -> {error, bad_message}; Error -> Error end; {ok,_} -> {error, bad_message}; Error -> Error end;client_kex(_S, _SSH, Kex) -> {error, {bad_kex_algorithm, Kex}}.server_kex(S, SSH, 'diffie-hellman-group1-sha1') -> ssh_bits:install_messages(kexdh_messages()), {G,P} = dh_group1(), {Private, Public} = dh_gen_key(G,P,1024), ?dbg(?DBG_KEX, "public: ~.16B\n", [Public]), case recv_msg(S, SSH) of {ok, R} when record(R, ssh_msg_kexdh_init) -> E = R#ssh_msg_kexdh_init.e, K = ssh_math:ipow(E, Private, P), {Key,K_S} = get_host_key(SSH), H = kex_h(SSH, K_S, E, Public, K), H_SIG = sign_host_key(S, SSH, Key, H), send_msg(S, SSH, #ssh_msg_kexdh_reply { public_host_key = K_S, f = Public, h_sig = H_SIG }), ?dbg(?DBG_KEX, "shared_secret: ~s\n", [fmt_binary(K, 16, 4)]), ?dbg(?DBG_KEX, "hash: ~s\n", [fmt_binary(H, 16, 4)]), {ok, SSH#ssh { shared_secret = K, exchanged_hash = H, session_id = H }}; {ok,_} -> {error, bad_message}; Error -> Error end;server_kex(S, SSH, 'diffie-hellman-group-exchange-sha1') -> ssh_bits:install_messages(kex_dh_gex_messages()), {ok, #ssh_msg_kex_dh_gex_request { min = Min, n = NBits, max = Max }} = recv_msg(S, SSH), {G,P} = dh_group1(), %% FIX ME!!! send_msg(S, SSH, #ssh_msg_kex_dh_gex_group { p = P, g = G }), {Private, Public} = dh_gen_key(G,P,1024), ?dbg(?DBG_KEX, "public: ~.16B\n", [Public]), case recv_msg(S, SSH) of {ok, R} when record(R, ssh_msg_kex_dh_gex_init) -> E = R#ssh_msg_kex_dh_gex_init.e, K = ssh_math:ipow(E, Private, P), {Key,K_S} = get_host_key(SSH), H = kex_h(SSH, K_S, Min, NBits, Max, P, G, E, Public, K), H_SIG = sign_host_key(S, SSH, Key, H), send_msg(S, SSH, #ssh_msg_kex_dh_gex_reply { public_host_key = K_S, f = Public, h_sig = H_SIG }), ?dbg(?DBG_KEX, "shared_secret: ~s\n", [fmt_binary(K, 16, 4)]), ?dbg(?DBG_KEX, "hash: ~s\n", [fmt_binary(H, 16, 4)]), {ok, SSH#ssh { shared_secret = K, exchanged_hash = H, session_id = H }}; {ok,_} -> {error, bad_message}; Error -> Error end;server_kex(_S, _SSH, Kex) -> {error, {bad_kex_algorithm, Kex}}.ssh_main(S, User, SSH) -> receive {tcp, S, Data} -> %% This is a lazy way of gettting events without block ?dbg(?DBG_PACKET, "UNRECEIVE: ~w BYTES\n", [size(Data)]), gen_tcp:unrecv(S, Data), case recv_msg(S, SSH) of {ok, M} when record(M, ssh_msg_unimplemented) -> ?dbg(true, "UNIMPLEMENTED: ~p\n", [M#ssh_msg_unimplemented.sequence]), inet:setopts(S, [{active, once}]), ssh_main(S, User, SSH); {ok,M} when record(M, ssh_msg_disconnect) -> User ! {ssh_msg, self(), M}, ?dbg(true, "DISCONNECT: ~w ~s\n", [M#ssh_msg_disconnect.code, M#ssh_msg_disconnect.description]), gen_tcp:close(S); {ok,M} when record(M, ssh_msg_kexinit) -> recv_negotiate(S, User, SSH, M, false); {ok,M} when is_record(M, ssh_msg_service_request) -> User ! {ssh_msg, self(), M}, await_user(User), inet:setopts(S, [{active, once}]), ssh_main(S, User, SSH); {ok,M} -> User ! {ssh_msg, self(), M}, inet:setopts(S, [{active, once}]), ssh_main(S, User, SSH); {error, unimplemented} -> send_msg(S, SSH, #ssh_msg_unimplemented { sequence = get(recv_sequence)-1}), inet:setopts(S, [{active, once}]), ssh_main(S, User, SSH); {error, _Other} -> inet:setopts(S, [{active, once}]), %% send disconnect! ssh_main(S, User, SSH) end; {tcp_closed, S} -> User ! {ssh_msg, self(), #ssh_msg_disconnect { code=?SSH_DISCONNECT_CONNECTION_LOST, description = "Connection closed", language = "" }}, gen_tcp:close(S), %% CHECK ME, is this needed ? ok; {ssh_msg, User, Msg} -> send_msg(S, SSH, Msg), if record(Msg, ssh_msg_disconnect) -> ok; true -> ssh_main(S, User, SSH) end; {ssh_install, Table} -> ssh_bits:install_messages(Table), ssh_main(S, User, SSH); {ssh_uninstall, Table} -> ssh_bits:uninstall_messages(Table), ssh_main(S, User, SSH); {ssh_renegotiate, UserAck, Opts} -> %% Of some reason, the socket is still active, once when we %% get here, which yelds EINVAL when doing recv. This might be a bug... inet:setopts(S, [{active, false}]), send_negotiate(S, User, ssh_setopts(Opts, SSH), UserAck); {ssh_call, From, close} -> ?dbg(true, "Call: close from ~p\n", [From]), gen_tcp:close(S), reply(From, ok), ok; {ssh_call, From, peername} -> P = inet:peername(S), reply(From, P), ssh_main(S, User, SSH); {ssh_call, From, Req} -> ?dbg(true, "Call: ~p from ~p\n", [Req,From]), SSH1 = handle_call(Req, From, SSH), ssh_main(S, User, SSH1); _Other -> ?dbg(true, "ssh_loop: got ~p\n", [_Other]), ssh_main(S, User, SSH) end.%%%% Handle call's to ssh_transport%%handle_call({get_cb,io}, From, SSH) -> reply(From, {ok, SSH#ssh.io_cb}), SSH;handle_call({get_cb,key}, From, SSH) -> reply(From, {ok, SSH#ssh.key_cb}), SSH;handle_call(get_session_id, From, SSH) -> reply(From, {ok, SSH#ssh.session_id}), SSH;handle_call(_Other, From, SSH) -> reply(From, {error, bad_call}), SSH.reply([Pid|Ref], Reply) -> ?dbg(true, "Reply: ~p\n", [Reply]), Pid ! {Ref, Reply}.%%%% The host key should be read from storage%%get_host_key(SSH) -> #ssh{key_cb = Mod, opts = Opts, algorithms = ALG} = SSH, Scope = proplists:get_value(key_scope, Opts, system), case ALG#alg.hkey of 'ssh-rsa' -> case Mod:private_host_rsa_key(Scope, Opts) of {ok,Key=#ssh_key { public={N,E}} } -> ?dbg(true, "x~n", []), {Key, ssh_bits:encode(["ssh-rsa",E,N],[string,mpint,mpint])}; Error -> ?dbg(true, "y~n", []), exit(Error) end; 'ssh-dss' -> case Mod:private_host_dsa_key(Scope, Opts) of {ok,Key=#ssh_key { public={P,Q,G,Y}}} -> {Key, ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint])}; Error -> exit(Error) end; _ -> exit({error, bad_key_type}) end.sign_host_key(_S, SSH, Private, H) -> ALG = SSH#ssh.algorithms, Module = case ALG#alg.hkey of 'ssh-rsa' -> ssh_rsa; 'ssh-dss' -> ssh_dsa; A -> A end, case catch Module:sign(Private, H) of {'EXIT', Reason} -> error_logger:format("SIGN FAILED: ~p\n", [Reason]), {error, Reason}; SIG -> ssh_bits:encode([Module:alg_name() ,SIG],[string,binary]) end. verify_host_key(_S, SSH, K_S, H, H_SIG) -> ALG = SSH#ssh.algorithms, case ALG#alg.hkey of 'ssh-rsa' -> case ssh_bits:decode(K_S,[string,mpint,mpint]) of ["ssh-rsa", E, N] -> ["ssh-rsa",SIG] = ssh_bits:decode(H_SIG,[string,binary]), Public = #ssh_key { type=rsa, public={N,E} }, case catch ssh_rsa:verify(Public, H, SIG) of {'EXIT', Reason} -> error_logger:format("VERIFY FAILED: ~p\n", [Reason]), {error, bad_signature}; ok -> known_host_key(SSH, Public, "ssh-rsa") end; _ -> {error, bad_format} end; 'ssh-dss' -> case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of ["ssh-dss",P,Q,G,Y] -> ["ssh-dss",SIG] = ssh_bits:decode(H_SIG,[string,binary]), Public = #ssh_key { type=dsa, public={P,Q,G,Y} }, case catch ssh_dsa:verify(Public, H, SIG) of {'EXIT', Reason} -> error_logger:format("VERIFY FAILED: ~p\n", [Reason]), {error, bad_signature}; ok -> known_host_key(SSH, Public, "ssh-dss") end; _ -> {error, bad_host_key_format} end; _ -> {error, bad_host_key_algorithm}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?