diff options
author | Ingela Anderton Andin <[email protected]> | 2012-02-13 10:28:11 +0100 |
---|---|---|
committer | Ingela Anderton Andin <[email protected]> | 2012-02-13 10:28:11 +0100 |
commit | 86231a380b27fc262e48aa8c3f0372d4d2056682 (patch) | |
tree | 3b815f956baee2968e3ec919099e358b5e804d14 /lib/ssh/src | |
parent | c3fb91d203412c16d008b4c36fb13c0d776d8c46 (diff) | |
parent | 7e3101ebcf816925e3f6b0d4fdd75b18999b63ad (diff) | |
download | otp-86231a380b27fc262e48aa8c3f0372d4d2056682.tar.gz otp-86231a380b27fc262e48aa8c3f0372d4d2056682.tar.bz2 otp-86231a380b27fc262e48aa8c3f0372d4d2056682.zip |
Merge branch 'ia/ssh/public_keys/OTP-9911' into maint
* ia/ssh/public_keys/OTP-9911:
Correct access flag handling in sftpd
Client fallbacks to ipv4 if connection is refused with ipv6
Cleaned up code so that ssh_file can become a template for a documented ssh_keys behavior
Test case maintenance
Better handling of IPv6
Ssh daemon handles RSA host keys
Removed no longer needed code
Use the public_key application for all public key handling
Diffstat (limited to 'lib/ssh/src')
-rw-r--r-- | lib/ssh/src/DSS.asn1 | 20 | ||||
-rw-r--r-- | lib/ssh/src/Makefile | 28 | ||||
-rw-r--r-- | lib/ssh/src/PKCS-1.asn1 | 116 | ||||
-rw-r--r-- | lib/ssh/src/prebuild.skip | 2 | ||||
-rw-r--r-- | lib/ssh/src/ssh.app.src | 6 | ||||
-rw-r--r-- | lib/ssh/src/ssh.erl | 92 | ||||
-rw-r--r-- | lib/ssh/src/ssh.hrl | 17 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 124 | ||||
-rw-r--r-- | lib/ssh/src/ssh_bits.erl | 6 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection.erl | 4 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection_handler.erl | 45 | ||||
-rw-r--r-- | lib/ssh/src/ssh_dsa.erl | 95 | ||||
-rw-r--r-- | lib/ssh/src/ssh_file.erl | 625 | ||||
-rw-r--r-- | lib/ssh/src/ssh_rsa.erl | 298 | ||||
-rw-r--r-- | lib/ssh/src/ssh_sftpd.erl | 22 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 266 | ||||
-rw-r--r-- | lib/ssh/src/ssh_xfer.erl | 16 |
17 files changed, 450 insertions, 1332 deletions
diff --git a/lib/ssh/src/DSS.asn1 b/lib/ssh/src/DSS.asn1 deleted file mode 100644 index 77aca3808b..0000000000 --- a/lib/ssh/src/DSS.asn1 +++ /dev/null @@ -1,20 +0,0 @@ -DSS DEFINITIONS EXPLICIT TAGS ::= - -BEGIN - --- EXPORTS ALL --- All types and values defined in this module are exported for use --- in other ASN.1 modules. - -DSAPrivateKey ::= SEQUENCE { - version INTEGER, - p INTEGER, -- p - q INTEGER, -- q - g INTEGER, -- q - y INTEGER, -- y - x INTEGER -- x -} - -END - - diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index da31d87369..cd9618c139 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. All Rights Reserved. # # 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 @@ -56,12 +56,10 @@ MODULES= \ ssh_auth\ ssh_bits \ ssh_cli \ - ssh_dsa \ ssh_file \ ssh_io \ ssh_math \ ssh_no_io \ - ssh_rsa \ ssh_sftp \ ssh_sftpd \ ssh_sftpd_file\ @@ -72,9 +70,9 @@ MODULES= \ PUBLIC_HRL_FILES= ssh.hrl ssh_userauth.hrl ssh_xfer.hrl -ERL_FILES= $(MODULES:%=%.erl) $(ASN_ERLS) +ERL_FILES= $(MODULES:%=%.erl) -ALL_MODULES= $(MODULES) $(ASN_MODULES) +ALL_MODULES= $(MODULES) TARGET_FILES= $(ALL_MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) @@ -87,22 +85,13 @@ APP_TARGET= $(EBIN)/$(APP_FILE) APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) -ASN_MODULES = PKCS-1 DSS -ASN_ASNS = $(ASN_MODULES:%=%.asn1) -ASN_ERLS = $(ASN_MODULES:%=%.erl) -ASN_HRLS = $(ASN_MODULES:%=%.hrl) -ASN_DBS = $(ASN_MODULES:%=%.asn1db) -ASN_TABLES = $(ASN_MODULES:%=%.table) - -ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj +inline - -INTERNAL_HRL_FILES = $(ASN_HRLS) ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl +INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -pa$(EBIN) - +ERL_COMPILE_FLAGS += -pa$(EBIN)\ + -pz $(ERL_TOP)/lib/public_key/ebin # ---------------------------------------------------- # Targets # ---------------------------------------------------- @@ -114,7 +103,6 @@ debug: ERLC_FLAGS += -Ddebug clean: rm -f $(TARGET_FILES) rm -f errs core *~ - rm -f $(ASN_ERLS) $(ASN_HRLS) $(ASN_DBS) $(TARGET_FILES): ssh.hrl @@ -127,10 +115,6 @@ $(APP_TARGET): $(APP_SRC) ../vsn.mk $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ -%.erl %.hrl: %.asn1 - $(ERLC) $(ASN_FLAGS) $< - -$(EBIN)/ssh_file.$(EMULATOR) $(EBIN)/ssh_rsa.$(EMULATOR): $(ASN_HRLS) docs: diff --git a/lib/ssh/src/PKCS-1.asn1 b/lib/ssh/src/PKCS-1.asn1 deleted file mode 100644 index e7d6b18c63..0000000000 --- a/lib/ssh/src/PKCS-1.asn1 +++ /dev/null @@ -1,116 +0,0 @@ -PKCS-1 { - iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) - modules(0) pkcs-1(1) -} - --- $Revision: 1.1 $ - -DEFINITIONS EXPLICIT TAGS ::= - -BEGIN - --- IMPORTS id-sha256, id-sha384, id-sha512 --- FROM NIST-SHA2 { --- joint-iso-itu-t(2) country(16) us(840) organization(1) --- gov(101) csor(3) nistalgorithm(4) modules(0) sha2(1) --- }; - -pkcs-1 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 -} - -rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } - -id-RSAES-OAEP OBJECT IDENTIFIER ::= { pkcs-1 7 } - -id-pSpecified OBJECT IDENTIFIER ::= { pkcs-1 9 } - -id-RSASSA-PSS OBJECT IDENTIFIER ::= { pkcs-1 10 } - -md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } -md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } -sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } -sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } -sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } -sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } - -id-sha1 OBJECT IDENTIFIER ::= { - iso(1) identified-organization(3) oiw(14) secsig(3) - algorithms(2) 26 -} - -id-md2 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 2 -} - -id-md5 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 -} - -id-mgf1 OBJECT IDENTIFIER ::= { pkcs-1 8 } - - -RSAPublicKey ::= SEQUENCE { - modulus INTEGER, -- n - publicExponent INTEGER -- e -} - -RSAPrivateKey ::= SEQUENCE { - version Version, - modulus INTEGER, -- n - publicExponent INTEGER, -- e - privateExponent INTEGER, -- d - prime1 INTEGER, -- p - prime2 INTEGER, -- q - exponent1 INTEGER, -- d mod (p-1) - exponent2 INTEGER, -- d mod (q-1) - coefficient INTEGER, -- (inverse of q) mod p - otherPrimeInfos OtherPrimeInfos OPTIONAL -} - -Version ::= INTEGER { two-prime(0), multi(1) } - (CONSTRAINED BY { - -- version must be multi if otherPrimeInfos present -- - }) - -OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo - -OtherPrimeInfo ::= SEQUENCE { - prime INTEGER, -- ri - exponent INTEGER, -- di - coefficient INTEGER -- ti -} - -Algorithm ::= SEQUENCE { - algorithm OBJECT IDENTIFIER, - parameters ANY DEFINED BY algorithm OPTIONAL -} - -AlgorithmNull ::= SEQUENCE { - algorithm OBJECT IDENTIFIER, - parameters NULL -} - - -RSASSA-PSS-params ::= SEQUENCE { - hashAlgorithm [0] Algorithm, -- DEFAULT sha1, - maskGenAlgorithm [1] Algorithm, -- DEFAULT mgf1SHA1, - saltLength [2] INTEGER DEFAULT 20, - trailerField [3] TrailerField DEFAULT trailerFieldBC -} - -TrailerField ::= INTEGER { trailerFieldBC(1) } - -DigestInfo ::= SEQUENCE { - digestAlgorithm Algorithm, - digest OCTET STRING -} - -DigestInfoNull ::= SEQUENCE { - digestAlgorithm AlgorithmNull, - digest OCTET STRING -} - - -END -- PKCS1Definitions - diff --git a/lib/ssh/src/prebuild.skip b/lib/ssh/src/prebuild.skip deleted file mode 100644 index 1d7552d98d..0000000000 --- a/lib/ssh/src/prebuild.skip +++ /dev/null @@ -1,2 +0,0 @@ -DSS.asn1db -PKCS-1.asn1db diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 8a3e15841f..154c72485e 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -3,9 +3,7 @@ {application, ssh, [{description, "SSH-2 for Erlang/OTP"}, {vsn, "%VSN%"}, - {modules, ['DSS', - 'PKCS-1', - ssh, + {modules, [ssh, ssh_app, ssh_acceptor, ssh_acceptor_sup, @@ -21,12 +19,10 @@ ssh_shell, sshc_sup, sshd_sup, - ssh_dsa, ssh_file, ssh_io, ssh_math, ssh_no_io, - ssh_rsa, ssh_sftp, ssh_sftpd, ssh_sftpd_file, diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index cada109df0..c47db64497 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% 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 @@ -23,6 +23,7 @@ -include("ssh.hrl"). -include("ssh_connect.hrl"). +-include_lib("public_key/include/public_key.hrl"). -export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2, channel_info/3, @@ -30,6 +31,9 @@ stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2, shell/1, shell/2, shell/3]). +-deprecated({sign_data, 2, next_major_release}). +-deprecated({verify_data, 3, next_major_release}). + -export([sign_data/2, verify_data/3]). %%-------------------------------------------------------------------- @@ -89,6 +93,9 @@ connect(Host, Port, Options, Timeout) -> %% might return undefined as the connection manager %% could allready have terminated, so we will not %% match the Manager in this case + {_, not_connected, {error, econnrefused}} when DisableIpv6 == false -> + do_demonitor(MRef, Manager), + connect(Host, Port, [{ip_v6_disabled, true} | Options], Timeout); {_, not_connected, {error, Reason}} -> do_demonitor(MRef, Manager), {error, Reason}; @@ -247,43 +254,6 @@ shell(Host, Port, Options) -> Error end. - -%%-------------------------------------------------------------------- -%% Function: sign_data(Data, Algorithm) -> binary() | -%% {error, Reason} -%% -%% Data = binary() -%% Algorithm = "ssh-rsa" -%% -%% Description: Use SSH key to sign data. -%%-------------------------------------------------------------------- -sign_data(Data, Algorithm) when is_binary(Data) -> - case ssh_file:private_identity_key(Algorithm,[]) of - {ok, Key} when Algorithm == "ssh-rsa" -> - ssh_rsa:sign(Key, Data); - Error -> - Error - end. - -%%-------------------------------------------------------------------- -%% Function: verify_data(Data, Signature, Algorithm) -> ok | -%% {error, Reason} -%% -%% Data = binary() -%% Signature = binary() -%% Algorithm = "ssh-rsa" -%% -%% Description: Use SSH signature to verify data. -%%-------------------------------------------------------------------- -verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signature) -> - case ssh_file:public_identity_key(Algorithm, []) of - {ok, Key} when Algorithm == "ssh-rsa" -> - ssh_rsa:verify(Key, Data, Signature); - Error -> - Error - end. - - %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -379,6 +349,50 @@ handle_options([Opt | Rest], SockOpts, Opts) -> inetopt(true) -> inet; inetopt(false) -> - inet6. + case gen_tcp:listen(0, [inet6, {ip, loopback}]) of + {ok, Dummyport} -> + gen_tcp:close(Dummyport), + inet6; + _ -> + inet + end. + +%%% +%% Deprecated +%%% +%%-------------------------------------------------------------------- +%% Function: sign_data(Data, Algorithm) -> binary() | +%% {error, Reason} +%% +%% Data = binary() +%% Algorithm = "ssh-rsa" +%% +%% Description: Use SSH key to sign data. +%%-------------------------------------------------------------------- +sign_data(Data, Algorithm) when is_binary(Data) -> + case ssh_file:user_key(Algorithm,[]) of + {ok, Key} when Algorithm == "ssh-rsa" -> + public_key:sign(Data, sha, Key); + Error -> + Error + end. + +%%-------------------------------------------------------------------- +%% Function: verify_data(Data, Signature, Algorithm) -> ok | +%% {error, Reason} +%% +%% Data = binary() +%% Signature = binary() +%% Algorithm = "ssh-rsa" +%% +%% Description: Use SSH signature to verify data. +%%-------------------------------------------------------------------- +verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signature) -> + case ssh_file:user_key(Algorithm, []) of + {ok, #'RSAPrivateKey'{publicExponent = E, modulus = N}} when Algorithm == "ssh-rsa" -> + public_key:verify(Data, sha, Signature, #'RSAPublicKey'{publicExponent = E, modulus = N}); + Error -> + Error + end. diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index ac249b05e3..da5750b6c3 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% 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 @@ -54,18 +54,6 @@ -define(string(X), << ?STRING(list_to_binary(X)) >> ). -define(binary(X), << ?STRING(X) >>). --ifdef(debug). --define(dbg(Debug, Fmt, As), - case (Debug) of - true -> - io:format([$# | (Fmt)], (As)); - _ -> - ok - end). --else. --define(dbg(Debug, Fmt, As), ok). --endif. - -define(SSH_CIPHER_NONE, 0). -define(SSH_CIPHER_3DES, 3). -define(SSH_CIPHER_AUTHFILE, ?SSH_CIPHER_3DES). @@ -138,7 +126,8 @@ userauth_quiet_mode, % boolean() userauth_supported_methods , % userauth_methods, - userauth_preference + userauth_preference, + available_host_keys }). -record(alg, diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 9dbd95886e..a2e74a12bb 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% 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 @@ -21,8 +21,9 @@ -module(ssh_auth). --include("ssh.hrl"). +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). -include("ssh_auth.hrl"). -include("ssh_transport.hrl"). @@ -36,18 +37,21 @@ %%-------------------------------------------------------------------- %%% Internal application API %%-------------------------------------------------------------------- -publickey_msg([Cb, #ssh{user = User, +publickey_msg([Alg, #ssh{user = User, session_id = SessionId, service = Service, opts = Opts} = Ssh]) -> + + Hash = sha, %% Maybe option?! ssh_bits:install_messages(userauth_pk_messages()), - Alg = Cb:alg_name(), - case ssh_file:private_identity_key(Alg, Opts) of - {ok, PrivKey} -> - PubKeyBlob = ssh_file:encode_public_key(PrivKey), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + + case KeyCb:user_key(Alg, Opts) of + {ok, Key} -> + PubKeyBlob = encode_public_key(Key), SigData = build_sig_data(SessionId, - User, Service, Alg, PubKeyBlob), - Sig = Cb:sign(PrivKey, SigData), + User, Service, PubKeyBlob, Alg), + Sig = ssh_transport:sign(SigData, Hash, Key), SigBlob = list_to_binary([?string(Alg), ?binary(Sig)]), ssh_transport:ssh_packet( #ssh_msg_userauth_request{user = User, @@ -58,8 +62,8 @@ publickey_msg([Cb, #ssh{user = User, ?binary(PubKeyBlob), ?binary(SigBlob)]}, Ssh); - _Error -> - not_ok + _Error -> + not_ok end. password_msg([#ssh{opts = Opts, io_cb = IoCb, @@ -103,12 +107,12 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> service = "ssh-connection", method = "none", data = <<>>}, - CbFirst = proplists:get_value(public_key_alg, Opts, - ?PREFERRED_PK_ALG), - CbSecond = other_cb(CbFirst), + FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts, + ?PREFERRED_PK_ALG)), + SecondAlg = other_alg(FirstAlg), AllowUserInt = proplists:get_value(allow_user_interaction, Opts, true), - Prefs = method_preference(CbFirst, CbSecond, AllowUserInt), + Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt), ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, userauth_preference = Prefs, userauth_methods = none, @@ -192,12 +196,12 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, ?TRUE -> case verify_sig(SessionId, User, "ssh-connection", Alg, KeyBlob, SigWLen, Opts) of - ok -> + true -> {authorized, User, ssh_transport:ssh_packet( #ssh_msg_userauth_success{}, Ssh)}; - {error, Reason} -> - {not_authorized, {User, {error, Reason}}, + false -> + {not_authorized, {User, {error, "Invalid signature"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ authentications="publickey,password", partial_success = false}, Ssh)} @@ -228,7 +232,6 @@ handle_userauth_info_request( PromptInfos = decode_keyboard_interactive_prompts(NumPrompts,Data), Resps = keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos), - %%?dbg(true, "keyboard_interactive_reply: resps=~n#~p ~n", [Resps]), RespBin = list_to_binary( lists:map(fun(S) -> <<?STRING(list_to_binary(S))>> end, Resps)), @@ -263,15 +266,15 @@ userauth_messages() -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -method_preference(Callback1, Callback2, true) -> - [{"publickey", ?MODULE, publickey_msg, [Callback1]}, - {"publickey", ?MODULE, publickey_msg,[Callback2]}, +method_preference(Alg1, Alg2, true) -> + [{"publickey", ?MODULE, publickey_msg, [Alg1]}, + {"publickey", ?MODULE, publickey_msg,[Alg2]}, {"password", ?MODULE, password_msg, []}, {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []} ]; -method_preference(Callback1, Callback2, false) -> - [{"publickey", ?MODULE, publickey_msg, [Callback1]}, - {"publickey", ?MODULE, publickey_msg,[Callback2]}, +method_preference(Alg1, Alg2, false) -> + [{"publickey", ?MODULE, publickey_msg, [Alg1]}, + {"publickey", ?MODULE, publickey_msg,[Alg2]}, {"password", ?MODULE, password_msg, []} ]. @@ -295,7 +298,6 @@ user_name(Opts) -> end. check_password(User, Password, Opts) -> - %%?dbg(true, " ~p ~p ~p ~n", [User, Password, Opts]), case proplists:get_value(pwdfun, Opts) of undefined -> Static = get_password_option(Opts, User), @@ -312,25 +314,22 @@ get_password_option(Opts, User) -> end. verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> - case ssh_file:lookup_user_key(User, Alg, Opts) of - {ok, OurKey} -> - {ok, Key} = ssh_file:decode_public_key_v2(KeyBlob, Alg), - case OurKey of - Key -> - NewSig = build_sig_data(SessionId, - User, Service, Alg, KeyBlob), - <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen, - <<?UINT32(AlgLen), _Alg:AlgLen/binary, - ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, - M = alg_to_module(Alg), - M:verify(OurKey, NewSig, Sig); - _ -> - {error, key_unacceptable} - end; - Error -> Error + {ok, Key} = decode_public_key_v2(KeyBlob, Alg), + KeyCb = proplists:get_value(key_cb, Opts, ssh_file), + + case KeyCb:is_auth_key(Key, User, Alg, Opts) of + true -> + PlainText = build_sig_data(SessionId, User, + Service, KeyBlob, Alg), + <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen, + <<?UINT32(AlgLen), _Alg:AlgLen/binary, + ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, + ssh_transport:verify(PlainText, sha, Sig, Key); + false -> + {error, key_unacceptable} end. -build_sig_data(SessionId, User, Service, Alg, KeyBlob) -> +build_sig_data(SessionId, User, Service, KeyBlob, Alg) -> Sig = [?binary(SessionId), ?SSH_MSG_USERAUTH_REQUEST, ?string(User), @@ -341,6 +340,11 @@ build_sig_data(SessionId, User, Service, Alg, KeyBlob) -> ?binary(KeyBlob)], list_to_binary(Sig). +algorithm(ssh_rsa) -> + "ssh-rsa"; +algorithm(ssh_dsa) -> + "ssh-dss". + decode_keyboard_interactive_prompts(NumPrompts, Data) -> Types = lists:append(lists:duplicate(NumPrompts, [string, boolean])), pairwise_tuplify(ssh_bits:decode(Data, Types)). @@ -412,12 +416,28 @@ userauth_pk_messages() -> binary]} % key blob ]. -alg_to_module("ssh-dss") -> - ssh_dsa; -alg_to_module("ssh-rsa") -> - ssh_rsa. - -other_cb(ssh_rsa) -> - ssh_dsa; -other_cb(ssh_dsa) -> - ssh_rsa. +other_alg("ssh-rsa") -> + "ssh-dss"; +other_alg("ssh-dss") -> + "ssh-rsa". +decode_public_key_v2(K_S, "ssh-rsa") -> + case ssh_bits:decode(K_S,[string,mpint,mpint]) of + ["ssh-rsa", E, N] -> + {ok, #'RSAPublicKey'{publicExponent = E, modulus = N}}; + _ -> + {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, {Y, #'Dss-Parms'{p = P, q = Q, g = G}}}; + _ -> + {error, bad_format} + end; +decode_public_key_v2(_, _) -> + {error, bad_format}. + +encode_public_key(#'RSAPrivateKey'{publicExponent = E, modulus = N}) -> + ssh_bits:encode(["ssh-rsa",E,N], [string,mpint,mpint]); +encode_public_key(#'DSAPrivateKey'{p = P, q = Q, g = G, y = Y}) -> + ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint]). diff --git a/lib/ssh/src/ssh_bits.erl b/lib/ssh/src/ssh_bits.erl index 3f0a06575c..5841f06d70 100644 --- a/lib/ssh/src/ssh_bits.erl +++ b/lib/ssh/src/ssh_bits.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% 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 @@ -105,16 +105,12 @@ bignum(X) -> install_messages(Codes) -> foreach(fun({Name, Code, Ts}) -> - %% ?dbg(true, "install msg: ~s = ~w ~w~n", -%% [Name,Code,Ts]), put({msg_name,Code}, {Name,Ts}), put({msg_code,Name}, {Code,Ts}) end, Codes). uninstall_messages(Codes) -> foreach(fun({Name, Code, _Ts}) -> - %% ?dbg(true, "uninstall msg: ~s = ~w ~w~n", -%% [Name,Code,_Ts]), erase({msg_name,Code}), erase({msg_code,Name}) end, Codes). diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index 7b9e9185bf..cb02d7b824 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% 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 @@ -722,8 +722,6 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, request_type = _Other, want_reply = WantReply}, Connection, ConnectionPid, _) -> - ?dbg(true, "ssh_msg ssh_msg_channel_request: Other=~p\n", - [_Other]), if WantReply == true -> FailMsg = channel_failure_msg(ChannelId), {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 00b30e5434..9079089d5d 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% 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 @@ -637,16 +637,18 @@ init_ssh(client = Role, Vsn, Version, Options, Socket) -> {ok, PeerAddr} = inet:peername(Socket), PeerName = proplists:get_value(host, Options), + KeyCb = proplists:get_value(key_cb, Options, ssh_file), #ssh{role = Role, c_vsn = Vsn, c_version = Version, - key_cb = proplists:get_value(key_cb, Options, ssh_file), + key_cb = KeyCb, io_cb = IOCb, userauth_quiet_mode = proplists:get_value(quiet_mode, Options, false), opts = Options, userauth_supported_methods = AuthMethods, - peer = {PeerName, PeerAddr} + peer = {PeerName, PeerAddr}, + available_host_keys = supported_host_keys(Role, KeyCb, Options) }; init_ssh(server = Role, Vsn, Version, Options, Socket) -> @@ -654,17 +656,48 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) -> AuthMethods = proplists:get_value(auth_methods, Options, ?SUPPORTED_AUTH_METHODS), {ok, PeerAddr} = inet:peername(Socket), - + KeyCb = proplists:get_value(key_cb, Options, ssh_file), + #ssh{role = Role, s_vsn = Vsn, s_version = Version, - key_cb = proplists:get_value(key_cb, Options, ssh_file), + key_cb = KeyCb, io_cb = proplists:get_value(io_cb, Options, ssh_io), opts = Options, userauth_supported_methods = AuthMethods, - peer = {undefined, PeerAddr} + peer = {undefined, PeerAddr}, + available_host_keys = supported_host_keys(Role, KeyCb, Options) }. +supported_host_keys(client, _, _) -> + ["ssh-rsa", "ssh-dss"]; +supported_host_keys(server, KeyCb, Options) -> + lists:foldl(fun(Type, Acc) -> + case available_host_key(KeyCb, Type, Options) of + {error, _} -> + Acc; + Alg -> + [Alg | Acc] + end + end, [], + %% Prefered alg last so no need to reverse + ["ssh-dss", "ssh-rsa"]). + +available_host_key(KeyCb, "ssh-dss"= Alg, Opts) -> + case KeyCb:host_key('ssh-dss', Opts) of + {ok, _} -> + Alg; + Other -> + Other + end; +available_host_key(KeyCb, "ssh-rsa" = Alg, Opts) -> + case KeyCb:host_key('ssh-rsa', Opts) of + {ok, _} -> + Alg; + Other -> + Other + end. + send_msg(Msg, #state{socket = Socket, transport_cb = Transport}) -> Transport:send(Socket, Msg). diff --git a/lib/ssh/src/ssh_dsa.erl b/lib/ssh/src/ssh_dsa.erl deleted file mode 100644 index cb2632beac..0000000000 --- a/lib/ssh/src/ssh_dsa.erl +++ /dev/null @@ -1,95 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. -%% -%% 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 online 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. -%% -%% %CopyrightEnd% -%% - -%% - -%%% Description: dsa public-key sign and verify - --module(ssh_dsa). - --export([verify/3]). --export([sign/2]). --export([alg_name/0]). - --include("ssh.hrl"). - -%% start() -> -%% crypto:start(). - -%% sign_file(File, Opts) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:private_host_dsa_key(user, Opts), -%% sign(Key, Bin). - -%% verify_file(File, Sig) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:public_host_key(user, dsa), -%% verify(Key, Bin, Sig). - -sign(_Private=#ssh_key { private={P,Q,G,X} },Mb) -> - K = ssh_bits:irandom(160) rem Q, - R = ssh_math:ipow(G, K, P) rem Q, - Ki = ssh_math:invert(K, Q), - <<M:160/big-unsigned-integer>> = crypto:sha(Mb), - S = (Ki * (M + X*R)) rem Q, - <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>. - - -%% the paramiko client sends a bad sig sometimes, -%% instead of crashing, we nicely return error, the -%% typcally manifests itself as Sb being 39 bytes -%% instead of 40. - -verify(Public, Mb, Sb) -> - case catch xverify(Public, Mb, Sb) of - {'EXIT', _Reason} -> - %store({Public, Mb, Sb, _Reason}), - {error, inconsistent_key}; - ok -> - %store({Public, Mb, Sb, ok}) - ok - end. - -%% store(Term) -> -%% {ok, Fd} = file:open("/tmp/dsa", [append]), -%% io:format(Fd, "~p~n~n~n", [Term]), -%% file:close(Fd). - - -xverify(_Public=#ssh_key { public={P,Q,G,Y} },Mb,Sb) -> - <<R0:160/big-unsigned-integer, S0:160/big-unsigned-integer>> = Sb, - ?ssh_assert(R0 >= 0 andalso R0 < Q andalso - S0 >= 0 andalso S0 < Q, out_of_range), - W = ssh_math:invert(S0,Q), - <<M0:160/big-unsigned-integer>> = crypto:sha(Mb), - U1 = (M0*W) rem Q, - U2 = (R0*W) rem Q, - T1 = ssh_math:ipow(G,U1,P), - T2 = ssh_math:ipow(Y,U2,P), - V = ((T1*T2) rem P) rem Q, - if V == R0 -> - ok; - true -> - {error, inconsistent_key} - end. - -alg_name() -> - "ssh-dss". diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index 12180f56bb..97ebf77e82 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% 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 @@ -23,80 +23,93 @@ -module(ssh_file). --include("ssh.hrl"). --include("PKCS-1.hrl"). --include("DSS.hrl"). - +-include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.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]). +-include("ssh.hrl"). --import(lists, [reverse/1, append/1]). +-export([host_key/2, + user_key/2, + is_host_key/4, + add_host_key/3, + is_auth_key/4]). --define(DBG_PATHS, true). -define(PERM_700, 8#700). -define(PERM_644, 8#644). + %% API -public_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). +%% Used by server +host_key(Algorithm, Opts) -> + File = file_name(system, file_base_name(Algorithm), Opts), + Password = proplists:get_value(password, Opts, ignore), + decode(File, Password). +is_auth_key(Key, User, Alg, Opts) -> + case lookup_user_key(Key, User, Alg, Opts) of + {ok, Key} -> + true; + _ -> + false + end. -%% in: "host" out: "host,1.2.3.4. -add_ip(Host) -> - case inet:getaddr(Host, inet) of - {ok, Addr} -> - case ssh_connection:encode_ip(Addr) of - false -> Host; - IPString -> Host ++ "," ++ IPString - end; - _ -> Host - end. -replace_localhost("localhost") -> - {ok, Hostname} = inet:gethostname(), - Hostname; -replace_localhost(Host) -> - Host. +%% Used by client +is_host_key(Key, PeerName, Algorithm, Opts) -> + case lookup_host_key(PeerName, Algorithm, Opts) of + {ok, Key} -> + true; + _ -> + false + end. + +user_key(Alg, Opts) -> + File = file_name(user, identity_key_filename(Alg), Opts), + Password = proplists:get_value(password, Opts, ignore), + decode(File, Password). + + +%% Internal functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +file_base_name('ssh-rsa') -> + "ssh_host_rsa_key"; +file_base_name('ssh-dss') -> + "ssh_host_dsa_key"; +file_base_name(_) -> + "ssh_host_key". + +decode(File, Password) -> + try + {ok, decode_ssh_file(read_ssh_file(File), Password)} + catch + throw:Reason -> + {error, Reason}; + error:Reason -> + {error, Reason} + end. + +read_ssh_file(File) -> + {ok, Bin} = file:read_file(File), + Bin. + +%% Public key +decode_ssh_file(SshBin, public_key) -> + public_key:ssh_decode(SshBin, public_key); + +%% Private Key +decode_ssh_file(Pem, Password) -> + case public_key:pem_decode(Pem) of + [{_, _, not_encrypted} = Entry] -> + public_key:pem_entry_decode(Entry); + [Entry] when Password =/= ignore -> + public_key:pem_entry_decode(Entry, Password); + _ -> + throw("No pass phrase provided for private key file") + end. + %% lookup_host_key %% return {ok, Key(s)} or {error, not_found} @@ -106,15 +119,6 @@ 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)), @@ -129,418 +133,161 @@ add_host_key(Host, Key, Opts) -> 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. +lookup_user_key(Key, User, Alg, Opts) -> + SshDir = ssh_dir({remoteuser,User}, Opts), + case lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys", Opts) of + {ok, Key} -> + {ok, Key}; + _ -> + lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys2", Opts) + end. + -identity_key_filename("ssh-dss") -> "id_dsa"; -identity_key_filename("ssh-rsa") -> "id_rsa". +%% +%% Utils +%% -private_identity_key(Alg, Opts) -> - Path = file_name(user, identity_key_filename(Alg), Opts), - read_private_key_v2(Path, Alg). - -public_identity_key(Alg, Opts) -> - Path = file_name(user, identity_key_filename(Alg) ++ ".pub", Opts), - read_public_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} +%% server use this to find individual keys for +%% an individual user when user tries to login +%% with publickey +ssh_dir({remoteuser, User}, Opts) -> + case proplists:get_value(user_dir_fun, Opts) of + undefined -> + case proplists:get_value(user_dir, Opts) of + undefined -> + default_user_dir(); + Dir -> + Dir 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} + FUN -> + FUN(User) 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} + +%% client use this to find client ssh keys +ssh_dir(user, Opts) -> + case proplists:get_value(user_dir, Opts, false) of + false -> default_user_dir(); + D -> D 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. +%% server use this to find server host keys +ssh_dir(system, Opts) -> + proplists:get_value(system_dir, Opts, "/etc/ssh"). -%% pem_type("ssh-dss") -> "DSA"; -%% pem_type("ssh-rsa") -> "RSA". - -read_private_key_v2(File, Type) -> - case file:read_file(File) of - {ok, PemBin} -> - case catch (public_key:pem_decode(PemBin)) of - [{_, Bin, not_encrypted}] -> - decode_private_key_v2(Bin, Type); - Error -> %% Note we do not handle password encrypted keys at the moment - {error, Error} - end; - {error, Reason} -> - {error, Reason} - end. -%% 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. +file_name(Type, Name, Opts) -> + FN = filename:join(ssh_dir(Type, Opts), Name), + FN. -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,""). +%% in: "host" out: "host,1.2.3.4. +add_ip(Host) -> + case inet:getaddr(Host, inet) of + {ok, Addr} -> + case ssh_connection:encode_ip(Addr) of + false -> Host; + IPString -> Host ++ "," ++ IPString + end; + _ -> Host + end. + +replace_localhost("localhost") -> + {ok, Hostname} = inet:gethostname(), + Hostname; +replace_localhost(Host) -> + Host. -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) +do_lookup_host_key(Host, Alg, Opts) -> + case file:open(file_name(user, "known_hosts", Opts), [read, binary]) of + {ok, Fd} -> + Res = lookup_host_key_fd(Fd, Host, Alg), + file:close(Fd), + {ok, Res}; + {error, enoent} -> {error, not_found}; + Error -> Error 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) -> +identity_key_filename("ssh-dss") -> "id_dsa"; +identity_key_filename("ssh-rsa") -> "id_rsa". + + +lookup_host_key_fd(Fd, Host, KeyType) -> case io:get_line(Fd, '') of eof -> {error, not_found}; Line -> - case string:tokens(Line, " ") of - [HostList, Alg, KeyData] -> -%% io:format(" ~p lookup_host_key_fd: HostList ~p Alg ~p KeyData ~p\n", -%% [Host, 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) + case public_key:ssh_decode(Line, known_hosts) of + [{Key, Attributes}] -> + handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType); + [] -> + lookup_host_key_fd(Fd, Host, KeyType) 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"]) +handle_host(Fd, Host, HostList, Key, KeyType) -> + Host1 = host_name(Host), + case lists:member(Host1, HostList) and key_match(Key, KeyType) of + true -> + Key; + false -> + lookup_host_key_fd(Fd, Host, KeyType) end. +host_name(Atom) when is_atom(Atom) -> + atom_to_list(Atom); +host_name(List) -> + List. -%% 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. +key_match(#'RSAPublicKey'{}, "ssh-rsa") -> + true; +key_match({_, #'Dss-Parms'{}}, "ssh-dss") -> + true; +key_match(_, _) -> + false. + +add_key_fd(Fd, Host,Key) -> + SshBin = public_key:ssh_encode([{Key, [{hostnames, [Host]}]}], known_hosts), + file:write(Fd, SshBin). -lookup_user_key_f(_User, [], _Alg, _F, _Opts) -> +lookup_user_key_f(_, _User, [], _Alg, _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(_User, nouserdir, _Alg, _F, _Opts) -> +lookup_user_key_f(_, _User, nouserdir, _Alg, _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(_User, Dir, Alg, F, _Opts) -> +lookup_user_key_f(Key, _User, Dir, _Alg, F, _Opts) -> FileName = filename:join(Dir, F), - case file:open(FileName, [read]) of + case file:open(FileName, [read, binary]) of {ok, Fd} -> - Res = lookup_user_key_fd(Fd, Alg), + Res = lookup_user_key_fd(Fd, Key), file:close(Fd), Res; {error, Reason} -> {error, {{openerr, Reason}, {file, FileName}}} end. -lookup_user_key_fd(Fd, Alg) -> +lookup_user_key_fd(Fd, Key) -> 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) + case public_key:ssh_decode(Line, auth_keys) of + [{AuthKey, _}] -> + case is_auth_key(Key, AuthKey) of + true -> + {ok, Key}; + false -> + lookup_user_key_fd(Fd, Key) + end; + [] -> + lookup_user_key_fd(Fd, Key) end end. +is_auth_key(Key, Key) -> + true; +is_auth_key(_,_) -> + false. -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 publickey -ssh_dir({remoteuser, User}, Opts) -> - case proplists:get_value(user_dir_fun, Opts) of - undefined -> - case proplists:get_value(user_dir, Opts) of - undefined -> - default_user_dir(); - Dir -> - Dir - end; - FUN -> - FUN(User) - end; - -%% client use this to find client ssh keys -ssh_dir(user, Opts) -> - case proplists:get_value(user_dir, Opts, false) of - false -> default_user_dir(); - D -> D - end; - -%% server use this to find server host keys -ssh_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. default_user_dir()-> {ok,[[Home|_]]} = init:get_argument(home), diff --git a/lib/ssh/src/ssh_rsa.erl b/lib/ssh/src/ssh_rsa.erl deleted file mode 100644 index 77c411b09f..0000000000 --- a/lib/ssh/src/ssh_rsa.erl +++ /dev/null @@ -1,298 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. -%% -%% 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 online 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. -%% -%% %CopyrightEnd% -%% - -%% - -%%% Description: rsa public-key sign and verify - --module(ssh_rsa). - --export([verify/3, sign/2]). --export([alg_name/0]). - --include("ssh.hrl"). --include("PKCS-1.hrl"). - - --define(MGF(Seed,Len), mgf1((Seed),(Len))). --define(HASH(X), crypto:sha((X))). --define(HLen, 20). - -%% start() -> -%% crypto:start(). - -%% sign_file(File) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:private_host_rsa_key(user), -%% sign(Key, Bin). - -%% verify_file(File, Sig) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:public_host_rsa_key(user), -%% verify(Key, Bin, Sig). - -sign(Private,Mb) -> - rsassa_pkcs1_v1_5_sign(Private,Mb). - -verify(Public,Mb,Sb) -> - rsassa_pkcs1_v1_5_verify(Public,Mb,Sb). - - - -%% Integer to octet string -i2osp(X, XLen) -> - ssh_bits:i2bin(X, XLen). - -%% Octet string to Integer -os2ip(X) -> - ssh_bits:bin2i(X). - -%% decrypt1, M = message representative -%% rsaep(#ssh_key { public={N,E}}, M) -> -%% ?ssh_assert(M >= 0 andalso M =< N-1, out_of_range), -%% ssh_math:ipow(M, E, N). - -%% encrypt1, C = cipher representative -%% rsadp(#ssh_key { public={N,_}, private={_,D}}, C) -> -%% ?ssh_assert(C >= 0 andalso C =< N-1, out_of_range), -%% ssh_math:ipow(C, D, N). - -%% sign1, M = message representative -rsasp1(#ssh_key { public={N,_}, private={_,D}}, M) -> - ?ssh_assert((M >= 0 andalso M =< N-1), out_of_range), - ssh_math:ipow(M, D, N). - -%% verify1, S =signature representative -rsavp1(#ssh_key { public={N,E}}, S) -> - ?ssh_assert(S >= 0 andalso S =< N-1, out_of_range), - ssh_math:ipow(S, E, N). - - -%% M messaage -%% rsaes_oaep_encrypt(Public, M) -> -%% rsaes_oaep_encrypt(Public, M, <<>>). - -%% rsaes_oaep_encrypt(Public=#ssh_key { public={N,_E}}, M, L) -> -%% ?ssh_assert(size(L) =< 16#ffffffffffffffff, label_to_long), -%% K = (ssh_bits:isize(N)+7) div 8, -%% MLen = size(M), -%% %% LLen = size(L), -%% ?ssh_assert(MLen =< K - 2*?HLen - 2, message_to_long), -%% LHash = ?HASH(L), -%% PS = ssh_bits:fill_bits(K - MLen - 2*?HLen - 2, 0), -%% DB = <<LHash/binary, PS/binary, 16#01, M/binary>>, -%% Seed = ssh_bits:random(?HLen), -%% DbMask = ?MGF(Seed, K - ?HLen - 1), -%% MaskedDB = ssh_bits:xor_bits(DB, DbMask), -%% SeedMask = ?MGF(MaskedDB, ?HLen), -%% MaskedSeed = ssh_bits:xor_bits(Seed, SeedMask), -%% EM = <<16#00, MaskedSeed/binary, MaskedDB/binary>>, -%% Mc = os2ip(EM), -%% C = rsaep(Public, Mc), -%% i2osp(C, K). - -%% rsaes_oaep_decrypt(Key, C) -> -%% rsaes_oaep_decrypt(Key, C, <<>>). - -%% rsaes_oaep_decrypt(Private=#ssh_key { public={N,_},private={_,_}},Cb,L) -> -%% ?ssh_assert(size(L) =< 16#ffffffffffffffff, label_to_long), -%% K = (ssh_bits:isize(N)+7) div 8, -%% ?ssh_assert(K == 2*?HLen + 2, decryption_error), -%% C = os2ip(Cb), -%% M = rsadp(Private, C), -%% EM = i2osp(M, K), -%% LHash = ?HASH(L), -%% MLen = K - ?HLen -1, -%% case EM of -%% <<16#00, MaskedSeed:?HLen/binary, MaskedDB:MLen>> -> -%% SeedMask = ?MGF(MaskedDB, ?HLen), -%% Seed = ssh_bits:xor_bits(MaskedSeed, SeedMask), -%% DbMask = ?MGF(Seed, K - ?HLen - 1), -%% DB = ssh_bits:xor_bits(MaskedDB, DbMask), -%% PSLen = K - MLen - 2*?HLen - 2, -%% case DB of -%% <<LHash:?HLen, _PS:PSLen/binary, 16#01, M/binary>> -> -%% M; -%% _ -> -%% exit(decryption_error) -%% end; -%% _ -> -%% exit(decryption_error) -%% end. - - -%% rsaes_pkcs1_v1_5_encrypt(Public=#ssh_key { public={N,_}}, M) -> -%% K = (ssh_bits:isize(N)+7) div 8, -%% MLen = size(M), -%% ?ssh_assert(MLen =< K - 11, message_to_long), -%% PS = ssh_bits:random(K - MLen - 3), -%% EM = <<16#00,16#02,PS/binary,16#00,M/binary>>, -%% Mc = os2ip(EM), -%% C = rsaep(Public, Mc), -%% i2osp(C, K). - - -%% rsaes_pkcs1_v1_5_decrypt(Private=#ssh_key { public={N,_},private={_,_}}, -%% Cb) -> -%% K = (ssh_bits:isize(N)+7) div 8, -%% CLen = size(Cb), -%% ?ssh_assert(CLen == K andalso K >= 11, decryption_error), -%% C = os2ip(Cb), -%% M = rsadp(Private, C), -%% EM = i2osp(M, K), -%% PSLen = K - CLen - 3, -%% case EM of -%% <<16#00, 16#02, _PS:PSLen/binary, 16#00, M>> -> -%% M; -%% _ -> -%% exit(decryption_error) -%% end. - -%% rsassa_pss_sign(Private=#ssh_key { public={N,_},private={_,_}},Mb) -> -%% ModBits = ssh_bits:isize(N), -%% K = (ModBits+7) div 8, -%% EM = emsa_pss_encode(Mb, ModBits - 1), -%% M = os2ip(EM), -%% S = rsasp1(Private, M), -%% i2osp(S, K). - -%% rsassa_pss_verify(Public=#ssh_key { public={N,_E}},Mb,Sb) -> -%% ModBits = ssh_bits:isize(N), -%% K = (ModBits+7) div 8, -%% ?ssh_assert(size(Sb) == K, invalid_signature), -%% S = os2ip(Sb), -%% M = rsavp1(Public,S), -%% EMLen = (ModBits-1+7) div 8, -%% EM = i2osp(M, EMLen), -%% emsa_pss_verify(Mb, EM, ModBits-1). - - -rsassa_pkcs1_v1_5_sign(Private=#ssh_key { public={N,_},private={_,_D}},Mb) -> - K = (ssh_bits:isize(N)+7) div 8, - EM = emsa_pkcs1_v1_5_encode(Mb, K), - M = os2ip(EM), - S = rsasp1(Private, M), - i2osp(S, K). - -rsassa_pkcs1_v1_5_verify(Public=#ssh_key { public={N,_E}}, Mb, Sb) -> - K = (ssh_bits:isize(N)+7) div 8, - ?ssh_assert(size(Sb) == K, invalid_signature), - S = os2ip(Sb), - M = rsavp1(Public, S), - EM = i2osp(M, K), - %?dbg(true, "verify K=~p S=~w ~n#M=~w~n#EM=~w~n", [K, S, M, EM]), - case emsa_pkcs1_v1_5_encode(Mb, K) of - EM -> ok; - _S -> - {error, invalid_signature} - end. - - -emsa_pkcs1_v1_5_encode(M, EMLen) -> - H = ?HASH(M), - %% Must use speical xxNull types here! - Alg = #'AlgorithmNull' { algorithm = ?'id-sha1', - parameters = <<>> }, - {ok,TCode} = 'PKCS-1':encode('DigestInfoNull', - #'DigestInfoNull'{ digestAlgorithm = Alg, - digest = H }), - T = list_to_binary(TCode), - TLen = size(T), - ?ssh_assert(EMLen >= TLen + 11, message_to_short), - PS = ssh_bits:fill_bits(EMLen - TLen - 3, 16#ff), - <<16#00, 16#01, PS/binary, 16#00, T/binary>>. - - -%% emsa_pss_encode(M, EMBits) -> -%% emsa_pss_encode(M, EMBits, 0). - -%% emsa_pss_encode(M, EMBits, SLen) -> -%% ?ssh_assert(size(M) =< 16#ffffffffffffffff, message_to_long), -%% EMLen = (EMBits + 7) div 8, -%% MHash = ?HASH(M), -%% ?ssh_assert(EMLen >= ?HLen + SLen + 2, encoding_error), -%% Salt = ssh_bits:random(SLen), -%% M1 = [16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00, -%% MHash, Salt], -%% H = ?HASH(M1), -%% PS = ssh_bits:fill_bits(EMLen-SLen-?HLen-2, 0), -%% DB = <<PS/binary, 16#01, Salt/binary>>, -%% DbMask = ?MGF(H, EMLen - ?HLen -1), -%% MaskedDB = ssh_bits:xor_bits(DB, DbMask), -%% ZLen = 8*EMLen - EMBits, -%% NZLen = (8 - (ZLen rem 8)) rem 8, -%% <<_:ZLen, NZ:NZLen, MaskedDB1/binary>> = MaskedDB, -%% MaskedDB2 = <<0:ZLen, NZ:NZLen, MaskedDB1/binary>>, -%% <<MaskedDB2/binary, H/binary, 16#BC>>. - - -%% emsa_pss_verify(M, EM, EMBits) -> -%% emsa_pss_verify(M, EM, EMBits, 0). - -%% emsa_pss_verify(M, EM, EMBits, SLen) -> -%% ?ssh_assert(size(M) =< 16#ffffffffffffffff, message_to_long), -%% EMLen = (EMBits + 7) div 8, -%% MHash = ?HASH(M), -%% ?ssh_assert(EMLen >= ?HLen + SLen + 2, inconsistent), -%% MaskLen = (EMLen - ?HLen - 1)-1, -%% ZLen = 8*EMLen - EMBits, -%% NZLen = (8 - (ZLen rem 8)) rem 8, -%% case EM of -%% <<0:ZLen,Nz:NZLen,MaskedDB1:MaskLen/binary, H:?HLen/binary, 16#BC>> -> -%% MaskedDB = <<0:ZLen,Nz:NZLen,MaskedDB1/binary>>, -%% DbMask = ?MGF(H, EMLen - ?HLen - 1), -%% DB = ssh_bits:xor_bits(MaskedDB, DbMask), -%% PSLen1 = (EMLen - SLen - ?HLen - 2) - 1, -%% PS = ssh_bits:fill_bits(PSLen1, 0), -%% case DB of -%% <<_:ZLen,0:NZLen,PS:PSLen1/binary,16#01,Salt:SLen/binary>> -> -%% M1 = [16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00, -%% MHash, Salt], -%% case ?HASH(M1) of -%% H -> ok; -%% _ -> exit(inconsistent) -%% end; -%% _ -> -%% exit(inconsistent) -%% end; -%% _ -> -%% exit(inconsistent) -%% end. - - - -%% Mask generating function MGF1 -%% mgf1(MGFSeed, MaskLen) -> -%% T = mgf1_loop(0, ((MaskLen + ?HLen -1) div ?HLen) - 1, MGFSeed, ""), -%% <<R:MaskLen/binary, _/binary>> = T, -%% R. - -%% mgf1_loop(Counter, N, _, T) when Counter > N -> -%% list_to_binary(T); -%% mgf1_loop(Counter, N, MGFSeed, T) -> -%% C = i2osp(Counter, 4), -%% mgf1_loop(Counter+1, N, MGFSeed, [T, ?HASH([MGFSeed, C])]). - - - - -alg_name() -> - "ssh-rsa". diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index da91817fd7..8cc414f83a 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% 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 @@ -231,8 +231,6 @@ handle_op(?SSH_FXP_REALPATH, ReqId, case Res of {ok, AbsPath} -> NewAbsPath = chroot_filename(AbsPath, State), - ?dbg(true, "handle_op ?SSH_FXP_REALPATH: RelPath=~p AbsPath=~p\n", - [RelPath, NewAbsPath]), XF = State#state.xf, Attr = #ssh_xfer_attr{type=directory}, ssh_xfer:xf_send_name(XF, ReqId, NewAbsPath, Attr), @@ -463,7 +461,6 @@ get_handle(Handles, BinHandle) -> read_dir(State0 = #state{file_handler = FileMod, max_files = MaxLength, file_state = FS0}, XF, ReqId, Handle, RelPath, {cache, Files}) -> AbsPath = relate_file_name(RelPath, State0), - ?dbg(true, "read_dir: AbsPath=~p\n", [AbsPath]), if length(Files) > MaxLength -> {ToSend, NewCache} = lists:split(MaxLength, Files), @@ -484,7 +481,6 @@ read_dir(State0 = #state{file_handler = FileMod, max_files = MaxLength, file_sta read_dir(State0 = #state{file_handler = FileMod, max_files = MaxLength, file_state = FS0}, XF, ReqId, Handle, RelPath, _Status) -> AbsPath = relate_file_name(RelPath, State0), - ?dbg(true, "read_dir: AbsPath=~p\n", [AbsPath]), {Res, FS1} = FileMod:list_dir(AbsPath, FS0), case Res of {ok, Files} when MaxLength == 0 orelse MaxLength > length(Files) -> @@ -516,7 +512,6 @@ get_attrs(_RelPath, [], _FileMod, FS, Acc) -> {lists:reverse(Acc), FS}; get_attrs(RelPath, [F | Rest], FileMod, FS0, Acc) -> Path = filename:absname(F, RelPath), - ?dbg(true, "get_attrs fun: F=~p\n", [F]), case FileMod:read_link_info(Path, FS0) of {{ok, Info}, FS1} -> Attrs = ssh_sftp:info_to_attr(Info), @@ -560,7 +555,6 @@ stat(ReqId, RelPath, State0=#state{file_handler=FileMod, file_state=FS0}, F) -> AbsPath = relate_file_name(RelPath, State0), XF = State0#state.xf, - ?dbg(false, "stat: AbsPath=~p\n", [AbsPath]), {Res, FS1} = FileMod:F(AbsPath, FS0), State1 = State0#state{file_state = FS1}, case Res of @@ -605,6 +599,8 @@ decode_4_access_flag(add_subdirectory) -> [read]; decode_4_access_flag(append_data) -> [append]; +decode_4_access_flag(write_attributes) -> + [write]; decode_4_access_flag(_) -> [read]. @@ -619,8 +615,7 @@ open(Vsn, ReqId, Data, State) when Vsn =< 3 -> <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(PFlags), _Attrs/binary>> = Data, Path = binary_to_list(BPath), - Flags = ssh_xfer:decode_open_flags(Vsn, PFlags) -- [creat, excl, trunc], - ?dbg(true, "open: Flags=~p\n", [Flags]), + Flags = ssh_xfer:decode_open_flags(Vsn, PFlags), do_open(ReqId, State, Path, Flags); open(Vsn, ReqId, Data, State) when Vsn >= 4 -> <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(Access), @@ -628,7 +623,6 @@ open(Vsn, ReqId, Data, State) when Vsn >= 4 -> Path = binary_to_list(BPath), FlagBits = ssh_xfer:decode_open_flags(Vsn, PFlags), AcessBits = ssh_xfer:decode_ace_mask(Access), - ?dbg(true, "open: Fl=~p\n", [FlagBits]), %% TODO: This is to make sure the Access flags are not ignored %% but this should be thought through better. This solution should %% be considered a hack in order to buy some time. At least @@ -638,15 +632,12 @@ open(Vsn, ReqId, Data, State) when Vsn >= 4 -> AcessFlags = decode_4_acess(AcessBits), Flags = lists:append(lists:umerge( [[decode_4_flags(FlagBits)] | AcessFlags])), - - ?dbg(true, "open: Flags=~p\n", [Flags]), - do_open(ReqId, State, Path, Flags). do_open(ReqId, State0, Path, Flags) -> #state{file_handler = FileMod, file_state = FS0, root = Root} = State0, XF = State0#state.xf, - F = [raw, binary | Flags], + F = [binary | Flags], %% case FileMod:is_dir(Path) of %% This is version 6 we still have 5 %% true -> %% ssh_xfer:xf_send_status(State#state.xf, ReqId, @@ -895,14 +886,11 @@ set_stat(Attr, Path, State0 = #state{file_handler=FileMod, file_state=FS0}) -> {DecodedAttr, _Rest} = ssh_xfer:decode_ATTR((State0#state.xf)#ssh_xfer.vsn, Attr), - ?dbg(true, "set_stat DecodedAttr=~p\n", [DecodedAttr]), Info = ssh_sftp:attr_to_info(DecodedAttr), {Res1, FS1} = FileMod:read_link_info(Path, FS0), case Res1 of {ok, OldInfo} -> NewInfo = set_file_info(Info, OldInfo), - ?dbg(true, "set_stat Path=~p\nInfo=~p\nOldInfo=~p\nNewInfo=~p\n", - [Path, Info, OldInfo, NewInfo]), {Res2, FS2} = FileMod:write_file_info(Path, NewInfo, FS1), State1 = State0#state{file_state = FS2}, {Res2, State1}; diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index de3e29e2f1..6140c87f6e 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% 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 @@ -23,10 +23,11 @@ -module(ssh_transport). --include("ssh_transport.hrl"). +-include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/inet.hrl"). +-include("ssh_transport.hrl"). -include("ssh.hrl"). --include_lib("kernel/include/inet.hrl"). -export([connect/5, accept/4]). -export([versions/2, hello_version_msg/1]). @@ -38,17 +39,8 @@ handle_kex_dh_gex_group/2, handle_kex_dh_gex_reply/2, handle_new_keys/2, handle_kex_dh_gex_request/2, handle_kexdh_reply/2, - unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1]). - -%% debug flagso --define(DBG_ALG, true). --define(DBG_KEX, true). --define(DBG_CRYPTO, false). --define(DBG_PACKET, false). --define(DBG_MESSAGE, true). --define(DBG_BIN_MESSAGE, true). --define(DBG_MAC, false). --define(DBG_ZLIB, true). + unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1, + sign/3, verify/4]). versions(client, Options)-> Vsn = proplists:get_value(vsn, Options, ?DEFAULT_CLIENT_VERSION), @@ -212,24 +204,24 @@ key_exchange_init_msg(Ssh0) -> {SshPacket, Ssh} = ssh_packet(Msg, Ssh0), {Msg, SshPacket, Ssh}. -kex_init(#ssh{role = Role, opts = Opts}) -> +kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) -> Random = ssh_bits:random(16), Compression = case proplists:get_value(compression, Opts, none) of zlib -> ["zlib", "none"]; none -> ["none", "zlib"] end, - kexinit_messsage(Role, Random, Compression). + kexinit_messsage(Role, Random, Compression, HostKeyAlgs). key_init(client, Ssh, Value) -> Ssh#ssh{c_keyinit = Value}; key_init(server, Ssh, Value) -> Ssh#ssh{s_keyinit = Value}. -kexinit_messsage(client, Random, Compression) -> +kexinit_messsage(client, Random, Compression, HostKeyAlgs) -> #ssh_msg_kexinit{ cookie = Random, kex_algorithms = ["diffie-hellman-group1-sha1"], - server_host_key_algorithms = ["ssh-rsa", "ssh-dss"], + server_host_key_algorithms = HostKeyAlgs, encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"], encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"], mac_algorithms_client_to_server = ["hmac-sha1"], @@ -240,11 +232,11 @@ kexinit_messsage(client, Random, Compression) -> languages_server_to_client = [] }; -kexinit_messsage(server, Random, Compression) -> +kexinit_messsage(server, Random, Compression, HostKeyAlgs) -> #ssh_msg_kexinit{ cookie = Random, kex_algorithms = ["diffie-hellman-group1-sha1"], - server_host_key_algorithms = ["ssh-dss"], + server_host_key_algorithms = HostKeyAlgs, encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"], encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"], mac_algorithms_client_to_server = ["hmac-sha1"], @@ -300,7 +292,6 @@ install_messages('diffie-hellman-group-exchange-sha1') -> key_exchange_first_msg('diffie-hellman-group1-sha1', Ssh0) -> {G, P} = dh_group1(), {Private, Public} = dh_gen_key(G, P, 1024), - %%?dbg(?DBG_KEX, "public: ~p~n", [Public]), {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_init{e = Public}, Ssh0), {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}}}; @@ -320,7 +311,6 @@ key_exchange_first_msg('diffie-hellman-group-exchange-sha1', Ssh0) -> handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> {G, P} = dh_group1(), {Private, Public} = dh_gen_key(G, P, 1024), - %%?dbg(?DBG_KEX, "public: ~p~n", [Public]), K = ssh_math:ipow(E, Private, P), {Key, K_S} = get_host_key(Ssh0), H = kex_h(Ssh0, K_S, E, Public, K), @@ -329,8 +319,7 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> f = Public, h_sig = H_SIG }, Ssh0), - %%?dbg(?DBG_KEX, "shared_secret: ~s ~n", [fmt_binary(K, 16, 4)]), - %%?dbg(?DBG_KEX, "hash: ~s ~n", [fmt_binary(H, 16, 4)]), + {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}, shared_secret = K, exchanged_hash = H, @@ -338,7 +327,6 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> handle_kex_dh_gex_group(#ssh_msg_kex_dh_gex_group{p = P, g = G}, Ssh0) -> {Private, Public} = dh_gen_key(G,P,1024), - %%?dbg(?DBG_KEX, "public: ~p ~n", [Public]), {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kex_dh_gex_init{e = Public}, Ssh0), {ok, SshPacket, @@ -362,8 +350,7 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, f = F, #ssh{keyex_key = {{Private, Public}, {_G, P}}} = Ssh0) -> K = ssh_math:ipow(F, Private, P), H = kex_h(Ssh0, HostKey, Public, F, K), - %%?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(Ssh0, HostKey, H, H_SIG) of ok -> {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), @@ -396,8 +383,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey, Ssh0) -> K = ssh_math:ipow(F, Private, P), H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, Public, F, K), - %%?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(Ssh0, HostKey, H, H_SIG) of ok -> {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), @@ -423,83 +409,68 @@ sid(#ssh{session_id = Id}, _) -> %% 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}) + + case Mod:host_key(ALG#alg.hkey, Opts) of + {ok, #'RSAPrivateKey'{modulus = N, publicExponent = E} = Key} -> + {Key, + ssh_bits:encode(["ssh-rsa",E,N],[string,mpint,mpint])}; + {ok, #'DSAPrivateKey'{y = Y, p = P, q = Q, g = G} = Key} -> + {Key, ssh_bits:encode(["ssh-dss",P,Q,G,Y], + [string,mpint,mpint,mpint,mpint])}; + Result -> + exit({error, {Result, unsupported_key_type}}) end. -sign_host_key(Ssh, Private, H) -> - ALG = Ssh#ssh.algorithms, - Module = case ALG#alg.hkey of - 'ssh-rsa' -> - ssh_rsa; - 'ssh-dss' -> - ssh_dsa - 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. +sign_host_key(_Ssh, #'RSAPrivateKey'{} = Private, H) -> + Hash = sha, %% Option ?! + Signature = sign(H, Hash, Private), + ssh_bits:encode(["ssh-rsa", Signature],[string, binary]); +sign_host_key(_Ssh, #'DSAPrivateKey'{} = Private, H) -> + Hash = sha, %% Option ?! + RawSignature = sign(H, Hash, Private), + ssh_bits:encode(["ssh-dss", RawSignature],[string, binary]). verify_host_key(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; + verify_host_key_rsa(SSH, K_S, H, H_SIG); '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; + verify_host_key_dss(SSH, K_S, H, H_SIG); _ -> {error, bad_host_key_algorithm} end. +verify_host_key_rsa(SSH, K_S, H, H_SIG) -> + 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 = #'RSAPublicKey'{publicExponent = E, modulus = N}, + case verify(H, sha, SIG, Public) of + false -> + {error, bad_signature}; + true -> + known_host_key(SSH, Public, "ssh-rsa") + end; + _ -> + {error, bad_format} + end. + +verify_host_key_dss(SSH, K_S, H, H_SIG) -> + 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 = {Y, #'Dss-Parms'{p = P, q = Q, g = G}}, + case verify(H, sha, SIG, Public) of + false -> + {error, bad_signature}; + true -> + known_host_key(SSH, Public, "ssh-dss") + end; + _ -> + {error, bad_host_key_format} + end. + accepted_host(Ssh, PeerName, Opts) -> case proplists:get_value(silently_accept_hosts, Opts, false) of true -> @@ -511,14 +482,10 @@ accepted_host(Ssh, PeerName, Opts) -> known_host_key(#ssh{opts = Opts, key_cb = Mod, peer = Peer} = Ssh, Public, Alg) -> PeerName = peer_name(Peer), - case Mod:lookup_host_key(PeerName, Alg, Opts) of - {ok, Public} -> + case Mod:is_host_key(Public, PeerName, Alg, Opts) of + true -> ok; - {ok, BadPublic} -> - error_logger:format("known_host_key: Public ~p BadPublic ~p\n", - [Public, BadPublic]), - {error, bad_public_key}; - {error, not_found} -> + false -> case accepted_host(Ssh, PeerName, Opts) of yes -> Mod:add_host_key(PeerName, Public, Opts); @@ -621,7 +588,6 @@ install_alg(SSH) -> 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, @@ -638,7 +604,6 @@ alg_setup(SSH) -> }. 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), @@ -648,7 +613,6 @@ alg_init(SSH0) -> 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), @@ -669,19 +633,15 @@ select(CL, SL) -> [] -> undefined; [ALG|_] -> ALG end, - %%?dbg(?DBG_ALG, "ALG: select: ~p ~p = ~p~n", [CL, SL, C]), C. ssh_packet(#ssh_msg_kexinit{} = Msg, Ssh0) -> BinMsg = ssh_bits:encode(Msg), Ssh = key_init(Ssh0#ssh.role, Ssh0, BinMsg), - %%?dbg(?DBG_MESSAGE, "SEND_MSG: ~p~n", [Msg]), pack(BinMsg, Ssh); ssh_packet(Msg, Ssh) -> BinMsg = ssh_bits:encode(Msg), - %%?dbg(?DBG_MESSAGE, "SEND_MSG: ~p~n", [Msg]), - %%?dbg(?DBG_BIN_MESSAGE, "Encoded: ~p~n", [BinMsg]), pack(BinMsg, Ssh). pack(Data0, #ssh{encrypt_block_size = BlockSize, @@ -732,17 +692,20 @@ msg_data(PacketData) -> _:PaddingLen/binary>> = PacketData, Data. +sign(SigData, Hash, #'DSAPrivateKey'{} = Key) -> + DerSignature = public_key:sign(SigData, Hash, Key), + #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature), + <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>; +sign(SigData, Hash, Key) -> + public_key:sign(SigData, Hash, Key). -%% Send a disconnect message -%% terminate(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}. +verify(PlainText, Hash, Sig, {_, #'Dss-Parms'{}} = Key) -> + <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> = Sig, + Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}), + public_key:verify(PlainText, Hash, Signature, Key); +verify(PlainText, Hash, Sig, Key) -> + public_key:verify(PlainText, Hash, Sig, Key). - %% public key algorithms %% %% ssh-dss REQUIRED sign Raw DSS Key @@ -761,9 +724,6 @@ msg_data(PacketData) -> %% %% - - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Encryption %% @@ -833,19 +793,13 @@ encrypt(#ssh{encrypt = none} = Ssh, Data) -> encrypt(#ssh{encrypt = '3des-cbc', encrypt_keys = {K1,K2,K3}, encrypt_ctx = IV0} = Ssh, Data) -> - %%?dbg(?DBG_CRYPTO, "encrypt: IV=~p K1=~p, K2=~p, K3=~p ~n", - %% [IV0,K1,K2,K3]), Enc = crypto:des3_cbc_encrypt(K1,K2,K3,IV0,Data), - %%?dbg(?DBG_CRYPTO, "encrypt: ~p -> ~p ~n", [Data, Enc]), IV = crypto:des_cbc_ivec(Enc), {Ssh#ssh{encrypt_ctx = IV}, Enc}; encrypt(#ssh{encrypt = 'aes128-cbc', encrypt_keys = K, encrypt_ctx = IV0} = Ssh, Data) -> - %%?dbg(?DBG_CRYPTO, "encrypt: IV=~p K=~p ~n", - %% [IV0,K]), Enc = crypto:aes_cbc_128_encrypt(K,IV0,Data), - %%?dbg(?DBG_CRYPTO, "encrypt: ~p -> ~p ~n", [Data, Enc]), IV = crypto:aes_cbc_ivec(Enc), {Ssh#ssh{encrypt_ctx = IV}, Enc}. @@ -893,18 +847,12 @@ decrypt(#ssh{decrypt = none} = Ssh, Data) -> decrypt(#ssh{decrypt = '3des-cbc', decrypt_keys = Keys, decrypt_ctx = IV0} = Ssh, Data) -> {K1, K2, K3} = Keys, - %%?dbg(?DBG_CRYPTO, "decrypt: IV=~p K1=~p, K2=~p, K3=~p ~n", - %%[IV0,K1,K2,K3]), Dec = crypto:des3_cbc_decrypt(K1,K2,K3,IV0,Data), - %%?dbg(?DBG_CRYPTO, "decrypt: ~p -> ~p ~n", [Data, Dec]), IV = crypto:des_cbc_ivec(Data), {Ssh#ssh{decrypt_ctx = IV}, Dec}; decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key, decrypt_ctx = IV0} = Ssh, Data) -> - %%?dbg(?DBG_CRYPTO, "decrypt: IV=~p Key=~p ~n", - %% [IV0,Key]), Dec = crypto:aes_cbc_128_decrypt(Key,IV0,Data), - %%?dbg(?DBG_CRYPTO, "decrypt: ~p -> ~p ~n", [Data, Dec]), IV = crypto:aes_cbc_ivec(Data), {Ssh#ssh{decrypt_ctx = IV}, Dec}. @@ -936,7 +884,6 @@ compress(#ssh{compress = none} = Ssh, Data) -> {Ssh, Data}; compress(#ssh{compress = zlib, compress_ctx = Context} = Ssh, Data) -> Compressed = zlib:deflate(Context, Data, sync), - %%?dbg(?DBG_ZLIB, "deflate: ~p -> ~p ~n", [Data, Compressed]), {Ssh, list_to_binary(Compressed)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -960,7 +907,6 @@ decompress(#ssh{decompress = none} = Ssh, Data) -> {Ssh, Data}; decompress(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh, Data) -> Decompressed = zlib:inflate(Context, Data), - %%?dbg(?DBG_ZLIB, "inflate: ~p -> ~p ~n", [Data, Decompressed]), {Ssh, list_to_binary(Decompressed)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1039,7 +985,6 @@ hash(SSH, Char, N, HASH) -> K1 = HASH([K, H, Char, SessionID]), Sz = N div 8, <<Key:Sz/binary, _/binary>> = hash(K, H, K1, N-128, HASH), - %%?dbg(?DBG_KEX, "Key ~s: ~s ~n", [Char, fmt_binary(Key, 16, 4)]), Key. hash(_K, _H, Ki, N, _HASH) when N =< 0 -> @@ -1055,6 +1000,7 @@ kex_h(SSH, K_S, E, F, K) -> [string,string,binary,binary,binary, mpint,mpint,mpint]), crypto:sha(L). + kex_h(SSH, K_S, Min, NBits, Max, Prime, Gen, E, F, K) -> L = if Min==-1; Max==-1 -> @@ -1075,7 +1021,7 @@ kex_h(SSH, K_S, Min, NBits, Max, Prime, Gen, E, F, K) -> Prime, Gen, E,F,K], Ts) end, crypto:sha(L). - + mac_key_size('hmac-sha1') -> 20*8; mac_key_size('hmac-sha1-96') -> 20*8; mac_key_size('hmac-md5') -> 16*8; @@ -1105,9 +1051,6 @@ dh_gen_key(G, P, _Bits) -> Public = ssh_math:ipow(G, Private, P), {Private,Public}. -%% trim(Str) -> -%% lists:reverse(trim_head(lists:reverse(trim_head(Str)))). - trim_tail(Str) -> lists:reverse(trim_head(lists:reverse(Str))). @@ -1116,48 +1059,3 @@ trim_head([$\t|Cs]) -> trim_head(Cs); trim_head([$\n|Cs]) -> trim_head(Cs); trim_head([$\r|Cs]) -> trim_head(Cs); trim_head(Cs) -> Cs. - -%% Retrieve session_id from ssh, needed by public-key auth -%get_session_id(SSH) -> -% {ok, SessionID} = call(SSH, get_session_id), - -%% DEBUG utils -%% Format integers and binaries as hex blocks -%% -%% -ifdef(debug). -%% fmt_binary(B, BlockSize, GroupSize) -> -%% fmt_block(fmt_bin(B), BlockSize, GroupSize). - -%% fmt_block(Bin, BlockSize, GroupSize) -> -%% fmt_block(Bin, BlockSize, 0, GroupSize). - - -%% fmt_block(Bin, 0, _I, _G) -> -%% binary_to_list(Bin); -%% fmt_block(Bin, Sz, G, G) when G =/= 0 -> -%% ["~n#" | fmt_block(Bin, Sz, 0, G)]; -%% fmt_block(Bin, Sz, I, G) -> -%% case Bin of -%% <<Block:Sz/binary, Tail/binary>> -> -%% if Tail == <<>> -> -%% [binary_to_list(Block)]; -%% true -> -%% [binary_to_list(Block), " " | fmt_block(Tail, Sz, I+1, G)] -%% end; -%% <<>> -> -%% []; -%% _ -> -%% [binary_to_list(Bin)] -%% end. - -%% %% Format integer or binary as hex -%% fmt_bin(X) when integer(X) -> -%% list_to_binary(io_lib:format("~p", [X])); -%% fmt_bin(X) when binary(X) -> -%% Sz = size(X)*8, -%% <<Y:Sz/unsigned-big>> = X, -%% %%Fmt = "~"++integer_to_list(size(X)*2)++"~p", -%% list_to_binary(io_lib:format("~p", [Y])). - -%% -endif. - diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index c9631a73b1..d5b6dd03d1 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% 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 @@ -298,8 +298,6 @@ xf_send_names(#ssh_xfer{cm = CM, channel = Channel, vsn = Vsn}, Size = 1 + 4 + 4 + Len, ToSend = [<<?UINT32(Size), ?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(Count)>>, Data], - %%?dbg(true, "xf_send_names: Size=~p size(ToSend)=~p\n", - %% [Size, size(list_to_binary(ToSend))]), ssh_connection:send(CM, Channel, ToSend). xf_send_status(XF, ReqId, ErrorCode) -> @@ -353,7 +351,6 @@ xf_reply(_XF, <<?SSH_FXP_DATA, ?UINT32(ReqID), {data, ReqID, Data}; xf_reply(XF, <<?SSH_FXP_NAME, ?UINT32(ReqID), ?UINT32(Count), AData/binary>>) -> - %%?dbg(true, "xf_reply ?SSH_FXP_NAME: AData=~p\n", [AData]), {name, ReqID, decode_names(XF#ssh_xfer.vsn, Count, AData)}; xf_reply(XF, <<?SSH_FXP_ATTRS, ?UINT32(ReqID), AData/binary>>) -> @@ -579,7 +576,6 @@ encode_attr_flags(Vsn, Flags) -> end, Flags). encode_file_type(Type) -> - %%?dbg(true, "encode_file_type(~p)\n", [Type]), case Type of regular -> ?SSH_FILEXFER_TYPE_REGULAR; directory -> ?SSH_FILEXFER_TYPE_DIRECTORY; @@ -660,15 +656,12 @@ encode_ATTR(Vsn, A) -> {extended, A#ssh_xfer_attr.extensions}], 0, []), Type = encode_file_type(A#ssh_xfer_attr.type), - %%?dbg(true, "encode_ATTR: Vsn=~p A=~p As=~p Flags=~p Type=~p", - %% [Vsn, A, As, Flags, Type]), Result = list_to_binary([?uint32(Flags), if Vsn >= 5 -> ?byte(Type); true -> (<<>>) end, As]), - %% ?dbg(true, " Result=~p\n", [Result]), Result. @@ -722,7 +715,6 @@ encode_As(_Vsn, [], Flags, Acc) -> decode_ATTR(Vsn, <<?UINT32(Flags), Tail/binary>>) -> - %%?dbg(true, "decode_ATTR: Vsn=~p Flags=~p Tail=~p\n", [Vsn, Flags, Tail]), {Type,Tail2} = if Vsn =< 3 -> {?SSH_FILEXFER_TYPE_UNKNOWN, Tail}; @@ -751,7 +743,6 @@ decode_ATTR(Vsn, <<?UINT32(Flags), Tail/binary>>) -> Tail2). decode_As(Vsn, [{AName, AField}|As], R, Flags, Tail) -> - %%?dbg(false, "decode_As: Vsn=~p AName=~p AField=~p Flags=~p Tail=~p\n", [Vsn, AName, AField, Flags, Tail]), case AName of size when ?is_set(?SSH_FILEXFER_ATTR_SIZE, Flags) -> <<?UINT64(X), Tail2/binary>> = Tail, @@ -762,7 +753,6 @@ decode_As(Vsn, [{AName, AField}|As], R, Flags, Tail) -> ownergroup when ?is_set(?SSH_FILEXFER_ATTR_OWNERGROUP, Flags),Vsn>=5 -> <<?UINT32(Len), Bin:Len/binary, Tail2/binary>> = Tail, X = binary_to_list(Bin), - %%?dbg(true, "ownergroup X=~p\n", [X]), decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); permissions when ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS,Flags),Vsn>=5-> @@ -824,13 +814,11 @@ decode_names(Vsn, I, <<?UINT32(Len), FileName:Len/binary, ?UINT32(LLen), _LongName:LLen/binary, Tail/binary>>) when Vsn =< 3 -> Name = binary_to_list(FileName), - %%?dbg(true, "decode_names: ~p\n", [Name]), {A, Tail2} = decode_ATTR(Vsn, Tail), [{Name, A} | decode_names(Vsn, I-1, Tail2)]; decode_names(Vsn, I, <<?UINT32(Len), FileName:Len/binary, Tail/binary>>) when Vsn >= 4 -> Name = binary_to_list(FileName), - %%?dbg(true, "decode_names: ~p\n", [Name]), {A, Tail2} = decode_ATTR(Vsn, Tail), [{Name, A} | decode_names(Vsn, I-1, Tail2)]. @@ -839,8 +827,6 @@ encode_names(Vsn, NamesAndAttrs) -> encode_name(Vsn, {Name,Attr}, Len) when Vsn =< 3 -> NLen = length(Name), - %%?dbg(true, "encode_name: Vsn=~p Name=~p Attr=~p\n", - %% [Vsn, Name, Attr]), EncAttr = encode_ATTR(Vsn, Attr), ALen = size(EncAttr), NewLen = Len + NLen*2 + 4 + 4 + ALen, |