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 + -
显示快捷键?