diff options
Diffstat (limited to 'lib/ssh')
47 files changed, 1875 insertions, 681 deletions
diff --git a/lib/ssh/doc/html/SSH_protocols.png b/lib/ssh/doc/html/SSH_protocols.png Binary files differnew file mode 100644 index 0000000000..145c96c4cd --- /dev/null +++ b/lib/ssh/doc/html/SSH_protocols.png diff --git a/lib/ssh/doc/man6/.gitignore b/lib/ssh/doc/man6/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/ssh/doc/man6/.gitignore diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile index da99c4ea0f..0e79d9979f 100644 --- a/lib/ssh/doc/src/Makefile +++ b/lib/ssh/doc/src/Makefile @@ -37,21 +37,30 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml -XML_REF3_FILES = \ - ssh.xml \ +XML_REF3_FILES = ssh.xml \ ssh_channel.xml \ - ssh_connection.xml\ + ssh_connection.xml \ + ssh_client_key_api.xml \ + ssh_server_key_api.xml \ ssh_sftp.xml \ ssh_sftpd.xml \ -XML_PART_FILES = part_notes.xml -XML_CHAPTER_FILES = notes.xml +XML_REF6_FILES = ssh_app.xml + +XML_PART_FILES = part_notes.xml \ + usersguide.xml +XML_CHAPTER_FILES = notes.xml \ + introduction.xml \ + ssh_protocol.xml \ + using_ssh.xml BOOK_FILES = book.xml -XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \ +XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES)\ $(XML_PART_FILES) $(XML_CHAPTER_FILES) +IMAGE_FILES = SSH_protocols.png + # ---------------------------------------------------- HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ @@ -62,10 +71,12 @@ EXTRA_FILES = \ $(DEFAULT_GIF_FILES) \ $(DEFAULT_HTML_FILES) \ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \ + $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) +MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6) HTML_REF_MAN_FILE = $(HTMLDIR)/index.html @@ -80,16 +91,19 @@ DVIPS_FLAGS += # ---------------------------------------------------- # Targets # ---------------------------------------------------- -$(HTMLDIR)/%.gif: %.gif +$(HTMLDIR)/%.png: %.png $(INSTALL_DATA) $< $@ docs: pdf html man $(TOP_PDF_FILE): $(XML_FILES) +images: $(IMAGE_FILES:%=$(HTMLDIR)/%) + pdf: $(TOP_PDF_FILE) -html: $(HTML_REF_MAN_FILE) +html: images $(HTML_REF_MAN_FILE) + clean clean_docs: rm -rf $(HTMLDIR)/* @@ -97,7 +111,7 @@ clean clean_docs: rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ -man: $(MAN3_FILES) +man: $(MAN3_FILES) $(MAN6_FILES) debug opt: @@ -117,5 +131,8 @@ release_docs_spec: docs $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" + release_spec: diff --git a/lib/ssh/doc/src/SSH_protocols.png b/lib/ssh/doc/src/SSH_protocols.png Binary files differnew file mode 100644 index 0000000000..145c96c4cd --- /dev/null +++ b/lib/ssh/doc/src/SSH_protocols.png diff --git a/lib/ssh/doc/src/book.xml b/lib/ssh/doc/src/book.xml index fcec1d6f70..3c2375f96d 100644 --- a/lib/ssh/doc/src/book.xml +++ b/lib/ssh/doc/src/book.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,6 +34,9 @@ <preamble> <contents level="2"></contents> </preamble> + <parts lift="yes"> + <xi:include href="usersguide.xml"/> + </parts> <applications> <xi:include href="ref_man.xml"/> </applications> diff --git a/lib/ssh/doc/src/fascicules.xml b/lib/ssh/doc/src/fascicules.xml index 43090b4aed..069d9002e0 100644 --- a/lib/ssh/doc/src/fascicules.xml +++ b/lib/ssh/doc/src/fascicules.xml @@ -1,7 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE fascicules SYSTEM "fascicules.dtd"> <fascicules> + <fascicule file="usersguide" href="usersguide_frame.html" entry="no"> + User's Guide + </fascicule> <fascicule file="ref_man" href="ref_man_frame.html" entry="yes"> Reference Manual </fascicule> diff --git a/lib/ssh/doc/src/introduction.xml b/lib/ssh/doc/src/introduction.xml new file mode 100644 index 0000000000..aac8de0f76 --- /dev/null +++ b/lib/ssh/doc/src/introduction.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + 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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Introduction</title> + <prepared>OTP team</prepared> + <file>introduction.xml</file> + </header> + + <section> + <title>Purpose</title> + + <p>Secure Shell (SSH) is a protocol for secure remote login and + other secure network services over an insecure network. SSH + provides a single, full-duplex, byte-oriented connection between + client and server. The protocol also provides privacy, integrity, + server authentication and man-in-the-middle protection.</p> + + <p>The Erlang SSH application is an implementation of the SSH + protocol in Erlang which offers API functions to write customized + SSH clients and servers as well as making the Erlang shell + available via SSH. Also included in the SSH application are an + SFTP (SSH File Transfer Protocol) client <seealso + marker="ssh_sftp">ssh_sftp</seealso> and server <seealso + marker="ssh_sftp">ssh_sftpd</seealso>.</p> + </section> + + <section> + <title>Prerequisites</title> + <p>It is assumed that the reader is familiar with the concepts of <seealso marker="doc/design_principals:users_guide">OTP</seealso> + and has a basic understanding of <url href="http://en.wikipedia.org/wiki/Public-key_cryptography">public keys</url>.</p> + </section> + +</chapter> diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 36010da07a..e58d4e2c36 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -29,6 +29,50 @@ <file>notes.xml</file> </header> +<section><title>Ssh 2.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SSH quiet mode</p> + <p> + A new option to ssh:connect/3,4, quiet_mode. If true, the + client will not print out anything on authorization.</p> + <p> + Own Id: OTP-10429 Aux Id: kunagi-273 [184] </p> + </item> + <item> + <p> + Restrict which key algorithms to use</p> + <p> + A new option to ssh:connect/3,4 is introduced, + public_key_algs, where you can restrict which key + algorithms to use and in which order to try them.</p> + <p> + Own Id: OTP-10498 Aux Id: kunagi-289 [200] </p> + </item> + <item> + <p> + Confidentiality of client password</p> + <p> + Unsets clients password after authentication.</p> + <p> + Own Id: OTP-10511 Aux Id: kunagi-292 [203] </p> + </item> + <item> + <p> + Fixed user interaction for SSH</p> + <p> + It's now available to accept hosts and input password</p> + <p> + Own Id: OTP-10513 Aux Id: kunagi-293 [204] </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 2.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -736,7 +780,7 @@ <list> <item> <p> - Ssh timeouts will now behave as expected e.i. defaults to + Ssh timeouts will now behave as expected i.e. defaults to infinity only the user of the ssh application can know of a reasonable timeout value for their application.</p> <p> @@ -789,7 +833,7 @@ caused the ssh daemon to close the connections to all currently logged in users if one user logged out. Another problem related to the supervision tree caused the closing - down of clients to leak processes e.i. all processes was + down of clients to leak processes i.e. all processes was not shutdown correctly.</p> <p> Own Id: OTP-7676</p> @@ -881,7 +925,7 @@ slightly changes the options to the API function ssh:daemon/[1,2,3] deprecating all no longer documented options. Note that the new API enforces the "logical way" - of using the old API e.i. making the subsystem process + of using the old API i.e. making the subsystem process part of the ssh applications supervisor tree, so missuses of the old API are not compatible with the new API.</p> <p> diff --git a/lib/ssh/doc/src/ref_man.xml b/lib/ssh/doc/src/ref_man.xml index 9ab56b28ec..88203b5034 100644 --- a/lib/ssh/doc/src/ref_man.xml +++ b/lib/ssh/doc/src/ref_man.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,19 +22,22 @@ </legalnotice> <title>SSH Reference Manual</title> - <prepared>Jakob Cederlund</prepared> - <docno></docno> + <prepared>OTP</prepared> <date>2007-10-06</date> <rev>%VSN%</rev> - <file>application.sgml</file> + <file>ref_man.xml</file> </header> <description> <p>The SSH application is an erlang implementation of the - secure shell protocol.</p> + secure shell protocol (SSH) as defined by RFC 4250 - 4254</p> + </description> + <xi:include href="ssh_app.xml"/> <xi:include href="ssh.xml"/> <xi:include href="ssh_channel.xml"/> <xi:include href="ssh_connection.xml"/> + <xi:include href="ssh_client_key_api.xml"/> + <xi:include href="ssh_server_key_api.xml"/> <xi:include href="ssh_sftp.xml"/> <xi:include href="ssh_sftpd.xml"/> </application> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index aac4b462a2..a0afb5056e 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -22,13 +22,7 @@ </legalnotice> <title>ssh</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible>Håkan Mattsson</responsible> - <docno></docno> - <approved>Håkan Mattsson</approved> - <checked></checked> <date>2007-10-06</date> - <rev>PA1</rev> </header> <module>ssh</module> <modulesummary>Main API of the SSH application</modulesummary> @@ -40,80 +34,85 @@ <title>SSH</title> <list type="bulleted"> - <item>ssh requires the crypto and public_key applications.</item> - <item>Supported SSH-version is 2.0 </item> - <item>Currently supports only a minimum of mac and encryption algorithms i.e. - hmac-sha1, and aes128-cb and 3des-cbc.</item> + <item>SSH requires the crypto and public_key applications.</item> + <item>Supported SSH version is 2.0 </item> + <item>Supported MAC algorithms: hmac-sha1</item> + <item>Supported encryption algorithms: aes128-cb and 3des-cbc</item> </list> </section> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p>Type definitions that are used more than once in - this module:</p> + this module and/or abstractions to indicate the intended use of the data + type:</p> <p><c>boolean() = true | false </c></p> - <p><c>string() = list of ASCII characters</c></p> + <p><c>string() = [byte()]</c></p> <p><c>ssh_daemon_ref() - opaque to the user returned by ssh:daemon/[1,2,3]</c></p> <p><c>ssh_connection_ref() - opaque to the user returned by ssh:connect/3</c></p> <p><c>ip_address() - {N1,N2,N3,N4} % IPv4 | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6</c></p> - <p><c>subsystem_spec() = {subsystem_name(), {channel_callback(), channel_init_args()}} </c></p> + <p><c>subsystem_spec() = {subsystem_name(), + {channel_callback(), channel_init_args()}} </c></p> <p><c>subsystem_name() = string() </c></p> <p><c>channel_callback() = atom() - Name of the erlang module implementing the subsystem using the ssh_channel behavior see</c> <seealso marker="ssh_channel">ssh_channel(3)</seealso></p> <p><c>channel_init_args() = list()</c></p> </section> - + <funcs> <func> <name>close(ConnectionRef) -> ok </name> - <fsummary>Closes a ssh connection</fsummary> + <fsummary>Closes an SSH connection</fsummary> <type> <v>ConnectionRef = ssh_connection_ref()</v> </type> - <desc><p>Closes a ssh connection.</p> + <desc><p>Closes an SSH connection.</p> </desc> </func> <func> <name>connect(Host, Port, Options) -> </name> - <name>connect(Host, Port, Options, Timeout) -> {ok, ssh_connection_ref()} - | {error, Reason}</name> + <name>connect(Host, Port, Options, Timeout) -> {ok, + ssh_connection_ref()} | {error, Reason}</name> <fsummary>Connect to an ssh server.</fsummary> <type> <v>Host = string()</v> <v>Port = integer()</v> - <d>The default is <c><![CDATA[22]]></c>, the registered port for SSH.</d> + <d>The default is <c><![CDATA[22]]></c>, the assigned well known port + number for SSH.</d> <v>Options = [{Option, Value}]</v> <v>Timeout = infinity | integer(milliseconds)</v> </type> <desc> - <p>Connects to an SSH server. No channel is started this is done + <p>Connects to an SSH server. No channel is started. This is done by calling ssh_connect:session_channel/2.</p> <p>Options are:</p> <taglist> <tag><c><![CDATA[{user_dir, string()}]]></c></tag> <item> - <p>Sets the user directory e.i. the directory containing + <p>Sets the user directory i.e. the directory containing ssh configuration files for the user such as - <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, id_dsa]]></c> and - <c><![CDATA[authorized_key]]></c>. Defaults to the directory normally - referred to as <c><![CDATA[~/.ssh]]></c> </p> + <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, + id_dsa]]></c> and + <c><![CDATA[authorized_key]]></c>. Defaults to the + directory normally referred to as + <c><![CDATA[~/.ssh]]></c> </p> </item> <tag><c><![CDATA[{dsa_pass_phrase, string()}]]></c></tag> <item> - <p>If the user dsa key is protected by a pass phrase it can be + <p>If the user dsa key is protected by a passphrase it can be supplied with this option. </p> </item> <tag><c><![CDATA[{rsa_pass_phrase, string()}]]></c></tag> <item> - <p>If the user rsa key is protected by a pass phrase it can be + <p>If the user rsa key is protected by a passphrase it can be supplied with this option. </p> </item> @@ -135,25 +134,26 @@ password. Do note that it may not always be desirable to use those options from a security point of view.</p> </item> - <tag><c><![CDATA[{public_key_alg, ssh_rsa | ssh_dsa}]]></c></tag> + <tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag> <item> <p>Sets the preferred public key algorithm to use for user - authentication. If the the preferred algorithm fails of + authentication. If the the preferred algorithm fails for some reason, the other algorithm is tried. The default is to try <c><![CDATA[ssh_rsa]]></c> first.</p> </item> <tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag> <item> - <p>List of public key algorithms to try to use, ssh_rsa and ssh_dsa available. - Will override <c><![CDATA[{public_key_alg, ssh_rsa | ssh_dsa}]]></c></p> + <p>List of public key algorithms to try to use, 'ssh-rsa' and 'ssh-dss' available. + Will override <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p> </item> <tag><c><![CDATA[{connect_timeout, timeout()}]]></c></tag> <item> - <p>Sets a timeout on the transport layer connection. Defaults to infinity.</p> + <p>Sets a timeout on the transport layer + connection. Defaults to <c>infinity</c>.</p> </item> - <tag><c><![CDATA[{user, String}]]></c></tag> + <tag><c><![CDATA[{user, string()}]]></c></tag> <item> - <p>Provide a user name. If this option is not given, ssh + <p>Provides a user name. If this option is not given, ssh reads from the environment (<c><![CDATA[LOGNAME]]></c> or <c><![CDATA[USER]]></c> on unix, <c><![CDATA[USERNAME]]></c> on Windows).</p> @@ -165,23 +165,11 @@ password if the password authentication method is attempted.</p> </item> - <tag><c><![CDATA[{user_auth, Fun/3}]]></c></tag> - <item> - <p>Provide a fun for password authentication. The fun - will be called as <c><![CDATA[fun(User, Password, Opts)]]></c> and - should return <c><![CDATA[true]]></c> or <c><![CDATA[false]]></c>.</p> - </item> - <tag><c><![CDATA[{key_cb, atom() = KeyCallbackModule}]]></c></tag> + <tag><c><![CDATA[{key_cb, atom()}]]></c></tag> <item> - <p>Provide a special call-back module for key handling. - The call-back module should be modeled after the - <c><![CDATA[ssh_file]]></c> module. The functions that must - be exported are: - <c><![CDATA[private_host_rsa_key/2]]></c>, - <c><![CDATA[private_host_dsa_key/2]]></c>, - <c><![CDATA[lookup_host_key/3]]></c> and - <c><![CDATA[add_host_key/3]]></c>. This is considered - somewhat experimental and will be better documented later on.</p> + <p>Module implementing the behaviour <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>. + Can be used to customize the handling of public keys. + </p> </item> <tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag> <item> @@ -189,20 +177,24 @@ </item> <tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag> <item> - <p>Allow an existing file-descriptor to be used + <p>Allow an existing file descriptor to be used (simply passed on to the transport protocol).</p></item> - <tag><c><![CDATA[{ip_v6_disabled, boolean()}]]></c></tag> + <tag><c><![CDATA[{ipv6_disabled, boolean()}]]></c></tag> <item> - <p>Determines if SSH shall use IPv6 or not.</p></item> - <tag><c><![CDATA[{idle_time, timeout()}]]></c></tag> + <p>Determines if SSH shall use IPv6 or not.</p> + </item> + <tag><c><![CDATA[{rekey_limit, integer()}]]></c></tag> <item> - <p>Sets a timeout on connection when no channels are active, default is infinity</p></item> + <p>Provide, in bytes, when rekeying should be initiated, + defaults to one time each GB and one time per hour.</p> + </item> </taglist> </desc> </func> <func> - <name>connection_info(ConnectionRef, [Option]) ->[{Option, Value}] </name> + <name>connection_info(ConnectionRef, [Option]) ->[{Option, + Value}] </name> <fsummary> Retrieves information about a connection. </fsummary> <type> <v>Option = client_version | server_version | peer</v> @@ -217,7 +209,8 @@ <func> <name>daemon(Port) -> </name> <name>daemon(Port, Options) -> </name> - <name>daemon(HostAddress, Port, Options) -> ssh_daemon_ref()</name> + <name>daemon(HostAddress, Port, Options) -> {ok, + ssh_daemon_ref()} | {error, atom()}</name> <fsummary>Starts a server listening for SSH connections on the given port.</fsummary> <type> @@ -228,30 +221,32 @@ <v>Value = term()</v> </type> <desc> - <p>Starts a server listening for SSH connections on the given port.</p> - - <p>Options are:</p> + <p>Starts a server listening for SSH connections on the given + port.</p> + <p>Options are:</p> <taglist> <tag><c><![CDATA[{subsystems, [subsystem_spec()]]]></c></tag> <item> - Provides specifications for handling of subsystems. The - "sftp" subsystem-spec can be retrieved by calling - ssh_sftpd:subsystem_spec/1. If the subsystems option in not present - the value of <c>[ssh_sftpd:subsystem_spec([])]</c> will be used. - It is of course possible to set the option to the empty list - if you do not want the daemon to run any subsystems at all. + Provides specifications for handling of subsystems. The + "sftp" subsystem spec can be retrieved by calling + ssh_sftpd:subsystem_spec/1. If the subsystems option in + not present the value of + <c>[ssh_sftpd:subsystem_spec([])]</c> will be used. It is + of course possible to set the option to the empty list if + you do not want the daemon to run any subsystems at all. </item> - <tag><c><![CDATA[{shell, {Module, Function, Args} | fun(string() = User) - > pid() | - fun(string() = User, ip_address() = PeerAddr) -> pid()}]]></c></tag> + <tag><c><![CDATA[{shell, {Module, Function, Args} | + fun(string() = User) - > pid() | fun(string() = User, + ip_address() = PeerAddr) -> pid()}]]></c></tag> <item> - Defines the read-eval-print loop used when a shell is requested - by the client. Example use the - erlang shell: <c><![CDATA[{shell, start, []}]]></c> which is - the default behavior. + Defines the read-eval-print loop used when a shell is + requested by the client. Default is to use the erlang shell: + <c><![CDATA[{shell, start, []}]]></c> </item> - <tag><c><![CDATA[{ssh_cli,{channel_callback(), channel_init_args()}}]]></c></tag> + <tag><c><![CDATA[{ssh_cli,{channel_callback(), + channel_init_args()}}]]></c></tag> <item> - Provide your own cli implementation, e.i. a channel callback + Provides your own cli implementation, i.e. a channel callback module that implements a shell and command execution. Note that you may customize the shell read-eval-print loop using the option <c>shell</c> which is much less work than implementing @@ -259,27 +254,30 @@ </item> <tag><c><![CDATA[{user_dir, String}]]></c></tag> <item> - <p>Sets the user directory e.i. the directory containing + <p>Sets the user directory i.e. the directory containing ssh configuration files for the user such as - <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, id_dsa]]></c> and - <c><![CDATA[authorized_key]]></c>. Defaults to the directory normally - referred to as <c><![CDATA[~/.ssh]]></c> </p> + <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, + id_dsa]]></c> and + <c><![CDATA[authorized_key]]></c>. Defaults to the + directory normally referred to as + <c><![CDATA[~/.ssh]]></c> </p> </item> <tag><c><![CDATA[{system_dir, string()}]]></c></tag> <item> - <p>Sets the system directory, containing the host files - that identifies the host for ssh. The default is - <c><![CDATA[/etc/ssh]]></c>, note that SSH normally - requires the host files there to be readable only by - root.</p> + <p>Sets the system directory, containing the host key files + that identifies the host keys for ssh. The default is + <c><![CDATA[/etc/ssh]]></c>, note that for security reasons + this directory is normally only accessible by the root user.</p> </item> <tag><c><![CDATA[{auth_methods, string()}]]></c></tag> <item> - <p>Comma separated string that determines which authentication methodes that the server - should support and in what order they will be tried. Defaults to + <p>Comma separated string that determines which + authentication methodes that the server should support and + in what order they will be tried. Defaults to <c><![CDATA["publickey,keyboard-interactive,password"]]></c></p> </item> - <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag> + <tag><c><![CDATA[{user_passwords, [{string() = User, + string() = Password}]}]]></c></tag> <item> <p>Provide passwords for password authentication.They will be used when someone tries to connect to the server and @@ -293,13 +291,19 @@ user. From a security perspective this option makes the server very vulnerable.</p> </item> - <tag><c><![CDATA[{pwdfun, fun/2}]]></c></tag> + <tag><c><![CDATA[{pwdfun, fun(User::string(), password::string() -> boolean()}]]></c></tag> <item> <p>Provide a function for password validation. This is called with user and password as strings, and should return <c><![CDATA[true]]></c> if the password is valid and <c><![CDATA[false]]></c> otherwise.</p> </item> + <tag><c><![CDATA[{key_cb, atom()}]]></c></tag> + <item> + <p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>. + Can be used to customize the handling of public keys. + </p> + </item> <tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag> <item> <p>Allow an existing file-descriptor to be used @@ -308,6 +312,18 @@ <item> <p>Determines if SSH shall use IPv6 or not (only used when HostAddress is set to any).</p></item> + <tag><c><![CDATA[{failfun, fun()}]]></c></tag> + <item> + <p>Provide a fun() to implement your own logging when a user fails to authenticate.</p> + </item> + <tag><c><![CDATA[{connectfun, fun()}]]></c></tag> + <item> + <p>Provide a fun() to implement your own logging when a user authenticates to the server.</p> + </item> + <tag><c><![CDATA[{disconnectfun, fun()}]]></c></tag> + <item> + <p>Provide a fun() to implement your own logging when a user disconnects from the server.</p> + </item> </taglist> </desc> </func> @@ -323,9 +339,9 @@ <v> Options - see ssh:connect/3</v> </type> <desc> - <p>Starts an interactive shell to an SSH server on the + <p>Starts an interactive shell via an SSH server on the given <c>Host</c>. The function waits for user input, - and will not return until the remote shell is ended (e.g. on + and will not return until the remote shell is ended (i.e. exit from the shell). </p> </desc> @@ -334,25 +350,24 @@ <func> <name>start() -> </name> <name>start(Type) -> ok | {error, Reason}</name> - <fsummary>Starts the Ssh application. </fsummary> + <fsummary>Starts the SSH application. </fsummary> <type> <v>Type = permanent | transient | temporary</v> <v>Reason = term() </v> </type> <desc> - <p>Starts the Ssh application. Default type - is temporary. See also - <seealso marker="kernel:application">application(3)</seealso> - Requires that the crypto application has been started. + <p>Utility function that starts crypto, public_key and the SSH + application. Defult type is temporary. + See also <seealso marker="kernel:application">application(3)</seealso> </p> </desc> </func> <func> <name>stop() -> ok </name> - <fsummary>Stops the Ssh application.</fsummary> + <fsummary>Stops the SSH application.</fsummary> <desc> - <p>Stops the Ssh application. See also + <p>Stops the SSH application. See also <seealso marker="kernel:application">application(3)</seealso></p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml new file mode 100644 index 0000000000..c01f44936a --- /dev/null +++ b/lib/ssh/doc/src/ssh_app.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE appref SYSTEM "appref.dtd"> + +<appref> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>SSH</title> + <file>ssh_app.xml</file> + </header> + <app>SSH</app> + <appsummary>The ssh application implements the SSH (Secure Shell) protocol and + provides an SFTP (SSH File Transfer Protocol) client and server. </appsummary> + + <section> + <title>DEPENDENCIES</title> + <p>The ssh application uses the Erlang applications public_key and + crypto to handle public keys and encryption, hence these + applications needs to be loaded for the ssh application to work. In + an embedded environment that means they need to be started with + application:start/[1,2] before the ssh application is started. + </p> + </section> + + <section> + <title>CONFIGURATION</title> + + <p>The ssh application does not currently have an application + specific configuration file as described in application(3), + however it will by default use the following configuration files + from openssh: known_hosts, authorized_keys, authorized_keys2, + id_dsa and id_rsa, ssh_host_dsa_key and ssh_host_rsa_key. By + default Erlang SSH will look for id_dsa, id_rsa, known_hosts + and authorized_keys in ~/.ssh, and the host key files in /etc/ssh + . These locations may be changed by the options user_dir and + system_dir. Public key handling may also be customized by + providing a callback module implementing the behaviors + <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> and + <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>. + </p> + + <section> + <title>PUBLIC KEYS</title> + <p> + id_dsa and id_rsa are the users private key files, note that + the public key is part of the private key so the ssh + application will not use the id_<*>.pub files. These are + for the users convenience when he/she needs to convey their + public key. + </p> + </section> + + <section> + <title>KNOW HOSTS</title> + <p>The known_hosts file contains a list of approved servers and + their public keys. Once a server is listed, it can be verified + without user interaction. + </p> + </section> + + <section> + <title>AUTHORIZED KEYS</title> + <p>The authorized key file keeps track of the user's authorized + public keys. The most common use of this file is to let users + log in without entering their password which is supported by the + Erlang SSH daemon. + </p> + </section> + + <section> + <title>HOST KEYS</title> + <p>Currently rsa and dsa host keys are supported and are + expected to be found in files named ssh_host_rsa_key and + ssh_host_dsa_key. + </p> + </section> + </section> + + <section> + <title>SEE ALSO</title> + <p>application(3)</p> + </section> + +</appref> diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml index c2b7aa94a5..f0083ae8d1 100644 --- a/lib/ssh/doc/src/ssh_channel.xml +++ b/lib/ssh/doc/src/ssh_channel.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> <year>2009</year> - <year>2009</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -22,31 +22,35 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>ssh_channel</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible></responsible> - <docno></docno> - <approved></approved> - <checked></checked> - <date></date> - <rev></rev> </header> <module>ssh_channel</module> - <modulesummary>Generic Ssh Channel Behavior + <modulesummary>-behaviour(ssh_channel). </modulesummary> <description> - <p>Ssh services are implemented as channels that are multiplexed - over an ssh connection and communicates via the ssh connection - protocol. This module provides a callback API that takes care of - generic channel aspects such as flow control and close messages - and lets the callback functions take care of the service specific - parts. + <p>SSH services (clients and servers) are implemented as channels + that are multiplexed over an SSH connection and communicates via + the <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH + Connection Protocol </url>. This module provides a callback API + that takes care of generic channel aspects such as flow control + and close messages and lets the callback functions take care of + the service (application) specific parts. This behavior also ensures + that the channel process honors the principal of an OTP-process so + that it can be part of a supervisor tree. This is a requirement of + channel processes implementing a subsystem that will be added to + the SSH applications supervisor tree. </p> + + <note> When implementing a SSH subsystem use the + <c>-behaviour(ssh_subsystem).</c> instead of <c>-behaviour(ssh_channel).</c> + as the only relevant callback functions for subsystems are + init/1, handle_ssh_msg/2, handle_msg/2 and terminate/2, so the ssh_subsystem + behaviour is limited version of the ssh_channel behaviour. + </note> </description> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p>Type definitions that are used more than once in this module and/or abstractions to indicate the intended use of the data @@ -56,10 +60,10 @@ <p><c>string() = list of ASCII characters</c></p> <p><c>timeout() = infinity | integer() - in milliseconds.</c></p> <p><c>ssh_connection_ref() - opaque to the user returned by - ssh:connect/3 or sent to a ssh channel process</c></p> + ssh:connect/3 or sent to an SSH channel process</c></p> <p><c>ssh_channel_id() = integer() </c></p> <p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are - currently valid values see RFC 4254 section 5.2.</c></p> + currently valid values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</c></p> </section> <funcs> @@ -74,13 +78,15 @@ <v>Timeout = timeout() </v> <v>Reply = term() </v> <v>Reason = closed | timeout </v> + </type> <desc> <p>Makes a synchronous call to the channel process by sending a message and waiting until a reply arrives or a timeout - occurs. The channel will call - <c>CallbackModule:handle_call/3</c> to handle the message. - If the channel process does not exist <c>{error, closed}</c> is returned. + occurs. The channel will call <seealso mark = + "#Module:handle_call-3">Module:handle_call/3</seealso> + to handle the message. If the channel process does not exist + <c>{error, closed}</c> is returned. </p> </desc> </func> @@ -98,26 +104,27 @@ <p>Sends an asynchronous message to the channel process and returns ok immediately, ignoring if the destination node or channel process does not exist. The channel will call - <c>CallbackModule:handle_cast/2</c> to handle the message. + <seealso mark = "#Module:handle_cast-3">Module:handle_cast/2</seealso> + to handle the message. </p> </desc> </func> <func> <name>enter_loop(State) -> _ </name> - <fsummary> Makes an existing process into a ssh_channel process. </fsummary> + <fsummary> Makes an existing process an ssh_channel process. </fsummary> <type> - <v> State = term() - as returned by ssh_channel:init/1</v> + <v> State = term() - as returned by <seealso mark = "#init-1">ssh_channel:init/1</seealso></v> </type> <desc> - <p> Makes an existing process into a <c>ssh_channel</c> + <p> Makes an existing process an <c>ssh_channel</c> process. Does not return, instead the calling process will - enter the <c>ssh_channel</c> process receive loop and become a + enter the <c>ssh_channel</c> process receive loop and become an <c>ssh_channel process.</c> The process must have been started using one of the start functions in proc_lib, see <seealso marker="stdlib:proc_lib">proc_lib(3)</seealso>. The user is responsible for any initialization of the process - and needs to call ssh_channel:init/1. + and needs to call <seealso mark = "#init-1">ssh_channel:init/1</seealso> </p> </desc> </func> @@ -126,7 +133,10 @@ <name>init(Options) -> {ok, State} | {ok, State, Timeout} | {stop, Reason} </name> <fsummary> Initiates a ssh_channel process.</fsummary> <type> - <v> Options = [{Option, Value}]</v> + <v>Options = [{Option, Value}]</v> + <v>State = term()</v> + <v>Timeout = timeout() </v> + <v>Reason = term() </v> </type> <desc> <p> @@ -134,24 +144,27 @@ </p> <taglist> <tag><c><![CDATA[{channel_cb, atom()}]]></c></tag> - <item>The module that implements the channel behavior.</item> + <item>The module that implements the channel behaviour.</item> <tag><c><![CDATA[{init_args(), list()}]]></c></tag> - <item> The list of arguments to the callback modules + <item> The list of arguments to the callback module's init function.</item> <tag><c><![CDATA[{cm, connection_ref()}]]></c></tag> - <item> Reference to the ssh connection.</item> + <item> Reference to the ssh connection as returned by <seealso + marker="ssh#connect-3">ssh:connect/3</seealso></item> <tag><c><![CDATA[{channel_id, channel_id()}]]></c></tag> <item> Id of the ssh channel.</item> </taglist> - <note><p>This function is normally not called by the user, it is - only needed if for some reason the channel process needs - to be started with help of <c>proc_lib</c> instead calling - <c>ssh_channel:start/4</c> or <c>ssh_channel:start_link/4</c> </p> + <note><p>This function is normally not called by the + user. The user only needs to call if for some reason the + channel process needs to be started with help of + <c>proc_lib</c> instead of calling + <c>ssh_channel:start/4</c> or + <c>ssh_channel:start_link/4</c> </p> </note> </desc> </func> @@ -167,12 +180,12 @@ <p>This function can be used by a channel to explicitly send a reply to a client that called <c>call/[2,3]</c> when the reply cannot be defined in the return value of - <c>CallbackModule:handle_call/3</c>.</p> + <seealso marker ="#Module:handle_call-3">Module:handle_call/3</seealso>.</p> <p><c>Client</c> must be the <c>From</c> argument provided to the callback function <c>handle_call/3</c>. <c>Reply</c> is an arbitrary term, - which will be given back to the client as the return value of - <c>ssh_channel:call/[2,3].</c></p> + which will be given back to the client as the return value of + <seealso marker="#call-2">ssh_channel:call/[2,3].</seealso>></p> </desc> </func> @@ -180,11 +193,12 @@ <name>start(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> </name> <name>start_link(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> {ok, ChannelRef} | {error, Reason}</name> - <fsummary> Starts a processes that handles a ssh channel. </fsummary> + <fsummary> Starts a processes that handles a SSH channel. </fsummary> <type> <v>SshConnection = ssh_connection_ref()</v> <v>ChannelId = ssh_channel_id() </v> - <d> As returned by ssh_connection:session_channel/[2,4]</d> + <d> As returned by cannot be defined in the return value of + <seealso marker ="ssh_connection#session_channel/2">ssh_connection:session_channel/[2,4]</seealso></d> <v>ChannelCb = atom()</v> <d> The name of the module implementing the service specific parts of the channel.</d> @@ -193,10 +207,10 @@ <v>ChannelRef = pid()</v> </type> <desc> - <p>Starts a processes that handles a ssh channel. Will be - called internally by the ssh daemon or explicitly by the ssh - client implementations. A channel process traps exit signals - by default. + <p>Starts a processes that handles an SSH channel. It will be + called internally by the SSH daemon or explicitly by the SSH + client implementations. The behavior will set the + <c>trap_exit</c> flag to true. </p> </desc> </func> @@ -204,72 +218,61 @@ </funcs> <section> - <title>CALLBACK FUNCTIONS</title> - - <p>The functions init/1, terminate/2, handle_ssh_msg/2 and - handle_msg/2 are the functions that are required to provide the - implementation for a server side channel, such as a ssh subsystem - channel that can be plugged into the erlang ssh daemon see - <seealso marker="ssh">ssh:daemon/[2, 3]</seealso>. The - handle_call/3, handle_cast/2 code_change/3 and enter_loop/1 - functions are only relevant when implementing a client side - channel.</p> - </section> - - <section> <marker id="cb_timeouts"></marker> <title> CALLBACK TIMEOUTS</title> - <p> If an integer timeout value is provided in a return value of - one of the callback functions, a timeout will occur unless a - message is received within <c>Timeout</c> milliseconds. A timeout - is represented by the atom <c>timeout</c> which should be handled - by the <seealso marker="#handle_msg">handle_msg/2</seealso> - callback function. The atom infinity can be used to wait - indefinitely, this is the default value. </p> + + <p>The timeout values that may be returned by the callback functions + has the same semantics as in a <seealso marker="stdlib#gen_server">gen_server</seealso> + If the timeout occurs <seealso marker="#handle_msg">handle_msg/2</seealso> + will be called as <c>handle_msg(timeout, State). </c></p> </section> <funcs> <func> - <name>CallbackModule:code_change(OldVsn, State, Extra) -> {ok, + <name>Module:code_change(OldVsn, State, Extra) -> {ok, NewState}</name> <fsummary> Converts process state when code is changed.</fsummary> <type> - <v> Converts process state when code is changed.</v> + <v>OldVsn = term()</v> + <d>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and + in the case of a downgrade, <c>OldVsn</c> is + <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c> + attribute(s) of the old version of the callback module + <c>Module</c>. If no such attribute is defined, the version is + the checksum of the BEAM file.</d> + <v>State = term()</v> + <d>The internal state of the channel.</d> + <v>Extra = term()</v> + <d>Passed as-is from the <c>{advanced,Extra}</c> + part of the update instruction.</d> </type> <desc> - <p>This function is called by a client side channel when it - should update its internal state during a release - upgrade/downgrade, i.e. when the instruction - <c>{update,Module,Change,...}</c> where - <c>Change={advanced,Extra}</c> is given in the <c>appup</c> - file. See <seealso - marker="doc/design_principles:release_handling#instr">OTP - Design Principles</seealso> for more information. Any new - connection will benefit from a server side upgrade but - already started connections on the server side will not be - affected. - </p> + <p> Converts process state when code is changed.</p> + + <p>This function is called by a client side channel when it + should update its internal state during a release + upgrade/downgrade, i.e. when the instruction + <c>{update,Module,Change,...}</c> where + <c>Change={advanced,Extra}</c> is given in the <c>appup</c> + file. See <seealso marker="doc/design_principles:release_handling#instr">OTP + Design Principles</seealso> for more information. + </p> - <note><p>If there are long lived ssh connections and more - than one upgrade in a short time this may cause the old - connections to fail as only two versions of the code may - be loaded simultaneously.</p></note> + <note><p>Soft upgrade according to the OTP release concept + is not straight forward for the server side, as subsystem + channel processes are spawned by the SSH application and + hence added to its supervisor tree. It could be possible to + upgrade the subsystem channels, when upgrading the user + application, if the callback functions can handle two + versions of the state, but this function can not be used in + the normal way.</p> + </note> - <p>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and - in the case of a downgrade, <c>OldVsn</c> is - <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c> - attribute(s) of the old version of the callback module - <c>Module</c>. If no such attribute is defined, the version - is the checksum of the BEAM file.</p> - <p><c>State</c> is the internal state of the channel.</p> - <p><c>Extra</c> is passed as-is from the <c>{advanced,Extra}</c> - part of the update instruction.</p> - <p>The function should return the updated internal state.</p> </desc> </func> <func> - <name>CallbackModule:init(Args) -> {ok, State} | {ok, State, Timeout} | + <name>Module:init(Args) -> {ok, State} | {ok, State, timeout()} | {stop, Reason}</name> <fsummary> Makes necessary initializations and returns the initial channel state if the initializations succeed.</fsummary> @@ -277,7 +280,6 @@ <v> Args = term() </v> <d> Last argument to ssh_channel:start_link/4.</d> <v> State = term() </v> - <v>Timeout = timeout() </v> <v> Reason = term() </v> </type> <desc> @@ -290,7 +292,7 @@ </func> <func> - <name>CallbackModule:handle_call(Msg, From, State) -> Result</name> + <name>Module:handle_call(Msg, From, State) -> Result</name> <fsummary> Handles messages sent by calling <c>ssh_channel:call/[2,3]</c></fsummary> <type> @@ -298,17 +300,16 @@ <v>From = opaque to the user should be used as argument to ssh_channel:reply/2</v> <v>State = term()</v> - <v>Result = {reply, Reply, NewState} | {reply, Reply, NewState, Timeout} - | {noreply, NewState} | {noreply , NewState, Timeout} + <v>Result = {reply, Reply, NewState} | {reply, Reply, NewState, timeout()} + | {noreply, NewState} | {noreply , NewState, timeout()} | {stop, Reason, Reply, NewState} | {stop, Reason, NewState} </v> <v>Reply = term() - will be the return value of ssh_channel:call/[2,3]</v> - <v>Timeout = timeout() </v> - <v>NewState = term() - a possible updated version of State</v> + <v>NewState = term()</v> <v>Reason = term()</v> </type> <desc> <p>Handles messages sent by calling - <c>ssh_channel:call/[2,3]</c> + <seealso marker="#call-2">ssh_channel:call/[2,3]</seealso> </p> <p>For more detailed information on timeouts see the section <seealso marker="#cb_timeouts">CALLBACK TIMEOUTS</seealso>. </p> @@ -316,16 +317,15 @@ </func> <func> - <name>CallbackModule:handle_cast(Msg, State) -> Result</name> + <name>Module:handle_cast(Msg, State) -> Result</name> <fsummary> Handles messages sent by calling <c>ssh_channel:cact/2</c></fsummary> <type> <v>Msg = term()</v> <v>State = term()</v> - <v>Result = {noreply, NewState} | {noreply, NewState, Timeout} + <v>Result = {noreply, NewState} | {noreply, NewState, timeout()} | {stop, Reason, NewState}</v> - <v>NewState = term() - a possible updated version of State</v> - <v>Timeout = timeout() </v> + <v>NewState = term() </v> <v>Reason = term()</v> </type> <desc> @@ -339,7 +339,7 @@ </func> <func> - <name>CallbackModule:handle_msg(Msg, State) -> {ok, State} | + <name>Module:handle_msg(Msg, State) -> {ok, State} | {stop, ChannelId, State}</name> <fsummary> Handle other messages than ssh connection protocol, @@ -359,136 +359,37 @@ <taglist> <tag><c><![CDATA[{ssh_channel_up, ssh_channel_id(), ssh_connection_ref()}]]></c></tag> - <item>This is the first messages that will be received - by the channel, it is sent just before - the ssh_channel:init/1 function returns successfully. - This is especially useful if the server wants - to send a message to the client without first receiving - a message from the client. If the message is not useful - for your particular problem just ignore it by immediately - returning {ok, State}. + <item>This is the first messages that will be received by + the channel, it is sent just before the <seealso + marker="#init-2">ssh_channel:init/1</seealso> function + returns successfully. This is especially useful if the + server wants to send a message to the client without first + receiving a message from it. If the message is not + useful for your particular scenario just ignore it by + immediately returning {ok, State}. </item> </taglist> </desc> </func> <func> - <name>CallbackModule:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, + <name>Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, ssh_channel_id(), State}</name> <fsummary> Handles ssh connection protocol messages. </fsummary> <type> - <v>Msg = {ssh_cm, ssh_connection_ref(), SshMsg}</v> - <v> SshMsg = tuple() - see message list below</v> + <v>Msg = <seealso marker="ssh_connection"> ssh_connection:event() </seealso> </v> <v>State = term()</v> </type> <desc> <p> Handles ssh connection protocol messages that may need service specific attention. </p> - - <p> All channels should handle the following messages. For - channels implementing subsystems the handle_ssh_msg-callback - will not be called for any other messages. </p> - - <taglist> - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {data, ssh_channel_id(), - ssh_data_type_code(), binary() = Data}}]]></c></tag> - <item> Data has arrived on the channel. When the callback - for this message returns the channel behavior will adjust - the ssh flow control window.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {eof, - ssh_channel_id()}}]]></c></tag> - <item>Indicteas that the other side will not send any more - data.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {signal, - ssh_channel_id(), ssh_signal()}} ]]></c></tag> - <item>A signal can be delivered to the remote - process/service using the following message. Some systems - may not implement signals, in which case they should ignore - this message.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), - {exit_signal, ssh_channel_id(), string() = exit_signal, - string() = ErrorMsg, string() = - LanguageString}}]]></c></tag> - <item>A remote execution may terminate violently due to a - signal then this message may be received. For details on valid string - values see RFC 4254 section 6.10</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {exit_status, - ssh_channel_id(), integer() = ExitStatus}}]]></c></tag> - <item> When the command running at the other end terminates, - the following message can be sent to return the exit status - of the command. A zero 'exit_status' usually means that the - command terminated successfully.</item> - </taglist> - - <p> Channels implementing a shell and command execution on the server side - should also handle the following messages. </p> - - <taglist> - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {env, ssh_channel_id(), - boolean() = WantReply, string() = Var, string() = Value}}]]></c></tag> - <item> Environment variables may be passed to the - shell/command to be started later. Note that before the - callback returns it should call the function - ssh_connection:reply_request/4 with the boolean value of <c> - WantReply</c> as the second argument. - </item> - - <tag><c><![CDATA[{ssh_cm, ConnectionRef, {exec, ssh_channel_id(), - boolean() = WantReply, string() = Cmd}}]]></c></tag> - <item> This message will request that the server start the - execution of the given command. Note that before the - callback returns it should call the function - ssh_connection:reply_request/4 with the boolean value of <c> - WantReply</c> as the second argument.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {pty, ssh_channel_id(), - boolean() = WantReply, {string() = Terminal, integer() = CharWidth, - integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight, - [{atom() | integer() = Opcode, - integer() = Value}] = TerminalModes}}}]]></c></tag> - <item>A pseudo-terminal has been requested for the - session. Terminal is the value of the TERM environment - variable value (e.g., vt100). Zero dimension parameters must - be ignored. The character/row dimensions override the pixel - dimensions (when nonzero). Pixel dimensions refer to the - drawable area of the window. The <c>Opcode</c> in the - <c>TerminalModes</c> list is the mnemonic name, represented - as an lowercase erlang atom, defined in RFC 4254 section 8, - or the opcode if the mnemonic name is not listed in the - RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom: - echo</c>. Note that before the callback returns it should - call the function ssh_connection:reply_request/4 with the - boolean value of <c> WantReply</c> as the second - argument.</item> - - <tag><c><![CDATA[{ssh_cm, ConnectionRef, {shell, boolean() = - WantReply}}]]></c></tag> - <item> This message will request that the user's default - shell be started at the other end. Note that before the - callback returns it should call the function - ssh_connection:reply_request/4 with the value of <c> - WantReply</c> as the second argument. - </item> - - <tag><c><![CDATA[ {ssh_cm, ssh_connection_ref(), {window_change, - ssh_channel_id(), integer() = CharWidth, integer() = RowHeight, - integer() = PixWidth, integer() = PixHeight}}]]></c></tag> - <item> When the window (terminal) size changes on the client - side, it MAY send a message to the other side to inform it - of the new dimensions.</item> - </taglist> <p> The following message is completely taken care of by the ssh channel behavior</p> <taglist> - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {closed, - ssh_channel_id()}}]]></c></tag> + <tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag> <item> The channel behavior will send a close message to the other side if such a message has not already been sent and then terminate the channel with reason normal.</item> @@ -497,7 +398,7 @@ </func> <func> - <name>CallbackModule:terminate(Reason, State) -> _</name> + <name>Module:terminate(Reason, State) -> _</name> <fsummary> </fsummary> <type> <v>Reason = term()</v> @@ -505,12 +406,12 @@ </type> <desc> <p>This function is called by a channel process when it is - about to terminate. Before this function is called ssh_connection:close/2 - will be called if it has not been called earlier. - This function should be the opposite of <c>CallbackModule:init/1</c> - and do any necessary cleaning up. When it returns, the - channel process terminates with reason <c>Reason</c>. The return value is - ignored. + about to terminate. Before this function is called <seealso + marker="ssh_connection#close-2"> ssh_connection:close/2 + </seealso> will be called if it has not been called earlier. + This function should do any necessary cleaning + up. When it returns, the channel process terminates with + reason <c>Reason</c>. The return value is ignored. </p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml new file mode 100644 index 0000000000..abc1070e78 --- /dev/null +++ b/lib/ssh/doc/src/ssh_client_key_api.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + 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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + <title>ssh_client_key_api</title> + </header> + <module>ssh_client_key_api</module> + <modulesummary> + -behaviour(ssh_client_key_api). + </modulesummary> + <description> + <p> Behavior describing the API for an SSH client's public key handling. + By implementing the callbacks defined. + in this behavior it is possible to customize the SSH client's public key + handling. By default the SSH application implements this behavior + with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>. </p> + </description> + + <section> + <title>DATA TYPES </title> + + <p>Type definitions that are used more than once in this module + and/or abstractions to indicate the intended use of the data + type:</p> + + <p> boolean() = true | false</p> + <p> string() = [byte()] </p> + <p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> private_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p> + + </section> + + <funcs> + <func> + <name>Module:add_host_key(HostNames, Key, ConnectOptions) -> ok | {error, Reason}</name> + <fsummary>Adds a host key to the set of trusted host keys</fsummary> + <type> + <v>HostNames = string()</v> + <d>Description of the host that owns the <c>PublicKey</c></d> + + <v>Key = public_key() </v> + <d> Normally an RSA or DSA public key but handling of other public keys can be added</d> + + <v>ConnectOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d> + <v>Reason = term() </v> + </type> + <desc> + <p> Adds a host key to the set of trusted host keys</p> + </desc> + </func> + + <func> + <name>Module:is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name> + <fsummary>Checks if a host key is trusted</fsummary> + <type> + <v>Key = public_key() </v> + <d> Normally an RSA or DSA public key but handling of other public keys can be added</d> + + <v>Host = string()</v> + <d>Description of the host</d> + + <v>Algorithm = public_key_algorithm()</v> + <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms + can be handled.</d> + + <v> ConnectOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d> + + <v> Result = boolean()</v> + </type> + <desc> + <p>Checks if a host key is trusted</p> + </desc> + </func> + + <func> + <name>Module:user_key(Algorithm, ConnectOptions) -> + {ok, PrivateKey} | {error, Reason}</name> + <fsummary>Fetches the users "public key" matching the <c>Algorithm</c>.</fsummary> + <type> + <v>Algorithm = public_key_algorithm()</v> + <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms + can be handled.</d> + + <v> ConnectOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d> + + <v> PrivateKey = private_key()</v> + <d> The private key of the user matching the <c>Algorithm</c></d> + + <v>Reason = term() </v> + </type> + + <desc> + <p>Fetches the users "public key" matching the <c>Algorithm</c>. + <note>The private key contains the public key</note> + </p> + </desc> + </func> + + </funcs> + +</erlref> diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml index a9ae13d556..c66622307f 100644 --- a/lib/ssh/doc/src/ssh_connection.xml +++ b/lib/ssh/doc/src/ssh_connection.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> <year>2008</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -24,69 +24,178 @@ </legalnotice> <title>ssh_connection</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible></responsible> - <docno></docno> - <approved></approved> - <checked></checked> <date></date> - <rev></rev> </header> <module>ssh_connection</module> - <modulesummary>This module provides an API to the ssh connection protocol. + <modulesummary>This module provides API functions to send <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH Connection Protocol </url> + events to the other side of an SSH channel. </modulesummary> + <description> - <p>This module provides an API to the ssh connection protocol. - Not all features of the connection protocol are officially supported yet. - Only the ones supported are documented here.</p> + <p>The SSH Connection Protocol is used by clients and servers + (i.e. SSH channels) to communicate over the SSH connection. The + API functions in this module sends SSH Connection Protocol events + that are received as messages by the remote channel. + In the case that the receiving channel is an Erlang process the + message will be on the following format + <c><![CDATA[{ssh_cm, ssh_connection_ref(), ssh_event_msg()}]]></c>. If the <seealso + marker="ssh_channel">ssh_channel</seealso> behavior is used to + implement the channel process these will be handled by + <seealso + marker="ssh_channel#CallbackModule:handled_ssh_msg-2">handle_ssh_msg/2 </seealso>.</p> </description> - <section> - <title>COMMON DATA TYPES </title> + <section> + <title>DATA TYPES </title> + <p>Type definitions that are used more than once in this module and/or abstractions to indicate the intended use of the data type:</p> - + <p><c>boolean() = true | false </c></p> <p><c>string() = list of ASCII characters</c></p> <p><c>timeout() = infinity | integer() - in milliseconds.</c></p> <p><c>ssh_connection_ref() - opaque to the user returned by - ssh:connect/3 or sent to a ssh channel processes</c></p> + ssh:connect/3 or sent to an SSH channel processes</c></p> <p><c>ssh_channel_id() = integer() </c></p> <p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are - currently valid values see RFC 4254 section 5.2.</c></p> + currently valid values see</c> <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</p> <p><c>ssh_request_status() = success | failure</c></p> - </section> + <p><c>event() = {ssh_cm, ssh_connection_ref(), ssh_event_msg()} </c></p> + <p><c>ssh_event_msg() = data_events() | status_events() | terminal_events() </c></p> + + <taglist> + <tag><b>data_events()</b></tag> + <item> + <taglist> + <tag><c><![CDATA[{data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}]]></c></tag> + <item> Data has arrived on the channel. This event is sent as + result of calling <seealso marker="#send-3"> ssh_connection:send/[3,4,5] </seealso></item> + + <tag><c><![CDATA[{eof, ssh_channel_id()}]]></c></tag> + <item>Indicates that the other side will not send any more + data. This event is sent as result of calling <seealso + marker="#send_eof-2"> ssh_connection:send_eof/2</seealso> + </item> + </taglist> + </item> + + <tag><b>status_events()</b></tag> + <item> + + <taglist> + <tag><c><![CDATA[{signal, ssh_channel_id(), ssh_signal()}]]></c></tag> + <item>A signal can be delivered to the remote process/service + using the following message. Some systems will not support + signals, in which case they should ignore this message. There is + currently no funtion to generate this event as the signals + refered to are on OS-level and not something generated by an + Erlang program.</item> + + <tag><c><![CDATA[{exit_signal, ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg, + string() = LanguageString}]]></c></tag> + + <item>A remote execution may terminate violently due to a signal + then this message may be received. For details on valid string + values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> section 6.10. Special case of the signals + mentioned above.</item> + + <tag><c><![CDATA[{exit_status, ssh_channel_id(), integer() = ExitStatus}]]></c></tag> + <item> When the command running at the other end terminates, the + following message can be sent to return the exit status of the + command. A zero 'exit_status' usually means that the command + terminated successfully. This event is sent as result of calling + <seealso marker="#exit_status"> + ssh_connection:exit_status/3</seealso></item> + + <tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag> + <item> This event is sent as result of calling + <seealso marker="#close">ssh_connection:close/2</seealso> Both the handling of this + event and sending of it will be taken care of by the + <seealso marker="ssh_channel">ssh_channel</seealso> behavior.</item> + + </taglist> + </item> - <section> - <title>MESSAGES SENT TO CHANNEL PROCESSES</title> + <tag><b>terminal_events()</b></tag> + + <item> + <p> Channels implementing a shell and command execution on the + server side should handle the following messages that may be sent by client channel processes. </p> + + <p><note>Events that includes a <c> WantReply</c> expects the event handling + process to call <seealso marker="#reply_request">ssh_connection:reply_request/4</seealso> + with the boolean value of <c> WantReply</c> as the second + argument. </note> </p> + + <taglist> + <tag><c><![CDATA[{env, ssh_channel_id(), boolean() = WantReply, + string() = Var, string() = Value}]]></c></tag> + <item> Environment variables may be passed to the shell/command + to be started later. This event is sent as result of calling <seealso + marker="#setenv"> ssh_connection:setenv/5</seealso> + </item> + + <tag><c><![CDATA[{pty, ssh_channel_id(), + boolean() = WantReply, {string() = Terminal, integer() = CharWidth, + integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight, + [{atom() | integer() = Opcode, + integer() = Value}] = TerminalModes}}]]></c></tag> + <item>A pseudo-terminal has been requested for the + session. Terminal is the value of the TERM environment + variable value (e.g., vt100). Zero dimension parameters must + be ignored. The character/row dimensions override the pixel + dimensions (when nonzero). Pixel dimensions refer to the + drawable area of the window. The <c>Opcode</c> in the + <c>TerminalModes</c> list is the mnemonic name, represented + as an lowercase erlang atom, defined in + <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8, + or the opcode if the mnemonic name is not listed in the + RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom: + echo</c>. There is currently no API function to generate this + event.</item> + + <tag><c><![CDATA[{shell, boolean() = WantReply}]]></c></tag> + <item> This message will request that the user's default shell + be started at the other end. This event is sent as result of calling <seealso + marker="#shell"> ssh_connection:shell/2</seealso> + </item> + + <tag><c><![CDATA[{window_change, ssh_channel_id(), integer() = CharWidth, + integer() = RowHeight, integer() = PixWidth, integer() = PixHeight}]]></c></tag> + <item> When the window (terminal) size changes on the client + side, it MAY send a message to the server side to inform it of + the new dimensions. There is currently no API function to generate this + event.</item> - <p>As a result of the ssh connection protocol messages on the form - <c><![CDATA[{ssh_cm, ssh_connection_ref(), term()}]]></c> - will be sent to a channel process. The term will contain - information regarding the ssh connection protocol event, - for details see the ssh channel behavior callback <seealso - marker="ssh_channel">handle_ssh_msg/2 </seealso> </p> - </section> - - <funcs> + <tag><c><![CDATA[{exec, ssh_channel_id(), + boolean() = WantReply, string() = Cmd}]]></c></tag> + <item> This message will request that the server starts + execution of the given command. This event is sent as result of calling <seealso + marker="#exec">ssh_connection:exec/4 </seealso> + </item> + </taglist> + </item> + </taglist> + </section> + + <funcs> <func> <name>adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name> - <fsummary>Adjusts the ssh flowcontrol window. </fsummary> + <fsummary>Adjusts the SSH flowcontrol window. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> <v> ChannelId = ssh_channel_id() </v> <v> NumOfBytes = integer()</v> </type> <desc> - <p>Adjusts the ssh flowcontrol window. </p> + <p>Adjusts the SSH flowcontrol window. This shall be done by both client and server side channel processes.</p> - <note><p>This will be taken care of by the ssh_channel - behavior when the callback <seealso marker="ssh_channel"> - handle_ssh_msg/2 </seealso> has returned after processing a - {ssh_cm, ssh_connection_ref(), {data, ssh_channel_id(), - ssh_data_type_code(), binary()}} - message, and should normally not be called explicitly.</p></note> + <note><p>Channels implemented with the <seealso marker="ssh_channel"> ssh_channel + behavior</seealso> will normaly not need to call this function as flow control + will be handled by the behavior. The behavior will adjust the window every time + the callback <seealso marker="ssh_channel#handled_ssh_msg-2"> + handle_ssh_msg/2 </seealso> has returned after processing channel data</p> </note> </desc> </func> @@ -98,20 +207,19 @@ <v> ChannelId = ssh_channel_id()</v> </type> <desc> - <p>Sends a close message on the channel <c>ChannelId</c> + <p>A server or client channel process can choose to close their session by sending a close event. </p> - <note><p>This function will be called by the ssh channel + <note><p>This function will be called by the ssh_channel behavior when the channel is terminated see <seealso - marker="ssh_channel"> ssh_channel(3) </seealso> and should - normally not be called explicitly.</p></note> + marker="ssh_channel"> ssh_channel(3) </seealso> so channels implemented with the + behavior should not call this function explicitly.</p></note> </desc> </func> <func> <name>exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() </name> - <fsummary>Will request that the server start the - execution of the given command. </fsummary> + <fsummary>Request that the server start the execution of the given command. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> <v> ChannelId = ssh_channel_id()</v> @@ -119,44 +227,39 @@ <v>Timeout = timeout() </v> </type> <desc> - <p>Will request that the server start the execution of the - given command, the result will be received as:</p> + <p>Should be called by a client channel process to request that the server starts execution of the + given command, the result will be several messages according to the following pattern. Note + that the last message will be a channel close message, as the exec request is a one time + execution that closes the channel when it is done.</p> <taglist> - <tag><c> N X {ssh_cm, - ssh_connection_ref(), {data, ssh_channel_id(), ssh_data_type_code(), - binary() = Data}} </c></tag> + <tag><c> N x {ssh_cm, ssh_connection_ref(), + {data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}} </c></tag> <item>The result of executing the command may be only one line or thousands of lines depending on the command.</item> - <tag><c> 1 X {ssh_cm, ssh_connection_ref(), {eof, ssh_channel_id()}}</c></tag> + <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {eof, ssh_channel_id()}}</c></tag> <item>Indicates that no more data will be sent.</item> - <tag><c>0 or 1 X {ssh_cm, + <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {exit_signal, ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg, string() = LanguageString}}</c></tag> <item>Not all systems send signals. For details on valid string values see RFC 4254 section 6.10 </item> - <tag><c>0 or 1 X {ssh_cm, ssh_connection_ref(), {exit_status, + <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {exit_status, ssh_channel_id(), integer() = ExitStatus}}</c></tag> <item>It is recommended by the <c>ssh connection protocol</c> that this message shall be sent, but that may not always be the case.</item> - <tag><c> 1 X {ssh_cm, ssh_connection_ref(), + <tag><c> 1 x {ssh_cm, ssh_connection_ref(), {closed, ssh_channel_id()}}</c></tag> <item>Indicates that the ssh channel started for the execution of the command has now been shutdown.</item> </taglist> - - <p> These message should be handled by the - client. The <seealso marker="ssh_channel">ssh channel - behavior</seealso> can be used when writing a client. - </p> </desc> </func> - <func> <name>exit_status(ConnectionRef, ChannelId, Status) -> ok</name> <fsummary>Sends the exit status of a command to the client.</fsummary> @@ -166,12 +269,12 @@ <v> Status = integer()</v> </type> <desc> - <p>Sends the exit status of a command to the client.</p> + <p>Should be called by a server channel process to sends the exit status of a command to the client.</p> </desc> - </func> + </func> <func> - <name>reply_request(ConnectionRef, WantReply, Status, CannelId) -> ok</name> + <name>reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name> <fsummary>Send status replies to requests that want such replies. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> @@ -183,10 +286,9 @@ <p>Sends status replies to requests where the requester has stated that they want a status report e.i .<c> WantReply = true</c>, if <c> WantReply</c> is false calling this function will be a - "noop". Should be called after handling an ssh connection + "noop". Should be called while handling an ssh connection protocol message containing a <c>WantReply</c> boolean - value. See the ssh_channel behavior callback <seealso - marker="ssh_channel"> handle_ssh_msg/2 </seealso> + value. </p> </desc> </func> @@ -206,7 +308,7 @@ <v> Timeout = timeout()</v> </type> <desc> - <p>Sends channel data. + <p>Should be called by client- and server channel processes to send data to each other. </p> </desc> </func> @@ -228,9 +330,7 @@ <name>session_channel(ConnectionRef, Timeout) -> </name> <name>session_channel(ConnectionRef, InitialWindowSize, MaxPacketSize, Timeout) -> {ok, ssh_channel_id()} | {error, Reason}</name> - <fsummary>Opens a channel for a ssh session. A session is a - remote execution of a program. The program may be a shell, an - application, a system command, or some built-in subsystem. </fsummary> + <fsummary>Opens a channel for a ssh session. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref()</v> <v> InitialWindowSize = integer() </v> @@ -239,9 +339,8 @@ <v> Reason = term() </v> </type> <desc> - <p>Opens a channel for a ssh session. A session is a - remote execution of a program. The program may be a shell, an - application, a system command, or some built-in subsystem. + <p>Opens a channel for an SSH session. The channel id returned from this function + is the id used as input to the other funtions in this module. </p> </desc> </func> @@ -258,8 +357,8 @@ <v> Timeout = timeout()</v> </type> <desc> - <p> Environment variables may be passed to the shell/command to be - started later. + <p> Environment variables may be passed before starting the + shell/command. Should be called by a client channel processes. </p> </desc> </func> @@ -267,17 +366,16 @@ <func> <name>shell(ConnectionRef, ChannelId) -> ssh_request_status() </name> - <fsummary> Will request that the user's default shell (typically - defined in /etc/passwd in UNIX systems) be started at the other + <fsummary> Requests that the user's default shell (typically + defined in /etc/passwd in UNIX systems) shall be executed at the server end. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> <v> ChannelId = ssh_channel_id()</v> </type> <desc> - <p> Will request that the user's default shell (typically - defined in /etc/passwd in UNIX systems) be started at the - other end. + <p> Should be called by a client channel process to request that the user's default shell (typically + defined in /etc/passwd in UNIX systems) shall be executed at the server end. </p> </desc> </func> @@ -292,7 +390,7 @@ <v> Timeout = timeout()</v> </type> <desc> - <p> Sends a request to execute a predefined subsystem. + <p> Should be called by a client channel process for requesting to execute a predefined subsystem on the server. </p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_protocol.xml b/lib/ssh/doc/src/ssh_protocol.xml new file mode 100644 index 0000000000..6a253c43eb --- /dev/null +++ b/lib/ssh/doc/src/ssh_protocol.xml @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> +<!-- %EricssonCopyright% --> +<chapter> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The program may be used and/or copied only with the written permission from + Ericsson AB, or in accordance with the terms and conditions stipulated in + the agreement/contract under which the program has been supplied. + </legalnotice> + <title>Secure Shell (SSH)</title> + <prepared>OTP</prepared> + <date></date> + <rev>%VSN%</rev> + <file>ssh_protocol.xml</file> + </header> + + <section> + <title>SSH Protocol Overview</title> + + <p> Conceptually the SSH protocol can be partitioned into four + layers:</p> + + <image file="SSH_protocols.png"> + <icaption>SSH Protocol Architecture</icaption> + </image> + + <section> + <title>Transport Protocol</title> + + <p> The SSH Transport Protocol is a secure, low level transport. + It provides strong encryption, cryptographic host + authentication and integrity protection. Currently, only a + minimum of MAC- (message authentication code, a short piece of + information used to authenticate a message) and encryption + algorithms are supported see <seealso marker="ssh">ssh(3)</seealso> + </p> + </section> + + <section> + <title>Authentication Protocol</title> + + <p>The SSH authentication protocol is a general-purpose user + authentication protocol run over the SSH transport + protocol. Erlang SSH supports user authentication using public + key technology (RSA and DSA, X509-certificates are currently not + supported). It is also possible to use a so called keyboard + interactive authentication. This method is suitable for + interactive authentication methods that do not need any special + software support on the client side. Instead, all authentication + data should be entered via the keyboard. It is also possible + to use a pure password based authentication scheme, note that in + this case the the plain text password will be encrypted before sent + over the network. There are several configuration options for + authentication handling available in + <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso> + and <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso> + It is also possible to customize the public key handling + by implementing the behaviours <seealso + marker="ssh_client_key_api">ssh_client_key_api</seealso> and + <seealso + marker="ssh_server_key_api">ssh_server_key_api</seealso> + </p> + </section> + + <section> + <title>Connection Protocol</title> + + <p>The SSH Connection Protocol provides application-support + services over the transport pipe, such as channel multiplexing, + flow control, remote program execution, signal propagation, + connection forwarding, etc. Functions for handling the SSH + Connection Protocol can be found in the module <seealso + marker="ssh_connection">ssh_connection</seealso>. + </p> + </section> + + <section> + <title>Channels</title> + + <p>All terminal sessions, forwarded connections etc., are + channels. Multiple channels are multiplexed into a single + connection, and all channels are flow-controlled. Typically an + SSH client will open a channel, send data/commands, receive + data/"control information" and when it is done close the + channel. The <seealso + marker="ssh_channel">ssh_channel</seealso> behaviour makes it easy to + write your own SSH client/server processes that use flow + control. It handles generic parts of SSH channel management and + lets you focus on the application logic. + </p> + + <p>Channels comes in three flavors</p> + + <list type="bulleted"> + <item><em>Subsystem</em> - named services that can be run as + part of an SSH server such as SFTP <seealso + marker="ssh_sftpd">ssh_sftpd</seealso>, that is built in to the + SSH daemon (server) by default but may be disabled. The Erlang SSH + daemon may be configured to run any Erlang + implemented SSH subsystem. + </item> + <item><em>Shell</em> - interactive shell. By default the + Erlang daemon will run the Erlang shell. It is + possible to customize the shell by providing your own + read-eval-print loop. It is also possible, but much more work, + to provide your own CLI (Command Line Interface) implementation. + </item> + <item><em>Exec</em> - one-time remote execution (like + SCP). See <seealso + marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso></item> + </list> + </section> + + <p>Channels are flow controlled. No data may be sent to a channel + peer until a message is received to indicate that window space is + available. The 'initial window size' specifies how many bytes of + channel data that can be sent to the channel peer without adjusting the + window. + </p> + + <p> + For more detailed information about the SSH protocol, see the + following RFCs: + </p> + + <list type="bulleted"> + <item><url href="http://www.ietf.org/rfc/rfc4250.txt">RFC 4250</url> - + Protocol Assigned Numbers.</item> + <item><url href="http://www.ietf.org/rfc/rfc4251.txt">RFC 4251</url> - + Protocol Architecture.</item> + <item><url href="http://www.ietf.org/rfc/rfc4252.txt">RFC 4252</url> - + Authentication Protocol.</item> + <item><url href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</url> - + Transport Layer Protocol.</item> + <item><url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> - + Connection Protocol.</item> + <item><url href="http://www.ietf.org/rfc/rfc4255.txt">RFC 4255</url> - + Key Fingerprints.</item> + <item><url href="http://www.ietf.org/rfc/rfc4344.txt">RFC 4344</url> - + Transport Layer Encryption Modes.</item> + <item><url href="http://www.ietf.org/rfc/rfc4716.txt">RFC 4716</url> - + Public Key File Format.</item> + </list> + </section> +</chapter> diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml new file mode 100644 index 0000000000..78ff105387 --- /dev/null +++ b/lib/ssh/doc/src/ssh_server_key_api.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + 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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + <title>ssh_server_key_api</title> + </header> + <module>ssh_server_key_api</module> + <modulesummary> + -behaviour(ssh_server_key_api). + </modulesummary> + <description> + <p> Behaviour describing the API for an SSH server's public key handling.By implementing the callbacks defined + in this behavior it is possible to customize the SSH server's public key + handling. By default the SSH application implements this behavior + with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>.</p> + </description> + + <section> + <title>DATA TYPES </title> + + <p>Type definitions that are used more than once in this module + and/or abstractions to indicate the intended use of the data + type:</p> + + <p> boolean() = true | false</p> + <p> string() = [byte()]</p> + <p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> private_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p> + </section> + + <funcs> + <func> + <name>Module:host_key(Algorithm, DaemonOptions) -> + {ok, Key} | {error, Reason}</name> + <fsummary>Fetches the hosts private key </fsummary> + <type> + <v>Algorithm = public_key_algorithm()</v> + <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms + can be handled.</d> + <v> DaemonOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:daemon/[2,3]</seealso></d> + <v> Key = private_key()</v> + <d> The private key of the host matching the <c>Algorithm</c></d> + <v>Reason = term() </v> + </type> + <desc> + <p>Fetches the hosts private key</p> + </desc> + </func> + + <func> + <name>Module:is_auth_key(Key, User, DaemonOptions) -> Result</name> + <fsummary> Checks if the user key is authorized</fsummary> + <type> + <v> Key = public_key() </v> + <d> Normally an RSA or DSA public key but handling of other public keys can be added</d> + <v> User = string()</v> + <d> The user owning the public key</d> + <v> DaemonOptions = proplists:proplist() </v> + <d> Options provided to <seealso marker="ssh#daemon">ssh:daemon/[2,3]</seealso></d> + <v> Result = boolean()</v> + </type> + <desc> + <p> Checks if the user key is authorized </p> + </desc> + </func> + + </funcs> + +</erlref> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index c1f75461b1..0d61e57edb 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,25 +22,20 @@ </legalnotice> <title>ssh_sftp</title> - <prepared>Jakob Cederlund</prepared> - <responsible></responsible> - <docno>1</docno> - <approved></approved> - <checked></checked> + <prepared>OTP</prepared> <date>2005-09-22</date> - <rev>PA1</rev> <file>ssh_sftp.sgml</file> </header> <module>ssh_sftp</module> <modulesummary>SFTP client.</modulesummary> <description> <p>This module implements an SFTP (SSH FTP) client. SFTP is a - secure, encrypted file transfer service available for - SSH.</p> + secure, encrypted file transfer service available for + SSH.</p> </description> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p>Type definitions that are used more than once in this module and/or abstractions to indicate the intended use of the data type: </p> @@ -51,8 +46,8 @@ <section> <title>TIMEOUTS </title> - <p>If the request functions for the sftp channel return {error, timeout} - it does not mean that the request did not reach the server and was + <p>If the request functions for the SFTP channel return {error, timeout} + it does not guarantee that the request did not reach the server and was not performed, it only means that we did not receive an answer from the server within the time that was expected.</p> </section> @@ -64,7 +59,7 @@ <name>start_channel(Host, Options) -></name> <name>start_channel(Host, Port, Options) -> {ok, Pid} | {ok, Pid, ConnectionRef} | {error, Reason}</name> - <fsummary>Starts a sftp client</fsummary> + <fsummary>Starts a SFTP client</fsummary> <type> <v>Host = string()</v> <v>ConnectionRef = ssh_connection_ref()</v> @@ -73,11 +68,11 @@ <v>Reason = term()</v> </type> <desc> - <p>If not provided, setups a ssh connection in this case a - connection reference will be returned too. A ssh channel - process is started to handle the communication with the SFTP - server, the returned pid for this process should be used as - input to all other API functions in this module.</p> + <p>If no connection reference is provided, a connection is set + up and the new connection is returned. An SSH channel process + is started to handle the communication with the SFTP server. + The returned pid for this process should be used as input to + all other API functions in this module.</p> <p>Options are:</p> <taglist> @@ -95,13 +90,13 @@ <func> <name>stop_channel(ChannelPid) -> ok</name> - <fsummary>Stops the sftp client channel.</fsummary> + <fsummary>Stops the SFTP client channel.</fsummary> <type> <v>ChannelPid = pid()</v> </type> <desc> - <p>Stops a sftp channel. If the ssh connection should be closed - call <seealso marker="ssh">ssh:close/1</seealso>.</p> + <p>Stops an SFTP channel. Does not close the SSH connetion. + Use <seealso marker="ssh">ssh:close/1</seealso> to close it.</p> </desc> </func> @@ -133,8 +128,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Writes a file to the server, like <c><![CDATA[file:write_file/2]]></c>. - The file is created if it's not there.</p> + <p>Writes a file to the server, like + <c><![CDATA[file:write_file/2]]></c>. The file is created if + it does not exist or is owerwritten if it does.</p> </desc> </func> <func> @@ -169,7 +165,7 @@ </type> <desc> <p>Opens a file on the server, and returns a handle that - is used for reading or writing.</p> + can be used for reading or writing.</p> </desc> </func> <func> @@ -184,7 +180,7 @@ </type> <desc> <p>Opens a handle to a directory on the server, the handle - is used for reading directory contents.</p> + can be used for reading directory contents.</p> </desc> </func> <func> @@ -218,7 +214,7 @@ </type> <desc> <p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by - <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, or <c><![CDATA[eof]]></c>, or + <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or <c><![CDATA[{error, Reason}]]></c>. If the file is opened with <c><![CDATA[binary]]></c>, <c><![CDATA[Data]]></c> is a binary, otherwise it is a string.</p> <p>If the file is read past eof, only the remaining bytes @@ -267,9 +263,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Write <c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>. + <p>Writes<c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>. The file should be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c> - flag. Returns <c><![CDATA[ok]]></c> if successful and <c><![CDATA[{error, Reason}]]></c> + flag. Returns <c><![CDATA[ok]]></c> if successful or S<c><![CDATA[{error, Reason}]]></c> otherwise.</p> <p>Typical error reasons are:</p> <taglist> @@ -317,14 +313,14 @@ <v>ChannelPid = pid()</v> <v>Handle = term()</v> <v>Location = Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v> - <v>Offset = int()</v> + <v>Offset = integer()</v> <v>Timeout = timeout()</v> <v>NewPosition = integer()</v> <v>Reason = term()</v> </type> <desc> <p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>. - Returns <c><![CDATA[{ok, NewPosition]]></c> (as an absolute offset) if + Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if successful, otherwise <c><![CDATA[{error, Reason}]]></c>. <c><![CDATA[Location]]></c> is one of the following:</p> <taglist> @@ -413,7 +409,7 @@ <v>Reason = term()</v> </type> <desc> - <p>Read the link target from the symbolic link specified + <p>Reads the link target from the symbolic link specified by <c><![CDATA[name]]></c>, like <c><![CDATA[file:read_link/1]]></c>.</p> </desc> </func> @@ -490,8 +486,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Deletes a directory specified by <c><![CDATA[Name]]></c>. The directory - should be empty.</p> + <p>Deletes a directory specified by <c><![CDATA[Name]]></c>. + Note that the directory must be empty before it can be successfully deleted + </p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml index b3d64e72b4..3666bc7692 100644 --- a/lib/ssh/doc/src/ssh_sftpd.xml +++ b/lib/ssh/doc/src/ssh_sftpd.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,23 +22,17 @@ </legalnotice> <title>ssh_sftpd</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible></responsible> - <docno>1</docno> - <approved></approved> - <checked></checked> <date>2005-09-22</date> - <rev>PA1</rev> <file>ssh_sftpd.sgml</file> </header> <module>ssh_sftpd</module> - <modulesummary>Specifies a channel process to handle a sftp subsystem.</modulesummary> + <modulesummary>Specifies the channel process to handle an sftp subsystem.</modulesummary> <description> <p>Specifies a channel process to handle a sftp subsystem.</p> </description> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p><c>subsystem_spec() = {subsystem_name(), {channel_callback(), channel_init_args()}} </c></p> <p><c>subsystem_name() = "sftp"</c></p> <p><c>channel_callback() = atom()</c> - Name of the erlang module implementing the @@ -65,27 +59,32 @@ </item> <tag><c><![CDATA[{file_handler, CallbackModule}]]></c></tag> <item> - <p>Determines which module to call for communicating with - the file server. Default value is <c>ssh_sftpd_file</c> that uses the - file and filelib API:s to access the standard OTP file - server. This option may be used to plug in the use of - other file servers.</p> - </item> - <tag><c><![CDATA[{max_files, Integer}]]></c></tag> - <item> - <p>The default value is <c>0</c>, which means that there is no upper limit. - If supplied, the number of filenames returned to the sftp client per <c>READDIR</c> - request, is limited to at most the given value.</p> - </item> + <p>Determines which module to call for accessing + the file server. The default value is <c>ssh_sftpd_file</c> that uses the + <seealso marker="kernel#file">file</seealso> and <seealso marker="kernel#filelib">filelib</seealso> API:s to access the standard OTP file + server. This option may be used to plug in + other file servers.</p> + </item> + <tag><c><![CDATA[{max_files, Integer}]]></c></tag> + <item> + <p>The default value is <c>0</c>, which means that there is no upper limit. + If supplied, the number of filenames returned to the sftp client per <c>READDIR</c> + request is limited to at most the given value.</p> + </item> <tag><c><![CDATA[{root, String}]]></c></tag> <item> <p>Sets the sftp root directory. The user will then not be - able to see any files above this root. If for instance - the root is set to <c>/tmp</c> the user will see this - directory as <c>/</c> and if the user does cd <c>/etc</c> - the user will end up in <c>/tmp/etc</c>. + able to see any files above this root. If for instance + the root is set to <c>/tmp</c> the user will see this + directory as <c>/</c> and if the user does cd <c>/etc</c> + the user will end up in <c>/tmp/etc</c>. </p> </item> + <tag><c><![CDATA[{sftpd_vsn, integer()}]]></c></tag> + <item> + <p>Sets the sftp version to use, defaults to 5. Version 6 is under + development and limited.</p> + </item> </taglist> </desc> </func> diff --git a/lib/ssh/doc/src/user_guide.gif b/lib/ssh/doc/src/user_guide.gif Binary files differdeleted file mode 100644 index e6275a803d..0000000000 --- a/lib/ssh/doc/src/user_guide.gif +++ /dev/null diff --git a/lib/ssh/doc/src/usersguide.xml b/lib/ssh/doc/src/usersguide.xml new file mode 100644 index 0000000000..c818003090 --- /dev/null +++ b/lib/ssh/doc/src/usersguide.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>SSH User's Guide</title> + <prepared>OTP Team</prepared> + <date>2012-10-11</date> + <file>usersguide.xml</file> + </header> + <description> + <p>The <em>SSH</em> application implements the SSH (Secure Shell) protocol and + provides an SFTP (Secret File Transfer Protocol) client and server. + </p> + </description> + <xi:include href="introduction.xml"/> + <xi:include href="ssh_protocol.xml"/> + <xi:include href="using_ssh.xml"/> +</part> diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml new file mode 100644 index 0000000000..87b811d591 --- /dev/null +++ b/lib/ssh/doc/src/using_ssh.xml @@ -0,0 +1,298 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2012</year> + <year>2013</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>Getting started</title> + <file>using_ssh.xml</file> + </header> + + <section> + <title> General information</title> + <p>The examples in the following sections use the utility function + <seealso marker="ssh#start"> ssh:start/0 </seealso> that starts + all needed applications (crypto, public_key and ssh). All examples + are run in an Erlang shell, or in a bash shell using openssh to + illustrate how the erlang ssh application can be used. The + exampels are run as the user otptest on a local network where the + user is authorized to login in over ssh to the host "tarlop". If + nothing else is stated it is persumed that the otptest user has an + entry in tarlop's authorized_keys file (may log in via ssh without + entering a password). Also tarlop is a known host in the user + otptest's known_hosts file so that host verification can be done + without user interaction. + </p> + </section> + + <section> + <title>Using the Erlang SSH Terminal Client</title> + + <p>The user otptest, that has bash as default shell, uses the + ssh:shell/1 client to connect to the openssh daemon running on a + host called tarlop. Note that currently this client is very simple + and you should not be expected to be as fancy as the openssh + client.</p> + + <code type="erl" > + 1> ssh:start(). + ok + 2> {ok, S} = ssh:shell("tarlop"). + >pwd + /home/otptest + >exit + logout + 3> + </code> + </section> + + <section> + <title>Running an Erlang SSH Daemon </title> + + <p> The option system_dir must be a directory containing a host + key file and it defaults to /etc/ssh. For details see section + Configuration Files in <seealso + marker="ssh_app">ssh(6)</seealso>. + </p> + + <note><p>Normally the /etc/ssh directory is only readable by root. </p> + </note> + + <p> The option user_dir defaults to the users ~/.ssh directory</p> + + <p>In the following example we generate new keys and host keys as + to be able to run the example without having root privilages</p> + + <code> + $bash> ssh-keygen -t rsa -f /tmp/ssh_daemon/ssh_host_rsa_key + [...] + $bash> ssh-keygen -t rsa -f /tmp/otptest_user/.ssh/id_rsa + [...] + </code> + + <p>Create the file /tmp/otptest_user/.ssh/authrized_keys and add the content + of /tmp/otptest_user/.ssh/id_rsa.pub Now we can do</p> + + <code type="erl"> + 1> ssh:start(). + ok + 2> {ok, Sshd} = ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"}, + {user_dir, "/tmp/otptest_user/.ssh"}]). + {ok,<0.54.0>} + 3> + </code> + + <p>Use the openssh client from a shell to connect to the Erlang ssh daemon.</p> + + <code> + $bash> ssh tarlop -p 8989 -i /tmp/otptest_user/.ssh/id_rsa\ + -o UserKnownHostsFile=/tmp/otptest_user/.ssh/known_hosts + The authenticity of host 'tarlop' can't be established. + RSA key fingerprint is 14:81:80:50:b1:1f:57:dd:93:a8:2d:2f:dd:90:ae:a8. + Are you sure you want to continue connecting (yes/no)? yes + Warning: Permanently added 'tarlop' (RSA) to the list of known hosts. + Eshell V5.10 (abort with ^G) + 1> + </code> + + <p>There are two ways of shutting down an SSH daemon</p> + + <p>1: Stops the listener, but leaves existing connections started by the listener up and running.</p> + + <code type="erl"> + 3> ssh:stop_listener(Sshd). + ok + 4> + </code> + + <p>2: Stops the listener and all connections started by the listener.</p> + + <code type="erl"> + 3> ssh:stop_daemon(Sshd) + ok + 4> + </code> + + </section> + + <section> + <title>One Time Execution</title> + + <p>In the following example the Erlang shell is the client process + that receives the channel replies. <note> If you run this example + in your environment you may get fewer or more messages back as + this depends on the OS and shell on the machine running the ssh + daemon. See also <seealso marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso> + </note> + </p> + + <code type="erl" > + 1> ssh:start(). + ok + 2> {ok, ConnectionRef} = ssh:connect("tarlop", 22, []). + {ok,<0.57.0>} + 3>{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity). + {ok,0} + 4> success = ssh_connection:exec(ConnectionRef, ChannelId, "pwd", infinity). + 5> flush(). + Shell got {ssh_cm,<0.57.0>,{data,0,0,<<"/home/otptest\n">>}} + Shell got {ssh_cm,<0.57.0>,{eof,0}} + Shell got {ssh_cm,<0.57.0>,{exit_status,0,0}} + Shell got {ssh_cm,<0.57.0>,{closed,0}} + ok + 6> + </code> + + <p>Note only the channel is closed the connection is still up and can handle other channels</p> + + <code type="erl" > + 6> {ok, NewChannelId} = ssh_connection:session_channel(ConnectionRef, infinity). + {ok,1} + ... + </code> + </section> + + <section> + <title>SFTP (SSH File Transport Protocol) server</title> + + <code type="erl" > + 1> ssh:start(). + ok + 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"}, + {user_dir, "/tmp/otptest_user/.ssh"}, + {subsystems, [ssh_sftpd:subsystem_spec([{cwd, "/tmp/sftp/example"}])]}]). + {ok,<0.54.0>} + 3> + </code> + + <p> Run the openssh sftp client</p> + + <code type="erl"> + $bash> sftp -oPort=8989 -o IdentityFile=/tmp/otptest_user/.ssh/id_rsa\ + -o UserKnownHostsFile=/tmp/otptest_user/.ssh/known_hosts tarlop + Connecting to tarlop... + sftp> pwd + Remote working directory: /tmp/sftp/example + sftp> + </code> + </section> + + <section> + <title>SFTP (SSH File Transport Protocol) client</title> + + <code type="erl" > + 1> ssh:start(). + ok + 2> {ok, ChannelPid, Connection} = ssh_sftp:start_channel("tarlop", []). + {ok,<0.57.0>,<0.51.0>} + 3> ssh_sftp:read_file(ChannelPid, "/home/otptest/test.txt"). + {ok,<<"This is a test file\n">>} + </code> + </section> + + <section> + <title>Creating a subsystem</title> + + <p>A very small SSH subsystem that echos N bytes could be implemented like this. + See also <seealso marker="ssh_channel"> ssh_channel(3)</seealso> </p> + + <code type="erl" > +-module(ssh_echo_server). +-behaviour(ssh_subsystem). +-record(state, { + n, + id, + cm + }). +-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]). + +init([N]) -> + {ok, #state{n = N}}. + +handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) -> + {ok, State#state{id = ChannelId, + cm = ConnectionManager}}. + +handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) -> + M = N - size(Data), + case M > 0 of + true -> + ssh_connection:send(CM, ChannelId, Data), + {ok, State#state{n = M}}; + false -> + <<SendData:N/binary, _/binary>> = Data, + ssh_connection:send(CM, ChannelId, SendData), + ssh_connection:send_eof(CM, ChannelId), + {stop, ChannelId, State} + end; +handle_ssh_msg({ssh_cm, _ConnectionManager, + {data, _ChannelId, 1, Data}}, State) -> + error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]), + {ok, State}; + +handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) -> + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> + %% Ignore signals according to RFC 4254 section 6.9. + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}}, + State) -> + {stop, ChannelId, State}; + +handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) -> + {stop, ChannelId, State}. + +terminate(_Reason, _State) -> + ok. + </code> + + <p>And run like this on the host tarlop with the keys generated in section 3.3</p> + + <code type="erl" > + 1> ssh:start(). + ok + 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"}, + {user_dir, "/tmp/otptest_user/.ssh"} + {subsystems, [{"echo_n", {ssh_echo_server, [10]}}]}]). + {ok,<0.54.0>} + 3> + </code> + + <code type="erl" > + 1> ssh:start(). + ok + 2>{ok, ConnectionRef} = ssh:connect("tarlop", 8989, [{user_dir, "/tmp/otptest_user/.ssh"}]). + {ok,<0.57.0>} + 3>{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity). + 4> success = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity). + 5> ok = ssh_connection:send(ConnectionRef, ChannelId, "0123456789", infinity). + 6> flush(). + {ssh_msg, <0.57.0>, {data, 0, 1, "0123456789"}} + {ssh_msg, <0.57.0>, {eof, 0}} + {ssh_msg, <0.57.0>, {closed, 0}} + 7> {error, closed} = ssh_connection:send(ConnectionRef, ChannelId, "10", infinity). + </code> + +</section> + +</chapter> diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index b8eecd3fa2..323f0af191 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -41,7 +41,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN) BEHAVIOUR_MODULES= \ ssh_sftpd_file_api \ ssh_channel \ - ssh_key_api + ssh_subsystem \ + ssh_client_key_api \ + ssh_server_key_api MODULES= \ ssh \ diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 316c09eb06..a0ba7cf7d9 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -10,6 +10,7 @@ ssh_auth, ssh_bits, ssh_cli, + ssh_client_key_api, ssh_channel, ssh_channel_sup, ssh_connection, @@ -21,13 +22,14 @@ sshd_sup, ssh_file, ssh_io, - ssh_key_api, ssh_math, ssh_no_io, + ssh_server_key_api, ssh_sftp, ssh_sftpd, ssh_sftpd_file, ssh_sftpd_file_api, + ssh_subsystem, ssh_subsystem_sup, ssh_sup, ssh_system_sup, @@ -35,7 +37,7 @@ ssh_userreg, ssh_xfer]}, {registered, []}, - {applications, [kernel, stdlib, crypto]}, + {applications, [kernel, stdlib, crypto, public_key]}, {env, []}, {mod, {ssh_app, []}}]}. diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 6ba32e018f..826a11f1f4 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -19,29 +19,15 @@ {"%VSN%", [ - {<<"2.1.1">>, [{restart_application, ssh}]}, - {<<"2.1">>, [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_connection, soft_purge, soft_purge, []}, - {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, - {load_module, ssh_auth, soft_purge, soft_purge, []}, - {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, - {load_module, ssh_channel, soft_purge, soft_purge, []}, - {load_module, ssh_file, soft_purge, soft_purge, []}]}, - {load_module, ssh, soft_purge, soft_purge, []}]}, + {<<"2.1.1">>, [{restart_application, ssh}]}, + {<<"2.1">>, [{restart_application, ssh}]}, {<<"2.0\\.*">>, [{restart_application, ssh}]}, {<<"1\\.*">>, [{restart_application, ssh}]} ], [ {<<"2.1.1">>, [{restart_application, ssh}]}, - {<<"2.1">>,[{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_connection, soft_purge, soft_purge, []}, - {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, - {load_module, ssh_auth, soft_purge, soft_purge, []}, - {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, - {load_module, ssh_channel, soft_purge, soft_purge, []}, - {load_module, ssh_file, soft_purge, soft_purge, []}]}, - {load_module, ssh, soft_purge, soft_purge, []}]}, + {<<"2.1">>,[{restart_application, ssh}]}, {<<"2.0\\.*">>, [{restart_application, ssh}]}, {<<"1\\.*">>, [{restart_application, ssh}]} ] -}.
\ No newline at end of file +}. diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index a569298056..3ef26b1678 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -41,19 +41,23 @@ %% %% Type = permanent | transient | temporary %% -%% Description: Starts the inets application. Default type +%% Description: Starts the ssh application. Default type %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> + application:start(crypto), + application:start(public_key), application:start(ssh). start(Type) -> + application:start(crypto, Type), + application:start(public_key, Type), application:start(ssh, Type). %%-------------------------------------------------------------------- %% Function: stop() -> ok %% -%% Description: Stops the inets application. +%% Description: Stops the ssh application. %%-------------------------------------------------------------------- stop() -> application:stop(ssh). @@ -76,7 +80,7 @@ connect(Host, Port, Options, Timeout) -> {error, _Reason} = Error -> Error; {SocketOptions, SshOptions} -> - DisableIpv6 = proplists:get_value(ip_v6_disabled, SshOptions, false), + DisableIpv6 = proplists:get_value(ipv6_disabled, SshOptions, false), Inet = inetopt(DisableIpv6), do_connect(Host, Port, [Inet | SocketOptions], [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)], Timeout, DisableIpv6) @@ -169,7 +173,7 @@ daemon(HostAddr, Port, Options0) -> _ -> Options0 end, - DisableIpv6 = proplists:get_value(ip_v6_disabled, Options0, false), + DisableIpv6 = proplists:get_value(ipv6_disabled, Options0, false), {Host, Inet, Options} = case HostAddr of any -> {ok, Host0} = inet:gethostname(), @@ -264,7 +268,7 @@ start_daemon(Host, Port, Options, Inet) -> do_start_daemon(Host, Port, Options, SocketOptions) -> case ssh_system_sup:system_supervisor(Host, Port) of undefined -> - %% TODO: It would proably make more sense to call the + %% It would proably make more sense to call the %% address option host but that is a too big change at the %% monent. The name is a legacy name! try sshd_sup:start_child([{address, Host}, @@ -325,8 +329,6 @@ handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{user_auth, _} = Opt | Rest],SocketOptions, SshOptions ) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) -> @@ -344,7 +346,10 @@ handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{ip_v6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) -> +%%Backwards compatibility should not be underscore between ip and v6 in API +handle_option([{ip_v6_disabled, Value} | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option({ipv6_disabled, Value}) | SshOptions]); +handle_option([{ipv6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{transport, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); @@ -364,6 +369,8 @@ handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{rekey_limit, _} = Opt|Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions). @@ -377,10 +384,14 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when Value == true; Valu Opt; handle_ssh_option({user_interaction, Value} = Opt) when Value == true; Value == false -> Opt; -handle_ssh_option({public_key_alg, Value} = Opt) when Value == ssh_rsa; Value == ssh_dsa -> +handle_ssh_option({public_key_alg, ssh_dsa}) -> + {public_key_alg, 'ssh-dss'}; +handle_ssh_option({public_key_alg, ssh_rsa}) -> + {public_key_alg, 'ssh-rsa'}; +handle_ssh_option({public_key_alg, Value} = Opt) when Value == 'ssh-rsa'; Value == 'ssh-dss' -> Opt; handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 -> - case check_pref_algs(Value) of + case handle_pref_algs(Value, []) of true -> Opt; _ -> @@ -400,8 +411,6 @@ handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)-> Opt; handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) -> Opt; -handle_ssh_option({user_auth, Value} = Opt) when is_function(Value) -> - Opt; handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) -> Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> @@ -420,7 +429,8 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) -> Opt; handle_ssh_option({failfun, Value} = Opt) when is_function(Value) -> Opt; -handle_ssh_option({ip_v6_disabled, Value} = Opt) when Value == true; + +handle_ssh_option({ipv6_disabled, Value} = Opt) when Value == true; Value == false -> Opt; handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol), @@ -441,6 +451,8 @@ handle_ssh_option({quiet_mode, Value} = Opt) when Value == true; Opt; handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 -> Opt; +handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) -> + Opt; handle_ssh_option(Opt) -> throw({error, {eoptions, Opt}}). @@ -449,7 +461,7 @@ handle_inet_option({active, _} = Opt) -> "and activ is handled internaly user is not allowd" "to specify this option"}}); handle_inet_option({inet, _} = Opt) -> - throw({error, {{eoptions, Opt},"Is set internaly use ip_v6_disabled to" + throw({error, {{eoptions, Opt},"Is set internaly use ipv6_disabled to" " enforce iv4 in the server, client will fallback to ipv4 if" " it can not use ipv6"}}); handle_inet_option({reuseaddr, _} = Opt) -> @@ -459,14 +471,18 @@ handle_inet_option({reuseaddr, _} = Opt) -> handle_inet_option(Opt) -> Opt. %% Check preferred algs -check_pref_algs([]) -> - true; -check_pref_algs([H|T]) -> +handle_pref_algs([], Acc) -> + {true, lists:reverse(Acc)}; +handle_pref_algs([H|T], Acc) -> case H of ssh_dsa -> - check_pref_algs(T); + handle_pref_algs(T, ['ssh-dss'| Acc]); ssh_rsa -> - check_pref_algs(T); + handle_pref_algs(T, ['ssh-rsa'| Acc]); + 'ssh-dss' -> + handle_pref_algs(T, ['ssh-dss'| Acc]); + 'ssh-rsa' -> + handle_pref_algs(T, ['ssh-rsa'| Acc]); _ -> false end. @@ -474,7 +490,7 @@ check_pref_algs([H|T]) -> inetopt(true) -> inet; inetopt(false) -> - case gen_tcp:listen(0, [inet6, {ip, loopback}]) of + case gen_tcp:listen(0, [inet6]) of {ok, Dummyport} -> gen_tcp:close(Dummyport), inet6; diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index c436793dc4..cb0c7751f0 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -48,17 +48,18 @@ publickey_msg([Alg, #ssh{user = User, case KeyCb:user_key(Alg, Opts) of {ok, Key} -> + StrAlgo = algorithm_string(Alg), PubKeyBlob = encode_public_key(Key), SigData = build_sig_data(SessionId, - User, Service, PubKeyBlob, Alg), + User, Service, PubKeyBlob, StrAlgo), Sig = ssh_transport:sign(SigData, Hash, Key), - SigBlob = list_to_binary([?string(Alg), ?binary(Sig)]), + SigBlob = list_to_binary([?string(StrAlgo), ?binary(Sig)]), ssh_transport:ssh_packet( #ssh_msg_userauth_request{user = User, service = Service, method = "publickey", data = [?TRUE, - ?string(Alg), + ?string(StrAlgo), ?binary(PubKeyBlob), ?binary(SigBlob)]}, Ssh); @@ -120,8 +121,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> data = <<>>}, case proplists:get_value(pref_public_key_algs, Opts, false) of false -> - FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts, - ?PREFERRED_PK_ALG)), + FirstAlg = proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG), SecondAlg = other_alg(FirstAlg), AllowUserInt = proplists:get_value(user_interaction, Opts, true), Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt), @@ -130,7 +130,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> userauth_methods = none, service = "ssh-connection"}); Algs -> - FirstAlg = algorithm(lists:nth(1, Algs)), + FirstAlg = lists:nth(1, Algs), case length(Algs) =:= 2 of true -> SecondAlg = other_alg(FirstAlg), @@ -358,7 +358,7 @@ verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> {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 + case KeyCb:is_auth_key(Key, User, Opts) of true -> PlainText = build_sig_data(SessionId, User, Service, KeyBlob, Alg), @@ -381,9 +381,9 @@ build_sig_data(SessionId, User, Service, KeyBlob, Alg) -> ?binary(KeyBlob)], list_to_binary(Sig). -algorithm(ssh_rsa) -> +algorithm_string('ssh-rsa') -> "ssh-rsa"; -algorithm(ssh_dsa) -> +algorithm_string('ssh-dss') -> "ssh-dss". decode_keyboard_interactive_prompts(NumPrompts, Data) -> @@ -457,10 +457,10 @@ userauth_pk_messages() -> binary]} % key blob ]. -other_alg("ssh-rsa") -> - "ssh-dss"; -other_alg("ssh-dss") -> - "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] -> diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl index e74ee10041..6cd8e6bf14 100644 --- a/lib/ssh/src/ssh_auth.hrl +++ b/lib/ssh/src/ssh_auth.hrl @@ -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 @@ -23,7 +23,7 @@ -define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password"). --define(PREFERRED_PK_ALG, ssh_rsa). +-define(PREFERRED_PK_ALG, 'ssh-rsa'). -define(SSH_MSG_USERAUTH_REQUEST, 50). -define(SSH_MSG_USERAUTH_FAILURE, 51). diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index 1938858420..4e8f8538c2 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -23,14 +23,35 @@ -include("ssh_connect.hrl"). -%%% Optional callbacks handle_call/3, handle_cast/2, handle_msg/2, -%%% code_change/3 -%% Should be further specified later --callback init(Options::list()) -> - {ok, State::term()} | {ok, State::term(), Timeout::timeout()} | - {stop, Reason ::term()}. - --callback terminate(term(), term()) -> term(). +-callback init(Args :: term()) -> + {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} | + {stop, Reason :: term()} | ignore. +-callback handle_call(Request :: term(), From :: {pid(), Tag :: term()}, + State :: term()) -> + {reply, Reply :: term(), NewState :: term()} | + {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} | + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), Reply :: term(), NewState :: term()} | + {stop, Reason :: term(), NewState :: term()}. +-callback handle_cast(Request :: term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. + +-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | + term()), + State :: term()) -> + term(). +-callback code_change(OldVsn :: (term() | {down, term()}), State :: term(), + Extra :: term()) -> + {ok, NewState :: term()} | {error, Reason :: term()}. + +-callback handle_msg(Msg ::term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. + -callback handle_ssh_msg({ssh_cm, ConnectionRef::term(), SshMsg::term()}, State::term()) -> {ok, State::term()} | diff --git a/lib/ssh/src/ssh_client_key.erl b/lib/ssh/src/ssh_client_key.erl new file mode 100644 index 0000000000..2c48884dc2 --- /dev/null +++ b/lib/ssh/src/ssh_client_key.erl @@ -0,0 +1,34 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-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 +%% 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% +%% + +-module(ssh_client_key). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). + +-callback is_host_key(Key :: public_key(), Host :: string(), + Algorithm :: 'ssh-rsa'| 'ssh-dsa'| atom(), Options :: proplists:proplist()) -> + boolean(). + +-callback user_key(Algorithm :: 'ssh-rsa'| 'ssh-dsa'| atom(), Options :: list()) -> + {ok, PrivateKey :: term()} | {error, string()}. + + +-callback add_host_key(Host :: string(), PublicKey :: term(), Options :: list()) -> + ok | {error, Error::term()}. diff --git a/lib/ssh/src/ssh_client_key_api.erl b/lib/ssh/src/ssh_client_key_api.erl new file mode 100644 index 0000000000..eed0b85f47 --- /dev/null +++ b/lib/ssh/src/ssh_client_key_api.erl @@ -0,0 +1,35 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-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 +%% 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% +%% + +-module(ssh_client_key_api). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). + +-callback is_host_key(PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term() , Host :: string(), + Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), ConnectOptions :: proplists:proplist()) -> + boolean(). + +-callback user_key(Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), ConnectOptions :: proplists:proplists()) -> + {ok, PrivateKey :: #'RSAPrivateKey'{}| #'DSAPrivateKey'{} | term()} | {error, string()}. + + +-callback add_host_key(Host :: string(), PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term(), + Options :: list()) -> + ok | {error, Error::term()}. diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index d8950a7b67..88b45111ff 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -35,7 +35,8 @@ -export([start_link/4, send/2, renegotiate/1, send_event/2, connection_info/3, - peer_address/1]). + peer_address/1, + renegotiate_data/1]). %% gen_fsm callbacks -export([hello/2, kexinit/2, key_exchange/2, new_keys/2, @@ -85,6 +86,8 @@ send(ConnectionHandler, Data) -> renegotiate(ConnectionHandler) -> send_all_state_event(ConnectionHandler, renegotiate). +renegotiate_data(ConnectionHandler) -> + send_all_state_event(ConnectionHandler, data_size). connection_info(ConnectionHandler, From, Options) -> send_all_state_event(ConnectionHandler, {info, From, Options}). @@ -500,7 +503,22 @@ handle_event(renegotiate, StateName, State) -> handle_event({info, From, Options}, StateName, #state{ssh_params = Ssh} = State) -> spawn(?MODULE, ssh_info_handler, [Options, Ssh, From]), {next_state, StateName, State}; - +handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) -> + Sent = inet:getstat(State#state.socket, [send_oct]), + MaxSent = proplists:get_value(rekey_limit, State#state.opts, 1024000000), + case Sent >= MaxSent of + true -> + {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0), + send_msg(SshPacket, State), + {next_state, connected, + next_packet(State#state{ssh_params = Ssh, + key_exchange_init_msg = KeyInitMsg, + renegotiate = true})}; + _ -> + {next_state, connected, next_packet(State)} + end; +handle_event(data_size, StateName, State) -> + {next_state, StateName, State}; handle_event({unknown, Data}, StateName, State) -> Msg = #ssh_msg_unimplemented{sequence = Data}, send_msg(Msg, State), @@ -749,9 +767,9 @@ extract_algs([], NewList) -> lists:reverse(NewList); extract_algs([H|T], NewList) -> case H of - ssh_dsa -> + 'ssh-dss' -> extract_algs(T, ["ssh-dss"|NewList]); - ssh_rsa -> + 'ssh-rsa' -> extract_algs(T, ["ssh-rsa"|NewList]) end. available_host_key(KeyCb, "ssh-dss"= Alg, Opts) -> diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index 0c1eee5186..94a9ed505f 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -125,7 +125,8 @@ info(ConnectionManager, ChannelProcess) -> %% or amount of data sent counter! renegotiate(ConnectionManager) -> cast(ConnectionManager, renegotiate). - +renegotiate_data(ConnectionManager) -> + cast(ConnectionManager, renegotiate_data). connection_info(ConnectionManager, Options) -> call(ConnectionManager, {connection_info, Options}). @@ -481,7 +482,9 @@ handle_cast({global_request, _, _, _, _} = Request, State0) -> handle_cast(renegotiate, #state{connection = Pid} = State) -> ssh_connection_handler:renegotiate(Pid), {noreply, State}; - +handle_cast(renegotiate_data, #state{connection = Pid} = State) -> + ssh_connection_handler:renegotiate_data(Pid), + {noreply, State}; handle_cast({adjust_window, ChannelId, Bytes}, #state{connection = Pid, connection_state = #connection{channel_cache = Cache}} = State) -> @@ -520,6 +523,8 @@ handle_info({start_connection, server, Exec = proplists:get_value(exec, Options), CliSpec = proplists:get_value(ssh_cli, Options, {ssh_cli, [Shell]}), ssh_connection_handler:send_event(Connection, socket_control), + erlang:send_after(3600000, self(), rekey), + erlang:send_after(60000, self(), rekey_data), {noreply, State#state{connection = Connection, connection_state = CState#connection{address = Address, @@ -536,6 +541,8 @@ handle_info({start_connection, client, case (catch ssh_transport:connect(Parent, Address, Port, SocketOpts, Options)) of {ok, Connection} -> + erlang:send_after(60000, self(), rekey_data), + erlang:send_after(3600000, self(), rekey), {noreply, State#state{connection = Connection}}; Reason -> Pid ! {self(), not_connected, Reason}, @@ -568,8 +575,15 @@ handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, State) -> %%% So that terminate will be run when supervisor is shutdown handle_info({'EXIT', _Sup, Reason}, State) -> - {stop, Reason, State}. - + {stop, Reason, State}; +handle_info(rekey, State) -> + renegotiate(self()), + erlang:send_after(3600000, self(), rekey), + {noreply, State}; +handle_info(rekey_data, State) -> + renegotiate_data(self()), + erlang:send_after(60000, self(), rekey_data), + {noreply, State}. handle_password(Opts) -> handle_rsa_password(handle_dsa_password(handle_normal_password(Opts))). handle_normal_password(Opts) -> diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index a6b82a7a13..f115a32710 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -23,7 +23,8 @@ -module(ssh_file). --behaviour(ssh_key_api). +-behaviour(ssh_server_key_api). +-behaviour(ssh_client_key_api). -include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.hrl"). @@ -34,7 +35,7 @@ user_key/2, is_host_key/4, add_host_key/3, - is_auth_key/4]). + is_auth_key/3]). -define(PERM_700, 8#700). @@ -53,8 +54,8 @@ host_key(Algorithm, Opts) -> decode(File, Password). -is_auth_key(Key, User, Alg, Opts) -> - case lookup_user_key(Key, User, Alg, Opts) of +is_auth_key(Key, User,Opts) -> + case lookup_user_key(Key, User, Opts) of {ok, Key} -> true; _ -> @@ -138,13 +139,13 @@ add_host_key(Host, Key, Opts) -> Error end. -lookup_user_key(Key, User, Alg, Opts) -> +lookup_user_key(Key, User, Opts) -> SshDir = ssh_dir({remoteuser,User}, Opts), - case lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys", Opts) of + case lookup_user_key_f(Key, User, SshDir, "authorized_keys", Opts) of {ok, Key} -> {ok, Key}; _ -> - lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys2", Opts) + lookup_user_key_f(Key, User, SshDir, "authorized_keys2", Opts) end. @@ -213,9 +214,9 @@ do_lookup_host_key(Host, Alg, Opts) -> Error -> Error end. -identity_key_filename("ssh-dss") -> +identity_key_filename('ssh-dss') -> "id_dsa"; -identity_key_filename("ssh-rsa") -> +identity_key_filename('ssh-rsa') -> "id_rsa". identity_pass_phrase("ssh-dss") -> @@ -261,9 +262,9 @@ host_name(Atom) when is_atom(Atom) -> host_name(List) -> List. -key_match(#'RSAPublicKey'{}, "ssh-rsa") -> +key_match(#'RSAPublicKey'{}, 'ssh-rsa') -> true; -key_match({_, #'Dss-Parms'{}}, "ssh-dss") -> +key_match({_, #'Dss-Parms'{}}, 'ssh-dss') -> true; key_match(_, _) -> false. @@ -272,11 +273,11 @@ 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, [], _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(_, _User, nouserdir, _Alg, _F, _Opts) -> +lookup_user_key_f(_, _User, nouserdir, _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(Key, _User, Dir, _Alg, F, _Opts) -> +lookup_user_key_f(Key, _User, Dir, F, _Opts) -> FileName = filename:join(Dir, F), case file:open(FileName, [read, binary]) of {ok, Fd} -> diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl index 17a7cebb4a..01fc713569 100644 --- a/lib/ssh/src/ssh_io.erl +++ b/lib/ssh/src/ssh_io.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 diff --git a/lib/ssh/src/ssh_key_api.erl b/lib/ssh/src/ssh_key_api.erl deleted file mode 100644 index 8085c12e21..0000000000 --- a/lib/ssh/src/ssh_key_api.erl +++ /dev/null @@ -1,45 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2011-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 -%% 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% -%% - --module(ssh_key_api). - --include_lib("public_key/include/public_key.hrl"). --include("ssh.hrl"). - --type ssh_algorithm() :: string(). --type file_error() :: file:posix() | badarg | system_limit | terminated. - --callback host_key(Algorithm :: ssh_algorithm(), Options :: list()) -> - {ok, [{public_key(), Attributes::list()}]} | public_key() - | {error, string()}. - --callback user_key(Algorithm :: ssh_algorithm(), Options :: list()) -> - {ok, [{public_key(), Attributes::list()}]} | public_key() - | {error, string()}. - --callback is_host_key(Key :: public_key(), PeerName :: string(), - Algorithm :: ssh_algorithm(), Options :: list()) -> - boolean(). - --callback add_host_key(Host :: string(), Key :: public_key(), Options :: list()) -> - ok | {error, file_error()}. - --callback is_auth_key(Key :: public_key(), User :: string(), - Algorithm :: ssh_algorithm(), Options :: list()) -> - boolean(). diff --git a/lib/ssh/src/ssh_server_key.erl b/lib/ssh/src/ssh_server_key.erl new file mode 100644 index 0000000000..8140114990 --- /dev/null +++ b/lib/ssh/src/ssh_server_key.erl @@ -0,0 +1,33 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-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 +%% 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% +%% + +-module(ssh_server_key). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). + +-type ssh_algorithm() :: string(). + +-callback host_key(Algorithm :: ssh_algorithm(), Options :: list()) -> + {ok, [{public_key(), Attributes::list()}]} | public_key() + | {error, string()}. + +-callback is_auth_key(Key :: public_key(), User :: string(), + Algorithm :: ssh_algorithm(), Options :: list()) -> + boolean(). diff --git a/lib/ssh/src/ssh_server_key_api.erl b/lib/ssh/src/ssh_server_key_api.erl new file mode 100644 index 0000000000..4fd660ecb5 --- /dev/null +++ b/lib/ssh/src/ssh_server_key_api.erl @@ -0,0 +1,30 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-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 +%% 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% +%% + +-module(ssh_server_key_api). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). + +-callback host_key(Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), DaemonOptions :: proplists:proplist()) -> + {ok, PrivateKey :: #'RSAPrivateKey'{}| #'DSAPrivateKey'{} | term()} | {error, string()}. + +-callback is_auth_key(PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term(), + User :: string(), DaemonOptions :: proplists:proplist()) -> + boolean(). diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index ec7b76b0b3..c7e8373840 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -24,7 +24,7 @@ -module(ssh_sftpd). %%-behaviour(gen_server). --behaviour(ssh_channel). +-behaviour(ssh_subsystem). -include_lib("kernel/include/file.hrl"). @@ -36,7 +36,7 @@ -export([subsystem_spec/1, listen/1, listen/2, listen/3, stop/1]). --export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2, code_change/3]). +-export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2]). -record(state, { xf, % [{channel,ssh_xfer states}...] @@ -119,23 +119,13 @@ init(Options) -> {Root0, State0} end, MaxLength = proplists:get_value(max_files, Options, 0), - - Vsn = proplists:get_value(vsn, Options, 5), - + Vsn = proplists:get_value(sftpd_vsn, Options, 5), {ok, State#state{cwd = CWD, root = Root, max_files = MaxLength, handles = [], pending = <<>>, xf = #ssh_xfer{vsn = Vsn, ext = []}}}. %%-------------------------------------------------------------------- -%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - - -%%-------------------------------------------------------------------- %% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State} %% %% Description: Handles channel messages @@ -369,17 +359,21 @@ handle_op(?SSH_FXP_FSETSTAT, ReqId, <<?UINT32(HLen), BinHandle:HLen/binary, State0 end; handle_op(?SSH_FXP_REMOVE, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, - State0 = #state{file_handler = FileMod, file_state = FS0}) -> + State0 = #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn}}) -> Path = relate_file_name(BPath, State0), - %% case FileMod:is_dir(Path) of %% This version 6 we still have ver 5 - %% true -> - %% ssh_xfer:xf_send_status(State#state.xf, ReqId, - %% ?SSH_FX_FILE_IS_A_DIRECTORY); - %% false -> - {Status, FS1} = FileMod:delete(Path, FS0), - State1 = State0#state{file_state = FS1}, - send_status(Status, ReqId, State1); - %%end; + {IsDir, _FS1} = FileMod:is_dir(Path, FS0), + case IsDir of %% This version 6 we still have ver 5 + true when Vsn > 5 -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"); + true -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FAILURE, "File is a directory"); + false -> + {Status, FS1} = FileMod:delete(Path, FS0), + State1 = State0#state{file_state = FS1}, + send_status(Status, ReqId, State1) + end; handle_op(?SSH_FXP_RMDIR, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, State0 = #state{file_handler = FileMod, file_state = FS0}) -> Path = relate_file_name(BPath, State0), @@ -637,31 +631,34 @@ open(Vsn, ReqId, Data, State) when Vsn >= 4 -> do_open(ReqId, State, Path, Flags). do_open(ReqId, State0, Path, Flags) -> - #state{file_handler = FileMod, file_state = FS0, root = Root} = State0, + #state{file_handler = FileMod, file_state = FS0, root = Root, xf = #ssh_xfer{vsn = Vsn}} = State0, XF = State0#state.xf, 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, - %% ?SSH_FX_FILE_IS_A_DIRECTORY); - %% false -> - - AbsPath = case Root of - "" -> - Path; - _ -> - relate_file_name(Path, State0) - end, - - {Res, FS1} = FileMod:open(AbsPath, F, FS0), - State1 = State0#state{file_state = FS1}, - case Res of - {ok, IoDevice} -> - add_handle(State1, XF, ReqId, file, {Path,IoDevice}); - {error, Error} -> - ssh_xfer:xf_send_status(State1#state.xf, ReqId, - ssh_xfer:encode_erlang_status(Error)), - State1 + {IsDir, _FS1} = FileMod:is_dir(Path, FS0), + case IsDir of + true when Vsn > 5 -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"); + true -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FAILURE, "File is a directory"); + false -> + AbsPath = case Root of + "" -> + Path; + _ -> + relate_file_name(Path, State0) + end, + {Res, FS1} = FileMod:open(AbsPath, F, FS0), + State1 = State0#state{file_state = FS1}, + case Res of + {ok, IoDevice} -> + add_handle(State1, XF, ReqId, file, {Path,IoDevice}); + {error, Error} -> + ssh_xfer:xf_send_status(State1#state.xf, ReqId, + ssh_xfer:encode_erlang_status(Error)), + State1 + end end. %% resolve all symlinks in a path diff --git a/lib/ssh/src/ssh_subsystem.erl b/lib/ssh/src/ssh_subsystem.erl new file mode 100644 index 0000000000..5a9fa32668 --- /dev/null +++ b/lib/ssh/src/ssh_subsystem.erl @@ -0,0 +1,47 @@ +-module(ssh_subsystem). + +%% API to special server side channel that can be pluged into the erlang ssh daemeon +-callback init(Args :: term()) -> + {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} | + {stop, Reason :: term()} | ignore. + +-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | + term()), + State :: term()) -> + term(). + +-callback handle_msg(Msg ::term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. + +-callback handle_ssh_msg({ssh_cm, ConnectionRef::term(), SshMsg::term()}, + State::term()) -> {ok, State::term()} | + {stop, ChannelId::integer(), + State::term()}. + +%%% API +-export([start/4, start/5, start_link/4, start_link/5, enter_loop/1]). + +%% gen_server callbacks +-export([init/1, terminate/2]). + +start(ConnectionManager, ChannelId, CallBack, CbInitArgs) -> + ssh_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs, undefined). + +start(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) -> + ssh_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec). + +start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs) -> + ssh_channel:start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, undefined). + +start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) -> + ssh_channel:start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec). + +enter_loop(State) -> + ssh_channel:enter_loop(State). + +init(Args) -> + ssh_channel:init(Args). +terminate(Reason, State) -> + ssh_channel:terminate(Reason, State). diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 7f6e7d9946..1abb69921d 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -449,7 +449,7 @@ verify_host_key_rsa(SSH, K_S, H, H_SIG) -> false -> {error, bad_signature}; true -> - known_host_key(SSH, Public, "ssh-rsa") + known_host_key(SSH, Public, 'ssh-rsa') end; _ -> {error, bad_format} @@ -464,7 +464,7 @@ verify_host_key_dss(SSH, K_S, H, H_SIG) -> false -> {error, bad_signature}; true -> - known_host_key(SSH, Public, "ssh-dss") + known_host_key(SSH, Public, 'ssh-dss') end; _ -> {error, bad_host_key_format} diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index d5b6dd03d1..4dfd9ed8b0 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -383,6 +383,8 @@ decode_status(Status) -> ?SSH_FX_UNKNOWN_PRINCIPLE -> unknown_principle; ?SSH_FX_LOCK_CONFlICT -> lock_conflict; ?SSH_FX_NOT_A_DIRECTORY -> not_a_directory; + ?SSH_FX_FILE_IS_A_DIRECTORY -> file_is_a_directory; + ?SSH_FX_CANNOT_DELETE -> cannot_delete; _ -> {error,Status} end. @@ -392,6 +394,9 @@ encode_erlang_status(Status) -> eof -> ?SSH_FX_EOF; enoent -> ?SSH_FX_NO_SUCH_FILE; eacces -> ?SSH_FX_PERMISSION_DENIED; + eisdir -> ?SSH_FX_FILE_IS_A_DIRECTORY; + eperm -> ?SSH_FX_CANNOT_DELETE; + eexist -> ?SSH_FX_FILE_ALREADY_EXISTS; _ -> ?SSH_FX_FAILURE end. diff --git a/lib/ssh/src/ssh_xfer.hrl b/lib/ssh/src/ssh_xfer.hrl index c13950eb6e..0d85cf2094 100644 --- a/lib/ssh/src/ssh_xfer.hrl +++ b/lib/ssh/src/ssh_xfer.hrl @@ -58,7 +58,6 @@ %%% # SSH_FX_xxx %%% Description: Response packet types for file transfer protocol. %%%---------------------------------------------------------------------- - -define(SSH_FX_OK, 0). -define(SSH_FX_EOF, 1). -define(SSH_FX_NO_SUCH_FILE, 2). @@ -79,7 +78,18 @@ -define(SSH_FX_LOCK_CONFlICT, 17). -define(SSH_FX_DIR_NOT_EMPTY, 18). -define(SSH_FX_NOT_A_DIRECTORY, 19). +-define(SSH_FX_INVALID_FILENAME, 20). +-define(SSH_FX_LINK_LOOP, 21). +-define(SSH_FX_CANNOT_DELETE, 22). +-define(SSH_FX_INVALID_PARAMETER, 23). -define(SSH_FX_FILE_IS_A_DIRECTORY, 24). +-define(SSH_FX_BYTE_RANGE_LOCK_CONFLICT,25). +-define(SSH_FX_BYTE_RANGE_LOCK_REFUSED, 26). +-define(SSH_FX_DELETE_PENDING, 27). +-define(SSH_FX_FILE_CORRUPT, 28). +-define(SSH_FX_OWNER_INVALID, 29). +-define(SSH_FX_GROUP_INVALID, 30). +-define(SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK,31). %%%---------------------------------------------------------------------- %%% # SSH_FILEXFER_xxx diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 5fec7f0cd7..efcb11f88f 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -42,15 +42,14 @@ all() -> {group, dsa_pass_key}, {group, rsa_pass_key}, {group, internal_error}, - {group, idle_time}, daemon_already_started, server_password_option, server_userpassword_option, close]. groups() -> - [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time]}, - {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time]}, + [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]}, + {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]}, {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, {internal_error, [], [internal_error]} @@ -247,7 +246,8 @@ idle_time(Config) -> ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, - {user_interaction, false}]), + {user_interaction, false}, + {idle_time, 2000}]), {ok, Id} = ssh_connection:session_channel(ConnectionRef, 1000), ssh_connection:close(ConnectionRef, Id), receive @@ -256,6 +256,28 @@ idle_time(Config) -> end, ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +rekey(doc) -> + ["Idle timeout test"]; +rekey(Config) -> + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}, + {rekey_limit, 0}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user_interaction, false}, + {rekey_limit, 0}]), + receive + after 15000 -> + %%By this time rekeying would have been done + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid) + end. +%%-------------------------------------------------------------------- shell(doc) -> ["Test that ssh:shell/2 works"]; shell(Config) when is_list(Config) -> diff --git a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl index 739aabe6fb..007b00c373 100644 --- a/lib/ssh/test/ssh_echo_server.erl +++ b/lib/ssh/test/ssh_echo_server.erl @@ -21,7 +21,7 @@ %%% Description: Example ssh server -module(ssh_echo_server). --behaviour(ssh_channel). +-behaviour(ssh_subsytem). -record(state, { n, id, @@ -50,7 +50,7 @@ handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) end; handle_ssh_msg({ssh_cm, _ConnectionManager, {data, _ChannelId, 1, Data}}, State) -> - error_logger:format("ssh: STDERR: ~s\n", [binary_to_list(Data)]), + error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]), {ok, State}; handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) -> diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index b995eb9f0e..5aa46872ee 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -58,7 +58,8 @@ all() -> links, ver3_rename, relpath, - sshd_read_file]. + sshd_read_file, + ver6_basic]. groups() -> []. @@ -106,12 +107,18 @@ init_per_testcase(TestCase, Config) -> SystemDir = filename:join(?config(priv_dir, Config), system), Port = ssh_test_lib:inet_port(node()), - - {ok, Sftpd} = - ssh_sftpd:listen(Port, [{system_dir, SystemDir}, - {user_dir, PrivDir}, - {user_passwords,[{?USER, ?PASSWD}]}, - {pwdfun, fun(_,_) -> true end}]), + Options = [{system_dir, SystemDir}, + {user_dir, PrivDir}, + {user_passwords,[{?USER, ?PASSWD}]}, + {pwdfun, fun(_,_) -> true end}], + {ok, Sftpd} = case TestCase of + ver6_basic -> + SubSystems = [ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}])], + ssh:daemon(Port, [{subsystems, SubSystems}|Options]); + _ -> + SubSystems = [ssh_sftpd:subsystem_spec([])], + ssh:daemon(Port, [{subsystems, SubSystems}|Options]) + end, Cm = ssh_test_lib:connect(Port, [{user_dir, ClientUserDir}, @@ -341,7 +348,7 @@ mk_rm_dir(Config) when is_list(Config) -> _/binary>>, _} = mkdir(DirName, Cm, Channel, ReqId), NewReqId = 1, - {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FAILURE), + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} = mkdir(DirName, Cm, Channel, NewReqId), NewReqId1 = 2, @@ -591,7 +598,18 @@ sshd_read_file(Config) when is_list(Config) -> read_file(Handle, 100, 0, Cm, Channel, NewReqId), {ok, Data} = file:read_file(FileName). - +ver6_basic(doc) -> + ["Test SFTP Version 6"]; +ver6_basic(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + %FileName = filename:join(PrivDir, "test.txt"), + {Cm, Channel} = ?config(sftp, Config), + ReqId = 0, + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), % Ver 6 we have 5 + ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} = + open_file(PrivDir, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index 7fc2312661..8f722941d4 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -42,7 +42,8 @@ all() -> quit, file_cb, root_dir, - list_dir_limited]. + list_dir_limited, + ver6_basic]. groups() -> []. @@ -112,7 +113,12 @@ init_per_testcase(TestCase, Config) -> [{system_dir, SystemDir}, {user_dir, PrivDir}, {subsystems, [Spec]}]; - + "ver6_basic" -> + Spec = + ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}]), + [{system_dir, SystemDir}, + {user_dir, PrivDir}, + {subsystems, [Spec]}]; _ -> [{user_dir, PrivDir}, {system_dir, SystemDir}] @@ -232,7 +238,7 @@ file_cb(Config) when is_list(Config) -> NewDir = filename:join(PrivDir, "testdir"), ok = ssh_sftp:make_dir(Sftp, NewDir), alt_file_handler_check(alt_make_dir), - + ok = ssh_sftp:del_dir(Sftp, NewDir), alt_file_handler_check(alt_read_link_info), alt_file_handler_check(alt_write_file_info), @@ -260,6 +266,15 @@ list_dir_limited(Config) when is_list(Config) -> ssh_sftp:list_dir(Sftp, "."), ct:pal("Listing: ~p~n", [Listing]). +ver6_basic(doc) -> + ["Test some version 6 features"]; +ver6_basic(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + NewDir = filename:join(PrivDir, "testdir2"), + {Sftp, _} = ?config(sftp, Config), + ok = ssh_sftp:make_dir(Sftp, NewDir), + %%Test file_is_a_directory + {error, file_is_a_directory} = ssh_sftp:delete(Sftp, NewDir). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl index 9e119c4929..9f8a7c496c 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl @@ -48,7 +48,7 @@ get_cwd(State) -> {file:get_cwd(), State}. is_dir(AbsPath, State) -> - sftpd_file_alt_tester ! alt_is_dir, + %sftpd_file_alt_tester ! alt_is_dir, {filelib:is_dir(AbsPath), State}. list_dir(AbsPath, State) -> |