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