ssh_transport.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,781 行 · 第 1/4 页

ERL
1,781
字号
    end.accepted_host(SSH, Peer, Opts) ->    case proplists:get_value(silently_accept_hosts, Opts, false) of	true ->	    yes;	false ->	    yes_no(SSH, "New host "++Peer++" accept")    end.known_host_key(SSH, Public, Alg) ->    #ssh{opts = Opts, key_cb = Mod, peer = Peer} = SSH,    case Mod:lookup_host_key(Peer, Alg, Opts) of	{ok, Public} ->	    ok;	{ok, BadPublic} ->	    error_logger:format("known_host_key: Public ~p BadPublic ~p\n", [Public, BadPublic]),	    {error, bad_public_key};	{error, not_found} ->	    case accepted_host(SSH, Peer, Opts) of		yes ->		    Mod:add_host_key(Peer, Public, Opts);		no ->		    {error, rejected}	    end    end.	    send_algorithms(S, SSH, KexInit) ->    Payload = ssh_bits:encode(KexInit),    ?dbg(?DBG_MESSAGE, "SEND_MSG: ~70p\n", [KexInit]),    Res = send_packet(S, SSH, Payload),    {Res,Payload}.		       recv_algorithms(S, SSH) ->    case recv_packet(S, SSH) of	{ok, Packet} ->	    case ssh_bits:decode(Packet) of		{ok, R} ->		    ?dbg(?DBG_MESSAGE, "RECV_MSG: ~70p\n", [R]),		    {ok, {Packet, R}};		Error ->		    Error	    end;	Error ->	    ?dbg(?DBG_MESSAGE, "RECV_MSG: ~p\n", [Error]),	    Error    end.%%   Each of the algorithm strings MUST be a comma-separated list of%%   algorithm names (see ''Algorithm Naming'' in [SSH-ARCH]).  Each%%   supported (allowed) algorithm MUST be listed in order of preference.%%%%   The first algorithm in each list MUST be the preferred (guessed)%%   algorithm.  Each string MUST contain at least one algorithm name.select_algorithm(SSH, ALG, C, S) ->    %% find out the selected algorithm    C_Enc = select(C#ssh_msg_kexinit.encryption_algorithms_client_to_server,		   S#ssh_msg_kexinit.encryption_algorithms_client_to_server),    C_Mac = select(C#ssh_msg_kexinit.mac_algorithms_client_to_server,		   S#ssh_msg_kexinit.mac_algorithms_client_to_server),    C_Cmp = select(C#ssh_msg_kexinit.compression_algorithms_client_to_server,		   S#ssh_msg_kexinit.compression_algorithms_client_to_server),    C_Lng = select(C#ssh_msg_kexinit.languages_client_to_server,		   S#ssh_msg_kexinit.languages_client_to_server),    S_Enc = select(C#ssh_msg_kexinit.encryption_algorithms_server_to_client,		   S#ssh_msg_kexinit.encryption_algorithms_server_to_client),    S_Mac = select(C#ssh_msg_kexinit.mac_algorithms_server_to_client,		   S#ssh_msg_kexinit.mac_algorithms_server_to_client),    S_Cmp = select(C#ssh_msg_kexinit.compression_algorithms_server_to_client,		   S#ssh_msg_kexinit.compression_algorithms_server_to_client),    S_Lng = select(C#ssh_msg_kexinit.languages_server_to_client,		   S#ssh_msg_kexinit.languages_server_to_client),    HKey = select_all(C#ssh_msg_kexinit.server_host_key_algorithms,		      S#ssh_msg_kexinit.server_host_key_algorithms),    HK = case HKey of	     [] -> undefined;	     [HK0|_] -> HK0	 end,    %% Fixme verify Kex against HKey list and algorithms        Kex = select(C#ssh_msg_kexinit.kex_algorithms,		 S#ssh_msg_kexinit.kex_algorithms),    ALG1 = ALG#alg { kex = Kex, hkey = HK },    ALG2 = save_alg(SSH#ssh.role, 		   ALG1,		   [{c_enc, C_Enc},		    {c_mac, C_Mac},		    {c_cmp, C_Cmp},		    {c_lng, C_Lng},		    {s_enc, S_Enc},		    {s_mac, S_Mac},		    {s_cmp, S_Cmp},		    {s_lng, S_Lng}]),    {ok, SSH#ssh { algorithms = ALG2 }}.save_alg(Role, ALG, [{Key,A} | As]) ->    if A == undefined ->	    save_alg(Role, ALG, As);       true ->	    case Key of		c_enc ->		    case Role of			client ->			    save_alg(Role,ALG#alg { encrypt = A }, As);			server ->			    save_alg(Role,ALG#alg { decrypt = A }, As)		    end;		s_enc -> 		    case Role of			server -> 			    save_alg(Role,ALG#alg { encrypt = A }, As);			client ->			    save_alg(Role,ALG#alg { decrypt = A }, As)		    end;		c_mac ->		    case Role of			client ->			    save_alg(Role,ALG#alg { send_mac=A }, As);			server ->			    save_alg(Role,ALG#alg { recv_mac=A }, As)		    end;		s_mac -> 		    case Role of			server -> 			    save_alg(Role,ALG#alg { send_mac = A }, As); 			client ->			    save_alg(Role,ALG#alg { recv_mac = A }, As)		    end;		c_cmp -> 		    case Role of			client ->			    save_alg(Role,ALG#alg { compress = A }, As);			server ->			    save_alg(Role,ALG#alg { decompress = A }, As)		    end;			    		s_cmp -> 		    case Role of			server ->			    save_alg(Role, ALG#alg { compress = A }, As);			client ->			    save_alg(Role, ALG#alg { decompress = A }, As)		    end;		c_lng -> save_alg(Role, ALG#alg { c_lng = A }, As);		s_lng -> save_alg(Role, ALG#alg { s_lng = A }, As)	    end    end;save_alg(_Role, ALG, []) ->    ALG.install_alg(SSH) ->    SSH1 = alg_final(SSH),    SSH2 = alg_setup(SSH1),    alg_init(SSH2).alg_setup(SSH) ->    ALG = SSH#ssh.algorithms,    ?dbg(?DBG_ALG, "ALG: setup ~p\n", [ALG]),    SSH#ssh { kex       = ALG#alg.kex,	      hkey      = ALG#alg.hkey,	      encrypt = ALG#alg.encrypt,	      decrypt = ALG#alg.decrypt,	      send_mac = ALG#alg.send_mac,	      send_mac_size = mac_digest_size(ALG#alg.send_mac),	      recv_mac = ALG#alg.recv_mac,	      recv_mac_size = mac_digest_size(ALG#alg.recv_mac),	      compress = ALG#alg.compress,	      decompress = ALG#alg.decompress,	      c_lng = ALG#alg.c_lng,	      s_lng = ALG#alg.s_lng,	      algorithms = undefined	      }.alg_init(SSH0) ->    ?dbg(?DBG_ALG, "ALG: init\n", []),    {ok,SSH1} = send_mac_init(SSH0),    {ok,SSH2} = recv_mac_init(SSH1),    {ok,SSH3} = encrypt_init(SSH2),    {ok,SSH4} = decrypt_init(SSH3),    {ok,SSH5} = compress_init(SSH4),    {ok,SSH6} = decompress_init(SSH5),    SSH6.alg_final(SSH0) ->    ?dbg(?DBG_ALG, "ALG: final\n", []),    {ok,SSH1} = send_mac_final(SSH0),    {ok,SSH2} = recv_mac_final(SSH1),    {ok,SSH3} = encrypt_final(SSH2),    {ok,SSH4} = decrypt_final(SSH3),    {ok,SSH5} = compress_final(SSH4),    {ok,SSH6} = decompress_final(SSH5),    SSH6.select_all(CL, SL) ->    A = CL -- SL,  %% algortihms only used by client    %% algorithms used by client and server (client pref)    map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A)).select([], []) ->    none;select(CL, SL) ->    C = case select_all(CL,SL) of	    [] -> undefined;	    [ALG|_] -> ALG	end,    ?dbg(?DBG_ALG, "ALG: select: ~p ~p = ~p\n", [CL, SL, C]),    C.	    send_version(S, Version) ->    gen_tcp:send(S, [Version,"\r\n"]).send_msg(S, SSH, Record) ->    ?dbg(?DBG_MESSAGE, "SEND_MSG: ~70p\n", [Record]),    Bin = ssh_bits:encode(Record),    ?dbg(?DBG_BIN_MESSAGE, "Encoded: ~70p\n", [Bin]),    send_packet(S, SSH, Bin).%%%% TotalLen = 4 + 1 + size(Data) + size(Padding)%% PaddingLen = TotalLen - (size(Data)+4+1)%% send_packet(S, SSH, Data0) when binary(Data0) ->    Data = compress(SSH, Data0),    BlockSize = SSH#ssh.encrypt_block_size,    PL = (BlockSize - ((4 + 1 + size(Data)) rem BlockSize)) rem BlockSize,    PaddingLen = if PL <  4 -> PL+BlockSize;		    true -> PL		 end,    Padding = ssh_bits:random(PaddingLen),    PacketLen = 1 + PaddingLen + size(Data),    Packet = <<?UINT32(PacketLen),?BYTE(PaddingLen), 	      Data/binary, Padding/binary>>,    EncPacket = encrypt(SSH, Packet),    Seq = get(send_sequence),    MAC = send_mac(SSH, Packet, Seq),    ?dbg(?DBG_PACKET, "SEND_PACKET:~w len=~p,payload=~p,padding=~p,mac=~p\n",	 [Seq, PacketLen, size(Data), PaddingLen, MAC]),    Res = gen_tcp:send(S, [EncPacket, MAC]),    put(send_sequence, (Seq+1) band 16#ffffffff),    Res.recv_msg(S, SSH) ->    case recv_packet(S, SSH) of	{ok, Packet} ->	    case ssh_bits:decode(Packet) of		{ok, M} when record(M, ssh_msg_debug) ->		    if M#ssh_msg_debug.always_display == true ->			    io:format("DEBUG: ~p\n",				      [M#ssh_msg_debug.message]);		       true ->			    ?dbg(true, "DEBUG: ~p\n",				 [M#ssh_msg_debug.message])		    end,		    inet:setopts(S, [{active, once}]),		    recv_msg(S, SSH);		{ok, M} when record(M, ssh_msg_ignore) ->		    inet:setopts(S, [{active, once}]),		    recv_msg(S, SSH);		{ok, Msg} ->		    ?dbg(?DBG_MESSAGE, "RECV_MSG: ~70p\n", [Msg]),		    {ok, Msg};		Error ->		    %% Fixme (send disconnect...)		    Error	    end;	Error ->	    ?dbg(?DBG_MESSAGE, "RECV_MSG: ~70p\n", [Error]),	    Error    end.%% receive ONE packetrecv_packet(S, SSH) ->    BlockSize = SSH#ssh.decrypt_block_size,    case gen_tcp:recv(S, BlockSize) of	{ok, EncData0} ->	    Data0 = decrypt(SSH, EncData0),	    <<?UINT32(PacketLen), _/binary>> = Data0,	    if PacketLen < 5; PacketLen > ?SSH_MAX_PACKET_SIZE ->		    terminate(S, SSH, ?SSH_DISCONNECT_PROTOCOL_ERROR,			      "Bad packet length "++			      integer_to_list(PacketLen));	       true ->		    case gen_tcp:recv(S, (PacketLen - BlockSize)+4) of			{ok, EncData1} ->			    Data1 = decrypt(SSH, EncData1),			    Data = <<Data0/binary, Data1/binary>>,			    recv_packet_data(S, SSH, PacketLen, Data);			Error ->			    Error		    end	    end;	Error ->	    Error    end.recv_packet_data(S, SSH, PacketLen, Data) ->    Seq = get(recv_sequence),    Res = valid_mac(SSH, S, Data, Seq),    put(recv_sequence, (Seq+1) band 16#ffffffff),    case Res of	true ->	    <<_:32, PaddingLen:8, _/binary>> = Data,	    PayloadLen = PacketLen - PaddingLen - 1,	    <<_:32, _:8, Payload:PayloadLen/binary, 	     _:PaddingLen/binary>> = Data,	    ?dbg(?DBG_PACKET, 		 "RECV_PACKET:~w, len=~p,payload=~w,padding=~w\n", 		 [Seq,PacketLen,PayloadLen,PaddingLen]),	    {ok, decompress(SSH, Payload)};	false ->	    ?dbg(?DBG_PACKET, "RECV_PACKET:~w, len=~p\n", 		 [Seq,PacketLen]),	    terminate(S, SSH, ?SSH_DISCONNECT_MAC_ERROR,		      "Bad MAC #"++ integer_to_list(Seq))    end.kexfailed(S, User, UserAck, Error) ->    Description =	case Error of	    {error, bad_message} ->		"key exchanged failed: bad message received";	    _ ->		"key exchanged failed"	end,    M = #ssh_msg_disconnect { code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,			      description = Description,			      language = "en"},    if UserAck == true ->	    User ! {self(), Error};       true ->	    User ! {ssh_msg, self(), M}    end,    gen_tcp:close(S),    Error.%% Send a disconnect messageterminate(S, SSH, Code, Message) ->    M = #ssh_msg_disconnect { code=Code, 			      description=Message,			      language = "en" },    send_msg(S, SSH, M),    gen_tcp:close(S),    {error, M}.        %% public key algorithms%%%%   ssh-dss              REQUIRED     sign    Raw DSS Key%%   ssh-rsa              RECOMMENDED  sign    Raw RSA Key%%   x509v3-sign-rsa      OPTIONAL     sign    X.509 certificates (RSA key)%%   x509v3-sign-dss      OPTIONAL     sign    X.509 certificates (DSS key)%%   spki-sign-rsa        OPTIONAL     sign    SPKI certificates (RSA key)%%   spki-sign-dss        OPTIONAL     sign    SPKI certificates (DSS key)%%   pgp-sign-rsa         OPTIONAL     sign    OpenPGP certificates (RSA key)%%   pgp-sign-dss         OPTIONAL     sign    OpenPGP certificates (DSS key)%%%% key exchange%%%%     diffie-hellman-group1-sha1       REQUIRED%%%%    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Encryption%%   context stored in dictionary as 'encrypt_ctx'%%%% chiphers%%%%       3des-cbc         REQUIRED          %%       three-key 3DES in CBC mode%%       blowfish-cbc     OPTIONAL          Blowfish in CBC mode%%       twofish256-cbc   OPTIONAL          Twofish in CBC mode,%%                                          with 256-bit key%%       twofish-cbc      OPTIONAL          alias for "twofish256-cbc" (this%%                                          is being retained for%%                                          historical reasons)%%       twofish192-cbc   OPTIONAL          Twofish with 192-bit key%%       twofish128-cbc   OPTIONAL          Twofish with 128-bit key%%       aes256-cbc       OPTIONAL          AES in CBC mode,%%                                          with 256-bit key%%       aes192-cbc       OPTIONAL          AES with 192-bit key%%       aes128-cbc       RECOMMENDED       AES with 128-bit key%%       serpent256-cbc   OPTIONAL          Serpent in CBC mode, with%%                                          256-bit key%%       serpent192-cbc   OPTIONAL          Serpent with 192-bit key%%       serpent128-cbc   OPTIONAL          Serpent with 128-bit key%%       arcfour          OPTIONAL          the ARCFOUR stream cipher%%       idea-cbc         OPTIONAL          IDEA in CBC mode%%       cast128-cbc      OPTIONAL          CAST-128 in CBC mode%%       none             OPTIONAL          no encryption; NOT RECOMMENDED%%  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%encrypt_init(SSH) ->    case SSH#ssh.encrypt of	none ->	    {ok,SSH};	'3des-cbc' ->	    {IV,KD} = 		case SSH#ssh.role of		    client ->			{hash(SSH, "A", 64),			 hash(SSH, "C", 192)};		    server ->			{hash(SSH, "B", 64),			 hash(SSH, "D", 192)}		end,	    <<K1:8/binary, K2:8/binary, K3:8/binary>> = KD,	    put(encrypt_ctx,  IV),	    {ok,SSH#ssh { encrypt_keys = {K1,K2,K3},			  encrypt_block_size = 8 }};	_ ->	    exit({bad_algorithm,SSH#ssh.encrypt})    end.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?