diff options
Diffstat (limited to 'lib/ssl')
95 files changed, 8242 insertions, 9117 deletions
diff --git a/lib/ssl/Makefile b/lib/ssl/Makefile index a3dec8da38..daad7dc3e6 100644 --- a/lib/ssl/Makefile +++ b/lib/ssl/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1999-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 1999-2010. 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% # @@ -24,22 +24,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # # Macros # -ifeq ($(findstring win32,$(TARGET)),win32) -ifeq ($(HOST_OS),) -HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess) -endif -ifeq ($(findstring solaris,$(HOST_OS)),solaris) -SKIP_BUILDING_BINARIES := true -endif -else -SKIP_BUILDING_BINARIES := false -endif - -ifeq ($(SKIP_BUILDING_BINARIES), true) -SUB_DIRECTORIES = pkix src c_src doc/src -else -SUB_DIRECTORIES = pkix src c_src doc/src examples/certs examples/src -endif + +SUB_DIRECTORIES = src c_src doc/src examples/certs examples/src include vsn.mk VSN = $(SSL_VSN) diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile index fa263d28ab..3119d37af0 100644 --- a/lib/ssl/doc/src/Makefile +++ b/lib/ssl/doc/src/Makefile @@ -37,7 +37,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = refman.xml -XML_REF3_FILES = ssl.xml new_ssl.xml +XML_REF3_FILES = ssl.xml old_ssl.xml ssl_session_cache_api.xml XML_REF6_FILES = ssl_app.xml XML_PART_FILES = release_notes.xml usersguide.xml @@ -45,9 +45,7 @@ XML_CHAPTER_FILES = \ ssl_protocol.xml \ using_ssl.xml \ pkix_certs.xml \ - create_certs.xml \ ssl_distribution.xml \ - licenses.xml \ notes.xml BOOK_FILES = book.xml diff --git a/lib/ssl/doc/src/book.xml b/lib/ssl/doc/src/book.xml index 9122addb74..85d6b56b26 100644 --- a/lib/ssl/doc/src/book.xml +++ b/lib/ssl/doc/src/book.xml @@ -28,9 +28,6 @@ <rev>A</rev> <file>book.sgml</file> </header> - <insidecover> - <include file="insidecover"></include> - </insidecover> <pagetext>SSL Application</pagetext> <preamble> <contents level="2"></contents> diff --git a/lib/ssl/doc/src/create_certs.xml b/lib/ssl/doc/src/create_certs.xml deleted file mode 100644 index 79cc8a0537..0000000000 --- a/lib/ssl/doc/src/create_certs.xml +++ /dev/null @@ -1,148 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</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>Creating Certificates</title> - <prepared>UAB/F/P Peter Högfeldt</prepared> - <docno></docno> - <date>2003-06-16</date> - <rev>A</rev> - <file>create_certs.xml</file> - </header> - <p>Here we consider the creation of example certificates. - </p> - - <section> - <title>The openssl Command</title> - <p>The <c>openssl</c> command is a utility that comes with the - OpenSSL distribution. It provides a variety of subcommands. Each - subcommand is invoked as</p> - <code type="none"><![CDATA[ - openssl subcmd <options and arguments> ]]></code> - <p>where <c>subcmd</c> denotes the subcommand in question. - </p> - <p>We shall use the following subcommands to create certificates for - the purpose of testing Erlang/OTP SSL: - </p> - <list type="bulleted"> - <item><em>req</em> to create certificate requests and a - self-signed certificates, - </item> - <item><em>ca</em> to create certificates from certificate requests.</item> - </list> - <p>We create the following certificates: - </p> - <list type="bulleted"> - <item>the <em>erlangCA</em> root certificate (a self-signed - certificate), </item> - <item>the <em>otpCA</em> certificate signed by the <em>erlangCA</em>, </item> - <item>a client certificate signed by the <em>otpCA</em>, and</item> - <item>a server certificate signed by the <em>otpCA</em>.</item> - </list> - - <section> - <title>The openssl configuration file</title> - <p>An <c>openssl</c> configuration file consist of a number of - sections, where each section starts with one line containing - <c>[ section_name ]</c>, where <c>section_name</c> is the name - of the section. The first section of the file is either - unnamed, or is named <c>[ default ]</c>. For further details - see the OpenSSL config(5) manual page. - </p> - <p>The required sections for the subcommands we are going to - use are as follows: - </p> - <table> - <row> - <cell align="left" valign="middle">subcommand</cell> - <cell align="left" valign="middle">required/default section</cell> - <cell align="left" valign="middle">override command line option</cell> - <cell align="left" valign="middle">configuration file option</cell> - </row> - <row> - <cell align="left" valign="middle">req</cell> - <cell align="left" valign="middle">[req]</cell> - <cell align="left" valign="middle">-</cell> - <cell align="left" valign="middle"><c>-config FILE</c></cell> - </row> - <row> - <cell align="left" valign="middle">ca</cell> - <cell align="left" valign="middle">[ca]</cell> - <cell align="left" valign="middle"><c>-name section</c></cell> - <cell align="left" valign="middle"><c>-config FILE</c></cell> - </row> - <tcaption>openssl subcommands to use</tcaption> - </table> - </section> - - <section> - <title>Creating the Erlang root CA</title> - <p>The Erlang root CA is created with the command</p> - <code type="none"> - openssl req -new -x509 -config /some/path/req.cnf \\ - -keyout /some/path/key.pem -out /some/path/cert.pem </code> - <p>where the option <c>-new</c> indicates that we want to create - a new certificate request and the option <c>-x509</c> implies - that a self-signed certificate is created. - </p> - </section> - - <section> - <title>Creating the OTP CA</title> - <p>The OTP CA is created by first creating a certificate request - with the command</p> - <code type="none"> - openssl req -new -config /some/path/req.cnf \\ - -keyout /some/path/key.pem -out /some/path/req.pem </code> - <p>and the ask the Erlang CA to sign it:</p> - <code type="none"> - openssl ca -batch -notext -config /some/path/req.cnf \\ - -extensions ca_cert -in /some/path/req.pem -out /some/path/cert.pem </code> - <p>where the option <c>-extensions</c> refers to a section in the - configuration file saying that it should create a CA certificate, - and not a plain user certificate. - </p> - <p>The <c>client</c> and <c>server</c> certificates are created - similarly, except that the option <c>-extensions</c> then has the - value <c>user_cert</c>. - </p> - </section> - </section> - - <section> - <title>An Example</title> - <p>The following module <c>create_certs</c> is used by the Erlang/OTP - SSL application for generating certificates to be used in tests. The - source code is also found in <c>ssl-X.Y.Z/examples/certs/src</c>. - </p> - <p>The purpose of the <c>create_certs:all/1</c> function is to make - it possible to provide from the <c>erl</c> command line, the - full path name of the <c>openssl</c> command. - </p> - <p>Note that the module creates temporary OpenSSL configuration files - for the <c>req</c> and <c>ca</c> subcommands. - </p> - <codeinclude file="../../examples/certs/src/make_certs.erl" tag="" type="erl"></codeinclude> - </section> -</chapter> - - diff --git a/lib/ssl/doc/src/insidecover.xml b/lib/ssl/doc/src/insidecover.xml deleted file mode 100644 index 4f3f5e5951..0000000000 --- a/lib/ssl/doc/src/insidecover.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE bookinsidecover SYSTEM "bookinsidecover.dtd"> - -<bookinsidecover> -The Erlang/OTP SSL application includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/). Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. <br></br> -This product includes cryptographic software written by Eric Young ([email protected]). This product includes software written by Tim Hudson ([email protected]). Copyright (C) 1995-1998 Eric Young ([email protected]). All rights reserved. <br></br> -For further OpenSSL and SSLeay license information se the chapter <bold>Licenses</bold> -. <vfill></vfill> - <br></br> - <tt>http://www.erlang.org</tt> - <br></br> -</bookinsidecover> - - diff --git a/lib/ssl/doc/src/licenses.xml b/lib/ssl/doc/src/licenses.xml deleted file mode 100644 index 0969f9ad6e..0000000000 --- a/lib/ssl/doc/src/licenses.xml +++ /dev/null @@ -1,156 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</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>Licenses</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-05-26</date> - <rev>A</rev> - <file>licenses.xml</file> - </header> - <p> <marker id="licenses"></marker> -This chapter contains in extenso versions - of the OpenSSL and SSLeay licenses. - </p> - - <section> - <title>OpenSSL License</title> - <code type="none"> -/* ==================================================================== - * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * [email protected]. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * ([email protected]). This product includes software written by Tim - * Hudson ([email protected]). - * - */ </code> - </section> - - <section> - <title>SSLeay License</title> - <code type="none"> -/* Copyright (C) 1995-1998 Eric Young ([email protected]) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young ([email protected]). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson ([email protected]). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young ([email protected])" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson ([email protected])" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ </code> - </section> -</chapter> - - diff --git a/lib/ssl/doc/src/make.dep b/lib/ssl/doc/src/make.dep deleted file mode 100644 index 2ff81bee1f..0000000000 --- a/lib/ssl/doc/src/make.dep +++ /dev/null @@ -1,30 +0,0 @@ -# ---------------------------------------------------- -# >>>> Do not edit this file <<<< -# This file was automaticly generated by -# /home/otp/bin/docdepend -# ---------------------------------------------------- - - -# ---------------------------------------------------- -# TeX files that the DVI file depend on -# ---------------------------------------------------- - -book.dvi: book.tex create_certs.tex licenses.tex new_ssl.tex \ - pkix_certs.tex refman.tex ssl.tex ssl_app.tex \ - ssl_distribution.tex ssl_protocol.tex usersguide.tex \ - using_ssl.tex - -# ---------------------------------------------------- -# Source inlined when transforming from source to LaTeX -# ---------------------------------------------------- - -book.tex: refman.xml - -create_certs.tex: ../../examples/certs/src/make_certs.erl - -using_ssl.tex: ../../examples/src/client_server.erl - -pkix_certs.tex: ../../../../system/doc/definitions/cite.defs - -ssl_protocol.tex: ../../../../system/doc/definitions/cite.defs - diff --git a/lib/ssl/doc/src/new_ssl.xml b/lib/ssl/doc/src/new_ssl.xml deleted file mode 100644 index 08868a1b3c..0000000000 --- a/lib/ssl/doc/src/new_ssl.xml +++ /dev/null @@ -1,681 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE erlref SYSTEM "erlref.dtd"> - -<erlref> - <header> - <copyright> - <year>1999</year> - <year>2007</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 aniline's 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>ssl</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible>Ingela Anderton Andin</responsible> - <docno></docno> - <approved></approved> - <checked></checked> - <date>2003-03-25</date> - <rev></rev> - <file>new_ssl.xml</file> - </header> - <module>new_ssl</module> - <modulesummary>Interface Functions for Secure Socket Layer</modulesummary> - <description> - <p>This module contains interface functions to the Secure Socket - Layer. - </p> - </description> - - <section> - <title>NEW SSL</title> - - <p>This manual page describes functions that are defined - in the ssl module and represents the new ssl implementation - that coexists with the old one, as the new implementation - is not yet complete enough to replace the old one.</p> - - <p>The new implementation can be - accessed by providing the option {ssl_imp, new} to the - ssl:connect and ssl:listen functions.</p> - - <p>The new implementation is Erlang based and all logic - is in Erlang and only payload encryption calculations are - done in C via the crypto application. The main reason for - making a new implementation is that the old solution was - very crippled as the control of the ssl-socket was deep - down in openssl making it hard if not impossible to - support all inet options, ipv6 and upgrade of a tcp - connection to a ssl connection. This version has a - few limitations that will be removed before the ssl-4.0 - release. Main differences and limitations are listed below.</p> - - <list type="bulleted"> - <item>New ssl requires the crypto - application.</item> - <item>The option reuseaddr is - supported and the default value is false as in gen_tcp. - Old ssl is patched to accept that the option is set to - true to provide a smoother migration between the - versions. In old ssl the option is hard coded to - true.</item> - <item>ssl:version/0 is replaced by - ssl:versions/0</item> - <item>ssl:ciphers/0 is replaced by - ssl:cipher_suites/0</item> - <item>ssl:pid/1 is a - meaningless function in new ssl and will be deprecated in - ssl-4.0 until it is removed it will return a valid but - meaningless pid.</item> - <item>New API functions are - ssl:shutdown/2, ssl:cipher_suites/[0,1] and - ssl:versions/0</item> - <item>CRL and policy certificate - extensions are not supported yet. </item> - <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item> - <item>For security reasons sslv2 is not supported.</item> - </list> - - </section> - - <section> - <title>COMMON DATA TYPES</title> - <p>The following data types are used in the functions below: - </p> - - <p><c>boolean() = true | false</c></p> - - <p><c>property() = atom()</c></p> - - <p><c>option() = socketoption() | ssloption() | transportoption()</c></p> - - <p><c>socketoption() = [{property(), term()}] - defaults to - [{mode,list},{packet, 0},{header, 0},{active, true}]. - </c></p> - - <p>For valid options - see <seealso marker="kernel:inet">inet(3) </seealso> and - <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso>. - </p> - - <p> <c>ssloption() = {verify, verify_type()} | - {fail_if_no_peer_cert, boolean()} - {depth, integer()} | - {certfile, path()} | {keyfile, path()} | {password, string()} | - {cacertfile, path()} | {dhfile, path()} | {ciphers, ciphers()} | - {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} - </c></p> - - <p><c>transportoption() = {CallbackModule, DataTag, ClosedTag} - - defaults to {gen_tcp, tcp, tcp_closed}. Ssl may be - run over any reliable transport protocol that has - an equivalent API to gen_tcp's.</c></p> - - <p><c> CallbackModule = - atom()</c> - </p> <p><c> DataTag = - atom() - tag used in socket data message.</c></p> - <p><c> ClosedTag = atom() - tag used in - socket close message.</c></p> - - <p><c>verify_type() = verify_none | verify_peer</c></p> - - <p><c>path() = string() - representing a file path.</c></p> - - <p><c>host() = hostname() | ipaddress()</c></p> - - <p><c>hostname() = string()</c></p> - - <p><c> - ip_address() = {N1,N2,N3,N4} % IPv4 - | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 </c></p> - - <p><c>sslsocket() - opaque to the user. </c></p> - - <p><c>protocol() = sslv3 | tlsv1 </c></p> - - <p><c>ciphers() = [ciphersuite()] | sting() (according to old API)</c></p> - - <p><c>ciphersuite() = - {key_exchange(), cipher(), hash(), exportable()}</c></p> - - <p><c>key_exchange() = rsa | dh_dss | dh_rsa | dh_anon | dhe_dss - | dhe_rsa | krb5 | KeyExchange_export - </c></p> - - <p><c>cipher() = rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc' - des40_cbc | dh_dss | aes_128_cbc | aes_256_cbc | - rc2_cbc_40 | rc4_40 </c></p> - - <p> <c>hash() = md5 | sha - </c></p> - - <p> <c>exportable() = export | no_export | ignore - </c></p> - - <p><c>ssl_imp() = new | old - default is old.</c></p> - - </section> - -<section> - <title>SSL OPTION DESCRIPTIONS</title> - - <taglist> - <tag>{verify, verify_type()}</tag> - <item> If <c>verify_none</c> is specified x509-certificate - path validation errors at the client side - will not automatically cause the connection to fail, as - it will if the verify type is <c>verify_peer</c>. See also - the option verify_fun. - Servers only do the path validation if <c>verify_peer</c> is set to - true, as it then will - send a certificate request to - the client (this message is not sent if the verify option is - <c>verify_none</c>) and you may then also want to specify - the option <c>fail_if_no_peer_cert</c>. - </item> - - <tag>{fail_if_no_peer_cert, boolean()}</tag> - <item>Used together with {verify, verify_peer} by a ssl server. - If set to true, - the server will fail if the client does not have a certificate - to send, e.i sends a empty certificate, if set to false it will - only fail if the client sends a invalid certificate (an empty - certificate is considered valid). - </item> - - <tag>{verify_fun, fun(ErrorList) -> boolean()}</tag> - <item>Used by the ssl client to determine if - x509-certificate path validations errors are acceptable or - if the connection should fail. Defaults to: - -<code> -fun(ErrorList) -> - case lists:foldl(fun({bad_cert,unknown_ca}, Acc) -> - Acc; - (Other, Acc) -> - [Other | Acc] - end, [], ErrorList) of - [] -> - true; - [_|_] -> - false - end -end -</code> - I.e. by default if the only error found was that the CA-certificate - holder was unknown this will be accepted. - - Possible errors in the error list are: - {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, - {bad_cert, invalid_signature}, {bad_cert, name_not_permitted}, - {bad_cert, unknown_ca}, - {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, - {bad_cert, invalid_signature}, {bad_cert, name_not_permitted}, - {bad_cert, cert_revoked} (not implemented yet), - {bad_cert, unknown_critical_extension} or {bad_cert, term()} (Will - be relevant later when an option is added for the user to be able to verify application specific extensions.) - </item> - - <tag>{depth, integer()}</tag> - <item>Specifies the maximum - verification depth, i.e. how far in a chain of certificates the - verification process can proceed before the verification is - considered to fail. Peer certificate = 0, CA certificate = 1, - higher level CA certificate = 2, etc. The value 2 thus means - that a chain can at most contain peer cert, CA cert, next CA - cert, and an additional CA cert. The default value is 1. - </item> - - <tag>{certfile, path()}</tag> - <item>Path to a file containing the - user's certificate. Optional for clients but note - that some servers requires that the client can certify - itself. </item> - <tag>{keyfile, path()}</tag> - <item>Path to file containing user's - private PEM encoded key. As PEM-files may contain several - entries this option defaults to the same file as given by - certfile option.</item> - <tag>{password, string()}</tag> - <item>String containing the user's password. - Only used if the private keyfile is password protected. - </item> - <tag>{cacertfile, path()}</tag> - <item>Path to file containing PEM encoded - CA certificates (trusted certificates used for verifying a peer - certificate). May be omitted if you do not want to verify - the peer.</item> - - <tag>{dhfile, path()}</tag> - <item>Path to file containing PEM encoded Diffie Hellman parameters, - for the server to use if a cipher suite using Diffie Hellman key exchange - is negotiated. If not specified hardcode parameters will be used. - </item> - - <tag>{ciphers, ciphers()}</tag> - <item>The function <c>ciphers_suites/0</c> can - be used to find all available ciphers. - </item> - - <tag>{ssl_imp, ssl_imp()}</tag> - <item>Specify which ssl implementation you want to use. - </item> - - <tag>{reuse_sessions, boolean()}</tag> - <item>Specifies if ssl sessions should be reused - when possible. - </item> - - <tag>{reuse_session, fun(SuggestedSessionId, - PeerCert, Compression, CipherSuite) -> boolean()}</tag> - <item>Enables the ssl server to have a local policy - for deciding if a session should be reused or not, - only meaning full if <c>reuse_sessions</c> is set to true. - SuggestedSessionId is a binary(), PeerCert is a DER encoded - certificate, Compression is an enumeration integer - and CipherSuite of type ciphersuite(). - </item> - </taglist> - </section> - - <section> - <title>General</title> - - <p>When a ssl socket is in active mode (the default), data from the - socket is delivered to the owner of the socket in the form of - messages: - </p> - <list type="bulleted"> - <item>{ssl, Socket, Data} - </item> - <item>{ssl_closed, Socket} - </item> - <item> - {ssl_error, Socket, Reason} - </item> - </list> - - <p>A <c>Timeout</c> argument specifies a timeout in milliseconds. The - default value for a <c>Timeout</c> argument is <c>infinity</c>. - </p> - </section> - - <funcs> - <func> - <name>cipher_suites() -></name> - <name>cipher_suites(Type) -> ciphers()</name> - <fsummary> Returns a list of supported cipher suites</fsummary> - <type> - <v>Type = erlang | openssl</v> - - </type> - <desc><p>Returns a list of supported cipher suites. - cipher_suites() is equivalent to cipher_suites(erlang). - Type openssl is provided for backwards compatibility with - old ssl that used openssl. - </p> - </desc> - </func> - - <func> - <name>connect(Socket, SslOptions) -> </name> - <name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket} - | {error, Reason}</name> - <fsummary> Upgrades a gen_tcp, or - equivalent, connected socket to a ssl socket. </fsummary> - <type> - <v>Socket = socket()</v> - <v>SslOptions = [ssloption()]</v> - <v>Timeout = integer() | infinity</v> - <v>SslSocket = sslsocket()</v> - <v>Reason = term()</v> - </type> - <desc> <p>Upgrades a gen_tcp, or equivalent, - connected socket to a ssl socket e.i performs the - client-side ssl handshake.</p> - </desc> - </func> - - <func> - <name>connect(Host, Port, Options) -></name> - <name>connect(Host, Port, Options, Timeout) -> - {ok, SslSocket} | {error, Reason}</name> - <fsummary>Opens an ssl connection to Host, Port. </fsummary> - <type> - <v>Host = host()</v> - <v>Port = integer()</v> - <v>Options = [option()]</v> - <v>Timeout = integer() | infinity</v> - <v>SslSocket = sslsocket()</v> - <v>Reason = term()</v> - </type> - <desc> <p>Opens an ssl connection to Host, Port.</p> </desc> - </func> - - <func> - <name>close(SslSocket) -> ok | {error, Reason}</name> - <fsummary>Close a ssl connection</fsummary> - <type> - <v>SslSocket = sslsocket()</v> - <v>Reason = term()</v> - </type> - <desc><p>Close a ssl connection.</p> - </desc> - </func> - - <func> - <name>controlling_process(SslSocket, NewOwner) -> - ok | {error, Reason}</name> - - <fsummary>Assigns a new controlling process to the - ssl-socket.</fsummary> - - <type> - <v>SslSocket = sslsocket()</v> - <v>NewOwner = pid()</v> - <v>Reason = term()</v> - </type> - <desc><p>Assigns a new controlling process to the ssl-socket. A - controlling process is the owner of a ssl-socket, and receives - all messages from the socket.</p> - </desc> - </func> - - <func> - <name>connection_info(SslSocket) -> - {ok, {ProtocolVersion, CipherSuite}} | {error, Reason} </name> - <fsummary>Returns the negotiated protocol version and cipher suite. - </fsummary> - <type> - <v>CipherSuite = ciphersuite()</v> - <v>ProtocolVersion = protocol()</v> - </type> - <desc><p>Returns the negotiated protocol version and cipher suite.</p> - </desc> - </func> - - <func> - <name>getopts(Socket) -> </name> - <name>getopts(Socket, OptionNames) -> - {ok, [socketoption()]} | {error, Reason}</name> - <fsummary>Get the value of the specified options.</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>OptionNames = [property()]</v> - </type> - <desc> - <p>Get the value of the specified socket options, if no - options are specified all options are returned. - </p> - </desc> - </func> - - <func> - <name>listen(Port, Options) -> - {ok, ListenSocket} | {error, Reason}</name> - <fsummary>Creates a ssl listen socket.</fsummary> - <type> - <v>Port = integer()</v> - <v>Options = options()</v> - <v>ListenSocket = sslsocket()</v> - </type> - <desc> - <p>Creates a ssl listen socket.</p> - </desc> - </func> - - <func> - <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name> - <fsummary>Return the peer certificate.</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>Cert = binary()</v> - <v>Subject = term()</v> - </type> - <desc> - <p>The peer certificate is returned as a DER encoded binary. - The certificate can be decoded with <c>public_key:pkix_decode_cert/2</c>. - </p> - </desc> - </func> - <func> - <name>peername(Socket) -> {ok, {Address, Port}} | - {error, Reason}</name> - <fsummary>Return peer address and port.</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>Address = ipaddress()</v> - <v>Port = integer()</v> - </type> - <desc> - <p>Returns the address and port number of the peer.</p> - </desc> - </func> - - <func> - <name>recv(Socket, Length) -> </name> - <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error, - Reason}</name> - <fsummary>Receive data on a socket.</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>Length = integer()</v> - <v>Timeout = integer()</v> - <v>Data = [char()] | binary()</v> - </type> - <desc> - <p>This function receives a packet from a socket in passive - mode. A closed socket is indicated by a return value - <c>{error, closed}</c>.</p> - <p>The <c>Length</c> argument is only meaningful when - the socket is in <c>raw</c> mode and denotes the number of - bytes to read. If <c>Length</c> = 0, all available bytes are - returned. If <c>Length</c> > 0, exactly <c>Length</c> - bytes are returned, or an error; possibly discarding less - than <c>Length</c> bytes of data when the socket gets closed - from the other side.</p> - <p>The optional <c>Timeout</c> parameter specifies a timeout in - milliseconds. The default value is <c>infinity</c>.</p> - </desc> - </func> - - <func> - <name>renegotiate(Socket) -> ok | {error, Reason}</name> - <fsummary> Initiates a new handshake.</fsummary> - <type> - <v>Socket = sslsocket()</v> - </type> - <desc><p>Initiates a new handshake. A notable return value is - <c>{error, renegotiation_rejected}</c> indicating that the peer - refused to go through with the renegotiation but the connection - is still active using the previously negotiated session.</p> - </desc> - </func> - - <func> - <name>send(Socket, Data) -> ok | {error, Reason}</name> - <fsummary>Write data to a socket.</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>Data = iolist() | binary()</v> - </type> - <desc> - <p>Writes <c>Data</c> to <c>Socket</c>. </p> - <p>A notable return value is <c>{error, closed}</c> indicating that - the socket is closed.</p> - </desc> - </func> - <func> - <name>setopts(Socket, Options) -> ok | {error, Reason}</name> - <fsummary>Set socket options.</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>Options = [socketoption]()</v> - </type> - <desc> - <p>Sets options according to <c>Options</c> for the socket - <c>Socket</c>. </p> - </desc> - </func> - - <func> - <name>shutdown(Socket, How) -> ok | {error, Reason}</name> - <fsummary>Immediately close a socket</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>How = read | write | read_write</v> - <v>Reason = reason()</v> - </type> - <desc> - <p>Immediately close a socket in one or two directions.</p> - <p><c>How == write</c> means closing the socket for writing, - reading from it is still possible.</p> - <p>To be able to handle that the peer has done a shutdown on - the write side, the <c>{exit_on_close, false}</c> option - is useful.</p> - </desc> - </func> - - <func> - <name>ssl_accept(ListenSocket) -> </name> - <name>ssl_accept(ListenSocket, Timeout) -> ok | {error, Reason}</name> - <fsummary>Perform server-side SSL handshake</fsummary> - <type> - <v>ListenSocket = sslsocket()</v> - <v>Timeout = integer()</v> - <v>Reason = term()</v> - </type> - <desc> - <p>The <c>ssl_accept</c> function establish the SSL connection - on the server side. It should be called directly after - <c>transport_accept</c>, in the spawned server-loop.</p> - </desc> - </func> - - <func> - <name>ssl_accept(ListenSocket, SslOptions) -> </name> - <name>ssl_accept(ListenSocket, SslOptions, Timeout) -> {ok, Socket} | {error, Reason}</name> - <fsummary>Perform server-side SSL handshake</fsummary> - <type> - <v>ListenSocket = socket()</v> - <v>SslOptions = ssloptions()</v> - <v>Timeout = integer()</v> - <v>Reason = term()</v> - </type> - <desc> - <p> Upgrades a gen_tcp, or - equivalent, socket to a ssl socket e.i performs the - ssl server-side handshake.</p> - </desc> - </func> - - <func> - <name>sockname(Socket) -> {ok, {Address, Port}} | - {error, Reason}</name> - <fsummary>Return the local address and port.</fsummary> - <type> - <v>Socket = sslsocket()</v> - <v>Address = ipaddress()</v> - <v>Port = integer()</v> - </type> - <desc> - <p>Returns the local address and port number of the socket - <c>Socket</c>.</p> - </desc> - </func> - - <func> - <name>start() -> </name> - <name>start(Type) -> ok | {error, Reason}</name> - <fsummary>Starts the Ssl application. </fsummary> - <type> - <v>Type = permanent | transient | temporary</v> - </type> - <desc> - <p>Starts the Ssl application. Default type - is temporary. - <seealso marker="kernel:application">application(3)</seealso></p> - </desc> - </func> - <func> - <name>stop() -> ok </name> - <fsummary>Stops the Ssl application.</fsummary> - <desc> - <p>Stops the Ssl application. - <seealso marker="kernel:application">application(3)</seealso></p> - </desc> - </func> - - <func> - <name>transport_accept(Socket) -></name> - <name>transport_accept(Socket, Timeout) -> - {ok, NewSocket} | {error, Reason}</name> - <fsummary>Accept an incoming connection and - prepare for <c>ssl_accept</c></fsummary> - <type> - <v>Socket = NewSocket = sslsocket()</v> - <v>Timeout = integer()</v> - <v>Reason = reason()</v> - </type> - <desc> - <p>Accepts an incoming connection request on a listen socket. - <c>ListenSocket</c> must be a socket returned from - <c>listen/2</c>. The socket returned should be passed to - <c>ssl_accept</c> to complete ssl handshaking and - establishing the connection.</p> - <warning> - <p>The socket returned can only be used with <c>ssl_accept</c>, - no traffic can be sent or received before that call.</p> - </warning> - <p>The accepted socket inherits the options set for - <c>ListenSocket</c> in <c>listen/2</c>.</p> - <p>The default - value for <c>Timeout</c> is <c>infinity</c>. If - <c>Timeout</c> is specified, and no connection is accepted - within the given time, <c>{error, timeout}</c> is - returned.</p> - </desc> - </func> - - <func> - <name>versions() -> - [{SslAppVer, SupportedSslVer, AvailableSslVsn}]</name> - <fsummary>Returns version information relevant for the - ssl application.</fsummary> - <type> - <v>SslAppVer = string()</v> - <v>SupportedSslVer = [protocol()]</v> - <v>AvailableSslVsn = [protocol()]</v> - </type> - <desc> - <p> - Returns version information relevant for the - ssl application.</p> - </desc> - </func> - </funcs> - - <section> - <title>SEE ALSO</title> - <p><seealso marker="kernel:inet">inet(3) </seealso> and - <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso> - </p> - </section> - -</erlref> - diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 9d13427677..313f1e59c9 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -30,6 +30,282 @@ </header> <p>This document describes the changes made to the SSL application. </p> + + <section><title>SSL 4.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct handling of client certificate verify message + When checking the client certificate verify message the + server used the wrong algorithm identifier to determine + the signing algorithm, causing a function clause error in + the public_key application when the key-exchange + algorithm and the public key algorithm of the client + certificate happen to differ.</p> + <p> + Own Id: OTP-8897</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + For testing purposes ssl now also support some anonymous + cipher suites when explicitly configured to do so.</p> + <p> + Own Id: OTP-8870</p> + </item> + <item> + <p> + Sends an error alert instead of crashing if a crypto + function for the selected cipher suite fails.</p> + <p> + Own Id: OTP-8930 Aux Id: seq11720 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 4.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Updated ssl to ignore CA certs that violate the asn1-spec + for a certificate, and updated public key asn1 spec to + handle inherited DSS-params.</p> + <p> + Own Id: OTP-7884</p> + </item> + <item> + <p> + Changed ssl implementation to retain backwards + compatibility for old option {verify, 0} that shall be + equivalent to {verify, verify_none}, also separate the + cases unknown ca and selfsigned peer cert, and restored + return value of deprecated function + public_key:pem_to_der/1.</p> + <p> + Own Id: OTP-8858</p> + </item> + <item> + <p> + Changed the verify fun so that it differentiate between + the peer certificate and CA certificates by using + valid_peer or valid as the second argument to the verify + fun. It may not always be trivial or even possible to + know when the peer certificate is reached otherwise.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8873</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 4.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The server now verifies the client certificate verify + message correctly, instead of causing a case-clause.</p> + <p> + Own Id: OTP-8721</p> + </item> + <item> + <p> + The client hello message now always include ALL available + cipher suites (or those specified by the ciphers option). + Previous implementation would filter them based on the + client certificate key usage extension (such filtering + only makes sense for the server certificate).</p> + <p> + Own Id: OTP-8772</p> + </item> + <item> + <p> + Fixed handling of the option {mode, list} that was broken + for some packet types for instance line.</p> + <p> + Own Id: OTP-8785</p> + </item> + <item> + <p> + Empty packets were not delivered to the client.</p> + <p> + Own Id: OTP-8790</p> + </item> + <item> + <p> Building in a source tree without prebuilt platform + independent build results failed on the SSL examples + when: </p> <list><item> cross building. This has been + solved by not building the SSL examples during a cross + build. </item><item> building on Windows. </item></list> + <p> + Own Id: OTP-8791</p> + </item> + <item> + <p> + Fixed a handshake error which occurred on some ssl + implementations.</p> + <p> + Own Id: OTP-8793</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Revise the public_key API - Cleaned up and documented the + public_key API to make it useful for general use, also + changed ssl to use the new API.</p> + <p> + Own Id: OTP-8722</p> + </item> + <item> + <p> + Added support for inputing certificates and keys directly + in DER format these options will override the pem-file + options if specified.</p> + <p> + Own Id: OTP-8723</p> + </item> + <item> + <p> + To gain interoperability ssl will not check for padding + errors when using TLS 1.0. It is first in TLS 1.1 that + checking the padding is an requirement.</p> + <p> + Own Id: OTP-8740</p> + </item> + <item> + <p> + Changed the semantics of the verify_fun option in the + ssl-application so that it takes care of both application + handling of path validation errors and verification of + application specific extensions. This means that it is + now possible for the server application in verify_peer + mode to handle path validation errors. This change moved + some functionality earlier in ssl to the public_key + application.</p> + <p> + Own Id: OTP-8770</p> + </item> + <item> + <p> + Added the functionality so that the verification fun will + be called when a certificate is considered valid by the + path validation to allow access to each certificate in + the path to the user application. Also try to verify + subject-AltName, if unable to verify it let the + application verify it.</p> + <p> + Own Id: OTP-8825</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 4.0</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + New ssl now support client/server-certificates signed by + dsa keys.</p> + <p> + Own Id: OTP-8587</p> + </item> + <item> + <p> + Ssl has now switched default implementation and removed + deprecated certificate handling. All certificate handling + is done by the public_key application.</p> + <p> + Own Id: OTP-8695</p> + </item> + </list> + </section> + + </section> + + + <section><title>SSL 3.11.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed handling of several ssl/tls packets arriving at the + same time. This was broken during a refactoring of the + code.</p> + <p> + Own Id: OTP-8679</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Added missing checks for padding and Mac value. Removed + code for export ciphers and DH certificates as we decided + not to support them.</p> + <p> + Own Id: OTP-7047</p> + </item> + <item> + <p> + New ssl will no longer return esslerrssl to be backwards + compatible with old ssl as this hids infomation from the + user. format_error/1 has been updated to support new ssl.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-7049</p> + </item> + <item> + <p> + New ssl now supports secure renegotiation as described by + RFC 5746.</p> + <p> + Own Id: OTP-8568</p> + </item> + <item> + <p> + Alert handling has been improved to better handle + unexpected but valid messages and the implementation is + also changed to avoid timing related issues that could + cause different error messages depending on network + latency. Packet handling was sort of broken but would + mostly work as expected when socket was in binary mode. + This has now been fixed.</p> + <p> + Own Id: OTP-8588</p> + </item> + </list> + </section> + +</section> + <section><title>SSL 3.11</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -733,7 +1009,7 @@ <title>Fixed Bugs and Malfunctions</title> <list type="bulleted"> <item> - <p>When a file descriptor was marked for closing, and and + <p>When a file descriptor was marked for closing, and end-of-file condition had already been detected, the file descriptor was never closed.</p> <p>Own Id: OTP-5093 Aux Id: seq8806 </p> diff --git a/lib/ssl/doc/src/old_ssl.xml b/lib/ssl/doc/src/old_ssl.xml new file mode 100644 index 0000000000..0d2e1afdbd --- /dev/null +++ b/lib/ssl/doc/src/old_ssl.xml @@ -0,0 +1,709 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1999</year><year>2010</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>ssl</title> + <prepared>Peter Högfeldt</prepared> + <responsible>Peter Högfeldt</responsible> + <docno></docno> + <approved>Peter Högfeldt</approved> + <checked></checked> + <date>2003-03-25</date> + <rev>D</rev> + <file>old_ssl.xml</file> + </header> + <module>old_ssl</module> + <modulesummary>Interface Functions for Secure Socket Layer</modulesummary> + <description> + <p>This module contains interface functions to the Secure Socket Layer.</p> + </description> + + <section> + <title>General</title> + + <p>This manual page describes functions that are defined + in the ssl module and represents the old ssl implementation + that coexists with the new one until it has been + totally phased out. </p> + + <p>The old implementation can be + accessed by providing the option {ssl_imp, old} to the + ssl:connect and ssl:listen functions.</p> + + <p>The reader is advised to also read the <c>ssl(6)</c> manual page + describing the SSL application. + </p> + <warning> + <p>It is strongly advised to seed the random generator after + the ssl application has been started (see <c>seed/1</c> + below), and before any connections are established. Although + the port program interfacing to the ssl libraries does a + "random" seeding of its own in order to make everything work + properly, that seeding is by no means random for the world + since it has a constant value which is known to everyone + reading the source code of the port program.</p> + </warning> + </section> + + <section> + <title>Common data types</title> + <p>The following datatypes are used in the functions below: + </p> + <list type="bulleted"> + <item> + <p><c>options() = [option()]</c></p> + </item> + <item> + <p><c>option() = socketoption() | ssloption()</c></p> + </item> + <item> + <p><c>socketoption() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {port, integer()}</c></p> + </item> + <item> + <p><c>ssloption() = {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</c></p> + </item> + <item> + <p><c>packettype()</c> (see inet(3))</p> + </item> + <item> + <p><c>activetype()</c> (see inet(3))</p> + </item> + <item> + <p><c>reason() = atom() | {atom(), string()}</c></p> + </item> + <item> + <p><c>bytes() = [byte()]</c></p> + </item> + <item> + <p><c>string() = [byte()]</c></p> + </item> + <item> + <p><c>byte() = 0 | 1 | 2 | ... | 255</c></p> + </item> + <item> + <p><c>code() = 0 | 1 | 2</c></p> + </item> + <item> + <p><c>depth() = byte()</c></p> + </item> + <item> + <p><c>address() = hostname() | ipstring() | ipaddress()</c></p> + </item> + <item> + <p><c>ipaddress() = ipstring() | iptuple()</c></p> + </item> + <item> + <p><c>hostname() = string()</c></p> + </item> + <item> + <p><c>ipstring() = string()</c></p> + </item> + <item> + <p><c>iptuple() = {byte(), byte(), byte(), byte()}</c></p> + </item> + <item> + <p><c>sslsocket()</c></p> + </item> + <item> + <p><c>protocol() = sslv2 | sslv3 | tlsv1</c></p> + </item> + <item> + <p><c></c></p> + </item> + </list> + <p>The socket option <c>{backlog, integer()}</c> is for + <c>listen/2</c> only, and the option <c>{port, integer()}</c> + is for <c>connect/3/4</c> only. + </p> + <p>The following socket options are set by default: <c>{mode, list}</c>, <c>{packet, 0}</c>, <c>{header, 0}</c>, <c>{nodelay, false}</c>, <c>{active, true}</c>, <c>{backlog, 5}</c>, + <c>{ip, {0,0,0,0}}</c>, and <c>{port, 0}</c>. + </p> + <p>Note that the options <c>{mode, binary}</c> and <c>binary</c> + are equivalent. Similarly <c>{mode, list}</c> and the absence of + option <c>binary</c> are equivalent. + </p> + <p>The ssl options are for setting specific SSL parameters as follows: + </p> + <list type="bulleted"> + <item> + <p><c>{verify, code()}</c> Specifies type of verification: + 0 = do not verify peer; 1 = verify peer, 2 = verify peer, + fail if no peer certificate. The default value is 0. + </p> + </item> + <item> + <p><c>{depth, depth()}</c> Specifies the maximum + verification depth, i.e. how far in a chain of certificates + the verification process can proceed before the verification + is considered to fail. + </p> + <p>Peer certificate = 0, CA certificate = 1, higher level CA + certificate = 2, etc. The value 2 thus means that a chain + can at most contain peer cert, CA cert, next CA cert, and an + additional CA cert. + </p> + <p>The default value is 1. + </p> + </item> + <item> + <p><c>{certfile, path()}</c> Path to a file containing the + user's certificate. + chain of PEM encoded certificates.</p> + </item> + <item> + <p><c>{keyfile, path()}</c> Path to file containing user's + private PEM encoded key.</p> + </item> + <item> + <p><c>{password, string()}</c> String containing the user's + password. Only used if the private keyfile is password protected.</p> + </item> + <item> + <p><c>{cacertfile, path()}</c> Path to file containing PEM encoded + CA certificates (trusted certificates used for verifying a peer + certificate).</p> + </item> + <item> + <p><c>{ciphers, string()}</c> String of ciphers as a colon + separated list of ciphers. The function <c>ciphers/0</c> can + be used to find all available ciphers.</p> + </item> + </list> + <p>The type <c>sslsocket()</c> is opaque to the user. + </p> + <p>The owner of a socket is the one that created it by a call to + <c>transport_accept/[1,2]</c>, <c>connect/[3,4]</c>, + or <c>listen/2</c>. + </p> + <p>When a socket is in active mode (the default), data from the + socket is delivered to the owner of the socket in the form of + messages: + </p> + <list type="bulleted"> + <item> + <p><c>{ssl, Socket, Data}</c></p> + </item> + <item> + <p><c>{ssl_closed, Socket}</c></p> + </item> + <item> + <p><c>{ssl_error, Socket, Reason}</c></p> + </item> + </list> + <p>A <c>Timeout</c> argument specifies a timeout in milliseconds. The + default value for a <c>Timeout</c> argument is <c>infinity</c>. + </p> + <p>Functions listed below may return the value <c>{error, closed}</c>, which only indicates that the SSL socket is + considered closed for the operation in question. It is for + instance possible to have <c>{error, closed}</c> returned from + an call to <c>send/2</c>, and a subsequent call to <c>recv/3</c> + returning <c>{ok, Data}</c>. + </p> + <p>Hence a return value of <c>{error, closed}</c> must not be + interpreted as if the socket was completely closed. On the + contrary, in order to free all resources occupied by an SSL + socket, <c>close/1</c> must be called, or else the process owning + the socket has to terminate. + </p> + <p>For each SSL socket there is an Erlang process representing the + socket. When a socket is opened, that process links to the + calling client process. Implementations that want to detect + abnormal exits from the socket process by receiving <c>{'EXIT', Pid, Reason}</c> messages, should use the function <c>pid/1</c> + to retrieve the process identifier from the socket, in order to + be able to match exit messages properly.</p> + </section> + <funcs> + <func> + <name>ciphers() -> {ok, string()} | {error, enotstarted}</name> + <fsummary>Get supported ciphers.</fsummary> + <desc> + <p>Returns a string consisting of colon separated cipher + designations that are supported by the current SSL library + implementation. + </p> + <p>The SSL application has to be started to return the string + of ciphers.</p> + </desc> + </func> + <func> + <name>close(Socket) -> ok | {error, Reason}</name> + <fsummary>Close a socket returned by <c>transport_accept/[1,2]</c>, <c>connect/3/4</c>, or <c>listen/2</c>.</fsummary> + <type> + <v>Socket = sslsocket()</v> + </type> + <desc> + <p>Closes a socket returned by <c>transport_accept/[1,2]</c>, + <c>connect/[3,4]</c>, or <c>listen/2</c></p> + </desc> + </func> + <func> + <name>connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}</name> + <name>connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason}</name> + <fsummary>Connect to <c>Port</c>at <c>Address</c>.</fsummary> + <type> + <v>Address = address()</v> + <v>Port = integer()</v> + <v>Options = [connect_option()]</v> + <v>connect_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {ip, ipaddress()} | {port, integer()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v> + <v>Timeout = integer()</v> + <v>Socket = sslsocket()</v> + </type> + <desc> + <p>Connects to <c>Port</c> at <c>Address</c>. If the optional + <c>Timeout</c> argument is specified, and a connection could not + be established within the given time, <c>{error, timeout}</c> is + returned. The default value for <c>Timeout</c> is <c>infinity</c>. + </p> + <p>The <c>ip</c> and <c>port</c> options are for binding to a + particular <em>local</em> address and port, respectively.</p> + </desc> + </func> + <func> + <name>connection_info(Socket) -> {ok, {Protocol, Cipher}} | {error, Reason}</name> + <fsummary>Get current protocol version and cipher.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Protocol = protocol()</v> + <v>Cipher = string()</v> + </type> + <desc> + <p>Gets the chosen protocol version and cipher for an established + connection (accepted och connected). </p> + </desc> + </func> + <func> + <name>controlling_process(Socket, NewOwner) -> ok | {error, Reason}</name> + <fsummary>Assign a new controlling process to the socket.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>NewOwner = pid()</v> + </type> + <desc> + <p>Assigns a new controlling process to <c>Socket</c>. A controlling + process is the owner of a socket, and receives all messages from + the socket.</p> + </desc> + </func> + <func> + <name>format_error(ErrorCode) -> string()</name> + <fsummary>Return an error string.</fsummary> + <type> + <v>ErrorCode = term()</v> + </type> + <desc> + <p>Returns a diagnostic string describing an error.</p> + </desc> + </func> + <func> + <name>getopts(Socket, OptionsTags) -> {ok, Options} | {error, Reason}</name> + <fsummary>Get options set for socket</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>OptionTags = [optiontag()]()</v> + </type> + <desc> + <p>Returns the options the tags of which are <c>OptionTags</c> for + for the socket <c>Socket</c>. </p> + </desc> + </func> + <func> + <name>listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name> + <fsummary>Set up a socket to listen on a port on the local host.</fsummary> + <type> + <v>Port = integer()</v> + <v>Options = [listen_option()]</v> + <v>listen_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v> + <v>ListenSocket = sslsocket()</v> + </type> + <desc> + <p>Sets up a socket to listen on port <c>Port</c> at the local host. + If <c>Port</c> is zero, <c>listen/2</c> picks an available port + number (use <c>port/1</c> to retrieve it). + </p> + <p>The listen queue size defaults to 5. If a different value is + wanted, the option <c>{backlog, Size}</c> should be added to the + list of options. + </p> + <p>An empty <c>Options</c> list is considered an error, and + <c>{error, enooptions}</c> is returned. + </p> + <p>The returned <c>ListenSocket</c> can only be used in calls to + <c>transport_accept/[1,2]</c>.</p> + </desc> + </func> + <func> + <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name> + <fsummary>Return the peer certificate.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Cert = binary()()</v> + <v>Subject = term()()</v> + </type> + <desc> + <p>Returns the DER encoded peer certificate, the certificate can be decoded with + <c>public_key:pkix_decode_cert/2</c>. + </p> + </desc> + </func> + <func> + <name>peername(Socket) -> {ok, {Address, Port}} | {error, Reason}</name> + <fsummary>Return peer address and port.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Address = ipaddress()</v> + <v>Port = integer()</v> + </type> + <desc> + <p>Returns the address and port number of the peer.</p> + </desc> + </func> + <func> + <name>pid(Socket) -> pid()</name> + <fsummary>Return the pid of the socket process.</fsummary> + <type> + <v>Socket = sslsocket()</v> + </type> + <desc> + <p>Returns the pid of the socket process. The returned pid should + only be used for receiving exit messages.</p> + </desc> + </func> + <func> + <name>recv(Socket, Length) -> {ok, Data} | {error, Reason}</name> + <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error, Reason}</name> + <fsummary>Receive data on socket.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Length = integer() >= 0</v> + <v>Timeout = integer()</v> + <v>Data = bytes() | binary()</v> + </type> + <desc> + <p>Receives data on socket <c>Socket</c> when the socket is in + passive mode, i.e. when the option <c>{active, false}</c> + has been specified. + </p> + <p>A notable return value is <c>{error, closed}</c> which + indicates that the socket is closed. + </p> + <p>A positive value of the <c>Length</c> argument is only + valid when the socket is in raw mode (option <c>{packet, 0}</c> is set, and the option <c>binary</c> is <em>not</em> + set); otherwise it should be set to 0, whence all available + bytes are returned. + </p> + <p>If the optional <c>Timeout</c> parameter is specified, and + no data was available within the given time, <c>{error, timeout}</c> is returned. The default value for + <c>Timeout</c> is <c>infinity</c>.</p> + </desc> + </func> + <func> + <name>seed(Data) -> ok | {error, Reason}</name> + <fsummary>Seed the ssl random generator.</fsummary> + <type> + <v>Data = iolist() | binary()</v> + </type> + <desc> + <p>Seeds the ssl random generator. + </p> + <p>It is strongly advised to seed the random generator after + the ssl application has been started, and before any + connections are established. Although the port program + interfacing to the OpenSSL libraries does a "random" seeding + of its own in order to make everything work properly, that + seeding is by no means random for the world since it has a + constant value which is known to everyone reading the source + code of the seeding. + </p> + <p>A notable return value is <c>{error, edata}}</c> indicating that + <c>Data</c> was not a binary nor an iolist.</p> + </desc> + </func> + <func> + <name>send(Socket, Data) -> ok | {error, Reason}</name> + <fsummary>Write data to a socket.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Data = iolist() | binary()</v> + </type> + <desc> + <p>Writes <c>Data</c> to <c>Socket</c>. </p> + <p>A notable return value is <c>{error, closed}</c> indicating that + the socket is closed.</p> + </desc> + </func> + <func> + <name>setopts(Socket, Options) -> ok | {error, Reason}</name> + <fsummary>Set socket options.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Options = [socketoption]()</v> + </type> + <desc> + <p>Sets options according to <c>Options</c> for the socket + <c>Socket</c>. </p> + </desc> + </func> + <func> + <name>ssl_accept(Socket) -> ok | {error, Reason}</name> + <name>ssl_accept(Socket, Timeout) -> ok | {error, Reason}</name> + <fsummary>Perform server-side SSL handshake and key exchange</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Timeout = integer()</v> + <v>Reason = atom()</v> + </type> + <desc> + <p>The <c>ssl_accept</c> function establish the SSL connection + on the server side. It should be called directly after + <c>transport_accept</c>, in the spawned server-loop.</p> + <p>Note that the ssl connection is not complete until <c>ssl_accept</c> + has returned <c>true</c>, and if an error is returned, the socket + is unavailable and for instance <c>close/1</c> will crash.</p> + </desc> + </func> + <func> + <name>sockname(Socket) -> {ok, {Address, Port}} | {error, Reason}</name> + <fsummary>Return the local address and port.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Address = ipaddress()</v> + <v>Port = integer()</v> + </type> + <desc> + <p>Returns the local address and port number of the socket + <c>Socket</c>.</p> + </desc> + </func> + <func> + <name>transport_accept(Socket) -> {ok, NewSocket} | {error, Reason}</name> + <name>transport_accept(Socket, Timeout) -> {ok, NewSocket} | {error, Reason}</name> + <fsummary>Accept an incoming connection and prepare for <c>ssl_accept</c></fsummary> + <type> + <v>Socket = NewSocket = sslsocket()</v> + <v>Timeout = integer()</v> + <v>Reason = atom()</v> + </type> + <desc> + <p>Accepts an incoming connection request on a listen socket. + <c>ListenSocket</c> must be a socket returned from <c>listen/2</c>. + The socket returned should be passed to <c>ssl_accept</c> to + complete ssl handshaking and establishing the connection.</p> + <warning> + <p>The socket returned can only be used with <c>ssl_accept</c>, + no traffic can be sent or received before that call.</p> + </warning> + <p>The accepted socket inherits the options set for <c>ListenSocket</c> + in <c>listen/2</c>.</p> + <p>The default value for <c>Timeout</c> is <c>infinity</c>. If + <c>Timeout</c> is specified, and no connection is accepted within + the given time, <c>{error, timeout}</c> is returned.</p> + </desc> + </func> + <func> + <name>version() -> {ok, {SSLVsn, CompVsn, LibVsn}}</name> + <fsummary>Return the version of SSL.</fsummary> + <type> + <v>SSLVsn = CompVsn = LibVsn = string()()</v> + </type> + <desc> + <p>Returns the SSL application version (<c>SSLVsn</c>), the library + version used when compiling the SSL application port program + (<c>CompVsn</c>), and the actual library version used when + dynamically linking in runtime (<c>LibVsn</c>). + </p> + <p>If the SSL application has not been started, <c>CompVsn</c> and + <c>LibVsn</c> are empty strings. + </p> + </desc> + </func> + </funcs> + + <section> + <title>ERRORS</title> + <p>The possible error reasons and the corresponding diagnostic strings + returned by <c>format_error/1</c> are either the same as those defined + in the <c>inet(3)</c> reference manual, or as follows: + </p> + <taglist> + <tag><c>closed</c></tag> + <item> + <p>Connection closed for the operation in question. + </p> + </item> + <tag><c>ebadsocket</c></tag> + <item> + <p>Connection not found (internal error). + </p> + </item> + <tag><c>ebadstate</c></tag> + <item> + <p>Connection not in connect state (internal error). + </p> + </item> + <tag><c>ebrokertype</c></tag> + <item> + <p>Wrong broker type (internal error). + </p> + </item> + <tag><c>ecacertfile</c></tag> + <item> + <p>Own CA certificate file is invalid. + </p> + </item> + <tag><c>ecertfile</c></tag> + <item> + <p>Own certificate file is invalid. + </p> + </item> + <tag><c>echaintoolong</c></tag> + <item> + <p>The chain of certificates provided by peer is too long. + </p> + </item> + <tag><c>ecipher</c></tag> + <item> + <p>Own list of specified ciphers is invalid. + </p> + </item> + <tag><c>ekeyfile</c></tag> + <item> + <p>Own private key file is invalid. + </p> + </item> + <tag><c>ekeymismatch</c></tag> + <item> + <p>Own private key does not match own certificate. + </p> + </item> + <tag><c>enoissuercert</c></tag> + <item> + <p>Cannot find certificate of issuer of certificate provided + by peer. + </p> + </item> + <tag><c>enoservercert</c></tag> + <item> + <p>Attempt to do accept without having set own certificate. + </p> + </item> + <tag><c>enotlistener</c></tag> + <item> + <p>Attempt to accept on a non-listening socket. + </p> + </item> + <tag><c>enoproxysocket</c></tag> + <item> + <p>No proxy socket found (internal error). + </p> + </item> + <tag><c>enooptions</c></tag> + <item> + <p>The list of options is empty. + </p> + </item> + <tag><c>enotstarted</c></tag> + <item> + <p>The SSL application has not been started. + </p> + </item> + <tag><c>eoptions</c></tag> + <item> + <p>Invalid list of options. + </p> + </item> + <tag><c>epeercert</c></tag> + <item> + <p>Certificate provided by peer is in error. + </p> + </item> + <tag><c>epeercertexpired</c></tag> + <item> + <p>Certificate provided by peer has expired. + </p> + </item> + <tag><c>epeercertinvalid</c></tag> + <item> + <p>Certificate provided by peer is invalid. + </p> + </item> + <tag><c>eselfsignedcert</c></tag> + <item> + <p>Certificate provided by peer is self signed. + </p> + </item> + <tag><c>esslaccept</c></tag> + <item> + <p>Server SSL handshake procedure between client and server failed. + </p> + </item> + <tag><c>esslconnect</c></tag> + <item> + <p>Client SSL handshake procedure between client and server failed. + </p> + </item> + <tag><c>esslerrssl</c></tag> + <item> + <p>SSL protocol failure. Typically because of a fatal alert + from peer. + </p> + </item> + <tag><c>ewantconnect</c></tag> + <item> + <p>Protocol wants to connect, which is not supported in + this version of the SSL application. + </p> + </item> + <tag><c>ex509lookup</c></tag> + <item> + <p>Protocol wants X.509 lookup, which is not supported in + this version of the SSL application. + </p> + </item> + <tag><c>{badcall, Call}</c></tag> + <item> + <p>Call not recognized for current mode (active or passive) and + state of socket. + </p> + </item> + <tag><c>{badcast, Cast}</c></tag> + <item> + <p>Call not recognized for current mode (active or passive) and + state of socket. + </p> + </item> + <tag><c>{badinfo, Info}</c></tag> + <item> + <p>Call not recognized for current mode (active or passive) and + state of socket. + </p> + </item> + </taglist> + </section> + + <section> + <title>SEE ALSO</title> + <p>gen_tcp(3), inet(3) public_key(3) </p> + </section> + +</erlref> + + diff --git a/lib/ssl/doc/src/refman.xml b/lib/ssl/doc/src/refman.xml index 3ad5a01b46..68f84660f3 100644 --- a/lib/ssl/doc/src/refman.xml +++ b/lib/ssl/doc/src/refman.xml @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1999</year><year>2009</year> + <year>1999</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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>SSL Reference Manual</title> @@ -45,7 +45,8 @@ </description> <xi:include href="ssl_app.xml"/> <xi:include href="ssl.xml"/> - <xi:include href="new_ssl.xml"/> + <xi:include href="old_ssl.xml"/> + <xi:include href="ssl_session_cache_api.xml"/> </application> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 217eb791d0..ec272379bb 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.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>1999</year><year>2009</year> + <year>1999</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,355 +13,513 @@ 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> + </legalnotice> <title>ssl</title> - <prepared>Peter Högfeldt</prepared> - <responsible>Peter Högfeldt</responsible> - <docno></docno> - <approved>Peter Högfeldt</approved> - <checked></checked> - <date>2003-03-25</date> - <rev>D</rev> - <file>ssl.sgml</file> + <file>ssl.xml</file> </header> <module>ssl</module> <modulesummary>Interface Functions for Secure Socket Layer</modulesummary> <description> - <p>This module contains interface functions to the Secure Socket Layer.</p> + <p>This module contains interface functions to the Secure Socket + Layer. + </p> </description> + + <section> + <title>SSL</title> + <list type="bulleted"> + <item>ssl requires the crypto an public_key applications.</item> + <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item> + <item>For security reasons sslv2 is not supported.</item> + <item>Ephemeral Diffie-Hellman cipher suites are supported + but not Diffie Hellman Certificates cipher suites.</item> + <item>Export cipher suites are not supported as the + U.S. lifted its export restrictions in early 2000.</item> + <item>CRL and policy certificate + extensions are not supported yet. </item> + </list> + + </section> + <section> - <title>General</title> + <title>COMMON DATA TYPES</title> + <p>The following data types are used in the functions below: + </p> + + <p><c>boolean() = true | false</c></p> + + <p><c>property() = atom()</c></p> + + <p><c>option() = socketoption() | ssloption() | transportoption()</c></p> + + <p><c>socketoption() = [{property(), term()}] - defaults to + [{mode,list},{packet, 0},{header, 0},{active, true}]. + </c></p> + + <p>For valid options + see <seealso marker="kernel:inet">inet(3) </seealso> and + <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso>. + </p> + + <p> <c>ssloption() = {verify, verify_type()} | + {verify_fun, {fun(), term()}} | + {fail_if_no_peer_cert, boolean()} + {depth, integer()} | + {cert, der_bin()}| {certfile, path()} | + {key, der_bin()} | {keyfile, path()} | {password, string()} | + {cacerts, [der_bin()]} | {cacertfile, path()} | + |{dh, der_bin()} | {dhfile, path()} | {ciphers, ciphers()} | + {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} + </c></p> - <p>There is a new implementation of ssl available in - this module but until it is 100 % complete, so that it can replace - the old implementation in all aspects it will be - described here <seealso marker="new_ssl"> new ssl API </seealso></p> + <p><c>transportoption() = {CallbackModule, DataTag, ClosedTag} + - defaults to {gen_tcp, tcp, tcp_closed}. Ssl may be + run over any reliable transport protocol that has + an equivalent API to gen_tcp's.</c></p> + + <p><c> CallbackModule = + atom()</c> + </p> <p><c> DataTag = + atom() - tag used in socket data message.</c></p> + <p><c> ClosedTag = atom() - tag used in + socket close message.</c></p> + + <p><c>verify_type() = verify_none | verify_peer</c></p> + + <p><c>path() = string() - representing a file path.</c></p> + + <p><c>der_bin() = binary() -Asn1 DER encoded entity as an erlang binary.</c></p> + + <p><c>host() = hostname() | ipaddress()</c></p> + + <p><c>hostname() = string()</c></p> + + <p><c> + ip_address() = {N1,N2,N3,N4} % IPv4 + | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 </c></p> + + <p><c>sslsocket() - opaque to the user. </c></p> + + <p><c>protocol() = sslv3 | tlsv1 </c></p> + + <p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p> + + <p><c>ciphersuite() = + {key_exchange(), cipher(), hash()}</c></p> + + <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa | dh_anon + </c></p> + + <p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc' + | aes_128_cbc | aes_256_cbc </c></p> + + <p> <c>hash() = md5 | sha + </c></p> + + <p><c>ssl_imp() = new | old - default is new.</c></p> - <p>The reader is advised to also read the <c>ssl(6)</c> manual page - describing the SSL application. - </p> - <warning> - <p>It is strongly advised to seed the random generator after - the ssl application has been started (see <c>seed/1</c> - below), and before any connections are established. Although - the port program interfacing to the ssl libraries does a - "random" seeding of its own in order to make everything work - properly, that seeding is by no means random for the world - since it has a constant value which is known to everyone - reading the source code of the port program.</p> - </warning> </section> <section> - <title>Common data types</title> - <p>The following datatypes are used in the functions below: - </p> - <list type="bulleted"> - <item> - <p><c>options() = [option()]</c></p> - </item> - <item> - <p><c>option() = socketoption() | ssloption()</c></p> - </item> - <item> - <p><c>socketoption() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {port, integer()}</c></p> - </item> - <item> - <p><c>ssloption() = {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</c></p> - </item> - <item> - <p><c>packettype()</c> (see inet(3))</p> - </item> - <item> - <p><c>activetype()</c> (see inet(3))</p> - </item> - <item> - <p><c>reason() = atom() | {atom(), string()}</c></p> - </item> - <item> - <p><c>bytes() = [byte()]</c></p> - </item> - <item> - <p><c>string() = [byte()]</c></p> - </item> - <item> - <p><c>byte() = 0 | 1 | 2 | ... | 255</c></p> - </item> - <item> - <p><c>code() = 0 | 1 | 2</c></p> - </item> - <item> - <p><c>depth() = byte()</c></p> - </item> - <item> - <p><c>address() = hostname() | ipstring() | ipaddress()</c></p> - </item> - <item> - <p><c>ipaddress() = ipstring() | iptuple()</c></p> + <title>SSL OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</title> + + <p>Options described here are options that are have the same + meaning in the client and the server. + </p> + + <taglist> + + <tag>{cert, der_bin()}</tag> + <item> The DER encoded users certificate. If this option + is supplied it will override the certfile option.</item> + + <tag>{certfile, path()}</tag> + <item>Path to a file containing the user's certificate.</item> + + <tag>{key, der_bin()}</tag> + <item> The DER encoded users private key. If this option + is supplied it will override the keyfile option.</item> + + <tag>{keyfile, path()}</tag> + <item>Path to file containing user's + private PEM encoded key. As PEM-files may contain several + entries this option defaults to the same file as given by + certfile option.</item> + + <tag>{password, string()}</tag> + <item>String containing the user's password. + Only used if the private keyfile is password protected. </item> - <item> - <p><c>hostname() = string()</c></p> + + <tag>{cacerts, [der_bin()]}</tag> + <item> The DER encoded trusted certificates. If this option + is supplied it will override the cacertfile option.</item> + + <tag>{cacertfile, path()}</tag> + <item>Path to file containing PEM encoded + CA certificates (trusted certificates used for verifying a peer + certificate). May be omitted if you do not want to verify + the peer.</item> + + <tag>{ciphers, ciphers()}</tag> + <item>The cipher suites that should be supported. The function + <c>cipher_suites/0</c> can be used to find all available + ciphers. Additionally some anonymous cipher suites ({dh_anon, + rc4_128, md5}, {dh_anon, des_cbc, sha}, {dh_anon, + '3des_ede_cbc', sha}, {dh_anon, aes_128_cbc, sha}, {dh_anon, + aes_256_cbc, sha}) are supported for testing purposes and will + only work if explicitly enabled by this option and they are supported/enabled + by the peer also. </item> - <item> - <p><c>ipstring() = string()</c></p> + + <tag>{ssl_imp, ssl_imp()}</tag> + <item>Specify which ssl implementation you want to use. Defaults to + new. </item> - <item> - <p><c>iptuple() = {byte(), byte(), byte(), byte()}</c></p> + + <tag>{secure_renegotiate, boolean()}</tag> + <item>Specifies if to reject renegotiation attempt that does + not live up to RFC 5746. By default secure_renegotiate is + set to false i.e. secure renegotiation will be used if possible + but it will fallback to unsecure renegotiation if the peer + does not support RFC 5746. </item> - <item> - <p><c>sslsocket()</c></p> + + <tag>{depth, integer()}</tag> + <item>Specifies the maximum + verification depth, i.e. how far in a chain of certificates the + verification process can proceed before the verification is + considered to fail. Peer certificate = 0, CA certificate = 1, + higher level CA certificate = 2, etc. The value 2 thus means + that a chain can at most contain peer cert, CA cert, next CA + cert, and an additional CA cert. The default value is 1. </item> + + <tag>{verify_fun, {Verifyfun :: fun(), InitialUserState :: term()}}</tag> <item> - <p><c>protocol() = sslv2 | sslv3 | tlsv1</c></p> + <p>The verification fun should be defined as:</p> + + <code> +fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | + {extension, #'Extension'{}}, InitialUserState :: term()) -> + {valid, UserState :: term()} | {valid_peer, UserState :: term()} | + {fail, Reason :: term()} | {unknown, UserState :: term()}. + </code> + + <p>The verify fun will be called during the X509-path + validation when an error or an extension unknown to the ssl + application is encountered. Additionally it will be called + when a certificate is considered valid by the path validation + to allow access to each certificate in the path to the user + application. Note that the it will differentiate between the + peer certificate and CA certificates by using valid_peer or + valid as the second argument to the verify fun. See <seealso + marker="public_key:cert_records">the public_key User's + Guide</seealso> for definition of #'OTPCertificate'{} and + #'Extension'{}.</p> + + <p>If the verify callback fun returns {fail, Reason}, the + verification process is immediately stopped and an alert is + sent to the peer and the TLS/SSL handshake is terminated. If + the verify callback fun returns {valid, UserState}, the + verification process is continued. If the verify callback fun + always returns {valid, UserState}, the TLS/SSL handshake will + not be terminated with respect to verification failures and + the connection will be established. If called with an + extension unknown to the user application the return value + {unknown, UserState} should be used.</p> + + <p>The default verify_fun option in verify_peer mode:</p> + + <code> +{fun(_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []} + </code> + + <p>The default verify_fun option in verify_none mode:</p> + + <code> +{fun(_,{bad_cert, _}, UserState) -> + {valid, UserState}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []} + </code> + +<p>Possible path validation errors: </p> + +<p> {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, unknown_ca}, {bad_cert, name_not_permitted}, {bad_cert, missing_basic_constraint}, {bad_cert, invalid_key_usage}</p> </item> - <item> - <p><c></c></p> + + </taglist> + + </section> + + <section> + <title>SSL OPTION DESCRIPTIONS - CLIENT SIDE</title> + + <p>Options described here are client specific or has a slightly different + meaning in the client than in the server.</p> + + <taglist> + <tag>{verify, verify_type()}</tag> + <item> In verify_none mode the default behavior will be to + allow all x509-path validation errors. See also the verify_fun + option. </item> - </list> - <p>The socket option <c>{backlog, integer()}</c> is for - <c>listen/2</c> only, and the option <c>{port, integer()}</c> - is for <c>connect/3/4</c> only. - </p> - <p>The following socket options are set by default: <c>{mode, list}</c>, <c>{packet, 0}</c>, <c>{header, 0}</c>, <c>{nodelay, false}</c>, <c>{active, true}</c>, <c>{backlog, 5}</c>, - <c>{ip, {0,0,0,0}}</c>, and <c>{port, 0}</c>. - </p> - <p>Note that the options <c>{mode, binary}</c> and <c>binary</c> - are equivalent. Similarly <c>{mode, list}</c> and the absence of - option <c>binary</c> are equivalent. - </p> - <p>The ssl options are for setting specific SSL parameters as follows: - </p> - <list type="bulleted"> - <item> - <p><c>{verify, code()}</c> Specifies type of verification: - 0 = do not verify peer; 1 = verify peer, 2 = verify peer, - fail if no peer certificate. The default value is 0. - </p> + <tag>{reuse_sessions, boolean()}</tag> + <item>Specifies if client should try to reuse sessions + when possible. </item> - <item> - <p><c>{depth, depth()}</c> Specifies the maximum - verification depth, i.e. how far in a chain of certificates - the verification process can proceed before the verification - is considered to fail. - </p> - <p>Peer certificate = 0, CA certificate = 1, higher level CA - certificate = 2, etc. The value 2 thus means that a chain - can at most contain peer cert, CA cert, next CA cert, and an - additional CA cert. - </p> - <p>The default value is 1. - </p> + + </taglist> + </section> + + <section> + <title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title> + + <p>Options described here are server specific or has a slightly different + meaning in the server than in the client.</p> + + <taglist> + + <tag>{dh, der_bin()}</tag> + <item>The DER encoded Diffie Hellman parameters. If this option + is supplied it will override the dhfile option. </item> - <item> - <p><c>{certfile, path()}</c> Path to a file containing the - user's certificate. - chain of PEM encoded certificates.</p> + + <tag>{dhfile, path()}</tag> + <item>Path to file containing PEM encoded Diffie Hellman parameters, + for the server to use if a cipher suite using Diffie Hellman key exchange + is negotiated. If not specified default parameters will be used. </item> - <item> - <p><c>{keyfile, path()}</c> Path to file containing user's - private PEM encoded key.</p> + + <tag>{verify, verify_type()}</tag> + <item>Servers only do the x509-path validation in verify_peer + mode, as it then will send a certificate request to the client + (this message is not sent if the verify option is verify_none) + and you may then also want to specify the option + fail_if_no_peer_cert. </item> - <item> - <p><c>{password, string()}</c> String containing the user's - password. Only used if the private keyfile is password protected.</p> + + <tag>{fail_if_no_peer_cert, boolean()}</tag> + <item>Used together with {verify, verify_peer} by a ssl server. + If set to true, the server will fail if the client does not have + a certificate to send, i.e. sends a empty certificate, if set to + false it will only fail if the client sends a invalid + certificate (an empty certificate is considered valid). </item> - <item> - <p><c>{cacertfile, path()}</c> Path to file containing PEM encoded - CA certificates (trusted certificates used for verifying a peer - certificate).</p> + + <tag>{reuse_sessions, boolean()}</tag> + <item>Specifies if the server should agree to reuse sessions + when the clients request to do so. See also the reuse_session + option. </item> - <item> - <p><c>{ciphers, string()}</c> String of ciphers as a colon - separated list of ciphers. The function <c>ciphers/0</c> can - be used to find all available ciphers.</p> + + <tag>{reuse_session, fun(SuggestedSessionId, + PeerCert, Compression, CipherSuite) -> boolean()}</tag> + <item>Enables the ssl server to have a local policy + for deciding if a session should be reused or not, + only meaning full if <c>reuse_sessions</c> is set to true. + SuggestedSessionId is a binary(), PeerCert is a DER encoded + certificate, Compression is an enumeration integer + and CipherSuite of type ciphersuite(). </item> - </list> - <p>The type <c>sslsocket()</c> is opaque to the user. - </p> - <p>The owner of a socket is the one that created it by a call to - <c>transport_accept/[1,2]</c>, <c>connect/[3,4]</c>, - or <c>listen/2</c>. - </p> - <p>When a socket is in active mode (the default), data from the + + </taglist> + </section> + + <section> + <title>General</title> + + <p>When a ssl socket is in active mode (the default), data from the socket is delivered to the owner of the socket in the form of messages: - </p> + </p> <list type="bulleted"> - <item> - <p><c>{ssl, Socket, Data}</c></p> + <item>{ssl, Socket, Data} </item> - <item> - <p><c>{ssl_closed, Socket}</c></p> + <item>{ssl_closed, Socket} </item> <item> - <p><c>{ssl_error, Socket, Reason}</c></p> + {ssl_error, Socket, Reason} </item> </list> + <p>A <c>Timeout</c> argument specifies a timeout in milliseconds. The default value for a <c>Timeout</c> argument is <c>infinity</c>. - </p> - <p>Functions listed below may return the value <c>{error, closed}</c>, which only indicates that the SSL socket is - considered closed for the operation in question. It is for - instance possible to have <c>{error, closed}</c> returned from - an call to <c>send/2</c>, and a subsequent call to <c>recv/3</c> - returning <c>{ok, Data}</c>. - </p> - <p>Hence a return value of <c>{error, closed}</c> must not be - interpreted as if the socket was completely closed. On the - contrary, in order to free all resources occupied by an SSL - socket, <c>close/1</c> must be called, or else the process owning - the socket has to terminate. - </p> - <p>For each SSL socket there is an Erlang process representing the - socket. When a socket is opened, that process links to the - calling client process. Implementations that want to detect - abnormal exits from the socket process by receiving <c>{'EXIT', Pid, Reason}</c> messages, should use the function <c>pid/1</c> - to retrieve the process identifier from the socket, in order to - be able to match exit messages properly.</p> + </p> </section> + <funcs> <func> - <name>ciphers() -> {ok, string()} | {error, enotstarted}</name> - <fsummary>Get supported ciphers.</fsummary> - <desc> - <p>Returns a string consisting of colon separated cipher - designations that are supported by the current SSL library - implementation. - </p> - <p>The SSL application has to be started to return the string - of ciphers.</p> - </desc> + <name>cipher_suites() -></name> + <name>cipher_suites(Type) -> ciphers()</name> + <fsummary> Returns a list of supported cipher suites</fsummary> + <type> + <v>Type = erlang | openssl</v> + + </type> + <desc><p>Returns a list of supported cipher suites. + cipher_suites() is equivalent to cipher_suites(erlang). + Type openssl is provided for backwards compatibility with + old ssl that used openssl. + </p> + </desc> </func> + <func> - <name>close(Socket) -> ok | {error, Reason}</name> - <fsummary>Close a socket returned by <c>transport_accept/[1,2]</c>, <c>connect/3/4</c>, or <c>listen/2</c>.</fsummary> + <name>connect(Socket, SslOptions) -> </name> + <name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket} + | {error, Reason}</name> + <fsummary> Upgrades a gen_tcp, or + equivalent, connected socket to a ssl socket. </fsummary> <type> - <v>Socket = sslsocket()</v> + <v>Socket = socket()</v> + <v>SslOptions = [ssloption()]</v> + <v>Timeout = integer() | infinity</v> + <v>SslSocket = sslsocket()</v> + <v>Reason = term()</v> </type> - <desc> - <p>Closes a socket returned by <c>transport_accept/[1,2]</c>, - <c>connect/[3,4]</c>, or <c>listen/2</c></p> - </desc> + <desc> <p>Upgrades a gen_tcp, or equivalent, + connected socket to a ssl socket i.e. performs the + client-side ssl handshake.</p> + </desc> </func> + <func> - <name>connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}</name> - <name>connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason}</name> - <fsummary>Connect to <c>Port</c>at <c>Address</c>.</fsummary> + <name>connect(Host, Port, Options) -></name> + <name>connect(Host, Port, Options, Timeout) -> + {ok, SslSocket} | {error, Reason}</name> + <fsummary>Opens an ssl connection to Host, Port. </fsummary> <type> - <v>Address = address()</v> - <v>Port = integer()</v> - <v>Options = [connect_option()]</v> - <v>connect_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {ip, ipaddress()} | {port, integer()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v> - <v>Timeout = integer()</v> - <v>Socket = sslsocket()</v> + <v>Host = host()</v> + <v>Port = integer()</v> + <v>Options = [option()]</v> + <v>Timeout = integer() | infinity</v> + <v>SslSocket = sslsocket()</v> + <v>Reason = term()</v> </type> - <desc> - <p>Connects to <c>Port</c> at <c>Address</c>. If the optional - <c>Timeout</c> argument is specified, and a connection could not - be established within the given time, <c>{error, timeout}</c> is - returned. The default value for <c>Timeout</c> is <c>infinity</c>. - </p> - <p>The <c>ip</c> and <c>port</c> options are for binding to a - particular <em>local</em> address and port, respectively.</p> - </desc> + <desc> <p>Opens an ssl connection to Host, Port.</p> </desc> </func> + <func> - <name>connection_info(Socket) -> {ok, {Protocol, Cipher}} | {error, Reason}</name> - <fsummary>Get current protocol version and cipher.</fsummary> + <name>close(SslSocket) -> ok | {error, Reason}</name> + <fsummary>Close a ssl connection</fsummary> <type> - <v>Socket = sslsocket()</v> - <v>Protocol = protocol()</v> - <v>Cipher = string()</v> + <v>SslSocket = sslsocket()</v> + <v>Reason = term()</v> </type> - <desc> - <p>Gets the chosen protocol version and cipher for an established - connection (accepted och connected). </p> + <desc><p>Close a ssl connection.</p> </desc> </func> + <func> - <name>controlling_process(Socket, NewOwner) -> ok | {error, Reason}</name> - <fsummary>Assign a new controlling process to the socket.</fsummary> + <name>controlling_process(SslSocket, NewOwner) -> + ok | {error, Reason}</name> + + <fsummary>Assigns a new controlling process to the + ssl-socket.</fsummary> + + <type> + <v>SslSocket = sslsocket()</v> + <v>NewOwner = pid()</v> + <v>Reason = term()</v> + </type> + <desc><p>Assigns a new controlling process to the ssl-socket. A + controlling process is the owner of a ssl-socket, and receives + all messages from the socket.</p> + </desc> + </func> + + <func> + <name>connection_info(SslSocket) -> + {ok, {ProtocolVersion, CipherSuite}} | {error, Reason} </name> + <fsummary>Returns the negotiated protocol version and cipher suite. + </fsummary> <type> - <v>Socket = sslsocket()</v> - <v>NewOwner = pid()</v> + <v>CipherSuite = ciphersuite()</v> + <v>ProtocolVersion = protocol()</v> </type> - <desc> - <p>Assigns a new controlling process to <c>Socket</c>. A controlling - process is the owner of a socket, and receives all messages from - the socket.</p> + <desc><p>Returns the negotiated protocol version and cipher suite.</p> </desc> </func> - <func> - <name>format_error(ErrorCode) -> string()</name> + + <func> + <name>format_error(Reason) -> string()</name> <fsummary>Return an error string.</fsummary> <type> - <v>ErrorCode = term()</v> + <v>Reason = term()</v> </type> <desc> - <p>Returns a diagnostic string describing an error.</p> + <p>Presents the error returned by an ssl function as a printable string.</p> </desc> </func> + <func> - <name>getopts(Socket, OptionsTags) -> {ok, Options} | {error, Reason}</name> - <fsummary>Get options set for socket</fsummary> + <name>getopts(Socket) -> </name> + <name>getopts(Socket, OptionNames) -> + {ok, [socketoption()]} | {error, Reason}</name> + <fsummary>Get the value of the specified options.</fsummary> <type> - <v>Socket = sslsocket()</v> - <v>OptionTags = [optiontag()]()</v> + <v>Socket = sslsocket()</v> + <v>OptionNames = [property()]</v> </type> <desc> - <p>Returns the options the tags of which are <c>OptionTags</c> for - for the socket <c>Socket</c>. </p> + <p>Get the value of the specified socket options, if no + options are specified all options are returned. + </p> </desc> </func> + <func> - <name>listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name> - <fsummary>Set up a socket to listen on a port on the local host.</fsummary> + <name>listen(Port, Options) -> + {ok, ListenSocket} | {error, Reason}</name> + <fsummary>Creates a ssl listen socket.</fsummary> <type> - <v>Port = integer()</v> - <v>Options = [listen_option()]</v> - <v>listen_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v> - <v>ListenSocket = sslsocket()</v> + <v>Port = integer()</v> + <v>Options = options()</v> + <v>ListenSocket = sslsocket()</v> </type> <desc> - <p>Sets up a socket to listen on port <c>Port</c> at the local host. - If <c>Port</c> is zero, <c>listen/2</c> picks an available port - number (use <c>port/1</c> to retrieve it). - </p> - <p>The listen queue size defaults to 5. If a different value is - wanted, the option <c>{backlog, Size}</c> should be added to the - list of options. - </p> - <p>An empty <c>Options</c> list is considered an error, and - <c>{error, enooptions}</c> is returned. - </p> - <p>The returned <c>ListenSocket</c> can only be used in calls to - <c>transport_accept/[1,2]</c>.</p> + <p>Creates a ssl listen socket.</p> </desc> </func> + <func> - <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name> + <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name> <fsummary>Return the peer certificate.</fsummary> - <type> + <type> <v>Socket = sslsocket()</v> - <v>Cert = binary()()</v> - <v>Subject = term()()</v> + <v>Cert = binary()</v> </type> <desc> - <p>Returns the DER encoded peer certificate, the certificate can be decoded with - <c>public_key:pkix_decode_cert/2</c>. - </p> + <p>The peer certificate is returned as a DER encoded binary. + The certificate can be decoded with <c>public_key:pkix_decode_cert/2</c>. + </p> </desc> </func> <func> - <name>peername(Socket) -> {ok, {Address, Port}} | {error, Reason}</name> + <name>peername(Socket) -> {ok, {Address, Port}} | + {error, Reason}</name> <fsummary>Return peer address and port.</fsummary> <type> <v>Socket = sslsocket()</v> @@ -372,67 +530,47 @@ <p>Returns the address and port number of the peer.</p> </desc> </func> + <func> - <name>pid(Socket) -> pid()</name> - <fsummary>Return the pid of the socket process.</fsummary> - <type> - <v>Socket = sslsocket()</v> - </type> - <desc> - <p>Returns the pid of the socket process. The returned pid should - only be used for receiving exit messages.</p> - </desc> - </func> - <func> - <name>recv(Socket, Length) -> {ok, Data} | {error, Reason}</name> - <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error, Reason}</name> - <fsummary>Receive data on socket.</fsummary> + <name>recv(Socket, Length) -> </name> + <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error, + Reason}</name> + <fsummary>Receive data on a socket.</fsummary> <type> <v>Socket = sslsocket()</v> - <v>Length = integer() >= 0</v> + <v>Length = integer()</v> <v>Timeout = integer()</v> - <v>Data = bytes() | binary()</v> + <v>Data = [char()] | binary()</v> </type> <desc> - <p>Receives data on socket <c>Socket</c> when the socket is in - passive mode, i.e. when the option <c>{active, false}</c> - has been specified. - </p> - <p>A notable return value is <c>{error, closed}</c> which - indicates that the socket is closed. - </p> - <p>A positive value of the <c>Length</c> argument is only - valid when the socket is in raw mode (option <c>{packet, 0}</c> is set, and the option <c>binary</c> is <em>not</em> - set); otherwise it should be set to 0, whence all available - bytes are returned. - </p> - <p>If the optional <c>Timeout</c> parameter is specified, and - no data was available within the given time, <c>{error, timeout}</c> is returned. The default value for - <c>Timeout</c> is <c>infinity</c>.</p> + <p>This function receives a packet from a socket in passive + mode. A closed socket is indicated by a return value + <c>{error, closed}</c>.</p> + <p>The <c>Length</c> argument is only meaningful when + the socket is in <c>raw</c> mode and denotes the number of + bytes to read. If <c>Length</c> = 0, all available bytes are + returned. If <c>Length</c> > 0, exactly <c>Length</c> + bytes are returned, or an error; possibly discarding less + than <c>Length</c> bytes of data when the socket gets closed + from the other side.</p> + <p>The optional <c>Timeout</c> parameter specifies a timeout in + milliseconds. The default value is <c>infinity</c>.</p> </desc> </func> + <func> - <name>seed(Data) -> ok | {error, Reason}</name> - <fsummary>Seed the ssl random generator.</fsummary> + <name>renegotiate(Socket) -> ok | {error, Reason}</name> + <fsummary> Initiates a new handshake.</fsummary> <type> - <v>Data = iolist() | binary()</v> + <v>Socket = sslsocket()</v> </type> - <desc> - <p>Seeds the ssl random generator. - </p> - <p>It is strongly advised to seed the random generator after - the ssl application has been started, and before any - connections are established. Although the port program - interfacing to the OpenSSL libraries does a "random" seeding - of its own in order to make everything work properly, that - seeding is by no means random for the world since it has a - constant value which is known to everyone reading the source - code of the seeding. - </p> - <p>A notable return value is <c>{error, edata}}</c> indicating that - <c>Data</c> was not a binary nor an iolist.</p> + <desc><p>Initiates a new handshake. A notable return value is + <c>{error, renegotiation_rejected}</c> indicating that the peer + refused to go through with the renegotiation but the connection + is still active using the previously negotiated session.</p> </desc> </func> + <func> <name>send(Socket, Data) -> ok | {error, Reason}</name> <fsummary>Write data to a socket.</fsummary> @@ -458,26 +596,65 @@ <c>Socket</c>. </p> </desc> </func> + <func> - <name>ssl_accept(Socket) -> ok | {error, Reason}</name> - <name>ssl_accept(Socket, Timeout) -> ok | {error, Reason}</name> - <fsummary>Perform server-side SSL handshake and key exchange</fsummary> + <name>shutdown(Socket, How) -> ok | {error, Reason}</name> + <fsummary>Immediately close a socket</fsummary> <type> <v>Socket = sslsocket()</v> + <v>How = read | write | read_write</v> + <v>Reason = reason()</v> + </type> + <desc> + <p>Immediately close a socket in one or two directions.</p> + <p><c>How == write</c> means closing the socket for writing, + reading from it is still possible.</p> + <p>To be able to handle that the peer has done a shutdown on + the write side, the <c>{exit_on_close, false}</c> option + is useful.</p> + </desc> + </func> + + <func> + <name>ssl_accept(ListenSocket) -> </name> + <name>ssl_accept(ListenSocket, Timeout) -> ok | {error, Reason}</name> + <fsummary>Perform server-side SSL handshake</fsummary> + <type> + <v>ListenSocket = sslsocket()</v> <v>Timeout = integer()</v> - <v>Reason = atom()</v> + <v>Reason = term()</v> </type> <desc> <p>The <c>ssl_accept</c> function establish the SSL connection on the server side. It should be called directly after <c>transport_accept</c>, in the spawned server-loop.</p> - <p>Note that the ssl connection is not complete until <c>ssl_accept</c> - has returned <c>true</c>, and if an error is returned, the socket - is unavailable and for instance <c>close/1</c> will crash.</p> </desc> </func> + + <func> + <name>ssl_accept(ListenSocket, SslOptions) -> </name> + <name>ssl_accept(ListenSocket, SslOptions, Timeout) -> {ok, Socket} | {error, Reason}</name> + <fsummary>Perform server-side SSL handshake</fsummary> + <type> + <v>ListenSocket = socket()</v> + <v>SslOptions = ssloptions()</v> + <v>Timeout = integer()</v> + <v>Reason = term()</v> + </type> + <desc> + <p> Upgrades a gen_tcp, or + equivalent, socket to a ssl socket i.e. performs the + ssl server-side handshake.</p> + <p><warning>Note that the listen socket should be in {active, false} mode + before telling the client that the server is ready to upgrade + and calling this function, otherwise the upgrade may + or may not succeed depending on timing.</warning></p> + </desc> + </func> + <func> - <name>sockname(Socket) -> {ok, {Address, Port}} | {error, Reason}</name> + <name>sockname(Socket) -> {ok, {Address, Port}} | + {error, Reason}</name> <fsummary>Return the local address and port.</fsummary> <type> <v>Socket = sslsocket()</v> @@ -489,217 +666,84 @@ <c>Socket</c>.</p> </desc> </func> + + <func> + <name>start() -> </name> + <name>start(Type) -> ok | {error, Reason}</name> + <fsummary>Starts the Ssl application. </fsummary> + <type> + <v>Type = permanent | transient | temporary</v> + </type> + <desc> + <p>Starts the Ssl application. Default type + is temporary. + <seealso marker="kernel:application">application(3)</seealso></p> + </desc> + </func> + <func> + <name>stop() -> ok </name> + <fsummary>Stops the Ssl application.</fsummary> + <desc> + <p>Stops the Ssl application. + <seealso marker="kernel:application">application(3)</seealso></p> + </desc> + </func> + <func> - <name>transport_accept(Socket) -> {ok, NewSocket} | {error, Reason}</name> - <name>transport_accept(Socket, Timeout) -> {ok, NewSocket} | {error, Reason}</name> - <fsummary>Accept an incoming connection and prepare for <c>ssl_accept</c></fsummary> + <name>transport_accept(Socket) -></name> + <name>transport_accept(Socket, Timeout) -> + {ok, NewSocket} | {error, Reason}</name> + <fsummary>Accept an incoming connection and + prepare for <c>ssl_accept</c></fsummary> <type> <v>Socket = NewSocket = sslsocket()</v> <v>Timeout = integer()</v> - <v>Reason = atom()</v> + <v>Reason = reason()</v> </type> <desc> <p>Accepts an incoming connection request on a listen socket. - <c>ListenSocket</c> must be a socket returned from <c>listen/2</c>. - The socket returned should be passed to <c>ssl_accept</c> to - complete ssl handshaking and establishing the connection.</p> + <c>ListenSocket</c> must be a socket returned from + <c>listen/2</c>. The socket returned should be passed to + <c>ssl_accept</c> to complete ssl handshaking and + establishing the connection.</p> <warning> <p>The socket returned can only be used with <c>ssl_accept</c>, no traffic can be sent or received before that call.</p> </warning> - <p>The accepted socket inherits the options set for <c>ListenSocket</c> - in <c>listen/2</c>.</p> - <p>The default value for <c>Timeout</c> is <c>infinity</c>. If - <c>Timeout</c> is specified, and no connection is accepted within - the given time, <c>{error, timeout}</c> is returned.</p> + <p>The accepted socket inherits the options set for + <c>ListenSocket</c> in <c>listen/2</c>.</p> + <p>The default + value for <c>Timeout</c> is <c>infinity</c>. If + <c>Timeout</c> is specified, and no connection is accepted + within the given time, <c>{error, timeout}</c> is + returned.</p> </desc> </func> + <func> - <name>version() -> {ok, {SSLVsn, CompVsn, LibVsn}}</name> - <fsummary>Return the version of SSL.</fsummary> + <name>versions() -> + [{SslAppVer, SupportedSslVer, AvailableSslVsn}]</name> + <fsummary>Returns version information relevant for the + ssl application.</fsummary> <type> - <v>SSLVsn = CompVsn = LibVsn = string()()</v> + <v>SslAppVer = string()</v> + <v>SupportedSslVer = [protocol()]</v> + <v>AvailableSslVsn = [protocol()]</v> </type> <desc> - <p>Returns the SSL application version (<c>SSLVsn</c>), the library - version used when compiling the SSL application port program - (<c>CompVsn</c>), and the actual library version used when - dynamically linking in runtime (<c>LibVsn</c>). - </p> - <p>If the SSL application has not been started, <c>CompVsn</c> and - <c>LibVsn</c> are empty strings. - </p> + <p> + Returns version information relevant for the + ssl application.</p> </desc> </func> - </funcs> - - <section> - <title>ERRORS</title> - <p>The possible error reasons and the corresponding diagnostic strings - returned by <c>format_error/1</c> are either the same as those defined - in the <c>inet(3)</c> reference manual, or as follows: - </p> - <taglist> - <tag><c>closed</c></tag> - <item> - <p>Connection closed for the operation in question. - </p> - </item> - <tag><c>ebadsocket</c></tag> - <item> - <p>Connection not found (internal error). - </p> - </item> - <tag><c>ebadstate</c></tag> - <item> - <p>Connection not in connect state (internal error). - </p> - </item> - <tag><c>ebrokertype</c></tag> - <item> - <p>Wrong broker type (internal error). - </p> - </item> - <tag><c>ecacertfile</c></tag> - <item> - <p>Own CA certificate file is invalid. - </p> - </item> - <tag><c>ecertfile</c></tag> - <item> - <p>Own certificate file is invalid. - </p> - </item> - <tag><c>echaintoolong</c></tag> - <item> - <p>The chain of certificates provided by peer is too long. - </p> - </item> - <tag><c>ecipher</c></tag> - <item> - <p>Own list of specified ciphers is invalid. - </p> - </item> - <tag><c>ekeyfile</c></tag> - <item> - <p>Own private key file is invalid. - </p> - </item> - <tag><c>ekeymismatch</c></tag> - <item> - <p>Own private key does not match own certificate. - </p> - </item> - <tag><c>enoissuercert</c></tag> - <item> - <p>Cannot find certificate of issuer of certificate provided - by peer. - </p> - </item> - <tag><c>enoservercert</c></tag> - <item> - <p>Attempt to do accept without having set own certificate. - </p> - </item> - <tag><c>enotlistener</c></tag> - <item> - <p>Attempt to accept on a non-listening socket. - </p> - </item> - <tag><c>enoproxysocket</c></tag> - <item> - <p>No proxy socket found (internal error). - </p> - </item> - <tag><c>enooptions</c></tag> - <item> - <p>The list of options is empty. - </p> - </item> - <tag><c>enotstarted</c></tag> - <item> - <p>The SSL application has not been started. - </p> - </item> - <tag><c>eoptions</c></tag> - <item> - <p>Invalid list of options. - </p> - </item> - <tag><c>epeercert</c></tag> - <item> - <p>Certificate provided by peer is in error. - </p> - </item> - <tag><c>epeercertexpired</c></tag> - <item> - <p>Certificate provided by peer has expired. - </p> - </item> - <tag><c>epeercertinvalid</c></tag> - <item> - <p>Certificate provided by peer is invalid. - </p> - </item> - <tag><c>eselfsignedcert</c></tag> - <item> - <p>Certificate provided by peer is self signed. - </p> - </item> - <tag><c>esslaccept</c></tag> - <item> - <p>Server SSL handshake procedure between client and server failed. - </p> - </item> - <tag><c>esslconnect</c></tag> - <item> - <p>Client SSL handshake procedure between client and server failed. - </p> - </item> - <tag><c>esslerrssl</c></tag> - <item> - <p>SSL protocol failure. Typically because of a fatal alert - from peer. - </p> - </item> - <tag><c>ewantconnect</c></tag> - <item> - <p>Protocol wants to connect, which is not supported in - this version of the SSL application. - </p> - </item> - <tag><c>ex509lookup</c></tag> - <item> - <p>Protocol wants X.509 lookup, which is not supported in - this version of the SSL application. - </p> - </item> - <tag><c>{badcall, Call}</c></tag> - <item> - <p>Call not recognized for current mode (active or passive) and - state of socket. - </p> - </item> - <tag><c>{badcast, Cast}</c></tag> - <item> - <p>Call not recognized for current mode (active or passive) and - state of socket. - </p> - </item> - <tag><c>{badinfo, Info}</c></tag> - <item> - <p>Call not recognized for current mode (active or passive) and - state of socket. - </p> - </item> - </taglist> - </section> - + </funcs> + <section> <title>SEE ALSO</title> - <p>gen_tcp(3), inet(3) public_key(3) </p> + <p><seealso marker="kernel:inet">inet(3) </seealso> and + <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso> + </p> </section> - -</erlref> +</erlref> diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index ae8bd87781..2ba6f48611 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -4,7 +4,7 @@ <appref> <header> <copyright> - <year>1999</year><year>2009</year> + <year>1999</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,45 +13,20 @@ 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>ssl</title> - <prepared>Peter Högfeldt</prepared> - <responsible>Peter Högfeldt</responsible> - <docno></docno> - <approved>Peter Högfeldt</approved> - <checked>Peter Högfeldt</checked> - <date>2005-03-10</date> - <rev>E</rev> <file>ssl_app.sgml</file> </header> <app>ssl</app> - <appsummary>The SSL Application</appsummary> - <description> - <p>The Secure Socket Layer (SSL) application provides secure - socket communication over TCP/IP. - </p> - </description> - - <section> - <title>Warning</title> - <p>In previous versions of Erlang/OTP SSL it was advised, as a - work-around, to set the operating system environment variable - <c>SSL_CERT_FILE</c> to point at a file containing CA - certificates. That variable is no longer needed, and is not - recognised by Erlang/OTP SSL any more. - </p> - <p>However, the OpenSSL package does interpret that environment - variable. Hence a setting of that variable might have - unpredictable effects on the Erlang/OTP SSL application. It is - therefore adviced to not used that environment variable at all.</p> - </section> + <appsummary>The SSL application provides secure communication over + sockets.</appsummary> <section> <title>Environment</title> @@ -61,115 +36,43 @@ </p> <p>Note that the environment parameters can be set on the command line, for instance,</p> - <p><c>erl ... -ssl protocol_version '[sslv2,sslv3]' ...</c>. + <p><c>erl ... -ssl protocol_version '[sslv3, tlsv1]' ...</c>. </p> <taglist> - <tag><c><![CDATA[ephemeral_rsa = true | false <optional>]]></c></tag> - <item> - <p>Enables all SSL servers (those that listen and accept) - to use ephemeral RSA key generation when a clients connect with - weak handshake cipher specifications, that need equally weak - ciphers from the server (i.e. obsolete restrictions on export - ciphers). Default is <c>false</c>. - </p> - </item> - <tag><c><![CDATA[debug = true | false <optional>]]></c></tag> - <item> - <p>Causes debug information to be written to standard - output. Default is <c>false</c>. - </p> - </item> - <tag><c><![CDATA[debugdir = path() | false <optional>]]></c></tag> - <item> - <p>Causes debug information output controlled by <c>debug</c> - and <c>msgdebug</c> to be printed to a file named - <c><![CDATA[ssl_esock.<pid>.log]]></c> in the directory specified by - <c>debugdir</c>, where <c><![CDATA[<pid>]]></c> is the operating system - specific textual representation of the process identifier - of the external port program of the SSL application. Default - is <c>false</c>, i.e. no log file is produced. - </p> - </item> - <tag><c><![CDATA[msgdebug = true | false <optional>]]></c></tag> + <tag><c><![CDATA[protocol_version = [sslv3|tlsv1] <optional>]]></c>.</tag> <item> - <p>Sets <c>debug = true</c> and causes also the contents - of low level messages to be printed to standard output. - Default is <c>false</c>. - </p> + <p>Protocol that will be supported by started clients and + servers. If this option is not set it will default to all + protocols currently supported by the erlang ssl application. + Note that this option may be overridden by the version option + to ssl:connect/[2,3] and ssl:listen/2. + </p> </item> - <tag><c><![CDATA[port_program = string() | false <optional>]]></c></tag> - <item> - <p>Name of port program. The default is <c>ssl_esock</c>. - </p> - </item> - <tag><c><![CDATA[protocol_version = [sslv2|sslv3|tlsv1] <optional>]]></c>.</tag> + + <tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag> <item> - <p>Name of protocols to use. If this option is not set, - all protocols are assumed, i.e. the default value is - <c>[sslv2, sslv3, tlsv1]</c>. - </p> + <p>The lifetime of session data in seconds. + </p> </item> - <tag><c><![CDATA[proxylsport = integer() | false <optional>]]></c></tag> + + <tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag> <item> - <p>Define the port number of the listen port of the - SSL port program. Almost never is this option needed. + <p> + Name of session cache callback module that implements + the ssl_session_cache_api behavior, defaults to + ssl_session_cache.erl. </p> </item> - <tag><c><![CDATA[proxylsbacklog = integer() | false <optional>]]></c></tag> + + <tag><c><![CDATA[session_cb_init_args = list() <optional>]]></c></tag> <item> - <p>Set the listen queue size of the listen port of the - SSL port program. The default is 128. - </p> + <p> + List of arguments to the init function in session cache + callback module, defaults to []. + </p> </item> - </taglist> - </section> - <section> - <title>OpenSSL libraries</title> - <p>The current implementation of the Erlang SSL application is - based on the <em>OpenSSL</em> package version 0.9.7 or higher. - There are source and binary releases on the web. - </p> - <p>Source releases of OpenSSL can be downloaded from the <url href="http://www.openssl.org">OpenSSL</url> project home page, - or mirror sites listed there. - </p> - <p>The same URL also contains links to some compiled binaries and - libraries of OpenSSL (see the <c>Related/Binaries</c> menu) of - which the <url href="http://www.shininglightpro.com/search.php?searchname=Win32+OpenSSL">Shining Light Productions Win32 and OpenSSL</url> pages are of - interest for the Win32 user. - </p> - <p>For some Unix flavours there are binary packages available - on the net. - </p> - <p>If you cannot find a suitable binary OpenSSL package, you - have to fetch an OpenSSL source release and compile it. - </p> - <p>You then have to compile and install the libraries - <c>libcrypto.so</c> and <c>libssl.so</c> (Unix), or the - libraries <c>libeay32.dll</c> and <c>ssleay32.dll</c> (Win32). - </p> - <p>For Unix The <c>ssl_esock</c> port program is delivered linked - to OpenSSL libraries in <c>/usr/local/lib</c>, but the default - dynamic linking will also accept libraries in <c>/lib</c> and - <c>/usr/lib</c>. - </p> - <p>If that is not applicable to the particular Unix operating - system used, the example <c>Makefile</c> in the SSL - <c>priv/obj</c> directory, should be used as a guide to - relinking the final version of the port program. - </p> - <p>For <c>Win32</c> it is only required that the libraries can be - found from the <c>PATH</c> environment variable, or that they - reside in the appropriate <c>SYSTEM32</c> directory; hence no - particular relinking is need. Hence no example <c>Makefile</c> - for Win32 is provided.</p> - </section> - - <section> - <title>Restrictions</title> - <p>Users must be aware of export restrictions and patent rights - concerning cryptographic software. - </p> + </taglist> </section> <section> @@ -178,5 +81,3 @@ </section> </appref> - - diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml index c743cd67a3..7bcc12eb5f 100644 --- a/lib/ssl/doc/src/ssl_distribution.xml +++ b/lib/ssl/doc/src/ssl_distribution.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,7 +32,13 @@ <file>ssl_distribution.xml</file> </header> <p>This chapter describes how the Erlang distribution can use - SSL to get additional verification and security.</p> + SSL to get additional verification and security. + + <note><p>Note this + documentation is written for the old ssl implementation and + will be updated for the new one once this functionality is + supported by the new implementation.</p></note> + </p> <section> <title>Introduction</title> @@ -49,7 +55,7 @@ all participating Erlang nodes in a distributed system must use this distribution module.</p> <p>The security depends on how the connections are set up, one can - use key files or certificates to just get a crypted + use key files or certificates to just get a encrypted connection. One can also make the SSL package verify the certificates of other nodes to get additional security. Cookies are however always used as they can be used to @@ -173,7 +179,7 @@ Eshell V5.0 (abort with ^G) <c>certfile</c> can (and usually needs to) be specified as <c>client_certfile</c> and <c>server_certfile</c>. The <c>client_certfile</c> is used when the distribution initiates a - connection to another node and the <c>server_cerfile</c> is used + connection to another node and the <c>server_certfile</c> is used when accepting a connection from a remote node. </p> <p>The command line argument for specifying the SSL options is named <c>-ssl_dist_opt</c> and should be followed by an even number of diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml index 3dc2332795..6936408881 100644 --- a/lib/ssl/doc/src/ssl_protocol.xml +++ b/lib/ssl/doc/src/ssl_protocol.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2003</year><year>2009</year> + <year>2003</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,337 +13,138 @@ 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>The SSL Protocol</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-04-28</date> - <rev>PA2</rev> + <title>Transport Layer Security (TLS) and its predecessor, Secure Socket Layer (SSL)</title> <file>ssl_protocol.xml</file> </header> - <p>Here we provide a short introduction to the SSL protocol. We only - consider those part of the protocol that are important from a - programming point of view. - </p> - <p>For a very good general introduction to SSL and TLS see the book - <cite id="rescorla"></cite>. - </p> - <p><em>Outline:</em></p> - <list type="bulleted"> - <item>Two types of connections - connection: handshake, data transfer, and - shutdown - - SSL/TLS protocol - server must have certificate - what the the - server sends to the client - client may verify the server - - server may ask client for certificate - what the client sends to - the server - server may then verify the client - verification - - certificate chains - root certificates - public keys - key - agreement - purpose of certificate - references</item> - </list> + + <p>The erlang ssl application currently supports SSL 3.0 and TLS 1.0 + RFC 2246, and will in the future also support later versions of TLS. + SSL 2.0 is not supported. + </p> - <section> - <title>SSL Connections</title> - <p>The SSL protocol is implemented on top of the TCP/IP protocol. - From an endpoint view it also has the same type of connections - as that protocol, almost always created by calls to socket - interface functions <em>listen</em>, <em>accept</em> and - <em>connect</em>. The endpoints are <em>servers</em> and - <em>clients</em>. - </p> - <p>A <em>server</em><em>listen</em>s for connections on a - specific address and port. This is done once. The server then - <em>accept</em>s each connections on that same address and - port. This is typically done indefinitely many times. - </p> - <p>A <em>client</em> connects to a server on a specific address - and port. For each purpose this is done once. - </p> - <p>For a plain TCP/IP connection the establishment of a connection - (through an accept or a connect) is followed by data transfer between - the client and server, finally ended by a connection close. - </p> - <p>An SSL connection also consists of data transfer and connection - close, However, the data transfer contains encrypted data, and - in order to establish the encryption parameters, the data - transfer is preceded by an SSL <em>handshake</em>. In this - handshake the server plays a dominant role, and the main - instrument used in achieving a valid SSL connection is the - server's <em>certificate</em>. We consider certificates in the - next section, and the SSL handshake in a subsequent section.</p> - </section> + <p>By default erlang ssl is run over the TCP/IP protocol even + though you could plug in an other reliable transport protocol + with the same API as gen_tcp.</p> + + <p>If a client and server wants to use an upgrade mechanism, such as + defined by RFC2817, to upgrade a regular TCP/IP connection to a ssl + connection the erlang ssl API supports this. This can be useful for + things such as supporting HTTP and HTTPS on the same port and + implementing virtual hosting. + </p> <section> - <title>Certificates</title> - <p>A certificate is similar to a driver's license, or a - passport. The holder of the certificate is called the - <em>subject</em>. First of all the certificate identifies the - subject in terms of the name of the subject, its postal address, - country name, company name (if applicable), etc. - </p> - <p>Although a driver's license is always issued by a well-known and - distinct authority, a certificate may have an <em>issuer</em> - that is not so well-known. Therefore a certificate also always - contains information on the issuer of the certificate. That - information is of the same type as the information on the - subject. The issuer of a certificate also signs the certificate - with a <em>digital signature</em> (the signature is an inherent - part of the certificate), which allow others to verify that the - issuer really is the issuer of the certificate. - </p> - <p>Now that a certificate can be checked by verifying the - signature of the issuer, the question is how to trust the - issuer. The answer to this question is to require that there is - a certificate for the issuer as well. That issuer has in turn an - issuer, which must also have a certificate, and so on. This - <em>certificate chain</em> has to have en end, which then must - be a certificate that is trusted by other means. We shall cover - this problem of <em>authentication</em> in a subsequent - section. - </p> + <title>Security overview</title> + + <p>To achieve authentication and privacy the client and server will + perform a TLS Handshake procedure before transmitting or receiving + any data. During the handshake they agree on a protocol version and + cryptographic algorithms, they generate shared secrets using public + key cryptographics and optionally authenticate each other with + digital certificates.</p> </section> - + <section> - <title>Encryption Algorithms</title> - <p>An encryption algorithm is a mathematical algorithm for - encryption and decryption of messages (arrays of bytes, - say). The algorithm as such is always required to be publicly - known, otherwise its strength cannot be evaluated, and hence it - cannot be used reliably. The secrecy of an encrypted message is - not achieved by the secrecy of the algorithm used, but by the - secrecy of the <em>keys</em> used as input to the encryption and - decryption algorithms. For an account of cryptography in general - see <cite id="schneier"></cite>. - </p> - <p>There are two classes of encryption algorithms: <em>symmetric key</em> algorithms and <em>public key</em> algorithms. Both - types of algorithms are used in the SSL protocol. - </p> - <p>In the sequel we assume holders of keys keep them secret (except - public keys) and that they in that sense are trusted. How a - holder of a secret key is proved to be the one it claims to be - is a question of <em>authentication</em>, which, in the context - of the SSL protocol, is described in a section further below. - </p> - - <section> - <title>Symmetric Key Algorithms</title> - <p>A <em>symmetric key</em> algorithm has one key only. The key - is used for both encryption and decryption. Obviously the key - of a symmetric key algorithm must always be kept secret by the - users of the key. DES is an example of a symmetric key - algorithm. - </p> - <p>Symmetric key algorithms are fast compared to public key - algorithms. They are therefore typically used for encrypting - bulk data. - </p> - </section> - - <section> - <title>Public Key Algorithms</title> - <p>A <em>public key</em> algorithm has two keys. Any of the two - keys can be used for encryption. A message encrypted with one - of the keys, can only be decrypted with the other key. One of - the keys is public (known to the world), while the other key - is private (i.e. kept secret) by the owner of the two keys. - </p> - <p>RSA is an example of a public key algorithm. - </p> - <p>Public key algorithms are slow compared to symmetric key - algorithms, and they are therefore seldom used for bulk data - encryption. They are therefore only used in cases where the - fact that one key is public and the other is private, provides - features that cannot be provided by symmetric algorithms. - </p> - </section> - - <section> - <title>Digital Signature Algorithms</title> - <p>An interesting feature of a public key algorithm is that its - public and private keys can both be used for encryption. - Anyone can use the public key to encrypt a message, and send - that message to the owner of the private key, and be sure of - that only the holder of the private key can decrypt the - message. - </p> - <p>On the other hand, the owner of the private key can encrypt a - message with the private key, thus obtaining an encrypted - message that can decrypted by anyone having the public key. - </p> - <p>The last approach can be used as a digital signature - algorithm. The holder of the private key signs an array of - bytes by performing a specified well-known <em>message digest algorithm</em> to compute a hash of the array, encrypts the - hash value with its private key, an then presents the original - array, the name of the digest algorithm, and the encryption of - the hash value as a <em>signed array of bytes</em>. - </p> - <p>Now anyone having the public key, can decrypt the encrypted - hash value with that key, compute the hash with the specified - digest algorithm, and check that the hash values compare equal - in order to verify that the original array was indeed signed - by the holder of the private key. - </p> - <p>What we have accounted for so far is by no means all that can - be said about digital signatures (see <cite id="schneier"></cite>for - further details). - </p> - </section> - - <section> - <title>Message Digests Algorithms</title> - <p>A message digest algorithm is a hash function that accepts - an array bytes of arbitrary but finite length of input, and - outputs an array of bytes of fixed length. Such an algorithm - is also required to be very hard to invert. - </p> - <p>MD5 (16 bytes output) and SHA1 (20 bytes output) are examples - of message digest algorithms. - </p> - </section> + <title>Data Privacy and Integrity</title> + + <p>A <em>symmetric key</em> algorithm has one key only. The key is + used for both encryption and decryption. These algorithms are fast + compared to public key algorithms (using two keys, a public and a + private one) and are therefore typically used for encrypting bulk + data. + </p> + + <p>The keys for the symmetric encryption are generated uniquely + for each connection and are based on a secret negotiated + in the TLS handshake. </p> + + <p>The TLS handshake protocol and data transfer is run on top of + the TLS Record Protocol that uses a keyed-hash MAC (Message + Authenticity Code), or HMAC, to protect the message's data + integrity. From the TLS RFC "A Message Authentication Code is a + one-way hash computed from a message and some secret data. It is + difficult to forge without knowing the secret data. Its purpose is + to detect if the message has been altered." + </p> + </section> - <section> - <title>SSL Handshake</title> - <p>The main purpose of the handshake performed before an an SSL - connection is established is to negotiate the encryption - algorithm and key to be used for the bulk data transfer between - the client and the server. We are writing <em>the</em> key, - since the algorithm to choose for bulk encryption one of the - symmetric algorithms. - </p> - <p>There is thus only one key to agree upon, and obviously that - key has to be kept secret between the client and the server. To - obtain that the handshake has to be encrypted as well. - </p> - <p>The SSL protocol requires that the server always sends its - certificate to the client in the beginning of the handshake. The - client then retrieves the server's public key from the - certificate, which means that the client can use the server's - public key to encrypt messages to the server, and the server can - decrypt those messages with its private key. Similarly, the - server can encrypt messages to the client with its private key, - and the client can decrypt messages with the server's public - key. It is thus is with the server's public and private keys - that messages in the handshake are encrypted and decrypted, and - hence the key agreed upon for symmetric encryption of bulk data - can be kept secret (there are more things to consider to really - keep it secret, see <cite id="rescorla"></cite>). - </p> - <p>The above indicates that the server does not care who is - connecting, and that only the client has the possibility to - properly identify the server based on the server's certificate. - That is indeed true in the minimal use of the protocol, but it - is possible to instruct the server to request the certificate of - the client, in order to have a means to identify the client, but - it is by no means required to establish an SSL connection. - </p> - <p>If a server request the client certificate, it verifies, as a - part of the protocol, that the client really holds the private - key of the certificate by sending the client a string of bytes - to encrypt with its private key, which the server then decrypts - with the client's public key, the result of which is compared - with the original string of bytes (a similar procedure is always - performed by the client when it has received the server's - certificate). - </p> - <p>The way clients and servers <em>authenticate</em> each other, - i.e. proves that their respective peers are what they claim to - be, is the topic of the next section. - </p> - </section> + <section> + <title>Digital Certificates</title> + <p>A certificate is similar to a driver's license, or a + passport. The holder of the certificate is called the + <em>subject</em>. The certificate is signed + with the private key of the issuer of the certificate. A chain + of trust is build by having the issuer in its turn being + certified by an other certificate and so on until you reach the + so called root certificate that is self signed i.e. issued + by itself.</p> + + <p>Certificates are issued by <em>certification + authorities</em> (<em>CA</em>s) only. There are a handful of + top CAs in the world that issue root certificates. You can + examine the certificates of several of them by clicking + through the menus of your web browser. + </p> + </section> + + <section> + <title>Authentication of Sender</title> + + <p>Authentication of the sender is done by public key path + validation as defined in RFC 3280. Simplified that means that + each certificate in the certificate chain is issued by the one + before, the certificates attributes are valid ones, and the + root cert is a trusted cert that is present in the trusted + certs database kept by the peer.</p> + + <p>The server will always send a certificate chain as part of + the TLS handshake, but the client will only send one if + the server requests it. If the client does not have + an appropriate certificate it may send an "empty" certificate + to the server.</p> + + <p>The client may choose to accept some path evaluation errors + for instance a web browser may ask the user if they want to + accept an unknown CA root certificate. The server, if it request + a certificate, will on the other hand not accept any path validation + errors. It is configurable if the server should accept + or reject an "empty" certificate as response to + a certificate request.</p> + </section> + + <section> + <title>TLS Sessions</title> + + <p>From the TLS RFC "A TLS session is an association between a + client and a server. Sessions are created by the handshake + protocol. Sessions define a set of cryptographic security + parameters, which can be shared among multiple + connections. Sessions are used to avoid the expensive negotiation + of new security parameters for each connection."</p> - <section> - <title>Authentication</title> - <p>As we have already seen the reception of a certificate from a - peer is not enough to prove that the peer is authentic. More - certificates are needed, and we have to consider how certificates - are issued and on what grounds. - </p> - <p>Certificates are issued by <em>certification authorities</em> - (<em>CA</em>s) only. They issue certificates both for other CAs - and ordinary users (which are not CAs). - </p> - <p>Certain CAs are <em>top CAs</em>, i.e. they do not have a - certificate issued by another CA. Instead they issue their own - certificate, where the subject and issuer part of the - certificate are identical (such a certificate is called a - self-signed certificate). A top CA has to be well-known, and has - to have a publicly available policy telling on what grounds it - issues certificates. - </p> - <p>There are a handful of top CAs in the world. You can examine the - certificates of several of them by clicking through the menus of - your web browser. - </p> - <p>A top CA typically issues certificates for other CAs, called - <em>intermediate CAs</em>, but possibly also to ordinary users. Thus - the certificates derivable from a top CA constitute a tree, where - the leaves of the tree are ordinary user certificates. - </p> - <p>A <em>certificate chain</em> is an ordered sequence of - certificates, <c>C1, C2, ..., Cn</c>, say, where <c>C1</c> is a - top CA certificate, and where <c>Cn</c> is an ordinary user - certificate, and where the holder of <c>C1</c> is the issuer of - <c>C2</c>, the holder of <c>C2</c> is the issuer of <c>C3</c>, - ..., and the holder of <c>Cn-1</c> is the issuer of <c>Cn</c>, - the ordinary user certificate. The holders of <c>C2, C3, ..., Cn-1</c> are then intermediate CAs. - </p> - <p>Now to verify that a certificate chain is unbroken we have to - take the public key from each certificate <c>Ck</c>, and apply - that key to decrypt the signature of certificate <c>Ck-1</c>, - thus obtaining the message digest computed by the holder of the - <c>Ck</c> certificate, compute the real message digest of the - <c>Ck-1</c> certificate and compare the results. If they compare - equal the link of the chain between <c>Ck</c> and <c>Ck-1</c> is - considered to unbroken. This is done for each link k = 1, 2, - ..., n-1. If all links are found to be unbroken, the user - certificate <c>Cn</c> is considered authenticated. - </p> + <p>Session data is by default kept by the ssl application in a + memory storage hence session data will be lost at application + restart or takeover. Users may define their own callback module + to handle session data storage if persistent data storage is + required. Session data will also be invalidated after 24 hours + from it was saved, for security reasons. It is of course + possible to configure the amount of time the session data should be + saved.</p> - <section> - <title>Trusted Certificates</title> - <p>Now that there is a way to authenticate a certificate by - checking that all links of a certificate chain are unbroken, - the question is how you can be sure to trust the certificates - in the chain, and in particular the top CA certificate of the - chain. - </p> - <p>To provide an answer to that question consider the - perspective of a client, which have just received the - certificate of the server. In order to authenticate the server - the client has to construct a certificate chain and to prove - that the chain is unbroken. The client has to have a set of CA - certificates (top CA or intermediate CA certificates) not - obtained from the server, but obtained by other means. Those - certificates are kept <c>locally</c> by the client, and are - trusted by the client. - </p> - <p>More specifically, the client does not really have to have - top CA certificates in its local storage. In order to - authenticate a server it is sufficient for the client to - posses the trusted certificate of the issuer of the server - certificate. - </p> - <p>Now that is not the whole story. A server can send an - (incomplete) certificate chain to its client, and then the - task of the client is to construct a certificate chain that - begins with a trusted certificate and ends with the server's - certificate. (A client can also send a chain to its server, - provided the server requested the client's certificate.) - </p> - <p>All this means that an unbroken certificate chain begins with - a trusted certificate (top CA or not), and ends with the peer - certificate. That is the end of the chain is obtained from the - peer, but the beginning of the chain is obtained from local - storage, which is considered trusted. - </p> - </section> - </section> -</chapter> + <p>Ssl clients will by default try to reuse an available session, + ssl servers will by default agree to reuse sessions when clients + ask to do so.</p> + + </section> + </chapter> diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml new file mode 100644 index 0000000000..e0b07961fb --- /dev/null +++ b/lib/ssl/doc/src/ssl_session_cache_api.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1999</year><year>2010</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>ssl</title> + <file>ssl_session_cache_api.xml</file> + </header> + <module>ssl_session_cache_api</module> + <modulesummary>Defines the API for the TLS session cache so + that the data storage scheme can be replaced by + defining a new callback module implementing this API.</modulesummary> + + <section> + <title>Common Data Types</title> + + <p>The following data types are used in the functions below: + </p> + + <p><c>cache_ref() = opaque()</c></p> + + <p><c>key() = {partialkey(), session_id()}</c></p> + + <p><c>partialkey() = opaque()</c></p> + + <p><c>session_id() = binary()</c></p> + + <p><c>session() = opaque()</c></p> + + </section> + + <funcs> + + <func> + <name>delete(Cache, Key) -> _</name> + <fsummary></fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + </type> + <desc> + <p> Deletes a cache entry. Will only be called from the cache + handling process. + </p> + </desc> + </func> + + <func> + <name>foldl(Fun, Acc0, Cache) -> Acc</name> + <fsummary></fsummary> + <type> + <v></v> + </type> + <desc> + <p>Calls Fun(Elem, AccIn) on successive elements of the + cache, starting with AccIn == Acc0. Fun/2 must return a new + accumulator which is passed to the next call. The function returns + the final value of the accumulator. Acc0 is returned if the cache is + empty. + </p> + </desc> + </func> + + <func> + <name>init() -> opaque() </name> + <fsummary>Return cache reference</fsummary> + <type> + <v></v> + </type> + <desc> + <p>Performs possible initializations of the cache and returns + a reference to it that will be used as parameter to the other + api functions. Will be called by the cache handling processes + init function, hence putting the same requirements on it as + a normal process init function. + </p> + </desc> + </func> + + <func> + <name>lookup(Cache, Key) -> Entry</name> + <fsummary> Looks up a cache entry.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + <v> Entry = session() | undefined </v> + </type> + <desc> + <p>Looks up a cache entry. Should be callable from any + process. + </p> + </desc> + </func> + + <func> + <name>select_session(Cache, PartialKey) -> [session()]</name> + <fsummary>>Selects sessions that could be reused.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> PartialKey = partialkey()</v> + <v> Session = session()</v> + </type> + <desc> + <p>Selects sessions that could be reused. Should be callable + from any process. + </p> + </desc> + </func> + + <func> + <name>terminate(Cache) -> _</name> + <fsummary>Called by the process that handles the cache when it + is about to terminate.</fsummary> + <type> + <v>Cache = term() - as returned by init/0</v> + </type> + <desc> + <p>Takes care of possible cleanup that is needed when the + cache handling process terminates. + </p> + </desc> + </func> + + <func> + <name>update(Cache, Key, Session) -> _</name> + <fsummary> Caches a new session or updates a already cached one.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + <v> Session = session()</v> + </type> + <desc> + <p> Caches a new session or updates a already cached one. Will + only be called from the cache handling process. + </p> + </desc> + </func> + + </funcs> + +</erlref> diff --git a/lib/ssl/doc/src/usersguide.xml b/lib/ssl/doc/src/usersguide.xml index 98071f5742..6528c00a0b 100644 --- a/lib/ssl/doc/src/usersguide.xml +++ b/lib/ssl/doc/src/usersguide.xml @@ -4,7 +4,7 @@ <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,43 +13,27 @@ 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>SSL User's Guide</title> <prepared>OTP Team</prepared> - <docno></docno> <date>2003-05-26</date> - <rev>B</rev> <file>usersguide.sgml</file> </header> <description> <p>The <em>SSL</em> application provides secure communication over sockets. </p> - <p>This product includes software developed by the OpenSSL Project for - use in the OpenSSL Toolkit (http://www.openssl.org/). - </p> - <p>This product includes cryptographic software written by Eric Young - ([email protected]). - </p> - <p>This product includes software written by Tim Hudson - ([email protected]). - </p> - <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>. - </p> </description> <xi:include href="ssl_protocol.xml"/> <xi:include href="using_ssl.xml"/> - <xi:include href="pkix_certs.xml"/> - <xi:include href="create_certs.xml"/> <xi:include href="ssl_distribution.xml"/> - <xi:include href="licenses.xml"/> </part> diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml index ba74dcfef4..4bdd8f97b4 100644 --- a/lib/ssl/doc/src/using_ssl.xml +++ b/lib/ssl/doc/src/using_ssl.xml @@ -21,93 +21,129 @@ </legalnotice> - <title>Using the SSL application</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-04-23</date> - <rev>PA2</rev> + <title>Using the SSL API</title> <file>using_ssl.xml</file> </header> - <p>Here we provide an introduction to using the Erlang/OTP SSL - application, which is accessed through the <c>ssl</c> interface - module. - </p> - <p>We also present example code in the Erlang module - <c>client_server</c>, also provided in the directory - <c>ssl-X.Y.Z/examples</c>, with source code in <c>src</c> and the - compiled module in <c>ebin</c> of that directory. - </p> <section> - <title>The ssl Module</title> - <p>The <c>ssl</c> module provides the user interface to the Erlang/OTP - SSL application. The interface functions provided are very similar - to those provided by the <c>gen_tcp</c> and <c>inet</c> modules. - </p> - <p>Servers use the interface functions <c>listen</c> and - <c>accept</c>. The <c>listen</c> function specifies a TCP port - to to listen to, and each call to the <c>accept</c> function - establishes an incoming connection. - </p> - <p>Clients use the <c>connect</c> function which specifies the address - and port of a server to connect to, and a successful call establishes - such a connection. - </p> - <p>The <c>listen</c> and <c>connect</c> functions have almost all - the options that the corresponding functions in <c>gen_tcp/</c> have, - but there are also additional options specific to the SSL protocol. - </p> - <p>The most important SSL specific option is the <c>cacertfile</c> - option which specifies a local file containing trusted CA - certificates which are and used for peer authentication. This - option is used by clients and servers in case they want to - authenticate their peers. - </p> - <p>The <c>certfile</c> option specifies a local path to a file - containing the certificate of the holder of the connection - endpoint. In case of a server endpoint this option is mandatory - since the contents of the sever certificate is needed in the - the handshake preceding the establishment of a connection. - </p> - <p>Similarly, the <c>keyfile</c> option points to a local file - containing the private key of the holder of the endpoint. If the - <c>certfile</c> option is present, this option has to be - specified as well, unless the private key is provided in the - same file as specified by the <c>certfile</c> option (a - certificate and a private key can thus coexist in the same file). - </p> - <p>The <c>verify</c> option specifies how the peer should be verified: - </p> - <taglist> - <tag>0</tag> - <item>Do not verify the peer,</item> - <tag>1</tag> - <item>Verify peer,</item> - <tag>2</tag> - <item>Verify peer, fail the verification if the peer has no - certificate. </item> - </taglist> - <p>The <c>depth</c> option specifies the maximum length of the - verification certificate chain. Depth = 0 means the peer - certificate, depth = 1 the CA certificate, depth = 2 the next CA - certificate etc. If the verification process does not find a - trusted CA certificate within the maximum length, the verification - fails. - </p> - <p>The <c>ciphers</c> option specifies which ciphers to use (a - string of colon separated cipher names). To obtain a list of - available ciphers, evaluate the <c>ssl:ciphers/0</c> function - (the SSL application has to be running). - </p> - </section> + <title>General information</title> + <p>To see relevant version information for ssl you can + call ssl:versions/0</p> + + <p>To see all supported cipher suites + call ssl:cipher_suites/0. Note that available cipher suites + for a connection will depend on your certificate. It is also + possible to specify a specific cipher suite(s) that you + want your connection to use. Default is to use the strongest + available.</p> - <section> - <title>A Client-Server Example</title> - <p>Here is a simple client server example. - </p> - <codeinclude file="../../examples/src/client_server.erl" tag="" type="erl"></codeinclude> </section> -</chapter> - - + + <section> + <title>Setting up connections</title> + + <p>Here follows some small example of how to set up client/server connections + using the erlang shell. The returned value of the sslsocket has been abbreviated with + <c>[...]</c> as it can be fairly large and is opaque.</p> + + <section> + <title>Minmal example</title> + + <note><p> The minimal setup is not the most secure setup of ssl.</p> + </note> + + <p> Start server side</p> + <code type="erl">1 server> ssl:start(). +ok</code> + + <p>Create a ssl listen socket</p> + <code type="erl">2 server> {ok, ListenSocket} = +ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]). +{ok,{sslsocket, [...]}}</code> + + <p>Do a transport accept on the ssl listen socket</p> + <code type="erl">3 server> {ok, Socket} = ssl:transport_accept(ListenSocket). +{ok,{sslsocket, [...]}}</code> + <p>Start client side</p> + <code type="erl">1 client> ssl:start(). +ok</code> + + <code type="erl">2 client> {ok, Socket} = ssl:connect("localhost", 9999, [], infinity). +{ok,{sslsocket, [...]}}</code> + + <p>Do the ssl handshake</p> + <code type="erl">4 server> ok = ssl:ssl_accept(Socket). +ok</code> + + <p>Send a messag over ssl</p> + <code type="erl">5 server> ssl:send(Socket, "foo"). +ok</code> + + <p>Flush the shell message queue to see that we got the message + sent on the server side</p> + <code type="erl">3 client> flush(). +Shell got {ssl,{sslsocket,[...]},"foo"} +ok</code> + </section> + + <section> + <title>Upgrade example</title> + + <note><p> To upgrade a TCP/IP connection to a ssl connection the + client and server have to aggre to do so. Agreement + may be accompliced by using a protocol such the one used by HTTP + specified in RFC 2817.</p> </note> + + <p>Start server side</p> + <code type="erl">1 server> ssl:start(). +ok</code> + + <p>Create a normal tcp listen socket</p> + <code type="erl">2 server> {ok, ListenSocket} = gen_tcp:listen(9999, [{reuseaddr, true}]). +{ok, #Port<0.475>}</code> + + <p>Accept client connection</p> + <code type="erl">3 server> {ok, Socket} = gen_tcp:accept(ListenSocket). +{ok, #Port<0.476>}</code> + + <p>Start client side</p> + <code type="erl">1 client> ssl:start(). +ok</code> + + <code type="erl">2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999, [], infinity).</code> + + <p>Make sure active is set to false before trying + to upgrade a connection to a ssl connection, otherwhise + ssl handshake messages may be deliverd to the wrong process.</p> + <code type="erl">4 server> inet:setopts(Socket, [{active, false}]). +ok</code> + + <p>Do the ssl handshake.</p> + <code type="erl">5 server> {ok, SSLSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"}, +{certfile, "cert.pem"}, {keyfile, "key.pem"}]). +{ok,{sslsocket,[...]}}</code> + + <p> Upgrade to a ssl connection. Note that the client and server + must agree upon the upgrade and the server must call + ssl:accept/2 before the client calls ssl:connect/3.</p> + <code type="erl">3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"}, +{certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity). +{ok,{sslsocket,[...]}}</code> + + <p>Send a messag over ssl</p> + <code type="erl">4 client> ssl:send(SSLSocket, "foo"). +ok</code> + + <p>Set active true on the ssl socket</p> + <code type="erl">4 server> ssl:setopts(SSLSocket, [{active, true}]). +ok</code> + + <p>Flush the shell message queue to see that we got the message + sent on the client side</p> + <code type="erl">5 server> flush(). +Shell got {ssl,{sslsocket,[...]},"foo"} +ok</code> + </section> + </section> + </chapter> diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile index 121fcc6950..b811b461dc 100644 --- a/lib/ssl/examples/certs/Makefile +++ b/lib/ssl/examples/certs/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2009. All Rights Reserved. +# Copyright Ericsson AB 2003-2010. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -21,4 +21,41 @@ # Invoke with GNU make or clearmake -C gnu. # -include $(ERL_TOP)/make/run_make.mk +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(SSL_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN) + +TARGET_FILES= + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(TARGET_FILES) + +clean: + rm -fr $(TARGET_FILES) *~ *.beam + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/certs + tar cf - etc | \ + (cd $(RELSYSDIR)/examples/certs; tar xf -) + chmod -f -R ug+rw $(RELSYSDIR)/examples +release_docs_spec: diff --git a/lib/ssl/examples/certs/Makefile.in b/lib/ssl/examples/certs/Makefile.in deleted file mode 100644 index 4ea7aaf6dc..0000000000 --- a/lib/ssl/examples/certs/Makefile.in +++ /dev/null @@ -1,80 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2003-2009. 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% -# - -# - -include $(ERL_TOP)/make/target.mk -include $(ERL_TOP)/make/$(TARGET)/otp.mk - -include ../../vsn.mk -VSN=$(SSL_VSN) - -RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN) - -EBIN = ebin -ETC = etc -SRC = src - -OPENSSL_CMD = @OPENSSL_CMD@ - -# We are generating more files than in the following list, but we take -# there existence as successful execution of make rules - -PEMS = cacerts.pem cert.pem key.pem - -PEMFILES = $(PEMS:%=$(ETC)/client/%) $(PEMS:%=$(ETC)/server/%) - -debug opt: $(PEMFILES) - -$(PEMFILES): done - -done: $(EBIN)/make_certs.beam - erl -noinput -pa $(EBIN) -run make_certs all $(OPENSSL_CMD) \ - -s erlang halt - echo >done - -$(EBIN)/make_certs.beam: $(SRC)/make_certs.erl - cd src; erlc -W -o ../$(EBIN) make_certs.erl - -clean: - rm -fr $(EBIN)/* $(SRC)/*~ $(SRC)/*.beam $(ETC) done \ - stderr.txt erl_crash.dump *~ - -docs: - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/certs - tar cf - Makefile ebin etc rnd src | \ - (cd $(RELSYSDIR)/examples/certs; tar xf -) - chmod -f -R ug+rw $(RELSYSDIR)/examples - -release_docs_spec: - - - - - - - - diff --git a/lib/ssl/examples/certs/ebin/.gitignore b/lib/ssl/examples/certs/ebin/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 --- a/lib/ssl/examples/certs/ebin/.gitignore +++ /dev/null diff --git a/lib/ssl/examples/certs/etc/client/cacerts.pem b/lib/ssl/examples/certs/etc/client/cacerts.pem new file mode 100644 index 0000000000..cb19d3d41e --- /dev/null +++ b/lib/ssl/examples/certs/etc/client/cacerts.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIICizCCAfSgAwIBAgIFdMMs9fEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI +ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD +VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS +BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw +MDAwMDBaMH0xETAPBgNVBAMTCGVybGFuZ0NBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0 +ZXJAZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEP +MA0GA1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEAgmHw2xApZqdzZOOPTzwHr1hRYd1OqbLOsXbAq6kJ +Kuu+qe5jAlMF3vnUhiHomuZeNZVJe3SP+JfBt3BHMjm2CLChCuNgfctKURMlEc/L +xo8fO1Jk9MD5mbG2Utx3m3gM6Liwt9fHVABlCTyB6/jXrK1tYpEG5CrwUXyy8Htl +jHECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAl +0tMEXWPgzXTpDuNmuKh6aGq9CuExUuEXXQQWPThzEuluA3aHFmObziQlMY1+KeO1 +AL0kpx0Yhvju/rfAJ+OF6MMni6hJoKlYTVml+fCY89A3nmY1rJHJavjHp0OIPGxh +4Sr+EcjROkqe8jE0DmbwmM6lzpwSJscxte+V6HvGRw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiDCCAfGgAwIBAgIFSHyFNTEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI +ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD +VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS +BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw +MDAwMDBaMHoxDjAMBgNVBAMTBW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJA +ZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0G +A1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAjEt9iy365+mTialKDKb3l2QPg71yavJA1ZC6aGC14X7x +KCm1FhUYsVKOlWjmC1VYJiCS01gvKqMXiogreHJGM93E+URlKkOm9kmOWQwLfFb8 +JLzafPi3/8TUdjl8UuIDHyPsoQiM2ZBDUVWezfl+CBsTYFO3U4Lqf9OKbCxTF78C +AwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAv6vHw +wK3MvxzlhDJIx7rUasOYJDZJyOt71KdOKeA7+ocbvDIblmV7sTbe3oQNqbSATZ6H +RUqHZdPhKIZ9wjEBSKdBTL8rc0TvbztMvd+i0rkTCL/bspQYchA2zCcjgkWqpaN4 +OhOjQR1+9/ntmaU/r5Ca7KmrXEf5XSQIGLSMag== +-----END CERTIFICATE----- + diff --git a/lib/ssl/examples/certs/etc/client/cert.pem b/lib/ssl/examples/certs/etc/client/cert.pem new file mode 100644 index 0000000000..a2f53aaf82 --- /dev/null +++ b/lib/ssl/examples/certs/etc/client/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV +wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53 +h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A +PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD +4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ +S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5 +-----END CERTIFICATE----- + diff --git a/lib/ssl/examples/certs/etc/client/key.pem b/lib/ssl/examples/certs/etc/client/key.pem new file mode 100644 index 0000000000..4d55b08f4c --- /dev/null +++ b/lib/ssl/examples/certs/etc/client/key.pem @@ -0,0 +1,16 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n +0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr +3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB +AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB +I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna +QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f +eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws +/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI +ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV +LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj +40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6 +UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80 +t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J +-----END RSA PRIVATE KEY----- + diff --git a/lib/ssl/examples/certs/etc/erlangCA/cert.pem b/lib/ssl/examples/certs/etc/erlangCA/cert.pem new file mode 100644 index 0000000000..c4386494dc --- /dev/null +++ b/lib/ssl/examples/certs/etc/erlangCA/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICizCCAfSgAwIBAgIFdMMs9fEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI +ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD +VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS +BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw +MDAwMDBaMH0xETAPBgNVBAMTCGVybGFuZ0NBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0 +ZXJAZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEP +MA0GA1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEAgmHw2xApZqdzZOOPTzwHr1hRYd1OqbLOsXbAq6kJ +Kuu+qe5jAlMF3vnUhiHomuZeNZVJe3SP+JfBt3BHMjm2CLChCuNgfctKURMlEc/L +xo8fO1Jk9MD5mbG2Utx3m3gM6Liwt9fHVABlCTyB6/jXrK1tYpEG5CrwUXyy8Htl +jHECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAl +0tMEXWPgzXTpDuNmuKh6aGq9CuExUuEXXQQWPThzEuluA3aHFmObziQlMY1+KeO1 +AL0kpx0Yhvju/rfAJ+OF6MMni6hJoKlYTVml+fCY89A3nmY1rJHJavjHp0OIPGxh +4Sr+EcjROkqe8jE0DmbwmM6lzpwSJscxte+V6HvGRw== +-----END CERTIFICATE----- + diff --git a/lib/ssl/examples/certs/etc/otpCA/cert.pem b/lib/ssl/examples/certs/etc/otpCA/cert.pem new file mode 100644 index 0000000000..8610621695 --- /dev/null +++ b/lib/ssl/examples/certs/etc/otpCA/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICiDCCAfGgAwIBAgIFSHyFNTEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI +ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD +VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS +BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw +MDAwMDBaMHoxDjAMBgNVBAMTBW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJA +ZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0G +A1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAjEt9iy365+mTialKDKb3l2QPg71yavJA1ZC6aGC14X7x +KCm1FhUYsVKOlWjmC1VYJiCS01gvKqMXiogreHJGM93E+URlKkOm9kmOWQwLfFb8 +JLzafPi3/8TUdjl8UuIDHyPsoQiM2ZBDUVWezfl+CBsTYFO3U4Lqf9OKbCxTF78C +AwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAv6vHw +wK3MvxzlhDJIx7rUasOYJDZJyOt71KdOKeA7+ocbvDIblmV7sTbe3oQNqbSATZ6H +RUqHZdPhKIZ9wjEBSKdBTL8rc0TvbztMvd+i0rkTCL/bspQYchA2zCcjgkWqpaN4 +OhOjQR1+9/ntmaU/r5Ca7KmrXEf5XSQIGLSMag== +-----END CERTIFICATE----- + diff --git a/lib/ssl/examples/certs/etc/server/cacerts.pem b/lib/ssl/examples/certs/etc/server/cacerts.pem new file mode 100644 index 0000000000..cb19d3d41e --- /dev/null +++ b/lib/ssl/examples/certs/etc/server/cacerts.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIICizCCAfSgAwIBAgIFdMMs9fEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI +ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD +VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS +BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw +MDAwMDBaMH0xETAPBgNVBAMTCGVybGFuZ0NBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0 +ZXJAZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEP +MA0GA1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEAgmHw2xApZqdzZOOPTzwHr1hRYd1OqbLOsXbAq6kJ +Kuu+qe5jAlMF3vnUhiHomuZeNZVJe3SP+JfBt3BHMjm2CLChCuNgfctKURMlEc/L +xo8fO1Jk9MD5mbG2Utx3m3gM6Liwt9fHVABlCTyB6/jXrK1tYpEG5CrwUXyy8Htl +jHECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAl +0tMEXWPgzXTpDuNmuKh6aGq9CuExUuEXXQQWPThzEuluA3aHFmObziQlMY1+KeO1 +AL0kpx0Yhvju/rfAJ+OF6MMni6hJoKlYTVml+fCY89A3nmY1rJHJavjHp0OIPGxh +4Sr+EcjROkqe8jE0DmbwmM6lzpwSJscxte+V6HvGRw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiDCCAfGgAwIBAgIFSHyFNTEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI +ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD +VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS +BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw +MDAwMDBaMHoxDjAMBgNVBAMTBW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJA +ZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0G +A1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAjEt9iy365+mTialKDKb3l2QPg71yavJA1ZC6aGC14X7x +KCm1FhUYsVKOlWjmC1VYJiCS01gvKqMXiogreHJGM93E+URlKkOm9kmOWQwLfFb8 +JLzafPi3/8TUdjl8UuIDHyPsoQiM2ZBDUVWezfl+CBsTYFO3U4Lqf9OKbCxTF78C +AwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAv6vHw +wK3MvxzlhDJIx7rUasOYJDZJyOt71KdOKeA7+ocbvDIblmV7sTbe3oQNqbSATZ6H +RUqHZdPhKIZ9wjEBSKdBTL8rc0TvbztMvd+i0rkTCL/bspQYchA2zCcjgkWqpaN4 +OhOjQR1+9/ntmaU/r5Ca7KmrXEf5XSQIGLSMag== +-----END CERTIFICATE----- + diff --git a/lib/ssl/examples/certs/etc/server/cert.pem b/lib/ssl/examples/certs/etc/server/cert.pem new file mode 100644 index 0000000000..f26adb7f5c --- /dev/null +++ b/lib/ssl/examples/certs/etc/server/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9 +Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ +4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk +QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L +psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg +/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M +-----END CERTIFICATE----- + diff --git a/lib/ssl/examples/certs/etc/server/key.pem b/lib/ssl/examples/certs/etc/server/key.pem new file mode 100644 index 0000000000..c1392ca557 --- /dev/null +++ b/lib/ssl/examples/certs/etc/server/key.pem @@ -0,0 +1,16 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6 +7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt +NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB +AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm +FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo +T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd +VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb +Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7 +5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX +BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm +zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa +GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z +h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV +-----END RSA PRIVATE KEY----- + diff --git a/lib/ssl/examples/certs/rnd/RAND b/lib/ssl/examples/certs/rnd/RAND Binary files differdeleted file mode 100644 index 70997bd01f..0000000000 --- a/lib/ssl/examples/certs/rnd/RAND +++ /dev/null diff --git a/lib/ssl/examples/certs/src/make_certs.erl b/lib/ssl/examples/certs/src/make_certs.erl index c374836568..fe267bed28 100644 --- a/lib/ssl/examples/certs/src/make_certs.erl +++ b/lib/ssl/examples/certs/src/make_certs.erl @@ -1,261 +1,48 @@ -%% The purpose of this module is to create example certificates for -%% testing. -%% Run it as: -%% -%% erl -noinput -run make_certs all "/path/to/openssl" -s erlang halt -%% +%% The purpose of this module is to log how the example certs where created, +%% it requires erl_make_certs found in the test directory. -module(make_certs). --export([all/0, all/1]). - --record(dn, {commonName, - organizationalUnitName = "Erlang OTP", - organizationName = "Ericsson AB", - localityName = "Stockholm", - countryName = "SE", - emailAddress = "[email protected]"}). +-export([all/0]). all() -> - all(["openssl"]). - -all([OpenSSLCmd]) -> - Root = filename:dirname(filename:dirname((code:which(?MODULE)))), - %% io:fwrite("Root : ~s~n", [Root]), - NRoot = filename:join([Root, "etc"]), - file:make_dir(NRoot), - create_rnd(Root, "etc"), % For all requests - rootCA(NRoot, OpenSSLCmd, "erlangCA"), - intermediateCA(NRoot, OpenSSLCmd, "otpCA", "erlangCA"), - endusers(NRoot, OpenSSLCmd, "otpCA", ["client", "server"]), - collect_certs(NRoot, ["erlangCA", "otpCA"], ["client", "server"]), - remove_rnd(Root, "etc"). - -rootCA(Root, OpenSSLCmd, Name) -> - create_ca_dir(Root, Name, ca_cnf(Name)), - DN = #dn{commonName = Name}, - create_self_signed_cert(Root, OpenSSLCmd, Name, req_cnf(DN)), - ok. - -intermediateCA(Root, OpenSSLCmd, CA, ParentCA) -> - CA = "otpCA", - create_ca_dir(Root, CA, ca_cnf(CA)), - CARoot = filename:join([Root, CA]), - DN = #dn{commonName = CA}, - CnfFile = filename:join([CARoot, "req.cnf"]), - file:write_file(CnfFile, req_cnf(DN)), - KeyFile = filename:join([CARoot, "private", "key.pem"]), - ReqFile = filename:join([CARoot, "req.pem"]), - create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile), - CertFile = filename:join([CARoot, "cert.pem"]), - sign_req(Root, OpenSSLCmd, ParentCA, "ca_cert", ReqFile, CertFile). - -endusers(Root, OpenSSLCmd, CA, Users) -> - lists:foreach(fun(User) -> enduser(Root, OpenSSLCmd, CA, User) end, Users). - -enduser(Root, OpenSSLCmd, CA, User) -> - UsrRoot = filename:join([Root, User]), - file:make_dir(UsrRoot), - CnfFile = filename:join([UsrRoot, "req.cnf"]), - DN = #dn{commonName = User}, - file:write_file(CnfFile, req_cnf(DN)), - KeyFile = filename:join([UsrRoot, "key.pem"]), - ReqFile = filename:join([UsrRoot, "req.pem"]), - create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile), - CertFile = filename:join([UsrRoot, "cert.pem"]), - sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFile). - -collect_certs(Root, CAs, Users) -> - Bins = lists:foldr( - fun(CA, Acc) -> - File = filename:join([Root, CA, "cert.pem"]), - {ok, Bin} = file:read_file(File), - [Bin, "\n" | Acc] - end, [], CAs), - lists:foreach( - fun(User) -> - File = filename:join([Root, User, "cacerts.pem"]), - file:write_file(File, Bins) - end, Users). - -create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) -> - CARoot = filename:join([Root, CAName]), - CnfFile = filename:join([CARoot, "req.cnf"]), - file:write_file(CnfFile, Cnf), - KeyFile = filename:join([CARoot, "private", "key.pem"]), - CertFile = filename:join([CARoot, "cert.pem"]), - Cmd = [OpenSSLCmd, " req" - " -new" - " -x509" - " -config ", CnfFile, - " -keyout ", KeyFile, - " -out ", CertFile], - Env = [{"ROOTDIR", Root}], - cmd(Cmd, Env). - -create_ca_dir(Root, CAName, Cnf) -> - CARoot = filename:join([Root, CAName]), - file:make_dir(CARoot), - create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]), - create_rnd(Root, filename:join([CAName, "private"])), - create_files(CARoot, [{"serial", "01\n"}, - {"index.txt", ""}, - {"ca.cnf", Cnf}]). - -create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) -> - Cmd = [OpenSSLCmd, " req" - " -new" - " -config ", CnfFile, - " -keyout ", KeyFile, - " -out ", ReqFile], - Env = [{"ROOTDIR", Root}], - cmd(Cmd, Env). - -sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) -> - CACnfFile = filename:join([Root, CA, "ca.cnf"]), - Cmd = [OpenSSLCmd, " ca" - " -batch" - " -notext" - " -config ", CACnfFile, - " -extensions ", CertType, - " -in ", ReqFile, - " -out ", CertFile], - Env = [{"ROOTDIR", Root}], - cmd(Cmd, Env). + LongTime = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+15*365), + Validity = {date(), LongTime}, + Subject = [{email, "[email protected]"}, + {city, "Stockholm"}, + {country, "SE"}, + {org, "erlang"}, + {org_unit, "testing dep"}], + + RootCa = erl_make_certs:make_cert([{validity, Validity}, {subject, [{name, "erlangCA"}|Subject]}]), + ImedCa = erl_make_certs:make_cert([{issuer, RootCa}, {validity, Validity}, + {subject, [{name, "otpCA"}|Subject]}]), + ClientCa = erl_make_certs:make_cert([{issuer, ImedCa}, {validity, Validity}, + {subject, [{name, "client"}|Subject]}]), + ServerCa = erl_make_certs:make_cert([{issuer, ImedCa}, {validity, Validity}, + {subject, [{name, "server"}|Subject]}]), + + Root0 = filename:dirname(filename:dirname((code:which(?MODULE)))), + Root = filename:join([Root0, "etc"]), file:make_dir(Root), + CaPath = filename:join([Root, "erlangCA"]), file:make_dir(CaPath), + IPath = filename:join([Root, "otpCA"]), file:make_dir(IPath), + CPath = filename:join([Root, "client"]), file:make_dir(CPath), + SPath = filename:join([Root, "server"]), file:make_dir(SPath), + + erl_make_certs:write_pem(CaPath,"cert", RootCa), + erl_make_certs:write_pem(IPath, "cert", ImedCa), + + {ok, CaBin0} = file:read_file(filename:join(CaPath, "cert.pem")), + {ok, CaBin1} = file:read_file(filename:join(IPath, "cert.pem")), + CaBin = <<CaBin0/binary, CaBin1/binary>>, + + erl_make_certs:write_pem(CPath, "cert", ClientCa), + ok = file:write_file(filename:join(CPath, "cacerts.pem"), CaBin), + erl_make_certs:write_pem(SPath, "cert", ServerCa), + ok = file:write_file(filename:join(SPath, "cacerts.pem"), CaBin), -%% -%% Misc -%% - -create_dirs(Root, Dirs) -> - lists:foreach(fun(Dir) -> - file:make_dir(filename:join([Root, Dir])) end, - Dirs). - -create_files(Root, NameContents) -> - lists:foreach( - fun({Name, Contents}) -> - file:write_file(filename:join([Root, Name]), Contents) end, - NameContents). - -create_rnd(Root, Dir) -> - From = filename:join([Root, "rnd", "RAND"]), - To = filename:join([Root, Dir, "RAND"]), - file:copy(From, To). - -remove_rnd(Root, Dir) -> - File = filename:join([Root, Dir, "RAND"]), - file:delete(File). - -cmd(Cmd, Env) -> - FCmd = lists:flatten(Cmd), - Port = open_port({spawn, FCmd}, [stream, eof, exit_status, - {env, Env}]), - eval_cmd(Port). - -eval_cmd(Port) -> - receive - {Port, {data, _}} -> - eval_cmd(Port); - {Port, eof} -> - ok - end, - receive - {Port, {exit_status, Status}} when Status /= 0 -> - %% io:fwrite("exit status: ~w~n", [Status]), - erlang:halt(Status) - after 0 -> - ok - end. - -%% -%% Contents of configuration files -%% - -req_cnf(DN) -> - ["# Purpose: Configuration for requests (end users and CAs)." - "\n" - "ROOTDIR = $ENV::ROOTDIR\n" - "\n" - - "[req]\n" - "input_password = secret\n" - "output_password = secret\n" - "default_bits = 1024\n" - "RANDFILE = $ROOTDIR/RAND\n" - "encrypt_key = no\n" - "default_md = sha1\n" - "#string_mask = pkix\n" - "x509_extensions = ca_ext\n" - "prompt = no\n" - "distinguished_name= name\n" - "\n" - - "[name]\n" - "commonName = ", DN#dn.commonName, "\n" - "organizationalUnitName = ", DN#dn.organizationalUnitName, "\n" - "organizationName = ", DN#dn.organizationName, "\n" - "localityName = ", DN#dn.localityName, "\n" - "countryName = ", DN#dn.countryName, "\n" - "emailAddress = ", DN#dn.emailAddress, "\n" - "\n" - - "[ca_ext]\n" - "basicConstraints = critical, CA:true\n" - "keyUsage = cRLSign, keyCertSign\n" - "subjectKeyIdentifier = hash\n" - "subjectAltName = email:copy\n"]. - - -ca_cnf(CA) -> - ["# Purpose: Configuration for CAs.\n" - "\n" - "ROOTDIR = $ENV::ROOTDIR\n" - "default_ca = ca\n" - "\n" - - "[ca]\n" - "dir = $ROOTDIR/", CA, "\n" - "certs = $dir/certs\n" - "crl_dir = $dir/crl\n" - "database = $dir/index.txt\n" - "new_certs_dir = $dir/newcerts\n" - "certificate = $dir/cert.pem\n" - "serial = $dir/serial\n" - "crl = $dir/crl.pem\n" - "private_key = $dir/private/key.pem\n" - "RANDFILE = $dir/private/RAND\n" - "\n" - "x509_extensions = user_cert\n" - "default_days = 3600\n" - "default_md = sha1\n" - "preserve = no\n" - "policy = policy_match\n" - "\n" - - "[policy_match]\n" - "commonName = supplied\n" - "organizationalUnitName = optional\n" - "organizationName = match\n" - "countryName = match\n" - "localityName = match\n" - "emailAddress = supplied\n" - "\n" - - "[user_cert]\n" - "basicConstraints = CA:false\n" - "keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n" - "subjectKeyIdentifier = hash\n" - "authorityKeyIdentifier = keyid,issuer:always\n" - "subjectAltName = email:copy\n" - "issuerAltName = issuer:copy\n" - "\n" - - "[ca_cert]\n" - "basicConstraints = critical,CA:true\n" - "keyUsage = cRLSign, keyCertSign\n" - "subjectKeyIdentifier = hash\n" - "authorityKeyIdentifier = keyid:always,issuer:always\n" - "subjectAltName = email:copy\n" - "issuerAltName = issuer:copy\n"]. - + file:delete(filename:join(CaPath, "cert_key.pem")), + file:delete(filename:join(IPath, "cert_key.pem")), + file:rename(filename:join(CPath, "cert_key.pem"), filename:join(CPath, "key.pem")), + file:rename(filename:join(SPath, "cert_key.pem"), filename:join(SPath, "key.pem")), + ok. diff --git a/lib/ssl/pkix/Makefile b/lib/ssl/pkix/Makefile deleted file mode 100644 index 260361c025..0000000000 --- a/lib/ssl/pkix/Makefile +++ /dev/null @@ -1,121 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 2003-2009. 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% -# - -# - -include $(ERL_TOP)/make/target.mk -include $(ERL_TOP)/make/$(TARGET)/otp.mk - -# ---------------------------------------------------- -# Application version -# ---------------------------------------------------- -include ../vsn.mk -VSN=$(SSL_VSN) - -# ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN) - -# ---------------------------------------------------- -# Common Macros -# ---------------------------------------------------- - -.SUFFIXES: .asn1 -.PRECIOUS: %.erl - -ASN_TOP = OTP-PKIX -ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \ - PKIXAttributeCertificate SSL-PKIX -ASN_ASNS = $(ASN_MODULES:%=%.asn1) -ASN_ERLS = $(ASN_TOP).erl -ASN_HRLS = $(ASN_TOP).hrl -ASN_CONFIGS = OTP-PKIX.asn1config -ASN_DBS = $(ASN_MODULES:%=%.asn1db) -ASN_TABLES = $(ASN_MODULES:%=%.table) - -GEN_MODULES = ssl_pkix_oid $(ORBER_TMP_FIX_ERL) -GEN_ERLS = $(GEN_MODULES:%=%.erl) -ERL_MODULES = $(ASN_TOP) $(GEN_MODULES) - -TARGET_FILES= $(ERL_MODULES:%=$(EBIN)/%.$(EMULATOR)) - -HRL_FILES = $(ASN_HRLS:%=$(INCLUDE)/%) - -ORBER_TMP_FIX_HRL = PKIX1Algorithms88.hrl PKIX1Explicit88.hrl \ - PKIX1Implicit88.hrl PKIXAttributeCertificate.hrl - -INCLUDE = ../include -EBIN = ../ebin - -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -EXTRA_ERLC_FLAGS = -ERL_COMPILE_FLAGS += $(EXTRA_ERLC_FLAGS) - -ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj +asn1config +inline - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- - -debug opt: $(TARGET_FILES) $(HRL_FILES) - -clean: - -rm -f $(ASN_ERLS) $(GEN_ERLS) $(ASN_HRLS) $(HRL_FILES) $(ASN_DBS) \ - $(ASN_TABLES) $(TARGET_FILES) *.beam *~ - -docs: - -%.erl: %.set.asn - erlc $(ASN_FLAGS) $< - -ssl_pkix_oid.erl: mk_ssl_pkix_oid.beam $(EBIN)/OTP-PKIX.beam - erl -pa $(EBIN) -noshell -s mk_ssl_pkix_oid make -s erlang halt - -$(HRL_FILES): $(ASN_HRLS) - cp -p $(ASN_HRLS) $(INCLUDE) - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/pkix - $(INSTALL_DATA) $(ASN_ASNS) $(ASN_ERLS) $(ASN_HRLS) $(ASN_CONFIGS) \ - $(ORBER_TMP_FIX_HRL) $(GEN_ERLS) mk_ssl_pkix_oid.erl $(RELSYSDIR)/pkix - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - -release_docs_spec: - -# -# Dependencies - -$(EBIN)/OTP-PKIX.beam: OTP-PKIX.erl OTP-PKIX.hrl -OTP-PKIX.erl OTP-PKIX.hrl: OTP-PKIX.asn1db -OTP-PKIX.asn1db: PKIX1Algorithms88.asn1 \ - PKIX1Explicit88.asn1 \ - PKIX1Implicit88.asn1 \ - PKIXAttributeCertificate.asn1 \ - SSL-PKIX.asn1 diff --git a/lib/ssl/pkix/OTP-PKIX.asn1config b/lib/ssl/pkix/OTP-PKIX.asn1config deleted file mode 100644 index 0caa158f52..0000000000 --- a/lib/ssl/pkix/OTP-PKIX.asn1config +++ /dev/null @@ -1,2 +0,0 @@ -{exclusive_decode,{'OTP-PKIX', - [{decode_TBSCert_exclusive,['Certificate',[{tbsCertificate,undecoded}]]}]}}. diff --git a/lib/ssl/pkix/OTP-PKIX.set.asn b/lib/ssl/pkix/OTP-PKIX.set.asn deleted file mode 100644 index 1c3483d519..0000000000 --- a/lib/ssl/pkix/OTP-PKIX.set.asn +++ /dev/null @@ -1,6 +0,0 @@ -SSL-PKIX.asn1 -PKIX1Explicit88.asn1 -PKIX1Implicit88.asn1 -PKIXAttributeCertificate.asn1 -PKIX1Algorithms88.asn1 -PKCS-1.asn1 diff --git a/lib/ssl/pkix/PKCS-1.asn1 b/lib/ssl/pkix/PKCS-1.asn1 deleted file mode 100755 index 547cc2e072..0000000000 --- a/lib/ssl/pkix/PKCS-1.asn1 +++ /dev/null @@ -1,54 +0,0 @@ -PKCS-1 { - iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) - modules(0) pkcs-1(1) -} - - -DEFINITIONS IMPLICIT TAGS ::= BEGIN - --- EXPORTS ALL -- - -IMPORTS - AlgorithmIdentifier - FROM PKIX1Explicit88 {iso(1) identified-organization(3) - dod(6) internet(1) security(5) mechanisms(5) - pkix(7) id-mod(0) id-pkix1-explicit-88(1)} ; - -pkcs-1 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } - -RSAPrivateKey ::= SEQUENCE { - version Version, - modulus INTEGER, -- n - publicExponent INTEGER, -- e - privateExponent INTEGER, -- d - prime1 INTEGER, -- p - prime2 INTEGER, -- q - exponent1 INTEGER, -- d mod (p-1) - exponent2 INTEGER, -- d mod (q-1) - coefficient INTEGER, -- (inverse of q) mod p - otherPrimeInfos OtherPrimeInfos OPTIONAL -} - -Version ::= INTEGER { two-prime(0), multi(1) } - (CONSTRAINED BY { - -- version must be multi if otherPrimeInfos present -- - }) - -OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo - -OtherPrimeInfo ::= SEQUENCE { - prime INTEGER, -- ri - exponent INTEGER, -- di - coefficient INTEGER -- ti -} - -DigestInfo ::= SEQUENCE { - digestAlgorithm DigestAlgorithmIdentifier, - digest OCTET STRING -} - -DigestAlgorithmIdentifier ::= AlgorithmIdentifier - -END -- PKCS1Definitions - diff --git a/lib/ssl/pkix/PKIX1Algorithms88.asn1 b/lib/ssl/pkix/PKIX1Algorithms88.asn1 deleted file mode 100644 index e78de69b0e..0000000000 --- a/lib/ssl/pkix/PKIX1Algorithms88.asn1 +++ /dev/null @@ -1,274 +0,0 @@ - PKIX1Algorithms88 { iso(1) identified-organization(3) dod(6) - internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) - id-mod-pkix1-algorithms(17) } - - DEFINITIONS EXPLICIT TAGS ::= BEGIN - - -- EXPORTS All; - - -- IMPORTS NONE; - - -- - -- One-way Hash Functions - -- - - md2 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) rsadsi(113549) - digestAlgorithm(2) 2 } - - md5 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) rsadsi(113549) - digestAlgorithm(2) 5 } - - id-sha1 OBJECT IDENTIFIER ::= { - iso(1) identified-organization(3) oiw(14) secsig(3) - algorithms(2) 26 } - - -- - -- DSA Keys and Signatures - -- - - -- OID for DSA public key - - id-dsa OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 } - - -- encoding for DSA public key - - DSAPublicKey ::= INTEGER -- public key, y - - Dss-Parms ::= SEQUENCE { - p INTEGER, - q INTEGER, - g INTEGER } - - -- OID for DSA signature generated with SHA-1 hash - - id-dsa-with-sha1 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 } - - -- encoding for DSA signature generated with SHA-1 hash - - Dss-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER } - - -- - -- RSA Keys and Signatures - -- - - -- arc for RSA public key and RSA signature OIDs - - pkcs-1 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } - - -- OID for RSA public keys - - rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } - - -- OID for RSA signature generated with MD2 hash - - md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } - - -- OID for RSA signature generated with MD5 hash - - md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } - - -- OID for RSA signature generated with SHA-1 hash - - sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } - - -- encoding for RSA public key - - RSAPublicKey ::= SEQUENCE { - modulus INTEGER, -- n - publicExponent INTEGER } -- e - - -- - -- Diffie-Hellman Keys - -- - - dhpublicnumber OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) ansi-x942(10046) - number-type(2) 1 } - - -- encoding for DSA public key - - DHPublicKey ::= INTEGER -- public key, y = g^x mod p - - DomainParameters ::= SEQUENCE { - p INTEGER, -- odd prime, p=jq +1 - g INTEGER, -- generator, g - q INTEGER, -- factor of p-1 - j INTEGER OPTIONAL, -- subgroup factor, j>= 2 - validationParms ValidationParms OPTIONAL } - - ValidationParms ::= SEQUENCE { - seed BIT STRING, - pgenCounter INTEGER } - - -- - -- KEA Keys - -- - - id-keyExchangeAlgorithm OBJECT IDENTIFIER ::= - { 2 16 840 1 101 2 1 1 22 } - - KEA-Parms-Id ::= OCTET STRING - - -- - -- Elliptic Curve Keys, Signatures, and Curves - -- - - ansi-X9-62 OBJECT IDENTIFIER ::= { - iso(1) member-body(2) us(840) 10045 } - - FieldID ::= SEQUENCE { -- Finite field - fieldType OBJECT IDENTIFIER, - parameters ANY DEFINED BY fieldType } - - -- Arc for ECDSA signature OIDS - - id-ecSigType OBJECT IDENTIFIER ::= { ansi-X9-62 signatures(4) } - - -- OID for ECDSA signatures with SHA-1 - - ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { id-ecSigType 1 } - - -- OID for an elliptic curve signature - -- format for the value of an ECDSA signature value - - ECDSA-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER } - - -- recognized field type OIDs are defined in the following arc - - id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1) } - - -- where fieldType is prime-field, the parameters are of type Prime-p - - prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } - - Prime-p ::= INTEGER -- Finite field F(p), where p is an odd prime - - -- where fieldType is characteristic-two-field, the parameters are - -- of type Characteristic-two - - characteristic-two-field OBJECT IDENTIFIER ::= { id-fieldType 2 } - - Characteristic-two ::= SEQUENCE { - m INTEGER, -- Field size 2^m - basis OBJECT IDENTIFIER, - parameters ANY DEFINED BY basis } - - -- recognized basis type OIDs are defined in the following arc - - id-characteristic-two-basis OBJECT IDENTIFIER ::= { - characteristic-two-field basisType(3) } - - -- gnbasis is identified by OID gnBasis and indicates - -- parameters are NULL - - gnBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 1 } - - -- parameters for this basis are NULL - - -- trinomial basis is identified by OID tpBasis and indicates - -- parameters of type Pentanomial - - tpBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 2 } - - -- Trinomial basis representation of F2^m - -- Integer k for reduction polynomial xm + xk + 1 - - Trinomial ::= INTEGER - - -- for pentanomial basis is identified by OID ppBasis and indicates - -- parameters of type Pentanomial - - ppBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 3 } - - -- Pentanomial basis representation of F2^m - -- reduction polynomial integers k1, k2, k3 - -- f(x) = x**m + x**k3 + x**k2 + x**k1 + 1 - - Pentanomial ::= SEQUENCE { - k1 INTEGER, - k2 INTEGER, - k3 INTEGER } - - -- The object identifiers gnBasis, tpBasis and ppBasis name - -- three kinds of basis for characteristic-two finite fields - - FieldElement ::= OCTET STRING -- Finite field element - - ECPoint ::= OCTET STRING -- Elliptic curve point - - -- Elliptic Curve parameters may be specified explicitly, - -- specified implicitly through a "named curve", or - -- inherited from the CA - - EcpkParameters ::= CHOICE { - ecParameters ECParameters, - namedCurve OBJECT IDENTIFIER, - implicitlyCA NULL } - - ECParameters ::= SEQUENCE { -- Elliptic curve parameters - version ECPVer, - fieldID FieldID, - curve Curve, - base ECPoint, -- Base point G - order INTEGER, -- Order n of the base point - cofactor INTEGER OPTIONAL } -- The integer h = #E(Fq)/n - - ECPVer ::= INTEGER {ecpVer1(1)} - - Curve ::= SEQUENCE { - a FieldElement, -- Elliptic curve coefficient a - b FieldElement, -- Elliptic curve coefficient b - seed BIT STRING OPTIONAL } - - id-publicKeyType OBJECT IDENTIFIER ::= { ansi-X9-62 keyType(2) } - - id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } - - -- Named Elliptic Curves in ANSI X9.62. - - ellipticCurve OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) } - - c-TwoCurve OBJECT IDENTIFIER ::= { - ellipticCurve characteristicTwo(0) } - - c2pnb163v1 OBJECT IDENTIFIER ::= { c-TwoCurve 1 } - c2pnb163v2 OBJECT IDENTIFIER ::= { c-TwoCurve 2 } - c2pnb163v3 OBJECT IDENTIFIER ::= { c-TwoCurve 3 } - c2pnb176w1 OBJECT IDENTIFIER ::= { c-TwoCurve 4 } - c2tnb191v1 OBJECT IDENTIFIER ::= { c-TwoCurve 5 } - c2tnb191v2 OBJECT IDENTIFIER ::= { c-TwoCurve 6 } - c2tnb191v3 OBJECT IDENTIFIER ::= { c-TwoCurve 7 } - c2onb191v4 OBJECT IDENTIFIER ::= { c-TwoCurve 8 } - c2onb191v5 OBJECT IDENTIFIER ::= { c-TwoCurve 9 } - c2pnb208w1 OBJECT IDENTIFIER ::= { c-TwoCurve 10 } - c2tnb239v1 OBJECT IDENTIFIER ::= { c-TwoCurve 11 } - c2tnb239v2 OBJECT IDENTIFIER ::= { c-TwoCurve 12 } - c2tnb239v3 OBJECT IDENTIFIER ::= { c-TwoCurve 13 } - c2onb239v4 OBJECT IDENTIFIER ::= { c-TwoCurve 14 } - c2onb239v5 OBJECT IDENTIFIER ::= { c-TwoCurve 15 } - c2pnb272w1 OBJECT IDENTIFIER ::= { c-TwoCurve 16 } - c2pnb304w1 OBJECT IDENTIFIER ::= { c-TwoCurve 17 } - c2tnb359v1 OBJECT IDENTIFIER ::= { c-TwoCurve 18 } - c2pnb368w1 OBJECT IDENTIFIER ::= { c-TwoCurve 19 } - c2tnb431r1 OBJECT IDENTIFIER ::= { c-TwoCurve 20 } - - primeCurve OBJECT IDENTIFIER ::= { ellipticCurve prime(1) } - - prime192v1 OBJECT IDENTIFIER ::= { primeCurve 1 } - prime192v2 OBJECT IDENTIFIER ::= { primeCurve 2 } - prime192v3 OBJECT IDENTIFIER ::= { primeCurve 3 } - prime239v1 OBJECT IDENTIFIER ::= { primeCurve 4 } - prime239v2 OBJECT IDENTIFIER ::= { primeCurve 5 } - prime239v3 OBJECT IDENTIFIER ::= { primeCurve 6 } - prime256v1 OBJECT IDENTIFIER ::= { primeCurve 7 } - - END diff --git a/lib/ssl/pkix/PKIX1Algorithms88.hrl b/lib/ssl/pkix/PKIX1Algorithms88.hrl deleted file mode 100644 index a11793618d..0000000000 --- a/lib/ssl/pkix/PKIX1Algorithms88.hrl +++ /dev/null @@ -1,94 +0,0 @@ -%% Generated by the Erlang ASN.1 compiler version:1.4.4.8 -%% Purpose: Erlang record definitions for each named and unnamed -%% SEQUENCE and SET, and macro definitions for each value -%% definition,in module PKIX1Algorithms88 - - - --record('Dss-Parms',{ -p, q, g}). - --record('Dss-Sig-Value',{ -r, s}). - --record('RSAPublicKey',{ -modulus, publicExponent}). - --record('DomainParameters',{ -p, g, q, j = asn1_NOVALUE, validationParms = asn1_NOVALUE}). - --record('ValidationParms',{ -seed, pgenCounter}). - --record('FieldID',{ -fieldType, parameters}). - --record('ECDSA-Sig-Value',{ -r, s}). - --record('Characteristic-two',{ -m, basis, parameters}). - --record('Pentanomial',{ -k1, k2, k3}). - --record('ECParameters',{ -version, fieldID, curve, base, order, cofactor = asn1_NOVALUE}). - --record('Curve',{ -a, b, seed = asn1_NOVALUE}). - --define('md2', {1,2,840,113549,2,2}). --define('md5', {1,2,840,113549,2,5}). --define('id-sha1', {1,3,14,3,2,26}). --define('id-dsa', {1,2,840,10040,4,1}). --define('id-dsa-with-sha1', {1,2,840,10040,4,3}). --define('pkcs-1', {1,2,840,113549,1,1}). --define('rsaEncryption', {1,2,840,113549,1,1,1}). --define('md2WithRSAEncryption', {1,2,840,113549,1,1,2}). --define('md5WithRSAEncryption', {1,2,840,113549,1,1,4}). --define('sha1WithRSAEncryption', {1,2,840,113549,1,1,5}). --define('dhpublicnumber', {1,2,840,10046,2,1}). --define('id-keyExchangeAlgorithm', {2,16,840,1,101,2,1,1,22}). --define('ansi-X9-62', {1,2,840,10045}). --define('id-ecSigType', {1,2,840,10045,4}). --define('ecdsa-with-SHA1', {1,2,840,10045,4,1}). --define('id-fieldType', {1,2,840,10045,1}). --define('prime-field', {1,2,840,10045,1,1}). --define('characteristic-two-field', {1,2,840,10045,1,2}). --define('id-characteristic-two-basis', {1,2,840,10045,1,2,3}). --define('gnBasis', {1,2,840,10045,1,2,3,1}). --define('tpBasis', {1,2,840,10045,1,2,3,2}). --define('ppBasis', {1,2,840,10045,1,2,3,3}). --define('id-publicKeyType', {1,2,840,10045,2}). --define('id-ecPublicKey', {1,2,840,10045,2,1}). --define('ellipticCurve', {1,2,840,10045,3}). --define('c-TwoCurve', {1,2,840,10045,3,0}). --define('c2pnb163v1', {1,2,840,10045,3,0,1}). --define('c2pnb163v2', {1,2,840,10045,3,0,2}). --define('c2pnb163v3', {1,2,840,10045,3,0,3}). --define('c2pnb176w1', {1,2,840,10045,3,0,4}). --define('c2tnb191v1', {1,2,840,10045,3,0,5}). --define('c2tnb191v2', {1,2,840,10045,3,0,6}). --define('c2tnb191v3', {1,2,840,10045,3,0,7}). --define('c2onb191v4', {1,2,840,10045,3,0,8}). --define('c2onb191v5', {1,2,840,10045,3,0,9}). --define('c2pnb208w1', {1,2,840,10045,3,0,10}). --define('c2tnb239v1', {1,2,840,10045,3,0,11}). --define('c2tnb239v2', {1,2,840,10045,3,0,12}). --define('c2tnb239v3', {1,2,840,10045,3,0,13}). --define('c2onb239v4', {1,2,840,10045,3,0,14}). --define('c2onb239v5', {1,2,840,10045,3,0,15}). --define('c2pnb272w1', {1,2,840,10045,3,0,16}). --define('c2pnb304w1', {1,2,840,10045,3,0,17}). --define('c2tnb359v1', {1,2,840,10045,3,0,18}). --define('c2pnb368w1', {1,2,840,10045,3,0,19}). --define('c2tnb431r1', {1,2,840,10045,3,0,20}). --define('primeCurve', {1,2,840,10045,3,1}). --define('prime192v1', {1,2,840,10045,3,1,1}). --define('prime192v2', {1,2,840,10045,3,1,2}). --define('prime192v3', {1,2,840,10045,3,1,3}). --define('prime239v1', {1,2,840,10045,3,1,4}). --define('prime239v2', {1,2,840,10045,3,1,5}). --define('prime239v3', {1,2,840,10045,3,1,6}). --define('prime256v1', {1,2,840,10045,3,1,7}). diff --git a/lib/ssl/pkix/PKIX1Explicit88.asn1 b/lib/ssl/pkix/PKIX1Explicit88.asn1 deleted file mode 100644 index 9b8068fed0..0000000000 --- a/lib/ssl/pkix/PKIX1Explicit88.asn1 +++ /dev/null @@ -1,619 +0,0 @@ -PKIX1Explicit88 { iso(1) identified-organization(3) dod(6) internet(1) - security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit(18) } - -DEFINITIONS EXPLICIT TAGS ::= - -BEGIN - --- EXPORTS ALL -- - --- IMPORTS NONE -- - --- UNIVERSAL Types defined in 1993 and 1998 ASN.1 --- and required by this specification - --- UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING - -- UniversalString is defined in ASN.1:1993 - --- BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING - -- BMPString is the subtype of UniversalString and models - -- the Basic Multilingual Plane of ISO/IEC/ITU 10646-1 - --- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING - -- The content of this type conforms to RFC 2279. - --- PKIX specific OIDs - -id-pkix OBJECT IDENTIFIER ::= - { iso(1) identified-organization(3) dod(6) internet(1) - security(5) mechanisms(5) pkix(7) } - --- PKIX arcs - -id-pe OBJECT IDENTIFIER ::= { id-pkix 1 } - -- arc for private certificate extensions -id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } - -- arc for policy qualifier types -id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } - -- arc for extended key purpose OIDS -id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } - -- arc for access descriptors - --- policyQualifierIds for Internet policy qualifiers - -id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } - -- OID for CPS qualifier -id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } - -- OID for user notice qualifier - --- access descriptor definitions - -id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } -id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } -id-ad-timeStamping OBJECT IDENTIFIER ::= { id-ad 3 } -id-ad-caRepository OBJECT IDENTIFIER ::= { id-ad 5 } - --- attribute data types - -Attribute ::= SEQUENCE { - type AttributeType, - values SET OF AttributeValue } - -- at least one value is required - -AttributeType ::= OBJECT IDENTIFIER - -AttributeValue ::= ANY - -AttributeTypeAndValue ::= SEQUENCE { - type AttributeType, - value AttributeValue } - --- suggested naming attributes: Definition of the following --- information object set may be augmented to meet local --- requirements. Note that deleting members of the set may --- prevent interoperability with conforming implementations. --- presented in pairs: the AttributeType followed by the --- type definition for the corresponding AttributeValue ---Arc for standard naming attributes -id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } - --- Naming attributes of type X520name - -id-at-name AttributeType ::= { id-at 41 } -id-at-surname AttributeType ::= { id-at 4 } -id-at-givenName AttributeType ::= { id-at 42 } -id-at-initials AttributeType ::= { id-at 43 } -id-at-generationQualifier AttributeType ::= { id-at 44 } - -X520name ::= CHOICE { - teletexString TeletexString (SIZE (1..ub-name)), - printableString PrintableString (SIZE (1..ub-name)), - universalString UniversalString (SIZE (1..ub-name)), - utf8String UTF8String (SIZE (1..ub-name)), - bmpString BMPString (SIZE (1..ub-name)) } - --- Naming attributes of type X520CommonName - -id-at-commonName AttributeType ::= { id-at 3 } - -X520CommonName ::= CHOICE { - teletexString TeletexString (SIZE (1..ub-common-name)), - printableString PrintableString (SIZE (1..ub-common-name)), - universalString UniversalString (SIZE (1..ub-common-name)), - utf8String UTF8String (SIZE (1..ub-common-name)), - bmpString BMPString (SIZE (1..ub-common-name)) } - --- Naming attributes of type X520LocalityName - -id-at-localityName AttributeType ::= { id-at 7 } - -X520LocalityName ::= CHOICE { - teletexString TeletexString (SIZE (1..ub-locality-name)), - printableString PrintableString (SIZE (1..ub-locality-name)), - universalString UniversalString (SIZE (1..ub-locality-name)), - utf8String UTF8String (SIZE (1..ub-locality-name)), - bmpString BMPString (SIZE (1..ub-locality-name)) } - --- Naming attributes of type X520StateOrProvinceName - -id-at-stateOrProvinceName AttributeType ::= { id-at 8 } - -X520StateOrProvinceName ::= CHOICE { - teletexString TeletexString (SIZE (1..ub-state-name)), - printableString PrintableString (SIZE (1..ub-state-name)), - universalString UniversalString (SIZE (1..ub-state-name)), - utf8String UTF8String (SIZE (1..ub-state-name)), - bmpString BMPString (SIZE(1..ub-state-name)) } - --- Naming attributes of type X520OrganizationName - -id-at-organizationName AttributeType ::= { id-at 10 } - -X520OrganizationName ::= CHOICE { - teletexString TeletexString - (SIZE (1..ub-organization-name)), - printableString PrintableString - (SIZE (1..ub-organization-name)), - universalString UniversalString - (SIZE (1..ub-organization-name)), - utf8String UTF8String - (SIZE (1..ub-organization-name)), - bmpString BMPString - (SIZE (1..ub-organization-name)) } - --- Naming attributes of type X520OrganizationalUnitName - -id-at-organizationalUnitName AttributeType ::= { id-at 11 } - -X520OrganizationalUnitName ::= CHOICE { - teletexString TeletexString - (SIZE (1..ub-organizational-unit-name)), - printableString PrintableString - (SIZE (1..ub-organizational-unit-name)), - universalString UniversalString - (SIZE (1..ub-organizational-unit-name)), - utf8String UTF8String - (SIZE (1..ub-organizational-unit-name)), - bmpString BMPString - (SIZE (1..ub-organizational-unit-name)) } - --- Naming attributes of type X520Title - -id-at-title AttributeType ::= { id-at 12 } - -X520Title ::= CHOICE { - teletexString TeletexString (SIZE (1..ub-title)), - printableString PrintableString (SIZE (1..ub-title)), - universalString UniversalString (SIZE (1..ub-title)), - utf8String UTF8String (SIZE (1..ub-title)), - bmpString BMPString (SIZE (1..ub-title)) } - --- Naming attributes of type X520dnQualifier - -id-at-dnQualifier AttributeType ::= { id-at 46 } - -X520dnQualifier ::= PrintableString - --- Naming attributes of type X520countryName (digraph from IS 3166) - -id-at-countryName AttributeType ::= { id-at 6 } - -X520countryName ::= PrintableString (SIZE (2)) - --- Naming attributes of type X520SerialNumber - -id-at-serialNumber AttributeType ::= { id-at 5 } - -X520SerialNumber ::= PrintableString (SIZE (1..ub-serial-number)) - --- Naming attributes of type X520Pseudonym - -id-at-pseudonym AttributeType ::= { id-at 65 } - -X520Pseudonym ::= CHOICE { - teletexString TeletexString (SIZE (1..ub-pseudonym)), - printableString PrintableString (SIZE (1..ub-pseudonym)), - universalString UniversalString (SIZE (1..ub-pseudonym)), - utf8String UTF8String (SIZE (1..ub-pseudonym)), - bmpString BMPString (SIZE (1..ub-pseudonym)) } - --- Naming attributes of type DomainComponent (from RFC 2247) - -id-domainComponent AttributeType ::= - { 0 9 2342 19200300 100 1 25 } - -DomainComponent ::= IA5String - --- Legacy attributes - -pkcs-9 OBJECT IDENTIFIER ::= - { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } - -id-emailAddress AttributeType ::= { pkcs-9 1 } - -EmailAddress ::= IA5String (SIZE (1..ub-emailaddress-length)) - --- naming data types -- - -Name ::= CHOICE { -- only one possibility for now -- - rdnSequence RDNSequence } - -RDNSequence ::= SEQUENCE OF RelativeDistinguishedName - -DistinguishedName ::= RDNSequence - -RelativeDistinguishedName ::= - SET SIZE (1 .. MAX) OF AttributeTypeAndValue - --- Directory string type -- - -DirectoryString ::= CHOICE { - teletexString TeletexString (SIZE (1..MAX)), - printableString PrintableString (SIZE (1..MAX)), - universalString UniversalString (SIZE (1..MAX)), - utf8String UTF8String (SIZE (1..MAX)), - bmpString BMPString (SIZE (1..MAX)) } - --- certificate and CRL specific structures begin here - -Certificate ::= SEQUENCE { - tbsCertificate TBSCertificate, - signatureAlgorithm AlgorithmIdentifier, - signature BIT STRING } - -TBSCertificate ::= SEQUENCE { - version [0] Version DEFAULT v1, - serialNumber CertificateSerialNumber, - signature AlgorithmIdentifier, - issuer Name, - validity Validity, - subject Name, - subjectPublicKeyInfo SubjectPublicKeyInfo, - issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - -- If present, version MUST be v2 or v3 - subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, - -- If present, version MUST be v2 or v3 - extensions [3] Extensions OPTIONAL - -- If present, version MUST be v3 -- } - -Version ::= INTEGER { v1(0), v2(1), v3(2) } - -CertificateSerialNumber ::= INTEGER - -Validity ::= SEQUENCE { - notBefore Time, - notAfter Time } - -Time ::= CHOICE { - utcTime UTCTime, - generalTime GeneralizedTime } - -UniqueIdentifier ::= BIT STRING - -SubjectPublicKeyInfo ::= SEQUENCE { - algorithm AlgorithmIdentifier, - subjectPublicKey BIT STRING } - -Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension - -Extension ::= SEQUENCE { - extnID OBJECT IDENTIFIER, - critical BOOLEAN DEFAULT FALSE, - extnValue OCTET STRING } - --- CRL structures - -CertificateList ::= SEQUENCE { - tbsCertList TBSCertList, - signatureAlgorithm AlgorithmIdentifier, - signature BIT STRING } - -TBSCertList ::= SEQUENCE { - version Version OPTIONAL, - -- if present, MUST be v2 - signature AlgorithmIdentifier, - issuer Name, - thisUpdate Time, - nextUpdate Time OPTIONAL, - revokedCertificates SEQUENCE OF SEQUENCE { - userCertificate CertificateSerialNumber, - revocationDate Time, - crlEntryExtensions Extensions OPTIONAL - -- if present, MUST be v2 - } OPTIONAL, - crlExtensions [0] Extensions OPTIONAL } - -- if present, MUST be v2 - --- Version, Time, CertificateSerialNumber, and Extensions were --- defined earlier for use in the certificate structure - -AlgorithmIdentifier ::= SEQUENCE { - algorithm OBJECT IDENTIFIER, - parameters ANY DEFINED BY algorithm OPTIONAL } - -- contains a value of the type - -- registered for use with the - -- algorithm object identifier value - --- X.400 address syntax starts here - -ORAddress ::= SEQUENCE { - built-in-standard-attributes BuiltInStandardAttributes, - built-in-domain-defined-attributes - BuiltInDomainDefinedAttributes OPTIONAL, - -- see also teletex-domain-defined-attributes - extension-attributes ExtensionAttributes OPTIONAL } - --- Built-in Standard Attributes - -BuiltInStandardAttributes ::= SEQUENCE { - country-name CountryName OPTIONAL, - administration-domain-name AdministrationDomainName OPTIONAL, - network-address [0] IMPLICIT NetworkAddress OPTIONAL, - -- see also extended-network-address - terminal-identifier [1] IMPLICIT TerminalIdentifier OPTIONAL, - private-domain-name [2] PrivateDomainName OPTIONAL, - organization-name [3] IMPLICIT OrganizationName OPTIONAL, - -- see also teletex-organization-name - numeric-user-identifier [4] IMPLICIT NumericUserIdentifier - OPTIONAL, - personal-name [5] IMPLICIT PersonalName OPTIONAL, - -- see also teletex-personal-name - organizational-unit-names [6] IMPLICIT OrganizationalUnitNames - OPTIONAL } - -- see also teletex-organizational-unit-names - -CountryName ::= [APPLICATION 1] CHOICE { - x121-dcc-code NumericString - (SIZE (ub-country-name-numeric-length)), - iso-3166-alpha2-code PrintableString - (SIZE (ub-country-name-alpha-length)) } - -AdministrationDomainName ::= [APPLICATION 2] CHOICE { - numeric NumericString (SIZE (0..ub-domain-name-length)), - printable PrintableString (SIZE (0..ub-domain-name-length)) } - -NetworkAddress ::= X121Address -- see also extended-network-address - -X121Address ::= NumericString (SIZE (1..ub-x121-address-length)) - -TerminalIdentifier ::= PrintableString (SIZE -(1..ub-terminal-id-length)) - -PrivateDomainName ::= CHOICE { - numeric NumericString (SIZE (1..ub-domain-name-length)), - printable PrintableString (SIZE (1..ub-domain-name-length)) } - -OrganizationName ::= PrintableString - (SIZE (1..ub-organization-name-length)) - -- see also teletex-organization-name - -NumericUserIdentifier ::= NumericString - (SIZE (1..ub-numeric-user-id-length)) - -PersonalName ::= SET { - surname [0] IMPLICIT PrintableString - (SIZE (1..ub-surname-length)), - given-name [1] IMPLICIT PrintableString - (SIZE (1..ub-given-name-length)) OPTIONAL, - initials [2] IMPLICIT PrintableString - (SIZE (1..ub-initials-length)) OPTIONAL, - generation-qualifier [3] IMPLICIT PrintableString - (SIZE (1..ub-generation-qualifier-length)) - OPTIONAL } - -- see also teletex-personal-name - -OrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units) - OF OrganizationalUnitName - -- see also teletex-organizational-unit-names - -OrganizationalUnitName ::= PrintableString (SIZE - (1..ub-organizational-unit-name-length)) - --- Built-in Domain-defined Attributes - -BuiltInDomainDefinedAttributes ::= SEQUENCE SIZE - (1..ub-domain-defined-attributes) OF - BuiltInDomainDefinedAttribute - -BuiltInDomainDefinedAttribute ::= SEQUENCE { - type PrintableString (SIZE - (1..ub-domain-defined-attribute-type-length)), - value PrintableString (SIZE - (1..ub-domain-defined-attribute-value-length)) } - --- Extension Attributes - -ExtensionAttributes ::= SET SIZE (1..ub-extension-attributes) OF - ExtensionAttribute - -ExtensionAttribute ::= SEQUENCE { - extension-attribute-type [0] IMPLICIT INTEGER - (0..ub-extension-attributes), - extension-attribute-value [1] - ANY DEFINED BY extension-attribute-type } - --- Extension types and attribute values - -common-name INTEGER ::= 1 - -CommonName ::= PrintableString (SIZE (1..ub-common-name-length)) - -teletex-common-name INTEGER ::= 2 - -TeletexCommonName ::= TeletexString (SIZE (1..ub-common-name-length)) - -teletex-organization-name INTEGER ::= 3 - -TeletexOrganizationName ::= - TeletexString (SIZE (1..ub-organization-name-length)) - -teletex-personal-name INTEGER ::= 4 - -TeletexPersonalName ::= SET { - surname [0] IMPLICIT TeletexString - (SIZE (1..ub-surname-length)), - given-name [1] IMPLICIT TeletexString - (SIZE (1..ub-given-name-length)) OPTIONAL, - initials [2] IMPLICIT TeletexString - (SIZE (1..ub-initials-length)) OPTIONAL, - generation-qualifier [3] IMPLICIT TeletexString - (SIZE (1..ub-generation-qualifier-length)) - OPTIONAL } - -teletex-organizational-unit-names INTEGER ::= 5 - -TeletexOrganizationalUnitNames ::= SEQUENCE SIZE - (1..ub-organizational-units) OF TeletexOrganizationalUnitName - -TeletexOrganizationalUnitName ::= TeletexString - (SIZE (1..ub-organizational-unit-name-length)) - -pds-name INTEGER ::= 7 - -PDSName ::= PrintableString (SIZE (1..ub-pds-name-length)) - -physical-delivery-country-name INTEGER ::= 8 - -PhysicalDeliveryCountryName ::= CHOICE { - x121-dcc-code NumericString (SIZE -(ub-country-name-numeric-length)), - iso-3166-alpha2-code PrintableString - (SIZE (ub-country-name-alpha-length)) } - -postal-code INTEGER ::= 9 - -PostalCode ::= CHOICE { - numeric-code NumericString (SIZE (1..ub-postal-code-length)), - printable-code PrintableString (SIZE (1..ub-postal-code-length)) } - -physical-delivery-office-name INTEGER ::= 10 - -PhysicalDeliveryOfficeName ::= PDSParameter - -physical-delivery-office-number INTEGER ::= 11 - -PhysicalDeliveryOfficeNumber ::= PDSParameter - -extension-OR-address-components INTEGER ::= 12 - -ExtensionORAddressComponents ::= PDSParameter - -physical-delivery-personal-name INTEGER ::= 13 - -PhysicalDeliveryPersonalName ::= PDSParameter - -physical-delivery-organization-name INTEGER ::= 14 - -PhysicalDeliveryOrganizationName ::= PDSParameter - -extension-physical-delivery-address-components INTEGER ::= 15 - -ExtensionPhysicalDeliveryAddressComponents ::= PDSParameter - -unformatted-postal-address INTEGER ::= 16 - -UnformattedPostalAddress ::= SET { - printable-address SEQUENCE SIZE (1..ub-pds-physical-address-lines) - OF PrintableString (SIZE (1..ub-pds-parameter-length)) - OPTIONAL, - teletex-string TeletexString - (SIZE (1..ub-unformatted-address-length)) OPTIONAL } - -street-address INTEGER ::= 17 - -StreetAddress ::= PDSParameter - -post-office-box-address INTEGER ::= 18 - -PostOfficeBoxAddress ::= PDSParameter - -poste-restante-address INTEGER ::= 19 - -PosteRestanteAddress ::= PDSParameter - -unique-postal-name INTEGER ::= 20 - -UniquePostalName ::= PDSParameter - -local-postal-attributes INTEGER ::= 21 - -LocalPostalAttributes ::= PDSParameter - -PDSParameter ::= SET { - printable-string PrintableString - (SIZE(1..ub-pds-parameter-length)) OPTIONAL, - teletex-string TeletexString - (SIZE(1..ub-pds-parameter-length)) OPTIONAL } - -extended-network-address INTEGER ::= 22 - -ExtendedNetworkAddress ::= CHOICE { - e163-4-address SEQUENCE { - number [0] IMPLICIT NumericString - (SIZE (1..ub-e163-4-number-length)), - sub-address [1] IMPLICIT NumericString - (SIZE (1..ub-e163-4-sub-address-length)) - OPTIONAL }, - psap-address [0] IMPLICIT PresentationAddress } - -PresentationAddress ::= SEQUENCE { - pSelector [0] EXPLICIT OCTET STRING OPTIONAL, - sSelector [1] EXPLICIT OCTET STRING OPTIONAL, - tSelector [2] EXPLICIT OCTET STRING OPTIONAL, - nAddresses [3] EXPLICIT SET SIZE (1..MAX) OF OCTET STRING } - -terminal-type INTEGER ::= 23 - -TerminalType ::= INTEGER { - telex (3), - teletex (4), - g3-facsimile (5), - g4-facsimile (6), - ia5-terminal (7), - videotex (8) } (0..ub-integer-options) - --- Extension Domain-defined Attributes - -teletex-domain-defined-attributes INTEGER ::= 6 - -TeletexDomainDefinedAttributes ::= SEQUENCE SIZE - (1..ub-domain-defined-attributes) OF TeletexDomainDefinedAttribute - -TeletexDomainDefinedAttribute ::= SEQUENCE { - type TeletexString - (SIZE (1..ub-domain-defined-attribute-type-length)), - value TeletexString - (SIZE (1..ub-domain-defined-attribute-value-length)) } - --- specifications of Upper Bounds MUST be regarded as mandatory --- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter --- Upper Bounds - --- Upper Bounds -ub-name INTEGER ::= 32768 -ub-common-name INTEGER ::= 64 -ub-locality-name INTEGER ::= 128 -ub-state-name INTEGER ::= 128 -ub-organization-name INTEGER ::= 64 -ub-organizational-unit-name INTEGER ::= 64 -ub-title INTEGER ::= 64 -ub-serial-number INTEGER ::= 64 -ub-match INTEGER ::= 128 -ub-emailaddress-length INTEGER ::= 128 -ub-common-name-length INTEGER ::= 64 -ub-country-name-alpha-length INTEGER ::= 2 -ub-country-name-numeric-length INTEGER ::= 3 -ub-domain-defined-attributes INTEGER ::= 4 -ub-domain-defined-attribute-type-length INTEGER ::= 8 -ub-domain-defined-attribute-value-length INTEGER ::= 128 -ub-domain-name-length INTEGER ::= 16 -ub-extension-attributes INTEGER ::= 256 -ub-e163-4-number-length INTEGER ::= 15 -ub-e163-4-sub-address-length INTEGER ::= 40 -ub-generation-qualifier-length INTEGER ::= 3 -ub-given-name-length INTEGER ::= 16 -ub-initials-length INTEGER ::= 5 -ub-integer-options INTEGER ::= 256 -ub-numeric-user-id-length INTEGER ::= 32 -ub-organization-name-length INTEGER ::= 64 -ub-organizational-unit-name-length INTEGER ::= 32 -ub-organizational-units INTEGER ::= 4 -ub-pds-name-length INTEGER ::= 16 -ub-pds-parameter-length INTEGER ::= 30 -ub-pds-physical-address-lines INTEGER ::= 6 -ub-postal-code-length INTEGER ::= 16 -ub-pseudonym INTEGER ::= 128 -ub-surname-length INTEGER ::= 40 -ub-terminal-id-length INTEGER ::= 24 -ub-unformatted-address-length INTEGER ::= 180 -ub-x121-address-length INTEGER ::= 16 - --- Note - upper bounds on string types, such as TeletexString, are --- measured in characters. Excepting PrintableString or IA5String, a --- significantly greater number of octets will be required to hold --- such a value. As a minimum, 16 octets, or twice the specified --- upper bound, whichever is the larger, should be allowed for --- TeletexString. For UTF8String or UniversalString at least four --- times the upper bound should be allowed. - -END diff --git a/lib/ssl/pkix/PKIX1Explicit88.hrl b/lib/ssl/pkix/PKIX1Explicit88.hrl deleted file mode 100644 index 5940c1e245..0000000000 --- a/lib/ssl/pkix/PKIX1Explicit88.hrl +++ /dev/null @@ -1,163 +0,0 @@ -%% Generated by the Erlang ASN.1 compiler version:1.4.4.8 -%% Purpose: Erlang record definitions for each named and unnamed -%% SEQUENCE and SET, and macro definitions for each value -%% definition,in module PKIX1Explicit88 - - - --record('Attribute',{ -type, values}). - --record('AttributeTypeAndValue',{ -type, value}). - --record('Certificate',{ -tbsCertificate, signatureAlgorithm, signature}). - --record('TBSCertificate',{ -version = asn1_DEFAULT, serialNumber, signature, issuer, validity, subject, subjectPublicKeyInfo, issuerUniqueID = asn1_NOVALUE, subjectUniqueID = asn1_NOVALUE, extensions = asn1_NOVALUE}). - --record('Validity',{ -notBefore, notAfter}). - --record('SubjectPublicKeyInfo',{ -algorithm, subjectPublicKey}). - --record('Extension',{ -extnID, critical = asn1_DEFAULT, extnValue}). - --record('CertificateList',{ -tbsCertList, signatureAlgorithm, signature}). - --record('TBSCertList',{ -version = asn1_NOVALUE, signature, issuer, thisUpdate, nextUpdate = asn1_NOVALUE, revokedCertificates = asn1_NOVALUE, crlExtensions = asn1_NOVALUE}). - --record('TBSCertList_revokedCertificates_SEQOF',{ -userCertificate, revocationDate, crlEntryExtensions = asn1_NOVALUE}). - --record('AlgorithmIdentifier',{ -algorithm, parameters = asn1_NOVALUE}). - --record('ORAddress',{ -'built-in-standard-attributes', 'built-in-domain-defined-attributes' = asn1_NOVALUE, 'extension-attributes' = asn1_NOVALUE}). - --record('BuiltInStandardAttributes',{ -'country-name' = asn1_NOVALUE, 'administration-domain-name' = asn1_NOVALUE, 'network-address' = asn1_NOVALUE, 'terminal-identifier' = asn1_NOVALUE, 'private-domain-name' = asn1_NOVALUE, 'organization-name' = asn1_NOVALUE, 'numeric-user-identifier' = asn1_NOVALUE, 'personal-name' = asn1_NOVALUE, 'organizational-unit-names' = asn1_NOVALUE}). - --record('PersonalName',{ -surname, 'given-name' = asn1_NOVALUE, initials = asn1_NOVALUE, 'generation-qualifier' = asn1_NOVALUE}). - --record('BuiltInDomainDefinedAttribute',{ -type, value}). - --record('ExtensionAttribute',{ -'extension-attribute-type', 'extension-attribute-value'}). - --record('TeletexPersonalName',{ -surname, 'given-name' = asn1_NOVALUE, initials = asn1_NOVALUE, 'generation-qualifier' = asn1_NOVALUE}). - --record('UnformattedPostalAddress',{ -'printable-address' = asn1_NOVALUE, 'teletex-string' = asn1_NOVALUE}). - --record('PDSParameter',{ -'printable-string' = asn1_NOVALUE, 'teletex-string' = asn1_NOVALUE}). - --record('ExtendedNetworkAddress_e163-4-address',{ -number, 'sub-address' = asn1_NOVALUE}). - --record('PresentationAddress',{ -pSelector = asn1_NOVALUE, sSelector = asn1_NOVALUE, tSelector = asn1_NOVALUE, nAddresses}). - --record('TeletexDomainDefinedAttribute',{ -type, value}). - --define('id-pkix', {1,3,6,1,5,5,7}). --define('id-pe', {1,3,6,1,5,5,7,1}). --define('id-qt', {1,3,6,1,5,5,7,2}). --define('id-kp', {1,3,6,1,5,5,7,3}). --define('id-ad', {1,3,6,1,5,5,7,48}). --define('id-qt-cps', {1,3,6,1,5,5,7,2,1}). --define('id-qt-unotice', {1,3,6,1,5,5,7,2,2}). --define('id-ad-ocsp', {1,3,6,1,5,5,7,48,1}). --define('id-ad-caIssuers', {1,3,6,1,5,5,7,48,2}). --define('id-ad-timeStamping', {1,3,6,1,5,5,7,48,3}). --define('id-ad-caRepository', {1,3,6,1,5,5,7,48,5}). --define('id-at', {2,5,4}). --define('id-at-name', {2,5,4,41}). --define('id-at-surname', {2,5,4,4}). --define('id-at-givenName', {2,5,4,42}). --define('id-at-initials', {2,5,4,43}). --define('id-at-generationQualifier', {2,5,4,44}). --define('id-at-commonName', {2,5,4,3}). --define('id-at-localityName', {2,5,4,7}). --define('id-at-stateOrProvinceName', {2,5,4,8}). --define('id-at-organizationName', {2,5,4,10}). --define('id-at-organizationalUnitName', {2,5,4,11}). --define('id-at-title', {2,5,4,12}). --define('id-at-dnQualifier', {2,5,4,46}). --define('id-at-countryName', {2,5,4,6}). --define('id-at-serialNumber', {2,5,4,5}). --define('id-at-pseudonym', {2,5,4,65}). --define('id-domainComponent', {0,9,2342,19200300,100,1,25}). --define('pkcs-9', {1,2,840,113549,1,9}). --define('id-emailAddress', {1,2,840,113549,1,9,1}). --define('common-name', 1). --define('teletex-common-name', 2). --define('teletex-organization-name', 3). --define('teletex-personal-name', 4). --define('teletex-organizational-unit-names', 5). --define('pds-name', 7). --define('physical-delivery-country-name', 8). --define('postal-code', 9). --define('physical-delivery-office-name', 10). --define('physical-delivery-office-number', 11). --define('extension-OR-address-components', 12). --define('physical-delivery-personal-name', 13). --define('physical-delivery-organization-name', 14). --define('extension-physical-delivery-address-components', 15). --define('unformatted-postal-address', 16). --define('street-address', 17). --define('post-office-box-address', 18). --define('poste-restante-address', 19). --define('unique-postal-name', 20). --define('local-postal-attributes', 21). --define('extended-network-address', 22). --define('terminal-type', 23). --define('teletex-domain-defined-attributes', 6). --define('ub-name', 32768). --define('ub-common-name', 64). --define('ub-locality-name', 128). --define('ub-state-name', 128). --define('ub-organization-name', 64). --define('ub-organizational-unit-name', 64). --define('ub-title', 64). --define('ub-serial-number', 64). --define('ub-match', 128). --define('ub-emailaddress-length', 128). --define('ub-common-name-length', 64). --define('ub-country-name-alpha-length', 2). --define('ub-country-name-numeric-length', 3). --define('ub-domain-defined-attributes', 4). --define('ub-domain-defined-attribute-type-length', 8). --define('ub-domain-defined-attribute-value-length', 128). --define('ub-domain-name-length', 16). --define('ub-extension-attributes', 256). --define('ub-e163-4-number-length', 15). --define('ub-e163-4-sub-address-length', 40). --define('ub-generation-qualifier-length', 3). --define('ub-given-name-length', 16). --define('ub-initials-length', 5). --define('ub-integer-options', 256). --define('ub-numeric-user-id-length', 32). --define('ub-organization-name-length', 64). --define('ub-organizational-unit-name-length', 32). --define('ub-organizational-units', 4). --define('ub-pds-name-length', 16). --define('ub-pds-parameter-length', 30). --define('ub-pds-physical-address-lines', 6). --define('ub-postal-code-length', 16). --define('ub-pseudonym', 128). --define('ub-surname-length', 40). --define('ub-terminal-id-length', 24). --define('ub-unformatted-address-length', 180). --define('ub-x121-address-length', 16). diff --git a/lib/ssl/pkix/PKIX1Implicit88.asn1 b/lib/ssl/pkix/PKIX1Implicit88.asn1 deleted file mode 100644 index ced270baf6..0000000000 --- a/lib/ssl/pkix/PKIX1Implicit88.asn1 +++ /dev/null @@ -1,349 +0,0 @@ -PKIX1Implicit88 { iso(1) identified-organization(3) dod(6) internet(1) - security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19) } - -DEFINITIONS IMPLICIT TAGS ::= - -BEGIN - --- EXPORTS ALL -- - -IMPORTS - id-pe, id-kp, id-qt-unotice, id-qt-cps, - -- delete following line if "new" types are supported -- - -- BMPString, - -- UTF8String, end "new" types -- - ORAddress, Name, RelativeDistinguishedName, - CertificateSerialNumber, Attribute, DirectoryString - FROM PKIX1Explicit88 { iso(1) identified-organization(3) - dod(6) internet(1) security(5) mechanisms(5) pkix(7) - id-mod(0) id-pkix1-explicit(18) }; - - --- ISO arc for standard certificate and CRL extensions - -id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} - --- authority key identifier OID and syntax - -id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } - -AuthorityKeyIdentifier ::= SEQUENCE { - keyIdentifier [0] KeyIdentifier OPTIONAL, - authorityCertIssuer [1] GeneralNames OPTIONAL, - authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } - -- authorityCertIssuer and authorityCertSerialNumber MUST both - -- be present or both be absent - -KeyIdentifier ::= OCTET STRING - --- subject key identifier OID and syntax - -id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } - -SubjectKeyIdentifier ::= KeyIdentifier - --- key usage extension OID and syntax - -id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } - -KeyUsage ::= BIT STRING { - digitalSignature (0), - nonRepudiation (1), - keyEncipherment (2), - dataEncipherment (3), - keyAgreement (4), - keyCertSign (5), - cRLSign (6), - encipherOnly (7), - decipherOnly (8) } - --- private key usage period extension OID and syntax - -id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } - -PrivateKeyUsagePeriod ::= SEQUENCE { - notBefore [0] GeneralizedTime OPTIONAL, - notAfter [1] GeneralizedTime OPTIONAL } - -- either notBefore or notAfter MUST be present - --- certificate policies extension OID and syntax - -id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } - -anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 } - -CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation - -PolicyInformation ::= SEQUENCE { - policyIdentifier CertPolicyId, - policyQualifiers SEQUENCE SIZE (1..MAX) OF - PolicyQualifierInfo OPTIONAL } - -CertPolicyId ::= OBJECT IDENTIFIER - -PolicyQualifierInfo ::= SEQUENCE { - policyQualifierId PolicyQualifierId, - qualifier ANY DEFINED BY policyQualifierId } - --- Implementations that recognize additional policy qualifiers MUST --- augment the following definition for PolicyQualifierId - -PolicyQualifierId ::= - OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) - --- CPS pointer qualifier - -CPSuri ::= IA5String - --- user notice qualifier - -UserNotice ::= SEQUENCE { - noticeRef NoticeReference OPTIONAL, - explicitText DisplayText OPTIONAL} - -NoticeReference ::= SEQUENCE { - organization DisplayText, - noticeNumbers SEQUENCE OF INTEGER } - -DisplayText ::= CHOICE { - ia5String IA5String (SIZE (1..200)), - visibleString VisibleString (SIZE (1..200)), - bmpString BMPString (SIZE (1..200)), - utf8String UTF8String (SIZE (1..200)) } - --- policy mapping extension OID and syntax - -id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } - -PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { - issuerDomainPolicy CertPolicyId, - subjectDomainPolicy CertPolicyId } - --- subject alternative name extension OID and syntax - -id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } - -SubjectAltName ::= GeneralNames - -GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - -GeneralName ::= CHOICE { - otherName [0] AnotherName, - rfc822Name [1] IA5String, - dNSName [2] IA5String, - x400Address [3] ORAddress, - directoryName [4] Name, - ediPartyName [5] EDIPartyName, - uniformResourceIdentifier [6] IA5String, - iPAddress [7] OCTET STRING, - registeredID [8] OBJECT IDENTIFIER } - --- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as --- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax - -AnotherName ::= SEQUENCE { - type-id OBJECT IDENTIFIER, - value [0] EXPLICIT ANY DEFINED BY type-id } - -EDIPartyName ::= SEQUENCE { - nameAssigner [0] DirectoryString OPTIONAL, - partyName [1] DirectoryString } - --- issuer alternative name extension OID and syntax - -id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } - -IssuerAltName ::= GeneralNames - -id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } - -SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute - --- basic constraints extension OID and syntax - -id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } - -BasicConstraints ::= SEQUENCE { - cA BOOLEAN DEFAULT FALSE, - pathLenConstraint INTEGER (0..MAX) OPTIONAL } - --- name constraints extension OID and syntax - -id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } - -NameConstraints ::= SEQUENCE { - permittedSubtrees [0] GeneralSubtrees OPTIONAL, - excludedSubtrees [1] GeneralSubtrees OPTIONAL } - -GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree - -GeneralSubtree ::= SEQUENCE { - base GeneralName, - minimum [0] BaseDistance DEFAULT 0, - maximum [1] BaseDistance OPTIONAL } - -BaseDistance ::= INTEGER (0..MAX) - --- policy constraints extension OID and syntax - -id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } - -PolicyConstraints ::= SEQUENCE { - requireExplicitPolicy [0] SkipCerts OPTIONAL, - inhibitPolicyMapping [1] SkipCerts OPTIONAL } - -SkipCerts ::= INTEGER (0..MAX) - --- CRL distribution points extension OID and syntax - -id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} - -CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - -DistributionPoint ::= SEQUENCE { - distributionPoint [0] DistributionPointName OPTIONAL, - reasons [1] ReasonFlags OPTIONAL, - cRLIssuer [2] GeneralNames OPTIONAL } - -DistributionPointName ::= CHOICE { - fullName [0] GeneralNames, - nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - -ReasonFlags ::= BIT STRING { - unused (0), - keyCompromise (1), - cACompromise (2), - affiliationChanged (3), - superseded (4), - cessationOfOperation (5), - certificateHold (6), - privilegeWithdrawn (7), - aACompromise (8) } - --- extended key usage extension OID and syntax - -id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} - -ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId - - -KeyPurposeId ::= OBJECT IDENTIFIER - --- permit unspecified key uses - -anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } - --- extended key purpose OIDs - -id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } -id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } -id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } -id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } -id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } -id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } - --- inhibit any policy OID and syntax - -id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } - -InhibitAnyPolicy ::= SkipCerts - --- freshest (delta)CRL extension OID and syntax - -id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 } - -FreshestCRL ::= CRLDistributionPoints - --- authority info access - -id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } - -AuthorityInfoAccessSyntax ::= - SEQUENCE SIZE (1..MAX) OF AccessDescription - -AccessDescription ::= SEQUENCE { - accessMethod OBJECT IDENTIFIER, - accessLocation GeneralName } - --- subject info access - -id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 } - -SubjectInfoAccessSyntax ::= - SEQUENCE SIZE (1..MAX) OF AccessDescription - --- CRL number extension OID and syntax - -id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } - -CRLNumber ::= INTEGER (0..MAX) - --- issuing distribution point extension OID and syntax - -id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } - -IssuingDistributionPoint ::= SEQUENCE { - distributionPoint [0] DistributionPointName OPTIONAL, - onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, - onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, - onlySomeReasons [3] ReasonFlags OPTIONAL, - indirectCRL [4] BOOLEAN DEFAULT FALSE, - onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } - -id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } - -BaseCRLNumber ::= CRLNumber - --- CRL reasons extension OID and syntax - -id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 } - -CRLReason ::= ENUMERATED { - unspecified (0), - keyCompromise (1), - cACompromise (2), - affiliationChanged (3), - superseded (4), - cessationOfOperation (5), - certificateHold (6), - removeFromCRL (8), - privilegeWithdrawn (9), - aACompromise (10) } - --- certificate issuer CRL entry extension OID and syntax - -id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } - -CertificateIssuer ::= GeneralNames - --- hold instruction extension OID and syntax - -id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 } - -HoldInstructionCode ::= OBJECT IDENTIFIER - --- ANSI x9 holdinstructions - --- ANSI x9 arc holdinstruction arc - -holdInstruction OBJECT IDENTIFIER ::= - {joint-iso-itu-t(2) member-body(2) us(840) x9cm(10040) 2} - --- ANSI X9 holdinstructions referenced by this standard - -id-holdinstruction-none OBJECT IDENTIFIER ::= - {holdInstruction 1} -- deprecated - -id-holdinstruction-callissuer OBJECT IDENTIFIER ::= - {holdInstruction 2} - -id-holdinstruction-reject OBJECT IDENTIFIER ::= - {holdInstruction 3} - --- invalidity date CRL entry extension OID and syntax - -id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 } - -InvalidityDate ::= GeneralizedTime - -END diff --git a/lib/ssl/pkix/PKIX1Implicit88.hrl b/lib/ssl/pkix/PKIX1Implicit88.hrl deleted file mode 100644 index 8fa1836284..0000000000 --- a/lib/ssl/pkix/PKIX1Implicit88.hrl +++ /dev/null @@ -1,93 +0,0 @@ -%% Generated by the Erlang ASN.1 compiler version:1.4.4.8 -%% Purpose: Erlang record definitions for each named and unnamed -%% SEQUENCE and SET, and macro definitions for each value -%% definition,in module PKIX1Implicit88 - - - --record('AuthorityKeyIdentifier',{ -keyIdentifier = asn1_NOVALUE, authorityCertIssuer = asn1_NOVALUE, authorityCertSerialNumber = asn1_NOVALUE}). - --record('PrivateKeyUsagePeriod',{ -notBefore = asn1_NOVALUE, notAfter = asn1_NOVALUE}). - --record('PolicyInformation',{ -policyIdentifier, policyQualifiers = asn1_NOVALUE}). - --record('PolicyQualifierInfo',{ -policyQualifierId, qualifier}). - --record('UserNotice',{ -noticeRef = asn1_NOVALUE, explicitText = asn1_NOVALUE}). - --record('NoticeReference',{ -organization, noticeNumbers}). - --record('PolicyMappings_SEQOF',{ -issuerDomainPolicy, subjectDomainPolicy}). - --record('AnotherName',{ -'type-id', value}). - --record('EDIPartyName',{ -nameAssigner = asn1_NOVALUE, partyName}). - --record('BasicConstraints',{ -cA = asn1_DEFAULT, pathLenConstraint = asn1_NOVALUE}). - --record('NameConstraints',{ -permittedSubtrees = asn1_NOVALUE, excludedSubtrees = asn1_NOVALUE}). - --record('GeneralSubtree',{ -base, minimum = asn1_DEFAULT, maximum = asn1_NOVALUE}). - --record('PolicyConstraints',{ -requireExplicitPolicy = asn1_NOVALUE, inhibitPolicyMapping = asn1_NOVALUE}). - --record('DistributionPoint',{ -distributionPoint = asn1_NOVALUE, reasons = asn1_NOVALUE, cRLIssuer = asn1_NOVALUE}). - --record('AccessDescription',{ -accessMethod, accessLocation}). - --record('IssuingDistributionPoint',{ -distributionPoint = asn1_NOVALUE, onlyContainsUserCerts = asn1_DEFAULT, onlyContainsCACerts = asn1_DEFAULT, onlySomeReasons = asn1_NOVALUE, indirectCRL = asn1_DEFAULT, onlyContainsAttributeCerts = asn1_DEFAULT}). - --define('id-ce', {2,5,29}). --define('id-ce-authorityKeyIdentifier', {2,5,29,35}). --define('id-ce-subjectKeyIdentifier', {2,5,29,14}). --define('id-ce-keyUsage', {2,5,29,15}). --define('id-ce-privateKeyUsagePeriod', {2,5,29,16}). --define('id-ce-certificatePolicies', {2,5,29,32}). --define('anyPolicy', {2,5,29,32,0}). --define('id-ce-policyMappings', {2,5,29,33}). --define('id-ce-subjectAltName', {2,5,29,17}). --define('id-ce-issuerAltName', {2,5,29,18}). --define('id-ce-subjectDirectoryAttributes', {2,5,29,9}). --define('id-ce-basicConstraints', {2,5,29,19}). --define('id-ce-nameConstraints', {2,5,29,30}). --define('id-ce-policyConstraints', {2,5,29,36}). --define('id-ce-cRLDistributionPoints', {2,5,29,31}). --define('id-ce-extKeyUsage', {2,5,29,37}). --define('anyExtendedKeyUsage', {2,5,29,37,0}). --define('id-kp-serverAuth', {1,3,6,1,5,5,7,3,1}). --define('id-kp-clientAuth', {1,3,6,1,5,5,7,3,2}). --define('id-kp-codeSigning', {1,3,6,1,5,5,7,3,3}). --define('id-kp-emailProtection', {1,3,6,1,5,5,7,3,4}). --define('id-kp-timeStamping', {1,3,6,1,5,5,7,3,8}). --define('id-kp-OCSPSigning', {1,3,6,1,5,5,7,3,9}). --define('id-ce-inhibitAnyPolicy', {2,5,29,54}). --define('id-ce-freshestCRL', {2,5,29,46}). --define('id-pe-authorityInfoAccess', {1,3,6,1,5,5,7,1,1}). --define('id-pe-subjectInfoAccess', {1,3,6,1,5,5,7,1,11}). --define('id-ce-cRLNumber', {2,5,29,20}). --define('id-ce-issuingDistributionPoint', {2,5,29,28}). --define('id-ce-deltaCRLIndicator', {2,5,29,27}). --define('id-ce-cRLReasons', {2,5,29,21}). --define('id-ce-certificateIssuer', {2,5,29,29}). --define('id-ce-holdInstructionCode', {2,5,29,23}). --define('holdInstruction', {2,2,840,10040,2}). --define('id-holdinstruction-none', {2,2,840,10040,2,1}). --define('id-holdinstruction-callissuer', {2,2,840,10040,2,2}). --define('id-holdinstruction-reject', {2,2,840,10040,2,3}). --define('id-ce-invalidityDate', {2,5,29,24}). diff --git a/lib/ssl/pkix/PKIXAttributeCertificate.asn1 b/lib/ssl/pkix/PKIXAttributeCertificate.asn1 deleted file mode 100644 index 7d93e6b37e..0000000000 --- a/lib/ssl/pkix/PKIXAttributeCertificate.asn1 +++ /dev/null @@ -1,189 +0,0 @@ - PKIXAttributeCertificate {iso(1) identified-organization(3) dod(6) - internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) - id-mod-attribute-cert(12)} - - DEFINITIONS IMPLICIT TAGS ::= - - BEGIN - - -- EXPORTS ALL -- - - IMPORTS - - -- IMPORTed module OIDs MAY change if [PKIXPROF] changes - -- PKIX Certificate Extensions - Attribute, AlgorithmIdentifier, CertificateSerialNumber, - Extensions, UniqueIdentifier, - id-pkix, id-pe, id-kp, id-ad, id-at - FROM PKIX1Explicit88 {iso(1) identified-organization(3) - dod(6) internet(1) security(5) mechanisms(5) - pkix(7) id-mod(0) id-pkix1-explicit-88(1)} - - GeneralName, GeneralNames, id-ce - FROM PKIX1Implicit88 {iso(1) identified-organization(3) - dod(6) internet(1) security(5) mechanisms(5) - pkix(7) id-mod(0) id-pkix1-implicit-88(2)} ; - - id-pe-ac-auditIdentity OBJECT IDENTIFIER ::= { id-pe 4 } - id-pe-aaControls OBJECT IDENTIFIER ::= { id-pe 6 } - id-pe-ac-proxying OBJECT IDENTIFIER ::= { id-pe 10 } - id-ce-targetInformation OBJECT IDENTIFIER ::= { id-ce 55 } - - id-aca OBJECT IDENTIFIER ::= { id-pkix 10 } - id-aca-authenticationInfo OBJECT IDENTIFIER ::= { id-aca 1 } - id-aca-accessIdentity OBJECT IDENTIFIER ::= { id-aca 2 } - id-aca-chargingIdentity OBJECT IDENTIFIER ::= { id-aca 3 } - id-aca-group OBJECT IDENTIFIER ::= { id-aca 4 } - -- { id-aca 5 } is reserved - id-aca-encAttrs OBJECT IDENTIFIER ::= { id-aca 6 } - - id-at-role OBJECT IDENTIFIER ::= { id-at 72} - id-at-clearance OBJECT IDENTIFIER ::= - { joint-iso-ccitt(2) ds(5) module(1) - selected-attribute-types(5) clearance (55) } - - -- Uncomment this if using a 1988 level ASN.1 compiler - -- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING - - AttributeCertificate ::= SEQUENCE { - acinfo AttributeCertificateInfo, - signatureAlgorithm AlgorithmIdentifier, - signatureValue BIT STRING - } - - AttributeCertificateInfo ::= SEQUENCE { - version AttCertVersion, -- version is v2 - holder Holder, - issuer AttCertIssuer, - signature AlgorithmIdentifier, - serialNumber CertificateSerialNumber, - attrCertValidityPeriod AttCertValidityPeriod, - attributes SEQUENCE OF Attribute, - issuerUniqueID UniqueIdentifier OPTIONAL, - extensions Extensions OPTIONAL - } - - AttCertVersion ::= INTEGER { v2(1) } - - Holder ::= SEQUENCE { - baseCertificateID [0] IssuerSerial OPTIONAL, - -- the issuer and serial number of - -- the holder's Public Key Certificate - entityName [1] GeneralNames OPTIONAL, - -- the name of the claimant or role - objectDigestInfo [2] ObjectDigestInfo OPTIONAL - -- used to directly authenticate the - -- holder, for example, an executable - } - - ObjectDigestInfo ::= SEQUENCE { - digestedObjectType ENUMERATED { - publicKey (0), - publicKeyCert (1), - otherObjectTypes (2) }, - -- otherObjectTypes MUST NOT - -- MUST NOT be used in this profile - otherObjectTypeID OBJECT IDENTIFIER OPTIONAL, - digestAlgorithm AlgorithmIdentifier, - objectDigest BIT STRING - } - - AttCertIssuer ::= CHOICE { - v1Form GeneralNames, -- MUST NOT be used in this - -- profile - v2Form [0] V2Form -- v2 only - } - - V2Form ::= SEQUENCE { - issuerName GeneralNames OPTIONAL, - baseCertificateID [0] IssuerSerial OPTIONAL, - objectDigestInfo [1] ObjectDigestInfo OPTIONAL - -- issuerName MUST be present in this profile - -- baseCertificateID and objectDigestInfo MUST - -- NOT be present in this profile - } - - IssuerSerial ::= SEQUENCE { - issuer GeneralNames, - serial CertificateSerialNumber, - issuerUID UniqueIdentifier OPTIONAL - } - - AttCertValidityPeriod ::= SEQUENCE { - notBeforeTime GeneralizedTime, - notAfterTime GeneralizedTime - } - - Targets ::= SEQUENCE OF Target - - Target ::= CHOICE { - targetName [0] GeneralName, - targetGroup [1] GeneralName, - targetCert [2] TargetCert - } - - TargetCert ::= SEQUENCE { - targetCertificate IssuerSerial, - targetName GeneralName OPTIONAL, - certDigestInfo ObjectDigestInfo OPTIONAL - } - - IetfAttrSyntax ::= SEQUENCE { - policyAuthority[0] GeneralNames OPTIONAL, - values SEQUENCE OF CHOICE { - octets OCTET STRING, - oid OBJECT IDENTIFIER, - string UTF8String - } - } - - SvceAuthInfo ::= SEQUENCE { - service GeneralName, - ident GeneralName, - authInfo OCTET STRING OPTIONAL - } - - RoleSyntax ::= SEQUENCE { - roleAuthority [0] GeneralNames OPTIONAL, - roleName [1] GeneralName - } - - Clearance ::= SEQUENCE { - policyId [0] OBJECT IDENTIFIER, - classList [1] ClassList DEFAULT {unclassified}, - securityCategories - [2] SET OF SecurityCategory OPTIONAL - } - - ClassList ::= BIT STRING { - unmarked (0), - unclassified (1), - restricted (2), - confidential (3), - secret (4), - topSecret (5) - } - - SecurityCategory ::= SEQUENCE { - type [0] IMPLICIT OBJECT IDENTIFIER, - value [1] ANY DEFINED BY type - } - - AAControls ::= SEQUENCE { - pathLenConstraint INTEGER (0..MAX) OPTIONAL, - permittedAttrs [0] AttrSpec OPTIONAL, - excludedAttrs [1] AttrSpec OPTIONAL, - permitUnSpecified BOOLEAN DEFAULT TRUE - } - - AttrSpec::= SEQUENCE OF OBJECT IDENTIFIER - - ACClearAttrs ::= SEQUENCE { - acIssuer GeneralName, - acSerial INTEGER, - attrs SEQUENCE OF Attribute - } - - ProxyInfo ::= SEQUENCE OF Targets - - END diff --git a/lib/ssl/pkix/PKIXAttributeCertificate.hrl b/lib/ssl/pkix/PKIXAttributeCertificate.hrl deleted file mode 100644 index 99389c4852..0000000000 --- a/lib/ssl/pkix/PKIXAttributeCertificate.hrl +++ /dev/null @@ -1,64 +0,0 @@ -%% Generated by the Erlang ASN.1 compiler version:1.4.4.8 -%% Purpose: Erlang record definitions for each named and unnamed -%% SEQUENCE and SET, and macro definitions for each value -%% definition,in module PKIXAttributeCertificate - - - --record('AttributeCertificate',{ -acinfo, signatureAlgorithm, signatureValue}). - --record('AttributeCertificateInfo',{ -version, holder, issuer, signature, serialNumber, attrCertValidityPeriod, attributes, issuerUniqueID = asn1_NOVALUE, extensions = asn1_NOVALUE}). - --record('Holder',{ -baseCertificateID = asn1_NOVALUE, entityName = asn1_NOVALUE, objectDigestInfo = asn1_NOVALUE}). - --record('ObjectDigestInfo',{ -digestedObjectType, otherObjectTypeID = asn1_NOVALUE, digestAlgorithm, objectDigest}). - --record('V2Form',{ -issuerName = asn1_NOVALUE, baseCertificateID = asn1_NOVALUE, objectDigestInfo = asn1_NOVALUE}). - --record('IssuerSerial',{ -issuer, serial, issuerUID = asn1_NOVALUE}). - --record('AttCertValidityPeriod',{ -notBeforeTime, notAfterTime}). - --record('TargetCert',{ -targetCertificate, targetName = asn1_NOVALUE, certDigestInfo = asn1_NOVALUE}). - --record('IetfAttrSyntax',{ -policyAuthority = asn1_NOVALUE, values}). - --record('SvceAuthInfo',{ -service, ident, authInfo = asn1_NOVALUE}). - --record('RoleSyntax',{ -roleAuthority = asn1_NOVALUE, roleName}). - --record('Clearance',{ -policyId, classList = asn1_DEFAULT, securityCategories = asn1_NOVALUE}). - --record('SecurityCategory',{ -type, value}). - --record('AAControls',{ -pathLenConstraint = asn1_NOVALUE, permittedAttrs = asn1_NOVALUE, excludedAttrs = asn1_NOVALUE, permitUnSpecified = asn1_DEFAULT}). - --record('ACClearAttrs',{ -acIssuer, acSerial, attrs}). - --define('id-pe-ac-auditIdentity', {1,3,6,1,5,5,7,1,4}). --define('id-pe-aaControls', {1,3,6,1,5,5,7,1,6}). --define('id-pe-ac-proxying', {1,3,6,1,5,5,7,1,10}). --define('id-ce-targetInformation', {2,5,29,55}). --define('id-aca', {1,3,6,1,5,5,7,10}). --define('id-aca-authenticationInfo', {1,3,6,1,5,5,7,10,1}). --define('id-aca-accessIdentity', {1,3,6,1,5,5,7,10,2}). --define('id-aca-chargingIdentity', {1,3,6,1,5,5,7,10,3}). --define('id-aca-group', {1,3,6,1,5,5,7,10,4}). --define('id-aca-encAttrs', {1,3,6,1,5,5,7,10,6}). --define('id-at-role', {2,5,4,72}). --define('id-at-clearance', {2,5,1,5,55}). diff --git a/lib/ssl/pkix/README b/lib/ssl/pkix/README deleted file mode 100644 index 8be2c15de5..0000000000 --- a/lib/ssl/pkix/README +++ /dev/null @@ -1,49 +0,0 @@ -The files - - PKIX1Algorithms88.asn1 - PKIX1Explicit88.asn1 - PKIX1Implicit88.asn1 - PKIXAttributeCertificate.asn1 - -are from RFCs 3279, 3280 and 3281. - -We have edited PKIX1Explicit88.asn1, PKIX1Implicit88.asn1, and -PKIXAttributeCertificate.asn1 as follows: - - -1. Removal of definition of UniversalString and BMPString: - -diff -r1.1 PKIX1Explicit88.asn1 -15c15 -< UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING ---- -> -- UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING -18c18 -< BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING ---- -> -- BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING - - -2. Removal of definition of BMPString: - -diff -r1.1 PKIX1Implicit88.asn1 -13c13,14 -< BMPString, UTF8String, -- end "new" types -- ---- -> -- BMPString, -> UTF8String, -- end "new" types -- - - -3. Addition of definition of UTF8String, and correction of a typo. - -diff -r1.1 PKIXAttributeCertificate.asn1 -46c46 -< -- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING ---- -> UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING -55c55 -< version AttCertVersion -- version is v2, ---- -> version AttCertVersion, -- version is v2 - -PKIX1Algorithms88.asn1 is unchanged. diff --git a/lib/ssl/pkix/SSL-PKIX.asn1 b/lib/ssl/pkix/SSL-PKIX.asn1 deleted file mode 100644 index ea6333f953..0000000000 --- a/lib/ssl/pkix/SSL-PKIX.asn1 +++ /dev/null @@ -1,704 +0,0 @@ -SSL-PKIX {iso(1) identified-organization(3) dod(6) internet(1) - private(4) enterprices(1) ericsson(193) otp(19) ssl(10) - pkix1(1)} - -DEFINITIONS EXPLICIT TAGS ::= - -BEGIN - --- EXPORTS ALL - -IMPORTS - -- Certificate (parts of) - Version, - CertificateSerialNumber, - --AlgorithmIdentifier, - Validity, - UniqueIdentifier, - - -- AttribyteTypeAndValue - Name, - AttributeType, - id-at-name, - id-at-surname, - id-at-givenName, - id-at-initials, - id-at-generationQualifier, X520name, - id-at-commonName, X520CommonName, - id-at-localityName, X520LocalityName, - id-at-stateOrProvinceName, X520StateOrProvinceName, - id-at-organizationName, X520OrganizationName, - id-at-organizationalUnitName, X520OrganizationalUnitName, - id-at-title, X520Title, - id-at-dnQualifier, X520dnQualifier, - id-at-countryName, X520countryName, - id-at-serialNumber, X520SerialNumber, - id-at-pseudonym, X520Pseudonym, - id-domainComponent, DomainComponent, - id-emailAddress, EmailAddress, - - -- Extension Attributes - common-name, CommonName, - teletex-common-name, TeletexCommonName, - teletex-personal-name, TeletexPersonalName, - pds-name, PDSName, - physical-delivery-country-name, PhysicalDeliveryCountryName, - postal-code, PostalCode, - physical-delivery-office-name, PhysicalDeliveryOfficeName, - physical-delivery-office-number, PhysicalDeliveryOfficeNumber, - extension-OR-address-components, ExtensionORAddressComponents, - physical-delivery-personal-name, PhysicalDeliveryPersonalName, - physical-delivery-organization-name, PhysicalDeliveryOrganizationName, - extension-physical-delivery-address-components, - ExtensionPhysicalDeliveryAddressComponents, - unformatted-postal-address, UnformattedPostalAddress, - street-address, StreetAddress, - post-office-box-address, PostOfficeBoxAddress, - poste-restante-address, PosteRestanteAddress, - unique-postal-name, UniquePostalName, - local-postal-attributes, LocalPostalAttributes, - extended-network-address, ExtendedNetworkAddress, - terminal-type, TerminalType, - teletex-domain-defined-attributes, TeletexDomainDefinedAttributes - - FROM PKIX1Explicit88 { iso(1) identified-organization(3) dod(6) - internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) - id-pkix1-explicit(18) } - - -- Extensions - id-ce-authorityKeyIdentifier, AuthorityKeyIdentifier, - id-ce-subjectKeyIdentifier, SubjectKeyIdentifier, - id-ce-keyUsage, KeyUsage, - id-ce-privateKeyUsagePeriod, PrivateKeyUsagePeriod, - id-ce-certificatePolicies, CertificatePolicies, - id-ce-policyMappings, PolicyMappings, - id-ce-subjectAltName, SubjectAltName, - id-ce-issuerAltName, IssuerAltName, - id-ce-subjectDirectoryAttributes, SubjectDirectoryAttributes, - id-ce-basicConstraints, BasicConstraints, - id-ce-nameConstraints, NameConstraints, - id-ce-policyConstraints, PolicyConstraints, - id-ce-cRLDistributionPoints, CRLDistributionPoints, - id-ce-extKeyUsage, ExtKeyUsageSyntax, - id-ce-inhibitAnyPolicy, InhibitAnyPolicy, - id-ce-freshestCRL, FreshestCRL, - id-pe-authorityInfoAccess, AuthorityInfoAccessSyntax, - id-pe-subjectInfoAccess, SubjectInfoAccessSyntax, - id-ce-cRLNumber, CRLNumber, - id-ce-issuingDistributionPoint, IssuingDistributionPoint, - id-ce-deltaCRLIndicator, BaseCRLNumber, - id-ce-cRLReasons, CRLReason, - id-ce-certificateIssuer, CertificateIssuer, - id-ce-holdInstructionCode, HoldInstructionCode, - id-ce-invalidityDate, InvalidityDate - - FROM PKIX1Implicit88 { iso(1) identified-organization(3) dod(6) - internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) - id-pkix1-implicit(19) } - - --Keys and Signatures - id-dsa, Dss-Parms, DSAPublicKey, - id-dsa-with-sha1, - md2WithRSAEncryption, - md5WithRSAEncryption, - sha1WithRSAEncryption, - rsaEncryption, RSAPublicKey, - dhpublicnumber, DomainParameters, DHPublicKey, - id-keyExchangeAlgorithm, KEA-Parms-Id, --KEA-PublicKey, - ecdsa-with-SHA1, - prime-field, Prime-p, - characteristic-two-field, --Characteristic-two, - gnBasis, - tpBasis, Trinomial, - ppBasis, Pentanomial, - id-ecPublicKey, EcpkParameters, ECPoint - FROM PKIX1Algorithms88 { iso(1) identified-organization(3) dod(6) - internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) - id-mod-pkix1-algorithms(17) }; - --- --- Certificate --- - -SSLCertificate ::= SEQUENCE { - tbsCertificate TBSCertificate, - signatureAlgorithm SignatureAlgorithm, - signature BIT STRING } - -SSLTBSCertificate ::= SEQUENCE { - version [0] Version DEFAULT v1, - serialNumber CertificateSerialNumber, - signature SignatureAlgorithm, - issuer Name, - validity Validity, - subject Name, - subjectPublicKeyInfo SubjectPublicKeyInfo, - issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - -- If present, version MUST be v2 or v3 - subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, - -- If present, version MUST be v2 or v3 - extensions [3] Extensions OPTIONAL - -- If present, version MUST be v3 -- } - - --- Attribute type and values --- - -ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= CLASS { - &id AttributeType UNIQUE, - &Type } - WITH SYNTAX { - ID &id - TYPE &Type } - -SSLAttributeTypeAndValue ::= SEQUENCE { - type ATTRIBUTE-TYPE-AND-VALUE-CLASS.&id - ({SupportedAttributeTypeAndValues}), - value ATTRIBUTE-TYPE-AND-VALUE-CLASS.&Type - ({SupportedAttributeTypeAndValues}{@type}) } - -SupportedAttributeTypeAndValues ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= - { name | surname | givenName | initials | generationQualifier | - commonName | localityName | stateOrProvinceName | organizationName | - organizationalUnitName | title | dnQualifier | countryName | - serialNumber | pseudonym | domainComponent | emailAddress } - -name ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-name - TYPE X520name } - -surname ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-surname - TYPE X520name } - -givenName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-givenName - TYPE X520name } - -initials ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-initials - TYPE X520name } - -generationQualifier ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-generationQualifier - TYPE X520name } - -commonName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-commonName - TYPE X520CommonName } - -localityName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-localityName - TYPE X520LocalityName } - -stateOrProvinceName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-stateOrProvinceName - TYPE X520StateOrProvinceName } - -organizationName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-organizationName - TYPE X520OrganizationName } - -organizationalUnitName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-organizationalUnitName - TYPE X520OrganizationalUnitName } - -title ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-title - TYPE X520Title } - -dnQualifier ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-dnQualifier - TYPE X520dnQualifier } - -countryName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-countryName - TYPE X520countryName } - -serialNumber ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-serialNumber - TYPE X520SerialNumber } - -pseudonym ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-at-pseudonym - TYPE X520Pseudonym } - -domainComponent ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-domainComponent - TYPE DomainComponent } - -emailAddress ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= { - ID id-emailAddress - TYPE EmailAddress } - --- --- Signature and Public Key Algorithms --- - -SSLSubjectPublicKeyInfo ::= SEQUENCE { - algorithm SEQUENCE { - algo PUBLIC-KEY-ALGORITHM-CLASS.&id - ({SupportedPublicKeyAlgorithms}), - parameters PUBLIC-KEY-ALGORITHM-CLASS.&Type - ({SupportedPublicKeyAlgorithms}{@.algo}) - OPTIONAL - }, - subjectPublicKey PUBLIC-KEY-ALGORITHM-CLASS.&PublicKeyType - ({SupportedPublicKeyAlgorithms}{@algorithm.algo}) } - --- The following is needed for conversion of SubjectPublicKeyInfo. - -SSLSubjectPublicKeyInfo-Any ::= SEQUENCE { - algorithm PublicKeyAlgorithm, - subjectPublicKey ANY } - - -SIGNATURE-ALGORITHM-CLASS ::= CLASS { - &id OBJECT IDENTIFIER UNIQUE, - &Type OPTIONAL } - WITH SYNTAX { - ID &id - [TYPE &Type] } - -PUBLIC-KEY-ALGORITHM-CLASS ::= CLASS { - &id OBJECT IDENTIFIER UNIQUE, - &Type OPTIONAL, - &PublicKeyType OPTIONAL } - WITH SYNTAX { - ID &id - [TYPE &Type] - [PUBLIC-KEY-TYPE &PublicKeyType] } - -SignatureAlgorithm ::= SEQUENCE { - algorithm SIGNATURE-ALGORITHM-CLASS.&id - ({SupportedSignatureAlgorithms}), - parameters SIGNATURE-ALGORITHM-CLASS.&Type - ({SupportedSignatureAlgorithms}{@algorithm}) - OPTIONAL } - -SignatureAlgorithm-Any ::= SEQUENCE { - algorithm OBJECT IDENTIFIER, - parameters ANY OPTIONAL } - -PublicKeyAlgorithm ::= SEQUENCE { - algorithm PUBLIC-KEY-ALGORITHM-CLASS.&id - ({SupportedPublicKeyAlgorithms}), - parameters PUBLIC-KEY-ALGORITHM-CLASS.&Type - ({SupportedPublicKeyAlgorithms}{@algorithm}) - OPTIONAL } - -SupportedSignatureAlgorithms SIGNATURE-ALGORITHM-CLASS ::= { - dsa-with-sha1 | md2-with-rsa-encryption | - md5-with-rsa-encryption | sha1-with-rsa-encryption | - ecdsa-with-sha1 } - -SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= { - dsa | rsa-encryption | dh | kea | ec-public-key } - - -- DSA Keys and Signatures - - -- SubjectPublicKeyInfo: - - dsa PUBLIC-KEY-ALGORITHM-CLASS ::= { - ID id-dsa - TYPE Dss-Parms -- XXX Must be OPTIONAL - PUBLIC-KEY-TYPE DSAPublicKey } - - -- Certificate.signatureAlgorithm - - dsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= { - ID id-dsa-with-sha1 - TYPE NULL } -- XXX Must be empty and not NULL - - -- - -- RSA Keys and Signatures - -- - - -- Certificate.signatureAlgorithm - - md2-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= { - ID md2WithRSAEncryption - TYPE NULL } - - md5-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= { - ID md5WithRSAEncryption - TYPE NULL } - - sha1-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= { - ID sha1WithRSAEncryption - TYPE NULL } - - -- Certificate.signature - -- See PKCS #1 (RFC 2313). XXX - - -- SubjectPublicKeyInfo: - - rsa-encryption PUBLIC-KEY-ALGORITHM-CLASS ::= { - ID rsaEncryption - TYPE NULL - PUBLIC-KEY-TYPE RSAPublicKey } - - -- - -- Diffie-Hellman Keys - -- - - -- SubjectPublicKeyInfo: - - dh PUBLIC-KEY-ALGORITHM-CLASS ::= { - ID dhpublicnumber - TYPE DomainParameters - PUBLIC-KEY-TYPE DHPublicKey } - - -- There are no Diffie-Hellman signature algorithms - - -- - -- KEA Keys - -- - - -- SubjectPublicKeyInfo: - - KEA-PublicKey ::= INTEGER - - kea PUBLIC-KEY-ALGORITHM-CLASS ::= { - ID id-keyExchangeAlgorithm - TYPE KEA-Parms-Id - PUBLIC-KEY-TYPE KEA-PublicKey } - - -- There are no KEA signature algorithms - - -- - -- Elliptic Curve Keys, Signatures, and Curves - -- - - -- Certificate.signatureAlgorithm - - ecdsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= { - ID ecdsa-with-SHA1 - TYPE NULL } -- XXX Must be empty and not NULL - - FIELD-ID-CLASS ::= CLASS { - &id OBJECT IDENTIFIER UNIQUE, - &Type } - WITH SYNTAX { - ID &id - TYPE &Type } - - SSLFieldID ::= SEQUENCE { -- Finite field - fieldType FIELD-ID-CLASS.&id({SupportedFieldIds}), - parameters FIELD-ID-CLASS.&Type({SupportedFieldIds}{@fieldType}) } - - SupportedFieldIds FIELD-ID-CLASS ::= { - field-prime-field | field-characteristic-two } - - field-prime-field FIELD-ID-CLASS ::= { - ID prime-field - TYPE Prime-p } - - CHARACTERISTIC-TWO-CLASS ::= CLASS { - &id OBJECT IDENTIFIER UNIQUE, - &Type } - WITH SYNTAX { - ID &id - TYPE &Type } - - SSLCharacteristic-two ::= SEQUENCE { -- Finite field - m INTEGER, -- Field size 2^m - basis CHARACTERISTIC-TWO-CLASS.&id({SupportedCharacteristicTwos}), - parameters CHARACTERISTIC-TWO-CLASS.&Type - ({SupportedCharacteristicTwos}{@basis}) } - - SupportedCharacteristicTwos CHARACTERISTIC-TWO-CLASS ::= { - gn-basis | tp-basis | pp-basis } - - field-characteristic-two FIELD-ID-CLASS ::= { - ID characteristic-two-field - TYPE Characteristic-two } - - gn-basis CHARACTERISTIC-TWO-CLASS ::= { - ID gnBasis - TYPE NULL } - - tp-basis CHARACTERISTIC-TWO-CLASS ::= { - ID tpBasis - TYPE Trinomial } - - pp-basis CHARACTERISTIC-TWO-CLASS ::= { - ID ppBasis - TYPE Pentanomial } - - -- SubjectPublicKeyInfo.algorithm - - ec-public-key PUBLIC-KEY-ALGORITHM-CLASS ::= { - ID id-ecPublicKey - TYPE EcpkParameters - PUBLIC-KEY-TYPE ECPoint } - --- --- Extension Attributes --- - -EXTENSION-ATTRIBUTE-CLASS ::= CLASS { - &id INTEGER UNIQUE, - &Type } - WITH SYNTAX { - ID &id - TYPE &Type } - -SSLExtensionAttributes ::= SET SIZE (1..MAX) OF ExtensionAttribute - --- XXX Below we should have extension-attribute-type and extension- --- attribute-value but Erlang ASN1 does not like it. -SSLExtensionAttribute ::= SEQUENCE { - extensionAttributeType [0] IMPLICIT EXTENSION-ATTRIBUTE-CLASS.&id - ({SupportedExtensionAttributes}), - extensionAttributeValue [1] EXTENSION-ATTRIBUTE-CLASS.&Type - ({SupportedExtensionAttributes}{@extensionAttributeType}) } - -SupportedExtensionAttributes EXTENSION-ATTRIBUTE-CLASS ::= { - x400-common-name | - x400-teletex-common-name | - x400-teletex-personal-name | - x400-pds-name | - x400-physical-delivery-country-name | - x400-postal-code | - x400-physical-delivery-office-name | - x400-physical-delivery-office-number | - x400-extension-OR-address-components | - x400-physical-delivery-personal-name | - x400-physical-delivery-organization-name | - x400-extension-physical-delivery-address-components | - x400-unformatted-postal-address | - x400-street-address | - x400-post-office-box-address | - x400-poste-restante-address | - x400-unique-postal-name | - x400-local-postal-attributes | - x400-extended-network-address | - x400-terminal-type | - x400-teletex-domain-defined-attributes } - --- Extension types and attribute values - -x400-common-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID common-name - TYPE CommonName } - -x400-teletex-common-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID teletex-common-name - TYPE TeletexCommonName } - -x400-teletex-personal-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID teletex-personal-name - TYPE TeletexPersonalName } - -x400-pds-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID pds-name - TYPE PDSName } - -x400-physical-delivery-country-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID physical-delivery-country-name - TYPE PhysicalDeliveryCountryName } - -x400-postal-code EXTENSION-ATTRIBUTE-CLASS ::= { - ID postal-code - TYPE PostalCode } - -x400-physical-delivery-office-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID physical-delivery-office-name - TYPE PhysicalDeliveryOfficeName } - -x400-physical-delivery-office-number EXTENSION-ATTRIBUTE-CLASS ::= { - ID physical-delivery-office-number - TYPE PhysicalDeliveryOfficeNumber } - -x400-extension-OR-address-components EXTENSION-ATTRIBUTE-CLASS ::= { - ID extension-OR-address-components - TYPE ExtensionORAddressComponents } - -x400-physical-delivery-personal-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID physical-delivery-personal-name - TYPE PhysicalDeliveryPersonalName } - -x400-physical-delivery-organization-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID physical-delivery-organization-name - TYPE PhysicalDeliveryOrganizationName } - -x400-extension-physical-delivery-address-components - EXTENSION-ATTRIBUTE-CLASS ::= { - ID extension-physical-delivery-address-components - TYPE ExtensionPhysicalDeliveryAddressComponents } - -x400-unformatted-postal-address EXTENSION-ATTRIBUTE-CLASS ::= { - ID unformatted-postal-address - TYPE UnformattedPostalAddress } - -x400-street-address EXTENSION-ATTRIBUTE-CLASS ::= { - ID street-address - TYPE StreetAddress } - -x400-post-office-box-address EXTENSION-ATTRIBUTE-CLASS ::= { - ID post-office-box-address - TYPE PostOfficeBoxAddress } - -x400-poste-restante-address EXTENSION-ATTRIBUTE-CLASS ::= { - ID poste-restante-address - TYPE PosteRestanteAddress } - -x400-unique-postal-name EXTENSION-ATTRIBUTE-CLASS ::= { - ID unique-postal-name - TYPE UniquePostalName } - -x400-local-postal-attributes EXTENSION-ATTRIBUTE-CLASS ::= { - ID local-postal-attributes - TYPE LocalPostalAttributes } - -x400-extended-network-address EXTENSION-ATTRIBUTE-CLASS ::= { - ID extended-network-address - TYPE ExtendedNetworkAddress } - -x400-terminal-type EXTENSION-ATTRIBUTE-CLASS ::= { - ID terminal-type - TYPE TerminalType } - -x400-teletex-domain-defined-attributes EXTENSION-ATTRIBUTE-CLASS ::= { - ID teletex-domain-defined-attributes - TYPE TeletexDomainDefinedAttributes } - --- Extensions - -SSLExtensions ::= SEQUENCE SIZE (1..MAX) OF Extension - -EXTENSION-CLASS ::= CLASS { - &id OBJECT IDENTIFIER UNIQUE, - &Type OPTIONAL} - WITH SYNTAX { - ID &id - [TYPE &Type] } - -SSLExtension ::= SEQUENCE { - extnID EXTENSION-CLASS.&id({SupportedExtensions}), - critical BOOLEAN DEFAULT FALSE, - extnValue EXTENSION-CLASS.&Type({SupportedExtensions}{@extnID}) } - --- The following is needed for conversion between Extension and Extension-Cd - -ObjId ::= OBJECT IDENTIFIER -Boolean ::= BOOLEAN -Any ::= ANY - -Extension-Any ::= SEQUENCE { - extnID OBJECT IDENTIFIER, - critical BOOLEAN DEFAULT FALSE, - extnValue ANY } - -SupportedExtensions EXTENSION-CLASS ::= { authorityKeyIdentifier | - subjectKeyIdentifier | keyUsage | privateKeyUsagePeriod | - certificatePolicies | policyMappings | subjectAltName | - issuerAltName | subjectDirectoryAttributes | basicConstraints | - nameConstraints | policyConstraints | cRLDistributionPoints | - extKeyUsage | inhibitAnyPolicy | freshestCRL | authorityInfoAccess | - subjectInfoAccess | cRLNumber | issuingDistributionPoint | - deltaCRLIndicator | cRLReasons | certificateIssuer | - holdInstructionCode | invalidityDate } - -authorityKeyIdentifier EXTENSION-CLASS ::= { - ID id-ce-authorityKeyIdentifier - TYPE AuthorityKeyIdentifier } - -subjectKeyIdentifier EXTENSION-CLASS ::= { - ID id-ce-subjectKeyIdentifier - TYPE SubjectKeyIdentifier } - -keyUsage EXTENSION-CLASS ::= { - ID id-ce-keyUsage - TYPE KeyUsage } - -privateKeyUsagePeriod EXTENSION-CLASS ::= { - ID id-ce-privateKeyUsagePeriod - TYPE PrivateKeyUsagePeriod } - -certificatePolicies EXTENSION-CLASS ::= { - ID id-ce-certificatePolicies - TYPE CertificatePolicies } - -policyMappings EXTENSION-CLASS ::= { - ID id-ce-policyMappings - TYPE PolicyMappings } - -subjectAltName EXTENSION-CLASS ::= { - ID id-ce-subjectAltName - TYPE SubjectAltName } - -issuerAltName EXTENSION-CLASS ::= { - ID id-ce-issuerAltName - TYPE IssuerAltName } - -subjectDirectoryAttributes EXTENSION-CLASS ::= { - ID id-ce-subjectDirectoryAttributes - TYPE SubjectDirectoryAttributes } - -basicConstraints EXTENSION-CLASS ::= { - ID id-ce-basicConstraints - TYPE BasicConstraints } - -nameConstraints EXTENSION-CLASS ::= { - ID id-ce-nameConstraints - TYPE NameConstraints } - -policyConstraints EXTENSION-CLASS ::= { - ID id-ce-policyConstraints - TYPE PolicyConstraints } - -cRLDistributionPoints EXTENSION-CLASS ::= { - ID id-ce-cRLDistributionPoints - TYPE CRLDistributionPoints } - -extKeyUsage EXTENSION-CLASS ::= { - ID id-ce-extKeyUsage - TYPE ExtKeyUsageSyntax } - -inhibitAnyPolicy EXTENSION-CLASS ::= { - ID id-ce-inhibitAnyPolicy - TYPE InhibitAnyPolicy } - -freshestCRL EXTENSION-CLASS ::= { - ID id-ce-freshestCRL - TYPE FreshestCRL } - -authorityInfoAccess EXTENSION-CLASS ::= { - ID id-pe-authorityInfoAccess - TYPE AuthorityInfoAccessSyntax } - -subjectInfoAccess EXTENSION-CLASS ::= { - ID id-pe-subjectInfoAccess - TYPE SubjectInfoAccessSyntax } - -cRLNumber EXTENSION-CLASS ::= { - ID id-ce-cRLNumber - TYPE CRLNumber } - -issuingDistributionPoint EXTENSION-CLASS ::= { - ID id-ce-issuingDistributionPoint - TYPE IssuingDistributionPoint } - -deltaCRLIndicator EXTENSION-CLASS ::= { - ID id-ce-deltaCRLIndicator - TYPE BaseCRLNumber } - -cRLReasons EXTENSION-CLASS ::= { - ID id-ce-cRLReasons - TYPE CRLReason } - -certificateIssuer EXTENSION-CLASS ::= { - ID id-ce-certificateIssuer - TYPE CertificateIssuer } - -holdInstructionCode EXTENSION-CLASS ::= { - ID id-ce-holdInstructionCode - TYPE HoldInstructionCode } - -invalidityDate EXTENSION-CLASS ::= { - ID id-ce-invalidityDate - TYPE InvalidityDate } - -END diff --git a/lib/ssl/pkix/mk_ssl_pkix_oid.erl b/lib/ssl/pkix/mk_ssl_pkix_oid.erl deleted file mode 100644 index 06edc5113a..0000000000 --- a/lib/ssl/pkix/mk_ssl_pkix_oid.erl +++ /dev/null @@ -1,94 +0,0 @@ --module(mk_ssl_pkix_oid). - --export([make/0]). - --define(PKIX_MODULES, ['OTP-PKIX']). - -make() -> - {ok, Fd} = file:open("ssl_pkix_oid.erl", [write]), - io:fwrite(Fd, "%%% File: ssl_pkix_oid.erl\n" - "%%% NB This file has been automatically generated by " - "mk_ssl_pkix_oid.\n" - "%%% Do not edit it.\n\n", []), - io:fwrite(Fd, "-module(ssl_pkix_oid).\n", []), - io:fwrite(Fd, "-export([id2atom/1, atom2id/1, all_atoms/0, " - "all_ids/0]).\n\n", []), - - - AIds0 = get_atom_ids(?PKIX_MODULES), - - AIds1 = modify_atoms(AIds0), - gen_id2atom(Fd, AIds1), - gen_atom2id(Fd, AIds1), - gen_all(Fd, AIds1), - file:close(Fd). - -get_atom_ids(Ms) -> - get_atom_ids(Ms, []). - -get_atom_ids([], AIdss) -> - lists:flatten(AIdss); -get_atom_ids([M| Ms], AIdss) -> - {value, {exports, Exports}} = - lists:keysearch(exports, 1, M:module_info()), - As = lists:zf( - fun ({info, 0}) -> false; - ({module_info, 0}) -> false; - ({encoding_rule, 0}) -> false; - ({F, 0}) -> - case atom_to_list(F) of - %% Remove upper-bound (ub-) functions - "ub-" ++ _Rest -> - false; - _ -> - {true, F} - end; - (_) -> false - end, Exports), - AIds = lists:map(fun(F) -> {F, M:F()} end, As), - get_atom_ids(Ms, [AIds| AIdss]). - -modify_atoms(AIds) -> - F = fun({A, I}) -> - NAS = case atom_to_list(A) of - "id-" ++ Rest -> - Rest; - Any -> - Any - end, - {list_to_atom(NAS), I} end, - lists:map(F, AIds). - -gen_id2atom(Fd, AIds0) -> - AIds1 = lists:keysort(2, AIds0), - Txt = join(";\n", - lists:map( - fun({Atom, Id}) -> - io_lib:fwrite("id2atom(~p) ->\n ~p", [Id, Atom]) - end, AIds1)), - io:fwrite(Fd, "~s;\nid2atom(Any)->\n Any.\n\n", [Txt]). - -gen_atom2id(Fd, AIds0) -> - AIds1 = lists:keysort(1, AIds0), - Txt = join(";\n", - lists:map( - fun({Atom, Id}) -> - io_lib:fwrite("atom2id(~p) ->\n ~p", [Atom, Id]) - end, AIds1)), - io:fwrite(Fd, "~s;\natom2id(Any)->\n Any.\n\n", [Txt]). - -gen_all(Fd, AIds) -> - Atoms = lists:sort([A || {A, _} <- AIds]), - Ids = lists:sort([I || {_, I} <- AIds]), - F = fun(X) -> io_lib:fwrite(" ~w", [X]) end, - ATxt = "all_atoms() ->\n" ++ join(",\n", lists:map(F, Atoms)), - io:fwrite(Fd, "~s.\n\n", [ATxt]), - ITxt = "all_ids() ->\n" ++ join(",\n", lists:map(F, Ids)), - io:fwrite(Fd, "~s.\n\n", [ITxt]). - -join(Sep, [H1, H2| T]) -> - [H1, Sep| join(Sep, [H2| T])]; -join(_Sep, [H1]) -> - H1; -join(_, []) -> - []. diff --git a/lib/ssl/pkix/prebuild.skip b/lib/ssl/pkix/prebuild.skip deleted file mode 100644 index ffe82be68b..0000000000 --- a/lib/ssl/pkix/prebuild.skip +++ /dev/null @@ -1,5 +0,0 @@ -PKIX1Algorithms88.asn1db -PKIXAttributeCertificate.asn1db -PKIX1Explicit88.asn1db -SSL-PKIX.asn1db -PKIX1Implicit88.asn1db diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index fabf8a4e0d..7514ad2aa2 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1999-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 1999-2010. 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% # @@ -46,9 +46,6 @@ MODULES= \ ssl_server \ ssl_sup \ ssl_prim \ - ssl_pkix \ - ssl_pem \ - ssl_base64 \ inet_ssl_dist \ ssl_certificate\ ssl_certificate_db\ @@ -71,8 +68,6 @@ INTERNAL_HRL_FILES = \ ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \ ssl_record.hrl -PUBLIC_HRL_FILES = ssl_pkix.hrl - ERL_FILES= $(MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) @@ -85,15 +80,12 @@ APP_TARGET= $(EBIN)/$(APP_FILE) APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) -INCLUDE = ../include - # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- EXTRA_ERLC_FLAGS = +warn_unused_vars ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ -pz $(ERL_TOP)/lib/public_key/ebin \ - -I$(INCLUDE) \ $(EXTRA_ERLC_FLAGS) -DVSN=\"$(VSN)\" @@ -101,7 +93,7 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ # Targets # ---------------------------------------------------- -debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(PUBLIC_HRL_FILES) +debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) clean: rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) @@ -113,9 +105,6 @@ $(APP_TARGET): $(APP_SRC) ../vsn.mk $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ -$(PUBLIC_HRL_FILES): - cp -f $(PUBLIC_HRL_FILES) $(INCLUDE) - docs: # ---------------------------------------------------- @@ -126,8 +115,6 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt $(INSTALL_DIR) $(RELSYSDIR)/src $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(PUBLIC_HRL_FILES) $(RELSYSDIR)/include $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) \ $(APPUP_TARGET) $(RELSYSDIR)/ebin diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 2a7d451341..b9716786e6 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -7,10 +7,6 @@ ssl_server, ssl_broker, ssl_broker_sup, - ssl_base64, - ssl_pem, - ssl_pkix, - ssl_pkix_oid, ssl_prim, inet_ssl_dist, ssl_tls1, @@ -28,11 +24,10 @@ ssl_cipher, ssl_certificate_db, ssl_certificate, - ssl_alert, - 'OTP-PKIX' + ssl_alert ]}, {registered, [ssl_sup, ssl_server, ssl_broker_sup]}, - {applications, [kernel, stdlib]}, + {applications, [crypto, public_key, kernel, stdlib]}, {env, []}, {mod, {ssl_app, []}}]}. diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index e8ae6846aa..51c5289bd2 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,26 +1,11 @@ %% -*- erlang -*- {"%VSN%", [ - {"3.10", [{restart_application, ssl}]}, - {"3.10.1", [{restart_application, ssl}]}, - {"3.10.2", [{restart_application, ssl}]}, - {"3.10.3", [{restart_application, ssl}]}, - {"3.10.4", [{restart_application, ssl}]}, - {"3.10.5", [{restart_application, ssl}]}, - {"3.10.6", [{restart_application, ssl}]}, - {"3.10.7", [{restart_application, ssl}]}, - {"3.10.8", [{restart_application, ssl}]}, - {"3.10.9", [{restart_application, ssl}]} + {"4.1", [{restart_application, ssl}]}, + {"4.0.1", [{restart_application, ssl}]} ], [ - {"3.10", [{restart_application, ssl}]}, - {"3.10.1", [{restart_application, ssl}]}, - {"3.10.2", [{restart_application, ssl}]}, - {"3.10.3", [{restart_application, ssl}]}, - {"3.10.4", [{restart_application, ssl}]}, - {"3.10.5", [{restart_application, ssl}]}, - {"3.10.6", [{restart_application, ssl}]}, - {"3.10.8", [{restart_application, ssl}]}, - {"3.10.9", [{restart_application, ssl}]} + {"4.1", [{restart_application, ssl}]}, + {"4.0.1", [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 3cd4c7fdbd..7e5929d708 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -34,10 +34,14 @@ %% Should be deprecated as soon as old ssl is removed %%-deprecated({pid, 1, next_major_release}). +-deprecated({peercert, 2, next_major_release}). -include("ssl_int.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). +-include("ssl_cipher.hrl"). + +-include_lib("public_key/include/public_key.hrl"). -record(config, {ssl, %% SSL parameters inet_user, %% User set inet options @@ -47,22 +51,25 @@ }). %%-------------------------------------------------------------------- -%% Function: start([, Type]) -> ok -%% -%% Type = permanent | transient | temporary -%% Vsns = [Vsn] -%% Vsn = ssl3 | tlsv1 | 'tlsv1.1' +-spec start() -> ok. +-spec start(permanent | transient | temporary) -> ok. %% -%% Description: Starts the ssl application. Default type +%% Description: Utility function that starts the ssl, +%% crypto and public_key applications. Default type %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> + application:start(crypto), + application:start(public_key), application:start(ssl). + start(Type) -> + application:start(crypto, Type), + application:start(public_key, Type), application:start(ssl, Type). %%-------------------------------------------------------------------- -%% Function: stop() -> ok +-spec stop() -> ok. %% %% Description: Stops the ssl application. %%-------------------------------------------------------------------- @@ -70,7 +77,10 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- -%% Function: connect(Address, Port, Options[, Timeout]) -> {ok, Socket} +-spec connect(host() | port(), list()) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), list() | port_num(), timeout() | list()) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), port_num(), list(), timeout()) -> {ok, #sslsocket{}}. + %% %% Description: Connect to a ssl server. %%-------------------------------------------------------------------- @@ -96,13 +106,13 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> {error, Reason} end; -connect(Address, Port, Options) -> - connect(Address, Port, Options, infinity). +connect(Host, Port, Options) -> + connect(Host, Port, Options, infinity). -connect(Address, Port, Options0, Timeout) -> - case proplists:get_value(ssl_imp, Options0, old) of +connect(Host, Port, Options0, Timeout) -> + case proplists:get_value(ssl_imp, Options0, new) of new -> - new_connect(Address, Port, Options0, Timeout); + new_connect(Host, Port, Options0, Timeout); old -> %% Allow the option reuseaddr to be present %% so that new and old ssl can be run by the same @@ -110,20 +120,21 @@ connect(Address, Port, Options0, Timeout) -> %% that hardcodes reuseaddr to true in its portprogram. Options1 = proplists:delete(reuseaddr, Options0), Options = proplists:delete(ssl_imp, Options1), - old_connect(Address, Port, Options, Timeout); + old_connect(Host, Port, Options, Timeout); Value -> {error, {eoptions, {ssl_imp, Value}}} end. %%-------------------------------------------------------------------- -%% Function: listen(Port, Options) -> {ok, ListenSock} | {error, Reason} +-spec listen(port_num(), list()) ->{ok, #sslsocket{}} | {error, reason()}. + %% %% Description: Creates a ssl listen socket. %%-------------------------------------------------------------------- listen(_Port, []) -> {error, enooptions}; listen(Port, Options0) -> - case proplists:get_value(ssl_imp, Options0, old) of + case proplists:get_value(ssl_imp, Options0, new) of new -> new_listen(Port, Options0); old -> @@ -139,7 +150,8 @@ listen(Port, Options0) -> end. %%-------------------------------------------------------------------- -%% Function: transport_accept(ListenSocket[, Timeout]) -> {ok, Socket}. +-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}}. +-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}}. %% %% Description: Performs transport accept on a ssl listen socket %%-------------------------------------------------------------------- @@ -147,14 +159,14 @@ transport_accept(ListenSocket) -> transport_accept(ListenSocket, infinity). transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}}, - fd = new_ssl} = SslSocket, Timeout) -> + fd = new_ssl}, Timeout) -> %% The setopt could have been invoked on the listen socket %% and options should be inherited. EmOptions = emulated_options(), {ok, InetValues} = inet:getopts(ListenSocket, EmOptions), ok = inet:setopts(ListenSocket, internal_inet_values()), - {CbModule,_,_} = CbInfo, + {CbModule,_,_, _} = CbInfo, case CbModule:accept(ListenSocket, Timeout) of {ok, Socket} -> ok = inet:setopts(ListenSocket, InetValues), @@ -163,8 +175,7 @@ transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts} {SslOpts, socket_options(InetValues)}, self(), CbInfo], case ssl_connection_sup:start_child(ConnArgs) of {ok, Pid} -> - CbModule:controlling_process(Socket, Pid), - {ok, SslSocket#sslsocket{pid = Pid}}; + ssl_connection:socket_control(Socket, Pid, CbModule); {error, Reason} -> {error, Reason} end; @@ -178,8 +189,9 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_broker:transport_accept(Pid, ListenSocket, Timeout). %%-------------------------------------------------------------------- -%% Function: ssl_accept(ListenSocket[, Timeout]) -> {ok, Socket} | -%% {error, Reason} +-spec ssl_accept(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(#sslsocket{}, list() | timeout()) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(port(), list(), timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. @@ -187,22 +199,9 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_accept(ListenSocket) -> ssl_accept(ListenSocket, infinity). -ssl_accept(#sslsocket{pid = Pid, fd = new_ssl}, Timeout) -> - gen_fsm:send_event(Pid, socket_control), - try gen_fsm:sync_send_all_state_event(Pid, started, Timeout) of - connected -> - ok; - {error, _} = Error -> - Error - catch - exit:{noproc, _} -> - {error, closed}; - exit:{timeout, _} -> - {error, timeout}; - exit:{normal, _} -> - {error, closed} - end; - +ssl_accept(#sslsocket{fd = new_ssl} = Socket, Timeout) -> + ssl_connection:handshake(Socket, Timeout); + ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> ssl_accept(ListenSocket, SslOptions, infinity); @@ -218,19 +217,19 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> try handle_options(SslOptions ++ InetValues, server) of {ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} -> {ok, Port} = inet:port(Socket), - ssl_connection:accept(Port, Socket, - {SslOpts, EmOpts}, - self(), CbInfo, Timeout) + ssl_connection:ssl_accept(Port, Socket, + {SslOpts, EmOpts}, + self(), CbInfo, Timeout) catch Error = {error, _Reason} -> Error end. %%-------------------------------------------------------------------- -%% Function: close() -> ok +-spec close(#sslsocket{}) -> term(). %% %% Description: Close a ssl connection %%-------------------------------------------------------------------- -close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _}}}, fd = new_ssl}) -> +close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}) -> CbMod:close(ListenSocket); close(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:close(Pid); @@ -239,7 +238,7 @@ close(Socket = #sslsocket{}) -> ssl_broker:close(Socket). %%-------------------------------------------------------------------- -%% Function: send(Socket, Data) -> ok +-spec send(#sslsocket{}, iolist()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -251,7 +250,8 @@ send(#sslsocket{} = Socket, Data) -> ssl_broker:send(Socket, Data). %%-------------------------------------------------------------------- -%% Function: recv(Socket, Length [,Timeout]) -> {ok, Data} | {error, reason} +-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. +-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- @@ -265,8 +265,8 @@ recv(Socket = #sslsocket{}, Length, Timeout) -> ssl_broker:recv(Socket, Length, Timeout). %%-------------------------------------------------------------------- -%% Function: controlling_process(Socket, NewOwner) -> ok | {error, Reason} -%% +-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. +%% %% Description: Changes process that receives the messages when active = true %% or once. %%-------------------------------------------------------------------- @@ -279,11 +279,8 @@ controlling_process(Socket, NewOwner) when is_pid(NewOwner) -> ssl_broker:controlling_process(Socket, NewOwner). %%-------------------------------------------------------------------- -%% Function: connection_info(Socket) -> {ok, {Protocol, CipherSuite}} | -%% {error, Reason} -%% Protocol = sslv3 | tlsv1 | tlsv1.1 -%% CipherSuite = {KeyExchange, Chipher, Hash, Exportable} -%% +-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | + {error, reason()}. %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- @@ -295,9 +292,9 @@ connection_info(#sslsocket{} = Socket) -> ssl_broker:connection_info(Socket). %%-------------------------------------------------------------------- -%% Function: peercert(Socket[, Opts]) -> {ok, Cert} | {error, Reason} +-spec peercert(#sslsocket{}) ->{ok, der_cert()} | {error, reason()}. %% -%% Description: +%% Description: Returns the peercert. %%-------------------------------------------------------------------- peercert(Socket) -> peercert(Socket, []). @@ -307,14 +304,7 @@ peercert(#sslsocket{pid = Pid, fd = new_ssl}, Opts) -> {ok, undefined} -> {error, no_peercert}; {ok, BinCert} -> - PKOpts = [case Opt of ssl -> otp; pkix -> plain end || - Opt <- Opts, Opt =:= ssl orelse Opt =:= pkix], - case PKOpts of - [Opt] -> - public_key:pkix_decode_cert(BinCert, Opt); - [] -> - {ok, BinCert} - end; + decode_peercert(BinCert, Opts); {error, Reason} -> {error, Reason} end; @@ -323,15 +313,44 @@ peercert(#sslsocket{} = Socket, Opts) -> ensure_old_ssl_started(), case ssl_broker:peercert(Socket) of {ok, Bin} -> - ssl_pkix:decode_cert(Bin, Opts); + decode_peercert(Bin, Opts); {error, Reason} -> {error, Reason} end. + +decode_peercert(BinCert, Opts) -> + PKOpts = [case Opt of ssl -> otp; pkix -> plain end || + Opt <- Opts, Opt =:= ssl orelse Opt =:= pkix], + case PKOpts of + [Opt] -> + select_part(Opt, public_key:pkix_decode_cert(BinCert, Opt), Opts); + [] -> + {ok, BinCert} + end. + +select_part(otp, Cert, Opts) -> + case lists:member(subject, Opts) of + true -> + TBS = Cert#'OTPCertificate'.tbsCertificate, + {ok, TBS#'OTPTBSCertificate'.subject}; + false -> + {ok, Cert} + end; + +select_part(plain, Cert, Opts) -> + case lists:member(subject, Opts) of + true -> + TBS = Cert#'Certificate'.tbsCertificate, + {ok, TBS#'TBSCertificate'.subject}; + false -> + {ok, Cert} + end. + %%-------------------------------------------------------------------- -%% Function: peername(Socket) -> {ok, {Address, Port}} | {error, Reason} +-spec peername(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% -%% Description: +%% Description: same as inet:peername/1. %%-------------------------------------------------------------------- peername(#sslsocket{fd = new_ssl, pid = Pid}) -> ssl_connection:peername(Pid); @@ -341,9 +360,10 @@ peername(#sslsocket{} = Socket) -> ssl_broker:peername(Socket). %%-------------------------------------------------------------------- -%% Function: cipher_suites() -> -%% -%% Description: +-spec cipher_suites() -> [erl_cipher_suite()]. +-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. + +%% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- cipher_suites() -> cipher_suites(erlang). @@ -357,7 +377,7 @@ cipher_suites(openssl) -> [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]. %%-------------------------------------------------------------------- -%% Function: getopts(Socket, OptTags) -> {ok, Options} | {error, Reason} +-spec getopts(#sslsocket{}, [atom()]) -> {ok, [{atom(), term()}]}| {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -370,7 +390,7 @@ getopts(#sslsocket{} = Socket, Options) -> ssl_broker:getopts(Socket, Options). %%-------------------------------------------------------------------- -%% Function: setopts(Socket, Options) -> ok | {error, Reason} +-spec setopts(#sslsocket{}, [{atom(), term()}]) -> ok | {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -385,18 +405,18 @@ setopts(#sslsocket{} = Socket, Options) -> ssl_broker:setopts(Socket, Options). %%--------------------------------------------------------------- -%% Function: shutdown(Socket, How) -> ok | {error, Reason} -%% +-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. +%% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- -shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _}}}, fd = new_ssl}, How) -> +shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}, How) -> CbMod:shutdown(ListenSocket, How); shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) -> ssl_connection:shutdown(Pid, How). %%-------------------------------------------------------------------- -%% Function: sockname(Socket) -> {ok, {Address, Port}} | {error, Reason} -%% +-spec sockname(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. +%% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}) -> @@ -410,9 +430,9 @@ sockname(#sslsocket{} = Socket) -> ssl_broker:sockname(Socket). %%--------------------------------------------------------------- -%% Function: seed(Data) -> ok | {error, edata} +-spec seed(term()) ->term(). %% -%% Description: +%% Description: Only used by old ssl. %%-------------------------------------------------------------------- %% TODO: crypto:seed ? seed(Data) -> @@ -420,20 +440,17 @@ seed(Data) -> ssl_server:seed(Data). %%--------------------------------------------------------------- -%% Function: session_id(Socket) -> {ok, PropList} | {error, Reason} +-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. %% -%% Description: +%% Description: Returns list of session info currently [{session_id, session_id(), +%% {cipher_suite, cipher_suite()}] %%-------------------------------------------------------------------- session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:session_info(Pid). %%--------------------------------------------------------------- -%% Function: versions() -> [{SslAppVer, SupportedSslVer, AvailableSslVsn}] -%% -%% SslAppVer = string() - t.ex: ssl-4.0 -%% SupportedSslVer = [SslVer] -%% AvailableSslVsn = [SSLVer] -%% SSLVer = sslv3 | tlsv1 | 'tlsv1.1' +-spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} | + {available, [tls_atom_version()]}]. %% %% Description: Returns a list of relevant versions. %%-------------------------------------------------------------------- @@ -444,9 +461,105 @@ versions() -> [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. +%%--------------------------------------------------------------- +-spec renegotiate(#sslsocket{}) -> ok | {error, reason()}. +%% +%% Description: Initiates a renegotiation. +%%-------------------------------------------------------------------- renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:renegotiation(Pid). +%%--------------------------------------------------------------- +-spec format_error({error, term()}) -> list(). +%% +%% Description: Creates error string. +%%-------------------------------------------------------------------- +format_error({error, Reason}) -> + format_error(Reason); +format_error(Reason) when is_list(Reason) -> + Reason; +format_error(closed) -> + "The connection is closed"; +format_error(ecacertfile) -> + "Own CA certificate file is invalid."; +format_error(ecertfile) -> + "Own certificate file is invalid."; +format_error(ekeyfile) -> + "Own private key file is invalid."; +format_error(esslaccept) -> + "Server SSL handshake procedure between client and server failed."; +format_error(esslconnect) -> + "Client SSL handshake procedure between client and server failed."; +format_error({eoptions, Options}) -> + lists:flatten(io_lib:format("Error in options list: ~p~n", [Options])); + +%%%%%%%%%%%% START OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +format_error(ebadsocket) -> + "Connection not found (internal error)."; +format_error(ebadstate) -> + "Connection not in connect state (internal error)."; +format_error(ebrokertype) -> + "Wrong broker type (internal error)."; +format_error(echaintoolong) -> + "The chain of certificates provided by peer is too long."; +format_error(ecipher) -> + "Own list of specified ciphers is invalid."; +format_error(ekeymismatch) -> + "Own private key does not match own certificate."; +format_error(enoissuercert) -> + "Cannot find certificate of issuer of certificate provided by peer."; +format_error(enoservercert) -> + "Attempt to do accept without having set own certificate."; +format_error(enotlistener) -> + "Attempt to accept on a non-listening socket."; +format_error(enoproxysocket) -> + "No proxy socket found (internal error or max number of file " + "descriptors exceeded)."; +format_error(enooptions) -> + "List of options is empty."; +format_error(enotstarted) -> + "The SSL application has not been started."; +format_error(eoptions) -> + "Invalid list of options."; +format_error(epeercert) -> + "Certificate provided by peer is in error."; +format_error(epeercertexpired) -> + "Certificate provided by peer has expired."; +format_error(epeercertinvalid) -> + "Certificate provided by peer is invalid."; +format_error(eselfsignedcert) -> + "Certificate provided by peer is self signed."; +format_error(esslerrssl) -> + "SSL protocol failure. Typically because of a fatal alert from peer."; +format_error(ewantconnect) -> + "Protocol wants to connect, which is not supported in this " + "version of the SSL application."; +format_error(ex509lookup) -> + "Protocol wants X.509 lookup, which is not supported in this " + "version of the SSL application."; +format_error({badcall, _Call}) -> + "Call not recognized for current mode (active or passive) and state " + "of socket."; +format_error({badcast, _Cast}) -> + "Call not recognized for current mode (active or passive) and state " + "of socket."; + +format_error({badinfo, _Info}) -> + "Call not recognized for current mode (active or passive) and state " + "of socket."; + +%%%%%%%%%%%%%%%%%% END OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +format_error(Error) -> + case (catch inet:format_error(Error)) of + "unkknown POSIX" ++ _ -> + no_format(Error); + {'EXIT', _} -> + no_format(Error); + Other -> + Other + end. + %%%-------------------------------------------------------------- %%% Internal functions %%%-------------------------------------------------------------------- @@ -463,7 +576,7 @@ do_new_connect(Address, Port, #config{cb=CbInfo, inet_user=UserOpts, ssl=SslOpts, emulated=EmOpts,inet_ssl=SocketOpts}, Timeout) -> - {CbModule, _, _} = CbInfo, + {CbModule, _, _, _} = CbInfo, try CbModule:connect(Address, Port, SocketOpts, Timeout) of {ok, Socket} -> ssl_connection:connect(Address, Port, Socket, {SslOpts,EmOpts}, @@ -485,7 +598,7 @@ old_connect(Address, Port, Options, Timeout) -> new_listen(Port, Options0) -> try {ok, Config} = handle_options(Options0, server), - #config{cb={CbModule, _, _},inet_user=Options} = Config, + #config{cb={CbModule, _, _, _},inet_user=Options} = Config, case CbModule:listen(Port, Options) of {ok, ListenSocket} -> {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}}; @@ -502,75 +615,85 @@ old_listen(Port, Options) -> {ok, Pid} = ssl_broker:start_broker(listener), ssl_broker:listen(Pid, Port, Options). -handle_options(Opts0, Role) -> +handle_options(Opts0, _Role) -> Opts = proplists:expand([{binary, [{mode, binary}]}, {list, [{mode, list}]}], Opts0), ReuseSessionFun = fun(_, _, _, _) -> true end, - AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; - (Other, Acc) -> [Other | Acc] - end, - - VerifyFun = - fun(ErrorList) -> - case lists:foldl(AcceptBadCa, [], ErrorList) of - [] -> true; - [_|_] -> false - end - end, + DefaultVerifyNoneFun = + {fun(_,{bad_cert, _}, UserState) -> + {valid, UserState}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun), - UserFailIfNoPeerCert = validate_option(fail_if_no_peer_cert, - proplists:get_value(fail_if_no_peer_cert, Opts, false)), + UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false), + UserVerifyFun = handle_option(verify_fun, Opts, undefined), + CaCerts = handle_option(cacerts, Opts, undefined), - {Verify, FailIfNoPeerCert, CaCertDefault} = + {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = %% Handle 0, 1, 2 for backwards compatibility case proplists:get_value(verify, Opts, verify_none) of 0 -> - {verify_none, false, ca_cert_default(verify_none, Role)}; + {verify_none, false, + ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun}; 1 -> - {verify_peer, false, ca_cert_default(verify_peer, Role)}; + {verify_peer, false, + ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; 2 -> - {verify_peer, true, ca_cert_default(verify_peer, Role)}; + {verify_peer, true, + ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; verify_none -> - {verify_none, false, ca_cert_default(verify_none, Role)}; + {verify_none, false, + ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun}; verify_peer -> - {verify_peer, UserFailIfNoPeerCert, ca_cert_default(verify_peer, Role)}; + {verify_peer, UserFailIfNoPeerCert, + ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun}; Value -> throw({error, {eoptions, {verify, Value}}}) - end, + end, CertFile = handle_option(certfile, Opts, ""), SSLOptions = #ssl_options{ versions = handle_option(versions, Opts, []), verify = validate_option(verify, Verify), - verify_fun = handle_option(verify_fun, Opts, VerifyFun), + verify_fun = VerifyFun, fail_if_no_peer_cert = FailIfNoPeerCert, verify_client_once = handle_option(verify_client_once, Opts, false), - validate_extensions_fun = handle_option(validate_extensions_fun, Opts, undefined), depth = handle_option(depth, Opts, 1), + cert = handle_option(cert, Opts, undefined), certfile = CertFile, - keyfile = handle_option(keyfile, Opts, CertFile), key = handle_option(key, Opts, undefined), + keyfile = handle_option(keyfile, Opts, CertFile), password = handle_option(password, Opts, ""), + cacerts = CaCerts, cacertfile = handle_option(cacertfile, Opts, CaCertDefault), + dh = handle_option(dh, Opts, undefined), dhfile = handle_option(dhfile, Opts, undefined), ciphers = handle_option(ciphers, Opts, []), %% Server side option reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), reuse_sessions = handle_option(reuse_sessions, Opts, true), + secure_renegotiate = handle_option(secure_renegotiate, Opts, false), renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT), debug = handle_option(debug, Opts, []) }, - CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed}), - SslOptions = [versions, verify, verify_fun, validate_extensions_fun, + CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}), + SslOptions = [versions, verify, verify_fun, fail_if_no_peer_cert, verify_client_once, - depth, certfile, keyfile, - key, password, cacertfile, dhfile, ciphers, + depth, cert, certfile, key, keyfile, + password, cacerts, cacertfile, dh, dhfile, ciphers, debug, reuse_session, reuse_sessions, ssl_imp, - cb_info, renegotiate_at], + cb_info, renegotiate_at, secure_renegotiate], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) @@ -592,7 +715,25 @@ validate_option(ssl_imp, Value) when Value == new; Value == old -> validate_option(verify, Value) when Value == verify_none; Value == verify_peer -> Value; -validate_option(verify_fun, Value) when is_function(Value) -> +validate_option(verify_fun, undefined) -> + undefined; +%% Backwards compatibility +validate_option(verify_fun, Fun) when is_function(Fun) -> + {fun(_,{bad_cert, _} = Reason, OldFun) -> + case OldFun([Reason]) of + true -> + {valid, OldFun}; + false -> + {fail, Reason} + end; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, Fun}; +validate_option(verify_fun, {Fun, _} = Value) when is_function(Fun) -> Value; validate_option(fail_if_no_peer_cert, Value) when Value == true; Value == false -> @@ -600,29 +741,38 @@ validate_option(fail_if_no_peer_cert, Value) validate_option(verify_client_once, Value) when Value == true; Value == false -> Value; - -validate_option(validate_extensions_fun, Value) when Value == undefined; is_function(Value) -> - Value; validate_option(depth, Value) when is_integer(Value), Value >= 0, Value =< 255-> Value; -validate_option(certfile, Value) when is_list(Value) -> +validate_option(cert, Value) when Value == undefined; + is_binary(Value) -> Value; -validate_option(keyfile, Value) when is_list(Value) -> +validate_option(certfile, Value) when Value == undefined; is_list(Value) -> Value; -validate_option(key, Value) when Value == undefined; - is_tuple(Value) -> - %% element(1, Value)=='RSAPrivateKey' -> + +validate_option(key, undefined) -> + undefined; +validate_option(key, {KeyType, Value}) when is_binary(Value), + KeyType == rsa; + KeyType == dsa -> + {KeyType, Value}; +validate_option(keyfile, Value) when is_list(Value) -> Value; validate_option(password, Value) when is_list(Value) -> Value; +validate_option(cacerts, Value) when Value == undefined; + is_list(Value) -> + Value; %% certfile must be present in some cases otherwhise it can be set %% to the empty string. validate_option(cacertfile, undefined) -> ""; validate_option(cacertfile, Value) when is_list(Value), Value =/= "" -> Value; +validate_option(dh, Value) when Value == undefined; + is_binary(Value) -> + Value; validate_option(dhfile, undefined = Value) -> Value; validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> @@ -641,8 +791,12 @@ validate_option(reuse_session, Value) when is_function(Value) -> validate_option(reuse_sessions, Value) when Value == true; Value == false -> Value; + +validate_option(secure_renegotiate, Value) when Value == true; + Value == false -> + Value; validate_option(renegotiate_at, Value) when is_integer(Value) -> - min(Value, ?DEFAULT_RENEGOTIATE_AT); + erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT); validate_option(debug, Value) when is_list(Value); Value == true -> Value; @@ -676,14 +830,16 @@ validate_inet_option(active, Value) validate_inet_option(_, _) -> ok. -ca_cert_default(verify_none, _) -> +%% The option cacerts overrides cacertsfile +ca_cert_default(_,_, [_|_]) -> undefined; -%% Client may leave verification up to the user -ca_cert_default(verify_peer, client) -> +ca_cert_default(verify_none, _, _) -> undefined; -%% Server that wants to verify_peer must have +ca_cert_default(verify_peer, {Fun,_}, _) when is_function(Fun) -> + undefined; +%% Server that wants to verify_peer and has no verify_fun must have %% some trusted certs. -ca_cert_default(verify_peer, server) -> +ca_cert_default(verify_peer, undefined, _) -> "". emulated_options() -> @@ -727,11 +883,14 @@ emulated_options([], Inet,Emulated) -> cipher_suites(Version, []) -> ssl_cipher:suites(Version); -cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> +cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility + Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0], + cipher_suites(Version, Ciphers); +cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0], cipher_suites(Version, Ciphers); cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> - Supported = ssl_cipher:suites(Version), + Supported = ssl_cipher:suites(Version) ++ ssl_cipher:anonymous_suites(), case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of [] -> Supported; @@ -747,85 +906,8 @@ cipher_suites(Version, Ciphers0) -> Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")], cipher_suites(Version, Ciphers). -format_error({error, Reason}) -> - format_error(Reason); -format_error(closed) -> - "Connection closed for the operation in question."; -format_error(ebadsocket) -> - "Connection not found (internal error)."; -format_error(ebadstate) -> - "Connection not in connect state (internal error)."; -format_error(ebrokertype) -> - "Wrong broker type (internal error)."; -format_error(ecacertfile) -> - "Own CA certificate file is invalid."; -format_error(ecertfile) -> - "Own certificate file is invalid."; -format_error(echaintoolong) -> - "The chain of certificates provided by peer is too long."; -format_error(ecipher) -> - "Own list of specified ciphers is invalid."; -format_error(ekeyfile) -> - "Own private key file is invalid."; -format_error(ekeymismatch) -> - "Own private key does not match own certificate."; -format_error(enoissuercert) -> - "Cannot find certificate of issuer of certificate provided by peer."; -format_error(enoservercert) -> - "Attempt to do accept without having set own certificate."; -format_error(enotlistener) -> - "Attempt to accept on a non-listening socket."; -format_error(enoproxysocket) -> - "No proxy socket found (internal error or max number of file " - "descriptors exceeded)."; -format_error(enooptions) -> - "List of options is empty."; -format_error(enotstarted) -> - "The SSL application has not been started."; -format_error(eoptions) -> - "Invalid list of options."; -format_error(epeercert) -> - "Certificate provided by peer is in error."; -format_error(epeercertexpired) -> - "Certificate provided by peer has expired."; -format_error(epeercertinvalid) -> - "Certificate provided by peer is invalid."; -format_error(eselfsignedcert) -> - "Certificate provided by peer is self signed."; -format_error(esslaccept) -> - "Server SSL handshake procedure between client and server failed."; -format_error(esslconnect) -> - "Client SSL handshake procedure between client and server failed."; -format_error(esslerrssl) -> - "SSL protocol failure. Typically because of a fatal alert from peer."; -format_error(ewantconnect) -> - "Protocol wants to connect, which is not supported in this " - "version of the SSL application."; -format_error(ex509lookup) -> - "Protocol wants X.509 lookup, which is not supported in this " - "version of the SSL application."; -format_error({badcall, _Call}) -> - "Call not recognized for current mode (active or passive) and state " - "of socket."; -format_error({badcast, _Cast}) -> - "Call not recognized for current mode (active or passive) and state " - "of socket."; - -format_error({badinfo, _Info}) -> - "Call not recognized for current mode (active or passive) and state " - "of socket."; -format_error(Error) -> - case (catch inet:format_error(Error)) of - "unkknown POSIX" ++ _ -> - no_format(Error); - {'EXIT', _} -> - no_format(Error); - Other -> - Other - end. - no_format(Error) -> - io_lib:format("No format string for error: \"~p\" available.", [Error]). + lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])). %% Start old ssl port program if needed. ensure_old_ssl_started() -> @@ -860,10 +942,6 @@ version() -> end, {ok, {SSLVsn, CompVsn, LibVsn}}. -min(N,M) when N < M -> - N; -min(_, M) -> - M. %% Only used to remove exit messages from old ssl %% First is a nonsense clause to provide some diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index d3f9c833f1..eb1228afa4 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -32,76 +32,87 @@ -export([alert_txt/1, reason_code/2]). +%%==================================================================== +%% Internal application API +%%==================================================================== +%%-------------------------------------------------------------------- +-spec reason_code(#alert{}, client | server) -> closed | esslconnect | + esslaccept | string(). +%% +%% Description: Returns the error reason that will be returned to the +%% user. +%%-------------------------------------------------------------------- + reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> closed; reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) -> esslconnect; reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) -> esslaccept; -reason_code(#alert{description = ?CERTIFICATE_EXPIRED}, _) -> - epeercertexpired; -reason_code(#alert{level = ?FATAL}, _) -> - esslerrssl; reason_code(#alert{description = Description}, _) -> description_txt(Description). +%%-------------------------------------------------------------------- +-spec alert_txt(#alert{}) -> string(). +%% +%% Description: Returns the error string for given alert. +%%-------------------------------------------------------------------- + alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}}) -> Mod ++ ":" ++ integer_to_list(Line) ++ ":" ++ level_txt(Level) ++" "++ description_txt(Description). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- level_txt(?WARNING) -> "Warning:"; level_txt(?FATAL) -> "Fatal error:". description_txt(?CLOSE_NOTIFY) -> - "close_notify"; + "close notify"; description_txt(?UNEXPECTED_MESSAGE) -> - "unexpected_message"; + "unexpected message"; description_txt(?BAD_RECORD_MAC) -> - "bad_record_mac"; + "bad record mac"; description_txt(?DECRYPTION_FAILED) -> - "decryption_failed"; + "decryption failed"; description_txt(?RECORD_OVERFLOW) -> - "record_overflow"; + "record overflow"; description_txt(?DECOMPRESSION_FAILURE) -> - "decompression_failure"; + "decompression failure"; description_txt(?HANDSHAKE_FAILURE) -> - "handshake_failure"; + "handshake failure"; description_txt(?BAD_CERTIFICATE) -> - "bad_certificate"; + "bad certificate"; description_txt(?UNSUPPORTED_CERTIFICATE) -> - "unsupported_certificate"; + "unsupported certificate"; description_txt(?CERTIFICATE_REVOKED) -> - "certificate_revoked"; + "certificate revoked"; description_txt(?CERTIFICATE_EXPIRED) -> - "certificate_expired"; + "certificate expired"; description_txt(?CERTIFICATE_UNKNOWN) -> - "certificate_unknown"; + "certificate unknown"; description_txt(?ILLEGAL_PARAMETER) -> - "illegal_parameter"; + "illegal parameter"; description_txt(?UNKNOWN_CA) -> - "unknown_ca"; + "unknown ca"; description_txt(?ACCESS_DENIED) -> - "access_denied"; + "access denied"; description_txt(?DECODE_ERROR) -> - "decode_error"; + "decode error"; description_txt(?DECRYPT_ERROR) -> - "decrypt_error"; + "decrypt error"; description_txt(?EXPORT_RESTRICTION) -> - "export_restriction"; + "export restriction"; description_txt(?PROTOCOL_VERSION) -> - "protocol_version"; + "protocol version"; description_txt(?INSUFFICIENT_SECURITY) -> - "insufficient_security"; + "insufficient security"; description_txt(?INTERNAL_ERROR) -> - "internal_error"; + "internal error"; description_txt(?USER_CANCELED) -> - "user_canceled"; + "user canceled"; description_txt(?NO_RENEGOTIATION) -> - "no_renegotiation". - - - - - + "no renegotiation". diff --git a/lib/ssl/src/ssl_app.erl b/lib/ssl/src/ssl_app.erl index 6ca1c42631..8d50fd7bdb 100644 --- a/lib/ssl/src/ssl_app.erl +++ b/lib/ssl/src/ssl_app.erl @@ -27,14 +27,16 @@ -export([start/2, stop/1]). -%% start/2(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} | -%% {error, Reason} -%% +%%-------------------------------------------------------------------- +-spec start(normal | {takeover, node()} | {failover, node()}, list()) -> + ignore | {ok, pid()} | {error, term()}. +%%-------------------------------------------------------------------- start(_Type, _StartArgs) -> ssl_sup:start_link(). -%% stop(State) -> void() -%% +%-------------------------------------------------------------------- +-spec stop(term())-> ok. +%%-------------------------------------------------------------------- stop(_State) -> ok. diff --git a/lib/ssl/src/ssl_base64.erl b/lib/ssl/src/ssl_base64.erl deleted file mode 100644 index cfc42407e8..0000000000 --- a/lib/ssl/src/ssl_base64.erl +++ /dev/null @@ -1,129 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. 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% -%% - -%% - -%%% Purpose : Base 64 encoding and decoding. - --module(ssl_base64). - --export([encode/1, encode_split/1, decode/1, join_decode/1]). - --define(st(X,A), ((X-A+256) div 256)). --define(CHARS, 64). - -%% A PEM encoding consists of characters A-Z, a-z, 0-9, +, / and -%% =. Each character encodes a 6 bits value from 0 to 63 (A = 0, / = -%% 63); = is a padding character. -%% - -%% -%% encode(Bytes|Binary) -> Chars -%% -%% Take 3 bytes a time (3 x 8 = 24 bits), and make 4 characters out of -%% them (4 x 6 = 24 bits). -%% -encode(Bs) when is_list(Bs) -> - encode(list_to_binary(Bs)); -encode(<<B:3/binary, Bs/binary>>) -> - <<C1:6, C2:6, C3:6, C4:6>> = B, - [enc(C1), enc(C2), enc(C3), enc(C4)| encode(Bs)]; -encode(<<B:2/binary>>) -> - <<C1:6, C2:6, C3:6, _:6>> = <<B/binary, 0>>, - [enc(C1), enc(C2), enc(C3), $=]; -encode(<<B:1/binary>>) -> - <<C1:6, C2:6, _:12>> = <<B/binary, 0, 0>>, - [enc(C1), enc(C2), $=, $=]; -encode(<<>>) -> - []. - -%% -%% encode_split(Bytes|Binary) -> Lines -%% -%% The encoding is divided into lines separated by <NL>, and each line -%% is precisely 64 characters long (excluding the <NL> characters, -%% except the last line which 64 characters long or shorter. <NL> may -%% follow the last line. -%% -encode_split(Bs) -> - split(encode(Bs)). - -%% -%% decode(Chars) -> Binary -%% -decode(Cs) -> - list_to_binary(decode1(Cs)). - -decode1([C1, C2, $=, $=]) -> - <<B1, _:16>> = <<(dec(C1)):6, (dec(C2)):6, 0:12>>, - [B1]; -decode1([C1, C2, C3, $=]) -> - <<B1, B2, _:8>> = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(0)):6>>, - [B1, B2]; -decode1([C1, C2, C3, C4| Cs]) -> - Bin = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(C4)):6>>, - [Bin| decode1(Cs)]; -decode1([]) -> - []. - -%% -%% join_decode(Lines) -> Binary -%% -%% Remove <NL> before decoding. -%% -join_decode(Cs) -> - decode(join(Cs)). - -%% -%% Locals -%% - -%% enc/1 and dec/1 -%% -%% Mapping: 0-25 -> A-Z, 26-51 -> a-z, 52-61 -> 0-9, 62 -> +, 63 -> / -%% -enc(C) -> - 65 + C + 6*?st(C,26) - 75*?st(C,52) -15*?st(C,62) + 3*?st(C,63). - -dec(C) -> - 62*?st(C,43) + ?st(C,47) + (C-59)*?st(C,48) - 69*?st(C,65) - 6*?st(C,97). - -%% split encoding into lines -%% -split(Cs) -> - split(Cs, ?CHARS). - -split([], _N) -> - [$\n]; -split(Cs, 0) -> - [$\n| split(Cs, ?CHARS)]; -split([C| Cs], N) -> - [C| split(Cs, N-1)]. - -%% join lines of encodings -%% -join([$\r, $\n| Cs]) -> - join(Cs); -join([$\n| Cs]) -> - join(Cs); -join([C| Cs]) -> - [C| join(Cs)]; -join([]) -> - []. - diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 686e90a70c..8c0c2bfa5d 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -28,91 +28,155 @@ -include("ssl_handshake.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). --include("ssl_debug.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([trusted_cert_and_path/3, +-export([trusted_cert_and_path/2, certificate_chain/2, file_to_certificats/1, - validate_extensions/6]). + validate_extension/3, + is_valid_extkey_usage/2, + is_valid_key_usage/2, + select_extension/2, + extensions_list/1, + signature_type/1 + ]). %%==================================================================== %% Internal application API %%==================================================================== -trusted_cert_and_path(CertChain, CertDbRef, Verify) -> - [Cert | RestPath] = lists:reverse(CertChain), - {ok, OtpCert} = public_key:pkix_decode_cert(Cert, otp), - IssuerAnPath = +%%-------------------------------------------------------------------- +-spec trusted_cert_and_path([der_cert()], certdb_ref()) -> + {der_cert() | unknown_ca, [der_cert()]}. +%% +%% Description: Extracts the root cert (if not presents tries to +%% look it up, if not found {bad_cert, unknown_ca} will be added verification +%% errors. Returns {RootCert, Path, VerifyErrors} +%%-------------------------------------------------------------------- +trusted_cert_and_path(CertChain, CertDbRef) -> + Path = [Cert | _] = lists:reverse(CertChain), + OtpCert = public_key:pkix_decode_cert(Cert, otp), + SignedAndIssuerID = case public_key:pkix_is_self_signed(OtpCert) of true -> {ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self), - {IssuerId, RestPath}; - false -> + {self, IssuerId}; + false -> case public_key:pkix_issuer_id(OtpCert, other) of {ok, IssuerId} -> - {IssuerId, [Cert | RestPath]}; + {other, IssuerId}; {error, issuer_not_found} -> case find_issuer(OtpCert, no_candidate) of {ok, IssuerId} -> - {IssuerId, [Cert | RestPath]}; + {other, IssuerId}; Other -> - {Other, RestPath} + Other end end end, - case IssuerAnPath of - {{error, issuer_not_found}, _ } -> - %% The root CA was not sent and can not be found, we fail if verify = true - not_valid(?ALERT_REC(?FATAL, ?UNKNOWN_CA), Verify, {Cert, RestPath}); - {{SerialNr, Issuer}, Path} -> - case ssl_certificate_db:lookup_trusted_cert(CertDbRef, - SerialNr, Issuer) of + case SignedAndIssuerID of + {error, issuer_not_found} -> + %% The root CA was not sent and can not be found. + {unknown_ca, Path}; + {self, _} when length(Path) == 1 -> + {selfsigned_peer, Path}; + {_ ,{SerialNr, Issuer}} -> + case ssl_manager:lookup_trusted_cert(CertDbRef, SerialNr, Issuer) of {ok, {BinCert,_}} -> - {BinCert, Path, []}; + {BinCert, Path}; _ -> - %% Fail if verify = true - not_valid(?ALERT_REC(?FATAL, ?UNKNOWN_CA), - Verify, {Cert, RestPath}) + %% Root CA could not be verified + {unknown_ca, Path} end end. - +%%-------------------------------------------------------------------- +-spec certificate_chain(undefined | binary(), certdb_ref()) -> + {error, no_cert} | {ok, [der_cert()]}. +%% +%% Description: Return the certificate chain to send to peer. +%%-------------------------------------------------------------------- certificate_chain(undefined, _CertsDbRef) -> {error, no_cert}; certificate_chain(OwnCert, CertsDbRef) -> - {ok, ErlCert} = public_key:pkix_decode_cert(OwnCert, otp), + ErlCert = public_key:pkix_decode_cert(OwnCert, otp), certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]). - -file_to_certificats(File) -> +%%-------------------------------------------------------------------- +-spec file_to_certificats(string()) -> [der_cert()]. +%% +%% Description: Return list of DER encoded certificates. +%%-------------------------------------------------------------------- +file_to_certificats(File) -> {ok, List} = ssl_manager:cache_pem_file(File), - [Bin || {cert, Bin, not_encrypted} <- List]. - - -%% Validates ssl/tls specific extensions -validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) -> - {UnknownExtensions, ValidationState, AccErr}; - -validate_extensions([#'Extension'{extnID = ?'id-ce-extKeyUsage', - extnValue = KeyUse, - critical = true} | Rest], - ValidationState, UnknownExtensions, Verify, AccErr0, Role) -> + [Bin || {'Certificate', Bin, not_encrypted} <- List]. +%%-------------------------------------------------------------------- +-spec validate_extension(term(), #'Extension'{} | {bad_cert, atom()} | valid, + term()) -> {valid, term()} | + {fail, tuple()} | + {unknown, term()}. +%% +%% Description: Validates ssl/tls specific extensions +%%-------------------------------------------------------------------- +validate_extension(_,{extension, #'Extension'{extnID = ?'id-ce-extKeyUsage', + extnValue = KeyUse}}, Role) -> case is_valid_extkey_usage(KeyUse, Role) of true -> - validate_extensions(Rest, ValidationState, UnknownExtensions, - Verify, AccErr0, Role); + {valid, Role}; false -> - AccErr = - not_valid_extension({bad_cert, invalid_ext_key_usage}, Verify, AccErr0), - validate_extensions(Rest, ValidationState, UnknownExtensions, Verify, AccErr, Role) + {fail, {bad_cert, invalid_ext_key_usage}} end; +validate_extension(_, {bad_cert, _} = Reason, _) -> + {fail, Reason}; +validate_extension(_, {extension, _}, Role) -> + {unknown, Role}; +validate_extension(_, valid, Role) -> + {valid, Role}; +validate_extension(_, valid_peer, Role) -> + {valid, Role}. + +%%-------------------------------------------------------------------- +-spec is_valid_key_usage(list(), term()) -> boolean(). +%% +%% Description: Checks if Use is a valid key usage. +%%-------------------------------------------------------------------- +is_valid_key_usage(KeyUse, Use) -> + lists:member(Use, KeyUse). + +%%-------------------------------------------------------------------- +-spec select_extension(term(), list()) -> undefined | #'Extension'{}. +%% +%% Description: Selects the extension identified by Id if present in +%% a list of extensions. +%%-------------------------------------------------------------------- +select_extension(_, []) -> + undefined; +select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) -> + Extension; +select_extension(Id, [_ | Extensions]) -> + select_extension(Id, Extensions). + +%%-------------------------------------------------------------------- +-spec extensions_list(asn1_NOVALUE | list()) -> list(). +%% +%% Description: Handles that +%%-------------------------------------------------------------------- +extensions_list(asn1_NOVALUE) -> + []; +extensions_list(Extensions) -> + Extensions. + +%%-------------------------------------------------------------------- +-spec signature_type(term()) -> rsa | dsa . +%% +%% Description: +%%-------------------------------------------------------------------- +signature_type(RSA) when RSA == ?sha1WithRSAEncryption; + RSA == ?md5WithRSAEncryption -> + rsa; +signature_type(?'id-dsa-with-sha1') -> + dsa. -validate_extensions([Extension | Rest], ValidationState, UnknownExtensions, - Verify, AccErr, Role) -> - validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions], - Verify, AccErr, Role). - %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -148,10 +212,10 @@ certificate_chain(_CertsDbRef, Chain, _SerialNr, _Issuer, true) -> {ok, lists:reverse(Chain)}; certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> - case ssl_certificate_db:lookup_trusted_cert(CertsDbRef, + case ssl_manager:lookup_trusted_cert(CertsDbRef, SerialNr, Issuer) of {ok, {IssuerCert, ErlCert}} -> - {ok, ErlCert} = public_key:pkix_decode_cert(IssuerCert, otp), + ErlCert = public_key:pkix_decode_cert(IssuerCert, otp), certificate_chain(ErlCert, IssuerCert, CertsDbRef, [IssuerCert | Chain]); _ -> @@ -164,7 +228,7 @@ certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> end. find_issuer(OtpCert, PrevCandidateKey) -> - case ssl_certificate_db:issuer_candidate(PrevCandidateKey) of + case ssl_manager:issuer_candidate(PrevCandidateKey) of no_more_candidates -> {error, issuer_not_found}; {Key, {_Cert, ErlCertCandidate}} -> @@ -176,22 +240,9 @@ find_issuer(OtpCert, PrevCandidateKey) -> end end. -not_valid(Alert, true, _) -> - throw(Alert); -not_valid(_, false, {ErlCert, Path}) -> - {ErlCert, Path, [{bad_cert, unknown_ca}]}. - is_valid_extkey_usage(KeyUse, client) -> %% Client wants to verify server is_valid_key_usage(KeyUse,?'id-kp-serverAuth'); is_valid_extkey_usage(KeyUse, server) -> %% Server wants to verify client is_valid_key_usage(KeyUse, ?'id-kp-clientAuth'). - -is_valid_key_usage(KeyUse, Use) -> - lists:member(Use, KeyUse). - -not_valid_extension(Error, true, _) -> - throw(Error); -not_valid_extension(Error, false, AccErrors) -> - [Error | AccErrors]. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index b8c3c6f6b7..2a5a7f3394 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- -module(ssl_certificate_db). - +-include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). -export([create/0, remove/1, add_trusted_certs/3, @@ -34,8 +34,7 @@ %%==================================================================== %%-------------------------------------------------------------------- -%% Function: create() -> Db -%% Db = term() - Reference to the crated database +-spec create() -> certdb_ref(). %% %% Description: Creates a new certificate db. %% Note: lookup_trusted_cert/3 may be called from any process but only @@ -47,8 +46,7 @@ create() -> ets:new(ssl_pid_to_file, [bag, private])]. %%-------------------------------------------------------------------- -%% Function: delete(Db) -> _ -%% Db = Database refererence as returned by create/0 +-spec remove(certdb_ref()) -> term(). %% %% Description: Removes database db %%-------------------------------------------------------------------- @@ -56,11 +54,9 @@ remove(Dbs) -> lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). %%-------------------------------------------------------------------- -%% Function: lookup_trusted_cert(Ref, SerialNumber, Issuer) -> {BinCert,DecodedCert} -%% Ref = ref() -%% SerialNumber = integer() -%% Issuer = {rdnSequence, IssuerAttrs} -%% BinCert = binary() +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> + undefined | {ok, {der_cert(), #'OTPCertificate'{}}}. + %% %% Description: Retrives the trusted certificate identified by %% <SerialNumber, Issuer>. Ref is used as it is specified @@ -78,16 +74,16 @@ lookup_cached_certs(File) -> ets:lookup(certificate_db_name(), {file, File}). %%-------------------------------------------------------------------- -%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref} -%% Pid = pid() -%% File = string() -%% Db = Database refererence as returned by create/0 -%% Ref = ref() +-spec add_trusted_certs(pid(), string() | {der, list()}, certdb_ref()) -> {ok, certdb_ref()}. %% %% Description: Adds the trusted certificates from file <File> to the %% runtime database. Returns Ref that should be handed to lookup_trusted_cert %% together with the cert serialnumber and issuer. %%-------------------------------------------------------------------- +add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) -> + NewRef = make_ref(), + add_certs_from_der(DerList, NewRef, CerDb), + {ok, NewRef}; add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> Ref = case lookup(File, FileToRefDb) of undefined -> @@ -101,20 +97,21 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> end, insert(Pid, File, PidToFileDb), {ok, Ref}. - %%-------------------------------------------------------------------- -%% Function: cache_pem_file(Pid, File, Db) -> FileContent +-spec cache_pem_file(pid(), string(), certdb_ref()) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) -> - Res = {ok, Content} = public_key:pem_to_der(File), + {ok, PemBin} = file:read_file(File), + Content = public_key:pem_decode(PemBin), insert({file, File}, Content, CertsDb), insert(Pid, File, PidToFileDb), - Res. + {ok, Content}. %%-------------------------------------------------------------------- -%% Function: remove_trusted_certs(Pid, Db) -> _ +-spec remove_trusted_certs(pid(), certdb_ref()) -> term(). + %% %% Description: Removes trusted certs originating from %% the file associated to Pid from the runtime database. @@ -144,15 +141,13 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> end. %%-------------------------------------------------------------------- -%% Function: issuer_candidate() -> {Key, Candidate} | no_more_candidates +-spec issuer_candidate(no_candidate | cert_key() | {file, term()}) -> + {cert_key(),{der_cert(), #'OTPCertificate'{}}} | no_more_candidates. %% -%% Candidate -%% -%% %% Description: If a certificat does not define its issuer through %% the extension 'ce-authorityKeyIdentifier' we can %% try to find the issuer in the database over known -%% certificates. +%% certificates. %%-------------------------------------------------------------------- issuer_candidate(no_candidate) -> Db = certificate_db_name(), @@ -210,15 +205,26 @@ lookup(Key, Db) -> remove_certs(Ref, CertsDb) -> ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}). +add_certs_from_der(DerList, Ref, CertsDb) -> + Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, + [Add(Cert) || Cert <- DerList]. + add_certs_from_file(File, Ref, CertsDb) -> - Decode = fun(Cert) -> - {ok, ErlCert} = public_key:pkix_decode_cert(Cert, otp), - TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, - SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber, - Issuer = public_key:pkix_normalize_general_name( - TBSCertificate#'OTPTBSCertificate'.issuer), - insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb) - end, - {ok,Der} = public_key:pem_to_der(File), - [Decode(Cert) || {cert, Cert, not_encrypted} <- Der]. + Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, + {ok, PemBin} = file:read_file(File), + PemEntries = public_key:pem_decode(PemBin), + [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries]. +add_certs(Cert, Ref, CertsDb) -> + try ErlCert = public_key:pkix_decode_cert(Cert, otp), + TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, + SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber, + Issuer = public_key:pkix_normalize_name( + TBSCertificate#'OTPTBSCertificate'.issuer), + insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb) + catch + error:_ -> + Report = io_lib:format("SSL WARNING: Ignoring a CA cert as " + "it could not be correctly decoded.~n", []), + error_logger:info_report(Report) + end. diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 3d3d11b7f3..72f02a4362 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -28,27 +28,25 @@ -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). --include("ssl_debug.hrl"). +-include("ssl_alert.hrl"). +-include_lib("public_key/include/public_key.hrl"). -export([security_parameters/2, suite_definition/1, - decipher/4, cipher/4, - suite/1, suites/1, - openssl_suite/1, openssl_suite_name/1]). + decipher/5, cipher/4, + suite/1, suites/1, anonymous_suites/0, + openssl_suite/1, openssl_suite_name/1, filter/2]). -compile(inline). %%-------------------------------------------------------------------- -%% Function: security_parameters(CipherSuite, SecParams) -> -%% #security_parameters{} -%% -%% CipherSuite - as defined in ssl_cipher.hrl -%% SecParams - #security_parameters{} +-spec security_parameters(cipher_suite(), #security_parameters{}) -> + #security_parameters{}. %% %% Description: Returns a security parameters record where the %% cipher values has been updated according to <CipherSuite> %%------------------------------------------------------------------- security_parameters(CipherSuite, SecParams) -> - { _, Cipher, Hash, Exportable} = suite_definition(CipherSuite), + { _, Cipher, Hash} = suite_definition(CipherSuite), SecParams#security_parameters{ cipher_suite = CipherSuite, bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher), @@ -58,19 +56,14 @@ security_parameters(CipherSuite, SecParams) -> key_material_length = key_material(Cipher), iv_size = iv_size(Cipher), mac_algorithm = mac_algorithm(Hash), - hash_size = hash_size(Hash), - exportable = Exportable}. + hash_size = hash_size(Hash)}. %%-------------------------------------------------------------------- -%% Function: cipher(Method, CipherState, Mac, Data) -> -%% {Encrypted, UpdateCipherState} +-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) -> + {binary(), #cipher_state{}}. %% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() -%% -%% Description: Encrypts the data and the mac using method, updating -%% the cipher state +%% Description: Encrypts the data and the MAC using chipher described +%% by cipher_enum() and updating the cipher state %%------------------------------------------------------------------- cipher(?NULL, CipherState, <<>>, Fragment) -> GenStreamCipherList = [Fragment, <<>>], @@ -81,20 +74,12 @@ cipher(?RC4, CipherState, Mac, Fragment) -> S -> S end, GenStreamCipherList = [Fragment, Mac], - - ?DBG_HEX(GenStreamCipherList), - ?DBG_HEX(State0), {State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList), - ?DBG_HEX(T), {T, CipherState#cipher_state{state = State1}}; cipher(?DES, CipherState, Mac, Fragment) -> block_cipher(fun(Key, IV, T) -> crypto:des_cbc_encrypt(Key, IV, T) end, block_size(des_cbc), CipherState, Mac, Fragment); -cipher(?DES40, CipherState, Mac, Fragment) -> - block_cipher(fun(Key, IV, T) -> - crypto:des_cbc_encrypt(Key, IV, T) - end, block_size(des_cbc), CipherState, Mac, Fragment); cipher(?'3DES', CipherState, Mac, Fragment) -> block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> crypto:des3_cbc_encrypt(K1, K2, K3, IV, T) @@ -104,101 +89,94 @@ cipher(?AES, CipherState, Mac, Fragment) -> crypto:aes_cbc_128_encrypt(Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> crypto:aes_cbc_256_encrypt(Key, IV, T) - end, block_size(aes_128_cbc), CipherState, Mac, Fragment); + end, block_size(aes_128_cbc), CipherState, Mac, Fragment). %% cipher(?IDEA, CipherState, Mac, Fragment) -> %% block_cipher(fun(Key, IV, T) -> %% crypto:idea_cbc_encrypt(Key, IV, T) %% end, block_size(idea_cbc), CipherState, Mac, Fragment); -cipher(?RC2, CipherState, Mac, Fragment) -> - block_cipher(fun(Key, IV, T) -> - crypto:rc2_40_cbc_encrypt(Key, IV, T) - end, block_size(rc2_cbc_40), CipherState, Mac, Fragment). block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, Mac, Fragment) -> TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1, {PaddingLength, Padding} = get_padding(TotSz, BlockSz), L = [Fragment, Mac, PaddingLength, Padding], - ?DBG_HEX(Key), - ?DBG_HEX(IV), - ?DBG_HEX(L), T = Fun(Key, IV, L), - ?DBG_HEX(T), NextIV = next_iv(T, IV), {T, CS0#cipher_state{iv=NextIV}}. %%-------------------------------------------------------------------- -%% Function: decipher(Method, CipherState, Mac, Data) -> -%% {Decrypted, UpdateCipherState} -%% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() +-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) -> + {binary(), binary(), #cipher_state{}} | #alert{}. %% -%% Description: Decrypts the data and the mac using method, updating -%% the cipher state +%% Description: Decrypts the data and the MAC using cipher described +%% by cipher_enum() and updating the cipher state. %%------------------------------------------------------------------- -decipher(?NULL, _HashSz, CipherState, Fragment) -> +decipher(?NULL, _HashSz, CipherState, Fragment, _) -> {Fragment, <<>>, CipherState}; -decipher(?RC4, HashSz, CipherState, Fragment) -> - ?DBG_TERM(CipherState#cipher_state.key), +decipher(?RC4, HashSz, CipherState, Fragment, _) -> State0 = case CipherState#cipher_state.state of undefined -> crypto:rc4_set_key(CipherState#cipher_state.key); S -> S end, - ?DBG_HEX(State0), - ?DBG_HEX(Fragment), - {State1, T} = crypto:rc4_encrypt_with_state(State0, Fragment), - ?DBG_HEX(T), - GSC = generic_stream_cipher_from_bin(T, HashSz), - #generic_stream_cipher{content=Content, mac=Mac} = GSC, - {Content, Mac, CipherState#cipher_state{state=State1}}; -decipher(?DES, HashSz, CipherState, Fragment) -> - block_decipher(fun(Key, IV, T) -> - crypto:des_cbc_decrypt(Key, IV, T) - end, CipherState, HashSz, Fragment); -decipher(?DES40, HashSz, CipherState, Fragment) -> + try crypto:rc4_encrypt_with_state(State0, Fragment) of + {State, Text} -> + GSC = generic_stream_cipher_from_bin(Text, HashSz), + #generic_stream_cipher{content = Content, mac = Mac} = GSC, + {Content, Mac, CipherState#cipher_state{state = State}} + catch + _:_ -> + %% This is a DECRYPTION_FAILED but + %% "differentiating between bad_record_mac and decryption_failed + %% alerts may permit certain attacks against CBC mode as used in + %% TLS [CBCATT]. It is preferable to uniformly use the + %% bad_record_mac alert to hide the specific type of the error." + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; + +decipher(?DES, HashSz, CipherState, Fragment, Version) -> block_decipher(fun(Key, IV, T) -> crypto:des_cbc_decrypt(Key, IV, T) - end, CipherState, HashSz, Fragment); -decipher(?'3DES', HashSz, CipherState, Fragment) -> + end, CipherState, HashSz, Fragment, Version); +decipher(?'3DES', HashSz, CipherState, Fragment, Version) -> block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> crypto:des3_cbc_decrypt(K1, K2, K3, IV, T) - end, CipherState, HashSz, Fragment); -decipher(?AES, HashSz, CipherState, Fragment) -> + end, CipherState, HashSz, Fragment, Version); +decipher(?AES, HashSz, CipherState, Fragment, Version) -> block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> crypto:aes_cbc_128_decrypt(Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> crypto:aes_cbc_256_decrypt(Key, IV, T) - end, CipherState, HashSz, Fragment); -%% decipher(?IDEA, HashSz, CipherState, Fragment) -> + end, CipherState, HashSz, Fragment, Version). +%% decipher(?IDEA, HashSz, CipherState, Fragment, Version) -> %% block_decipher(fun(Key, IV, T) -> %% crypto:idea_cbc_decrypt(Key, IV, T) -%% end, CipherState, HashSz, Fragment); -decipher(?RC2, HashSz, CipherState, Fragment) -> - block_decipher(fun(Key, IV, T) -> - crypto:rc2_40_cbc_decrypt(Key, IV, T) - end, CipherState, HashSz, Fragment). +%% end, CipherState, HashSz, Fragment, Version); block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, - HashSz, Fragment) -> - ?DBG_HEX(Key), - ?DBG_HEX(IV), - ?DBG_HEX(Fragment), - T = Fun(Key, IV, Fragment), - ?DBG_HEX(T), - GBC = generic_block_cipher_from_bin(T, HashSz), - ok = check_padding(GBC), %% TODO kolla ocks�... - Content = GBC#generic_block_cipher.content, - Mac = GBC#generic_block_cipher.mac, - CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)}, - {Content, Mac, CipherState1}. - + HashSz, Fragment, Version) -> + try Fun(Key, IV, Fragment) of + Text -> + GBC = generic_block_cipher_from_bin(Text, HashSz), + case is_correct_padding(GBC, Version) of + true -> + Content = GBC#generic_block_cipher.content, + Mac = GBC#generic_block_cipher.mac, + CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)}, + {Content, Mac, CipherState1}; + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end + catch + _:_ -> + %% This is a DECRYPTION_FAILED but + %% "differentiating between bad_record_mac and decryption_failed + %% alerts may permit certain attacks against CBC mode as used in + %% TLS [CBCATT]. It is preferable to uniformly use the + %% bad_record_mac alert to hide the specific type of the error." + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end. %%-------------------------------------------------------------------- -%% Function: suites(Version) -> [Suite] -%% -%% Version = version() -%% Suite = binary() from ssl_cipher.hrl +-spec suites(tls_version()) -> [cipher_suite()]. %% %% Description: Returns a list of supported cipher suites. %%-------------------------------------------------------------------- @@ -208,294 +186,138 @@ suites({3, N}) when N == 1; N == 2 -> ssl_tls1:suites(). %%-------------------------------------------------------------------- -%% Function: suite_definition(CipherSuite) -> -%% {KeyExchange, Cipher, Hash, Exportable} -%% +-spec anonymous_suites() -> [cipher_suite()]. %% -%% CipherSuite - as defined in ssl_cipher.hrl -%% KeyExchange - rsa | dh_dss | dh_rsa | dh_anon | dhe_dss | dhe_rsa -%% krb5 | *_export (old ssl) -%% Cipher - null | rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc' -%% des40_cbc | dh_dss | aes_128_cbc | aes_256_cbc | -%% rc2_cbc_40 | rc4_40 -%% Hash - null | md5 | sha -%% Exportable - export | no_export | ignore(?) +%% Description: Returns a list of the anonymous cipher suites, only supported +%% if explicitly set by user. Intended only for testing. +%%-------------------------------------------------------------------- +anonymous_suites() -> + [?TLS_DH_anon_WITH_RC4_128_MD5, + ?TLS_DH_anon_WITH_DES_CBC_SHA, + ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, + ?TLS_DH_anon_WITH_AES_128_CBC_SHA, + ?TLS_DH_anon_WITH_AES_256_CBC_SHA]. + +%%-------------------------------------------------------------------- +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). %% -%% Description: Returns a security parameters record where the -%% cipher values has been updated according to <CipherSuite> -%% Note: since idea is unsupported on the openssl version used by -%% crypto (as of OTP R12B), we've commented away the idea stuff +%% Description: Return erlang cipher suite definition. +%% Note: Currently not supported suites are commented away. +%% They should be supported or removed in the future. %%------------------------------------------------------------------- %% TLS v1.1 suites suite_definition(?TLS_NULL_WITH_NULL_NULL) -> - {null, null, null, ignore}; -suite_definition(?TLS_RSA_WITH_NULL_MD5) -> - {rsa, null, md5, ignore}; -suite_definition(?TLS_RSA_WITH_NULL_SHA) -> - {rsa, null, sha, ignore}; -suite_definition(?TLS_RSA_WITH_RC4_128_MD5) -> % ok - {rsa, rc4_128, md5, no_export}; -suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> % ok - {rsa, rc4_128, sha, no_export}; -%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) -> % unsupported -%% {rsa, idea_cbc, sha, no_export}; -suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> % ok - {rsa, des_cbc, sha, no_export}; + {null, null, null}; +%% suite_definition(?TLS_RSA_WITH_NULL_MD5) -> +%% {rsa, null, md5}; +%% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> +%% {rsa, null, sha}; +suite_definition(?TLS_RSA_WITH_RC4_128_MD5) -> + {rsa, rc4_128, md5}; +suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> + {rsa, rc4_128, sha}; +%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) -> +%% {rsa, idea_cbc, sha}; +suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> + {rsa, des_cbc, sha}; suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) -> - {rsa, '3des_ede_cbc', sha, no_export}; -suite_definition(?TLS_DH_DSS_WITH_DES_CBC_SHA) -> - {dh_dss, des_cbc, sha, no_export}; -suite_definition(?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA) -> - {dh_dss, '3des_ede_cbc', sha, no_export}; -suite_definition(?TLS_DH_RSA_WITH_DES_CBC_SHA) -> - {dh_rsa, des_cbc, sha, no_export}; -suite_definition(?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA) -> - {dh_rsa, '3des_ede_cbc', sha, no_export}; + {rsa, '3des_ede_cbc', sha}; suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) -> - {dhe_dss, des_cbc, sha, no_export}; + {dhe_dss, des_cbc, sha}; suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) -> - {dhe_dss, '3des_ede_cbc', sha, no_export}; + {dhe_dss, '3des_ede_cbc', sha}; suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> - {dhe_rsa, des_cbc, sha, no_export}; + {dhe_rsa, des_cbc, sha}; suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) -> - {dhe_rsa, '3des_ede_cbc', sha, no_export}; -suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) -> - {dh_anon, rc4_128, md5, no_export}; -suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) -> - {dh_anon, des40_cbc, sha, no_export}; -suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) -> - {dh_anon, '3des_ede_cbc', sha, no_export}; + {dhe_rsa, '3des_ede_cbc', sha}; %%% TSL V1.1 AES suites -suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> % ok - {rsa, aes_128_cbc, sha, ignore}; -suite_definition(?TLS_DH_DSS_WITH_AES_128_CBC_SHA) -> - {dh_dss, aes_128_cbc, sha, ignore}; -suite_definition(?TLS_DH_RSA_WITH_AES_128_CBC_SHA) -> - {dh_rsa, aes_128_cbc, sha, ignore}; +suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> + {rsa, aes_128_cbc, sha}; suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> - {dhe_dss, aes_128_cbc, sha, ignore}; + {dhe_dss, aes_128_cbc, sha}; suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) -> - {dhe_rsa, aes_128_cbc, sha, ignore}; -suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) -> - {dh_anon, aes_128_cbc, sha, ignore}; -suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> % ok - {rsa, aes_256_cbc, sha, ignore}; -suite_definition(?TLS_DH_DSS_WITH_AES_256_CBC_SHA) -> - {dh_dss, aes_256_cbc, sha, ignore}; -suite_definition(?TLS_DH_RSA_WITH_AES_256_CBC_SHA) -> - {dh_rsa, aes_256_cbc, sha, ignore}; + {dhe_rsa, aes_128_cbc, sha}; +suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> + {rsa, aes_256_cbc, sha}; suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> - {dhe_dss, aes_256_cbc, sha, ignore}; + {dhe_dss, aes_256_cbc, sha}; suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> - {dhe_rsa, aes_256_cbc, sha, ignore}; + {dhe_rsa, aes_256_cbc, sha}; + +%%% DH-ANON deprecated by TLS spec and not available +%%% by default, but good for testing purposes. +suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) -> + {dh_anon, rc4_128, md5}; +suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) -> + {dh_anon, des_cbc, sha}; +suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) -> + {dh_anon, '3des_ede_cbc', sha}; +suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) -> + {dh_anon, aes_128_cbc, sha}; suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> - {dh_anon, aes_256_cbc, sha, ignore}; - -%% TSL V1.1 KRB SUITES -suite_definition(?TLS_KRB5_WITH_DES_CBC_SHA) -> - {krb5, des_cbc, sha, ignore}; -suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_SHA) -> - {krb5, '3des_ede_cbc', sha, ignore}; -suite_definition(?TLS_KRB5_WITH_RC4_128_SHA) -> - {krb5, rc4_128, sha, ignore}; -%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_SHA) -> -%% {krb5, idea_cbc, sha, ignore}; -suite_definition(?TLS_KRB5_WITH_DES_CBC_MD5) -> - {krb5, des_cbc, md5, ignore}; -suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_MD5) -> - {krb5, '3des_ede_cbc', md5, ignore}; -suite_definition(?TLS_KRB5_WITH_RC4_128_MD5) -> - {krb5, rc4_128, md5, ignore}; -%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_MD5) -> -%% {krb5, idea_cbc, md5, ignore}; - -suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) -> - {rsa, rc4_56, md5, export}; -suite_definition(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) -> - {rsa, rc2_cbc_56, md5, export}; -suite_definition(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) -> - {rsa, des_cbc, sha, export}; -suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) -> - {dhe_dss, des_cbc, sha, export}; -suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) -> - {rsa, rc4_56, sha, export}; -suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) -> - {dhe_dss, rc4_56, sha, export}; -suite_definition(?TLS_DHE_DSS_WITH_RC4_128_SHA) -> - {dhe_dss, rc4_128, sha, export}; - -%% Export suites TLS 1.0 OR SSLv3-only servers. -suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA) -> - {krb5_export, des40_cbc, sha, export}; -suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA) -> - {krb5_export, rc2_cbc_40, sha, export}; -suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_SHA) -> - {krb5_export, des40_cbc, sha, export}; -suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5) -> - {krb5_export, des40_cbc, md5, export}; -suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5) -> - {krb5_export, rc2_cbc_40, md5, export}; -suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_MD5) -> - {krb5_export, rc2_cbc_40, md5, export}; -suite_definition(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) -> % ok - {rsa, rc4_40, md5, export}; -suite_definition(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) -> % ok - {rsa, rc2_cbc_40, md5, export}; -suite_definition(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) -> - {rsa, des40_cbc, sha, export}; -suite_definition(?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA) -> - {dh_dss, des40_cbc, sha, export}; -suite_definition(?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA) -> - {dh_rsa, des40_cbc, sha, export}; -suite_definition(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) -> - {dhe_dss, des40_cbc, sha, export}; -suite_definition(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) -> - {dhe_rsa, des40_cbc, sha, export}; -suite_definition(?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5) -> - {dh_anon, rc4_40, md5, export}; -suite_definition(?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA) -> - {dh_anon, des40_cbc, sha, export}. + {dh_anon, aes_256_cbc, sha}. + +%%-------------------------------------------------------------------- +-spec suite(erl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- %% TLS v1.1 suites -suite({rsa, null, md5, ignore}) -> - ?TLS_RSA_WITH_NULL_MD5; -suite({rsa, null, sha, ignore}) -> - ?TLS_RSA_WITH_NULL_SHA; -suite({rsa, rc4_128, md5, no_export}) -> +%%suite({rsa, null, md5}) -> +%% ?TLS_RSA_WITH_NULL_MD5; +%%suite({rsa, null, sha}) -> +%% ?TLS_RSA_WITH_NULL_SHA; +suite({rsa, rc4_128, md5}) -> ?TLS_RSA_WITH_RC4_128_MD5; -suite({rsa, rc4_128, sha, no_export}) -> +suite({rsa, rc4_128, sha}) -> ?TLS_RSA_WITH_RC4_128_SHA; -%% suite({rsa, idea_cbc, sha, no_export}) -> +%% suite({rsa, idea_cbc, sha}) -> %% ?TLS_RSA_WITH_IDEA_CBC_SHA; -suite({rsa, des_cbc, sha, no_export}) -> +suite({rsa, des_cbc, sha}) -> ?TLS_RSA_WITH_DES_CBC_SHA; -suite({rsa, '3des_ede_cbc', sha, no_export}) -> +suite({rsa, '3des_ede_cbc', sha}) -> ?TLS_RSA_WITH_3DES_EDE_CBC_SHA; -suite({dh_dss, des_cbc, sha, no_export}) -> - ?TLS_DH_DSS_WITH_DES_CBC_SHA; -suite({dh_dss, '3des_ede_cbc', sha, no_export}) -> - ?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA; -suite({dh_rsa, des_cbc, sha, no_export}) -> - ?TLS_DH_RSA_WITH_DES_CBC_SHA; -suite({dh_rsa, '3des_ede_cbc', sha, no_export}) -> - ?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA; -suite({dhe_dss, des_cbc, sha, no_export}) -> +suite({dhe_dss, des_cbc, sha}) -> ?TLS_DHE_DSS_WITH_DES_CBC_SHA; -suite({dhe_dss, '3des_ede_cbc', sha, no_export}) -> +suite({dhe_dss, '3des_ede_cbc', sha}) -> ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA; -suite({dhe_rsa, des_cbc, sha, no_export}) -> +suite({dhe_rsa, des_cbc, sha}) -> ?TLS_DHE_RSA_WITH_DES_CBC_SHA; -suite({dhe_rsa, '3des_ede_cbc', sha, no_export}) -> +suite({dhe_rsa, '3des_ede_cbc', sha}) -> ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; -suite({dh_anon, rc4_128, md5, no_export}) -> +suite({dh_anon, rc4_128, md5}) -> ?TLS_DH_anon_WITH_RC4_128_MD5; -suite({dh_anon, des40_cbc, sha, no_export}) -> +suite({dh_anon, des_cbc, sha}) -> ?TLS_DH_anon_WITH_DES_CBC_SHA; -suite({dh_anon, '3des_ede_cbc', sha, no_export}) -> +suite({dh_anon, '3des_ede_cbc', sha}) -> ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; %%% TSL V1.1 AES suites -suite({rsa, aes_128_cbc, sha, ignore}) -> +suite({rsa, aes_128_cbc, sha}) -> ?TLS_RSA_WITH_AES_128_CBC_SHA; -suite({dh_dss, aes_128_cbc, sha, ignore}) -> - ?TLS_DH_DSS_WITH_AES_128_CBC_SHA; -suite({dh_rsa, aes_128_cbc, sha, ignore}) -> - ?TLS_DH_RSA_WITH_AES_128_CBC_SHA; -suite({dhe_dss, aes_128_cbc, sha, ignore}) -> +suite({dhe_dss, aes_128_cbc, sha}) -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; -suite({dhe_rsa, aes_128_cbc, sha, ignore}) -> +suite({dhe_rsa, aes_128_cbc, sha}) -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA; -suite({dh_anon, aes_128_cbc, sha, ignore}) -> +suite({dh_anon, aes_128_cbc, sha}) -> ?TLS_DH_anon_WITH_AES_128_CBC_SHA; -suite({rsa, aes_256_cbc, sha, ignore}) -> +suite({rsa, aes_256_cbc, sha}) -> ?TLS_RSA_WITH_AES_256_CBC_SHA; -suite({dh_dss, aes_256_cbc, sha, ignore}) -> - ?TLS_DH_DSS_WITH_AES_256_CBC_SHA; -suite({dh_rsa, aes_256_cbc, sha, ignore}) -> - ?TLS_DH_RSA_WITH_AES_256_CBC_SHA; -suite({dhe_dss, aes_256_cbc, sha, ignore}) -> +suite({dhe_dss, aes_256_cbc, sha}) -> ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA; -suite({dhe_rsa, aes_256_cbc, sha, ignore}) -> +suite({dhe_rsa, aes_256_cbc, sha}) -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; -suite({dh_anon, aes_256_cbc, sha, ignore}) -> - ?TLS_DH_anon_WITH_AES_256_CBC_SHA; - -%% TSL V1.1 KRB SUITES -suite({krb5, des_cbc, sha, ignore}) -> - ?TLS_KRB5_WITH_DES_CBC_SHA; -suite({krb5_cbc, '3des_ede_cbc', sha, ignore}) -> - ?TLS_KRB5_WITH_3DES_EDE_CBC_SHA; -suite({krb5, rc4_128, sha, ignore}) -> - ?TLS_KRB5_WITH_RC4_128_SHA; -%% suite({krb5_cbc, idea_cbc, sha, ignore}) -> -%% ?TLS_KRB5_WITH_IDEA_CBC_SHA; -suite({krb5_cbc, md5, ignore}) -> - ?TLS_KRB5_WITH_DES_CBC_MD5; -suite({krb5_ede_cbc, des_cbc, md5, ignore}) -> - ?TLS_KRB5_WITH_3DES_EDE_CBC_MD5; -suite({krb5_128, rc4_128, md5, ignore}) -> - ?TLS_KRB5_WITH_RC4_128_MD5; -%% suite({krb5, idea_cbc, md5, ignore}) -> -%% ?TLS_KRB5_WITH_IDEA_CBC_MD5; - -%% Export suites TLS 1.0 OR SSLv3-only servers. -suite({rsa, rc4_40, md5, export}) -> - ?TLS_RSA_EXPORT_WITH_RC4_40_MD5; -suite({rsa, rc2_cbc_40, md5, export}) -> - ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5; -suite({rsa, des40_cbc, sha, export}) -> - ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA; -suite({rsa, rc4_56, md5, export}) -> - ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5; -suite({rsa, rc2_cbc_56, md5, export}) -> - ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5; -suite({rsa, des_cbc, sha, export}) -> - ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA; -suite({dhe_dss, des_cbc, sha, export}) -> - ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA; -suite({rsa, rc4_56, sha, export}) -> - ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA; -suite({dhe_dss, rc4_56, sha, export}) -> - ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA; -suite({dhe_dss, rc4_128, sha, export}) -> - ?TLS_DHE_DSS_WITH_RC4_128_SHA; -suite({krb5_export, des40_cbc, sha, export}) -> - ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA; -suite({krb5_export, rc2_cbc_40, sha, export}) -> - ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA; -suite({krb5_export, rc4_cbc_40, sha, export}) -> - ?TLS_KRB5_EXPORT_WITH_RC4_40_SHA; -suite({krb5_export, des40_cbc, md5, export}) -> - ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5; -suite({krb5_export, rc2_cbc_40, md5, export}) -> - ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5; -suite({krb5_export, rc4_cbc_40, md5, export}) -> - ?TLS_KRB5_EXPORT_WITH_RC4_40_MD5; -suite({rsa_export, rc4_cbc_40, md5, export}) -> - ?TLS_RSA_EXPORT_WITH_RC4_40_MD5; -suite({rsa_export, rc2_cbc_40, md5, export}) -> - ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5; -suite({rsa_export, des40_cbc, sha, export}) -> - ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA; -suite({dh_dss_export, des40_cbc, sha, export}) -> - ?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA; -suite({dh_rsa_export, des40_cbc, sha, export}) -> - ?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA; -suite({dhe_dss_export, des40_cbc, sha, export}) -> - ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA; -suite({dhe_rsa_export, des40_cbc, sha, export}) -> - ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA; -suite({dh_anon_export, rc4_40, md5, export}) -> - ?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5; -suite({dh_anon_export, des40_cbc, sha, export}) -> - ?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA. - +suite({dh_anon, aes_256_cbc, sha}) -> + ?TLS_DH_anon_WITH_AES_256_CBC_SHA. +%%-------------------------------------------------------------------- +-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- %% translate constants <-> openssl-strings -%% TODO: Is there a pattern in the nameing -%% that is useable to make a nicer function defention? - openssl_suite("DHE-RSA-AES256-SHA") -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; openssl_suite("DHE-DSS-AES256-SHA") -> @@ -514,46 +336,21 @@ openssl_suite("DHE-DSS-AES128-SHA") -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; openssl_suite("AES128-SHA") -> ?TLS_RSA_WITH_AES_128_CBC_SHA; -%% TODO: Do we want to support this? -%% openssl_suite("DHE-DSS-RC4-SHA") -> -%% ?TLS_DHE_DSS_WITH_RC4_128_SHA; %%openssl_suite("IDEA-CBC-SHA") -> %% ?TLS_RSA_WITH_IDEA_CBC_SHA; openssl_suite("RC4-SHA") -> ?TLS_RSA_WITH_RC4_128_SHA; openssl_suite("RC4-MD5") -> ?TLS_RSA_WITH_RC4_128_MD5; -%% TODO: Do we want to support this? -openssl_suite("EXP1024-RC4-MD5") -> - ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5; -openssl_suite("EXP1024-RC2-CBC-MD5") -> - ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5; -openssl_suite("EXP1024-DES-CBC-SHA") -> - ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA; -openssl_suite("EXP1024-DHE-DSS-DES-CBC-SHA") -> - ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA; -openssl_suite("EXP1024-RC4-SHA") -> - ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA; -openssl_suite("EXP1024-DHE-DSS-RC4-SHA") -> - ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA; -openssl_suite("DHE-DSS-RC4-SHA") -> - ?TLS_DHE_DSS_WITH_RC4_128_SHA; - openssl_suite("EDH-RSA-DES-CBC-SHA") -> ?TLS_DHE_RSA_WITH_DES_CBC_SHA; openssl_suite("DES-CBC-SHA") -> - ?TLS_RSA_WITH_DES_CBC_SHA; -openssl_suite("EXP-EDH-RSA-DES-CBC-SHA") -> - ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA; -openssl_suite("EXP-EDH-DSS-DES-CBC-SHA") -> - ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA; -openssl_suite("EXP-DES-CBC-SHA") -> - ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA; -openssl_suite("EXP-RC2-CBC-MD5") -> - ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5; -openssl_suite("EXP-RC4-MD5") -> - ?TLS_RSA_EXPORT_WITH_RC4_40_MD5. - + ?TLS_RSA_WITH_DES_CBC_SHA. +%%-------------------------------------------------------------------- +-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite(). +%% +%% Description: Return openssl cipher suite name. +%%------------------------------------------------------------------- openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> "DHE-RSA-AES256-SHA"; openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> @@ -582,37 +379,28 @@ openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> "EDH-RSA-DES-CBC-SHA"; openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) -> "DES-CBC-SHA"; -openssl_suite_name(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) -> - "EXP-EDH-RSA-DES-CBC-SHA"; -openssl_suite_name(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) -> - "EXP-EDH-DSS-DES-CBC-SHA"; -openssl_suite_name(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) -> - "EXP-DES-CBC-SHA"; -openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) -> - "EXP-RC2-CBC-MD5"; -openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) -> - "EXP-RC4-MD5"; - -openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) -> - "EXP1024-RC4-MD5"; -openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) -> - "EXP1024-RC2-CBC-MD5"; -openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) -> - "EXP1024-DES-CBC-SHA"; -openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) -> - "EXP1024-DHE-DSS-DES-CBC-SHA"; -openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) -> - "EXP1024-RC4-SHA"; -openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) -> - "EXP1024-DHE-DSS-RC4-SHA"; -openssl_suite_name(?TLS_DHE_DSS_WITH_RC4_128_SHA) -> - "DHE-DSS-RC4-SHA"; - %% No oppenssl name openssl_suite_name(Cipher) -> suite_definition(Cipher). %%-------------------------------------------------------------------- +-spec filter(undefined | binary(), [cipher_suite()]) -> [cipher_suite()]. +%% +%% Description: . +%%------------------------------------------------------------------- +filter(undefined, Ciphers) -> + Ciphers; +filter(DerCert, Ciphers) -> + OtpCert = public_key:pkix_decode_cert(DerCert, otp), + SigAlg = OtpCert#'OTPCertificate'.signatureAlgorithm, + case ssl_certificate:signature_type(SigAlg#'SignatureAlgorithm'.algorithm) of + rsa -> + filter_rsa(OtpCert, Ciphers -- dsa_signed_suites()); + dsa -> + Ciphers -- rsa_signed_suites() + end. + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -621,15 +409,8 @@ bulk_cipher_algorithm(null) -> %% Not supported yet %% bulk_cipher_algorithm(idea_cbc) -> %% ?IDEA; -bulk_cipher_algorithm(Cipher) when Cipher == rc2_cbc_40; - Cipher == rc2_cbc_56 -> - ?RC2; -bulk_cipher_algorithm(Cipher) when Cipher == rc4_40; - Cipher == rc4_56; - Cipher == rc4_128 -> +bulk_cipher_algorithm(rc4_128) -> ?RC4; -bulk_cipher_algorithm(des40_cbc) -> - ?DES40; bulk_cipher_algorithm(des_cbc) -> ?DES; bulk_cipher_algorithm('3des_ede_cbc') -> @@ -639,15 +420,10 @@ bulk_cipher_algorithm(Cipher) when Cipher == aes_128_cbc; ?AES. type(Cipher) when Cipher == null; - Cipher == rc4_40; - Cipher == rc4_56; Cipher == rc4_128 -> ?STREAM; type(Cipher) when Cipher == idea_cbc; - Cipher == rc2_cbc_40; - Cipher == rc2_cbc_56; - Cipher == des40_cbc; Cipher == des_cbc; Cipher == '3des_ede_cbc'; Cipher == aes_128_cbc; @@ -659,13 +435,6 @@ key_material(null) -> key_material(Cipher) when Cipher == idea_cbc; Cipher == rc4_128 -> 16; -key_material(Cipher) when Cipher == rc2_cbc_56; - Cipher == rc4_56 -> - 7; -key_material(Cipher) when Cipher == rc2_cbc_40; - Cipher == rc4_40; - Cipher == des40_cbc -> - 5; key_material(des_cbc) -> 8; key_material('3des_ede_cbc') -> @@ -678,14 +447,9 @@ key_material(aes_256_cbc) -> expanded_key_material(null) -> 0; expanded_key_material(Cipher) when Cipher == idea_cbc; - Cipher == rc2_cbc_40; - Cipher == rc2_cbc_56; - Cipher == rc4_40; - Cipher == rc4_56; Cipher == rc4_128 -> 16; -expanded_key_material(Cipher) when Cipher == des_cbc; - Cipher == des40_cbc -> +expanded_key_material(Cipher) when Cipher == des_cbc -> 8; expanded_key_material('3des_ede_cbc') -> 24; @@ -696,13 +460,7 @@ expanded_key_material(Cipher) when Cipher == aes_128_cbc; effective_key_bits(null) -> 0; -effective_key_bits(Cipher) when Cipher == rc2_cbc_40; - Cipher == rc4_40; - Cipher == des40_cbc -> - 40; -effective_key_bits(Cipher) when Cipher == rc2_cbc_56; - Cipher == rc4_56; - Cipher == des_cbc -> +effective_key_bits(des_cbc) -> 56; effective_key_bits(Cipher) when Cipher == idea_cbc; Cipher == rc4_128; @@ -714,17 +472,12 @@ effective_key_bits(aes_256_cbc) -> 256. iv_size(Cipher) when Cipher == null; - Cipher == rc4_40; - Cipher == rc4_56; Cipher == rc4_128 -> 0; iv_size(Cipher) -> block_size(Cipher). block_size(Cipher) when Cipher == idea_cbc; - Cipher == rc2_cbc_40; - Cipher == rc2_cbc_56; - Cipher == des40_cbc; Cipher == des_cbc; Cipher == '3des_ede_cbc' -> 8; @@ -763,9 +516,18 @@ generic_stream_cipher_from_bin(T, HashSz) -> #generic_stream_cipher{content=Content, mac=Mac}. -check_padding(_GBC) -> - ok. - +is_correct_padding(_, {3, 0}) -> + true; +%% For interoperability reasons we do not check the padding in TLS 1.0 as it +%% is not strictly required and breaks interopability with for instance +%% Google. +is_correct_padding(_, {3, 1}) -> + true; +%% Padding must be check in TLS 1.1 and after +is_correct_padding(#generic_block_cipher{padding_length = Len, padding = Padding}, _) -> + list_to_binary(lists:duplicate(Len, Len)) == Padding. + + get_padding(Length, BlockSize) -> get_padding_aux(BlockSize, Length rem BlockSize). @@ -782,3 +544,51 @@ next_iv(Bin, IV) -> <<_:FirstPart/binary, NextIV:IVSz/binary>> = Bin, NextIV. +rsa_signed_suites() -> + dhe_rsa_suites() ++ rsa_suites(). + +dhe_rsa_suites() -> + [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_RSA_WITH_DES_CBC_SHA]. + +rsa_suites() -> + [?TLS_RSA_WITH_AES_256_CBC_SHA, + ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_WITH_AES_128_CBC_SHA, + %%?TLS_RSA_WITH_IDEA_CBC_SHA, + ?TLS_RSA_WITH_RC4_128_SHA, + ?TLS_RSA_WITH_RC4_128_MD5, + ?TLS_RSA_WITH_DES_CBC_SHA]. + +dsa_signed_suites() -> + dhe_dss_suites(). + +dhe_dss_suites() -> + [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA]. + +filter_rsa(OtpCert, RsaCiphers) -> + TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, + TBSExtensions = TBSCert#'OTPTBSCertificate'.extensions, + Extensions = ssl_certificate:extensions_list(TBSExtensions), + case ssl_certificate:select_extension(?'id-ce-keyUsage', Extensions) of + undefined -> + RsaCiphers; + #'Extension'{extnValue = KeyUse} -> + Result = filter_rsa_suites(keyEncipherment, + KeyUse, RsaCiphers, rsa_suites()), + filter_rsa_suites(digitalSignature, + KeyUse, Result, dhe_rsa_suites()) + end. + +filter_rsa_suites(Use, KeyUse, CipherSuits, RsaSuites) -> + case ssl_certificate:is_valid_key_usage(KeyUse, Use) of + true -> + CipherSuits; + false -> + CipherSuits -- RsaSuites + end. diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 4304c501b7..8bd68cc190 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -26,6 +26,14 @@ -ifndef(ssl_cipher). -define(ssl_cipher, true). +-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' + | aes_128_cbc | aes_256_cbc. +-type hash() :: null | sha | md5. +-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. +-type cipher_suite() :: binary(). +-type cipher_enum() :: integer(). +-type openssl_cipher_suite() :: string(). + %%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with % SSL record protocol @@ -57,7 +65,7 @@ %% TLS_NULL_WITH_NULL_NULL = { 0x00,0x00 }; -define(TLS_NULL_WITH_NULL_NULL, <<?BYTE(16#00), ?BYTE(16#00)>>). -%%% The following CipherSuite definitions require that the server +%%% The following cipher suite definitions require that the server %%% provide an RSA certificate that can be used for key exchange. The %%% server may request either an RSA or a DSS signature-capable %%% certificate in the certificate request message. @@ -68,24 +76,15 @@ %% TLS_RSA_WITH_NULL_SHA = { 0x00,0x02 }; -define(TLS_RSA_WITH_NULL_SHA, <<?BYTE(16#00), ?BYTE(16#02)>>). -%% TLS_RSA_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x03 }; --define(TLS_RSA_EXPORT_WITH_RC4_40_MD5, <<?BYTE(16#00), ?BYTE(16#03)>>). - %% TLS_RSA_WITH_RC4_128_MD5 = { 0x00,0x04 }; -define(TLS_RSA_WITH_RC4_128_MD5, <<?BYTE(16#00), ?BYTE(16#04)>>). %% TLS_RSA_WITH_RC4_128_SHA = { 0x00,0x05 }; -define(TLS_RSA_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#05)>>). -%% TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00,0x06 }; --define(TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, <<?BYTE(16#00), ?BYTE(16#06)>>). - %% TLS_RSA_WITH_IDEA_CBC_SHA = { 0x00,0x07 }; -define(TLS_RSA_WITH_IDEA_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#07)>>). -%% TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x08 }; --define(TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#08)>>). - %% TLS_RSA_WITH_DES_CBC_SHA = { 0x00,0x09 }; -define(TLS_RSA_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#09)>>). @@ -106,51 +105,33 @@ %%% provided by the client must use the parameters (group and %%% generator) described by the server. -%% TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0B }; --define(TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0B)>>). - %% TLS_DH_DSS_WITH_DES_CBC_SHA = { 0x00,0x0C }; -define(TLS_DH_DSS_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0C)>>). %% TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0D }; -define(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0D)>>). -%% TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0E }; --define(TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0E)>>). - %% TLS_DH_RSA_WITH_DES_CBC_SHA = { 0x00,0x0F }; -define(TLS_DH_RSA_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0F)>>). %% TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x10 }; -define(TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#10)>>). -%% TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x11 }; --define(TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#11)>>). - %% TLS_DHE_DSS_WITH_DES_CBC_SHA = { 0x00,0x12 }; -define(TLS_DHE_DSS_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#12)>>). %% TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x13 }; -define(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#13)>>). -%% TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x14 }; --define(TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#14)>>). - %% TLS_DHE_RSA_WITH_DES_CBC_SHA = { 0x00,0x15 }; -define(TLS_DHE_RSA_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#15)>>). %% TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x16 }; -define(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#16)>>). -%% TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x17 }; --define(TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, <<?BYTE(16#00), ?BYTE(16#17)>>). - %% TLS_DH_anon_WITH_RC4_128_MD5 = { 0x00,0x18 }; -define(TLS_DH_anon_WITH_RC4_128_MD5, <<?BYTE(16#00),?BYTE(16#18)>>). -%% TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x19 }; --define(TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#19)>>). - %% TLS_DH_anon_WITH_DES_CBC_SHA = { 0x00,0x1A }; -define(TLS_DH_anon_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#1A)>>). @@ -222,32 +203,9 @@ %% TLS_KRB5_WITH_IDEA_CBC_MD5 = { 0x00,0x25 }; -define(TLS_KRB5_WITH_IDEA_CBC_MD5, <<?BYTE(16#00), ?BYTE(16#25)>>). -%% TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = { 0x00,0x26 }; --define(TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, <<?BYTE(16#00), ?BYTE(16#26)>>). - -%% TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = { 0x00,0x27 }; --define(TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA, <<?BYTE(16#00), ?BYTE(16#27)>>). - -%% TLS_KRB5_EXPORT_WITH_RC4_40_SHA = { 0x00,0x28 }; --define(TLS_KRB5_EXPORT_WITH_RC4_40_SHA, <<?BYTE(16#00), ?BYTE(16#28)>>). - -%% TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = { 0x00,0x29 }; --define(TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5, <<?BYTE(16#00), ?BYTE(16#29)>>). - -%% TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00,0x2A }; --define(TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5, <<?BYTE(16#00), ?BYTE(16#2A)>>). - -%% TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x2B }; --define(TLS_KRB5_EXPORT_WITH_RC4_40_MD5, <<?BYTE(16#00), ?BYTE(16#2B)>>). - -%% Additional TLS ciphersuites from draft-ietf-tls-56-bit-ciphersuites-00.txt - --define(TLS_RSA_EXPORT1024_WITH_RC4_56_MD5, <<?BYTE(16#00), ?BYTE(16#60)>>). --define(TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5, <<?BYTE(16#00), ?BYTE(16#61)>>). --define(TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#62)>>). --define(TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#63)>>). --define(TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, <<?BYTE(16#00), ?BYTE(16#64)>>). --define(TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA, <<?BYTE(16#00), ?BYTE(16#65)>>). --define(TLS_DHE_DSS_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#66)>>). +%% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension +%% to avoid handshake failure from old servers that do not ignore +%% hello extension data as they should. +-define(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, <<?BYTE(16#00), ?BYTE(16#FF)>>). -endif. % -ifdef(ssl_cipher). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 8ff001b172..6c9ac65b64 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -29,7 +29,6 @@ -behaviour(gen_fsm). --include("ssl_debug.hrl"). -include("ssl_handshake.hrl"). -include("ssl_alert.hrl"). -include("ssl_record.hrl"). @@ -39,7 +38,8 @@ -include_lib("public_key/include/public_key.hrl"). %% Internal application API --export([send/2, send/3, recv/3, connect/7, accept/6, close/1, shutdown/2, +-export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2, + socket_control/3, close/1, shutdown/2, new_user/2, get_opts/2, set_opts/2, info/1, session_info/1, peer_certificate/1, sockname/1, peername/1, renegotiation/1]). @@ -57,22 +57,24 @@ transport_cb, % atom() - callback module data_tag, % atom() - ex tcp. close_tag, % atom() - ex tcp_closed + error_tag, % atom() - ex tcp_error host, % string() | ipadress() port, % integer() socket, % socket() ssl_options, % #ssl_options{} socket_options, % #socket_options{} connection_states, % #connection_states{} from ssl_record.hrl + tls_packets = [], % Not yet handled decode ssl/tls packets. tls_record_buffer, % binary() buffer of incomplete records tls_handshake_buffer, % binary() buffer of incomplete handshakes %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) tls_handshake_hashes, % see above tls_cipher_texts, % list() received but not deciphered yet own_cert, % binary() - session, % #session{} from ssl_handshake.erl + session, % #session{} from ssl_handshake.hrl session_cache, % session_cache_cb, % - negotiated_version, % #protocol_version{} + negotiated_version, % tls_version() supported_protocol_versions, % [atom()] client_certificate_requested = false, key_algorithm, % atom as defined by cipher_suite @@ -85,7 +87,6 @@ from, % term(), where to reply bytes_to_read, % integer(), # bytes to read in passive mode user_data_buffer, % binary() -%% tls_buffer, % Keeps a lookahead one packet if available log_alert, % boolean() renegotiation, % {boolean(), From | internal | peer} recv_during_renegotiation, %boolean() @@ -96,46 +97,92 @@ #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME, base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). +-type state_name() :: hello | abbreviated | certify | cipher | connection. +-type gen_fsm_state_return() :: {next_state, state_name(), #state{}} | + {next_state, state_name(), #state{}, timeout()} | + {stop, term(), #state{}}. + %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: +-spec send(pid(), iolist()) -> ok | {error, reason()}. %% -%% Description: +%% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- send(Pid, Data) -> - sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity). -send(Pid, Data, Timeout) -> - sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout). + sync_send_all_state_event(Pid, {application_data, + erlang:iolist_to_binary(Data)}, infinity). + %%-------------------------------------------------------------------- -%% Function: +-spec recv(pid(), integer(), timeout()) -> + {ok, binary() | list()} | {error, reason()}. %% -%% Description: +%% Description: Receives data when active = false %%-------------------------------------------------------------------- recv(Pid, Length, Timeout) -> sync_send_all_state_event(Pid, {recv, Length}, Timeout). %%-------------------------------------------------------------------- -%% Function: +-spec connect(host(), port_num(), port(), {#ssl_options{}, #socket_options{}}, + pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% -%% Description: +%% Description: Connect to a ssl server. %%-------------------------------------------------------------------- connect(Host, Port, Socket, Options, User, CbInfo, Timeout) -> - start_fsm(client, Host, Port, Socket, Options, User, CbInfo, - Timeout). + try start_fsm(client, Host, Port, Socket, Options, User, CbInfo, + Timeout) + catch + exit:{noproc, _} -> + {error, ssl_not_started} + end. %%-------------------------------------------------------------------- -%% Function: +-spec ssl_accept(port_num(), port(), {#ssl_options{}, #socket_options{}}, + pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% -%% Description: +%% Description: Performs accept on a ssl listen socket. e.i. performs +%% ssl handshake. %%-------------------------------------------------------------------- -accept(Port, Socket, Opts, User, CbInfo, Timeout) -> - start_fsm(server, "localhost", Port, Socket, Opts, User, - CbInfo, Timeout). +ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> + try start_fsm(server, "localhost", Port, Socket, Opts, User, + CbInfo, Timeout) + catch + exit:{noproc, _} -> + {error, ssl_not_started} + end. + %%-------------------------------------------------------------------- -%% Function: +-spec handshake(#sslsocket{}, timeout()) -> ok | {error, reason()}. %% -%% Description: +%% Description: Starts ssl handshake. +%%-------------------------------------------------------------------- +handshake(#sslsocket{pid = Pid}, Timeout) -> + case sync_send_all_state_event(Pid, start, Timeout) of + connected -> + ok; + Error -> + Error + end. +%-------------------------------------------------------------------- +-spec socket_control(port(), pid(), atom()) -> + {ok, #sslsocket{}} | {error, reason()}. +%% +%% Description: Set the ssl process to own the accept socket +%%-------------------------------------------------------------------- +socket_control(Socket, Pid, CbModule) -> + case CbModule:controlling_process(Socket, Pid) of + ok -> + {ok, sslsocket(Pid)}; + {error, Reason} -> + {error, Reason} + end. + +%%-------------------------------------------------------------------- +-spec close(pid()) -> ok | {error, reason()}. +%% +%% Description: Close a ssl connection %%-------------------------------------------------------------------- close(ConnectionPid) -> case sync_send_all_state_event(ConnectionPid, close) of @@ -146,80 +193,78 @@ close(ConnectionPid) -> end. %%-------------------------------------------------------------------- -%% Function: +-spec shutdown(pid(), atom()) -> ok | {error, reason()}. %% -%% Description: +%% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- shutdown(ConnectionPid, How) -> sync_send_all_state_event(ConnectionPid, {shutdown, How}). - %%-------------------------------------------------------------------- -%% Function: +-spec new_user(pid(), pid()) -> ok | {error, reason()}. %% -%% Description: +%% Description: Changes process that receives the messages when active = true +%% or once. %%-------------------------------------------------------------------- new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). %%-------------------------------------------------------------------- -%% Function: +-spec sockname(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% -%% Description: +%% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, sockname). %%-------------------------------------------------------------------- -%% Function: +-spec peername(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% -%% Description: +%% Description: Same as inet:peername/1 %%-------------------------------------------------------------------- peername(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peername). %%-------------------------------------------------------------------- -%% Function: +-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}. %% -%% Description: +%% Description: Same as inet:getopts/2 %%-------------------------------------------------------------------- -get_opts({ListenSocket, {_SslOpts, SockOpts}, _}, OptTags) -> - get_socket_opts(ListenSocket, OptTags, SockOpts, []); get_opts(ConnectionPid, OptTags) -> sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}). %%-------------------------------------------------------------------- -%% Function: +-spec set_opts(pid(), list()) -> ok | {error, reason()}. %% -%% Description: +%% Description: Same as inet:setopts/2 %%-------------------------------------------------------------------- set_opts(ConnectionPid, Options) -> sync_send_all_state_event(ConnectionPid, {set_opts, Options}). %%-------------------------------------------------------------------- -%% Function: +-spec info(pid()) -> {ok, {atom(), tuple()}} | {error, reason()}. %% -%% Description: +%% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, info). %%-------------------------------------------------------------------- -%% Function: +-spec session_info(pid()) -> {ok, list()} | {error, reason()}. %% -%% Description: +%% Description: Returns info about the ssl session %%-------------------------------------------------------------------- session_info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, session_info). %%-------------------------------------------------------------------- -%% Function: +-spec peer_certificate(pid()) -> {ok, binary()| undefined} | {error, reason()}. %% -%% Description: +%% Description: Returns the peer cert %%-------------------------------------------------------------------- peer_certificate(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peer_certificate). %%-------------------------------------------------------------------- -%% Function: +-spec renegotiation(pid()) -> ok | {error, reason()}. %% -%% Description: +%% Description: Starts a renegotiation of the ssl session. %%-------------------------------------------------------------------- renegotiation(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, renegotiate). @@ -229,7 +274,8 @@ renegotiation(ConnectionPid) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(atom(), host(), port_num(), port(), list(), pid(), tuple()) -> + {ok, pid()} | ignore | {error, reason()}. %% %% Description: Creates a gen_fsm process which calls Module:init/1 to %% initialize. To ensure a synchronized start-up procedure, this function @@ -243,20 +289,20 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> %% gen_fsm callbacks %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, StateName, State} | -%% {ok, StateName, State, Timeout} | -%% ignore | -%% {stop, StopReason} +-spec init(list()) -> {ok, state_name(), #state{}} | {stop, term()}. +%% Possible return values not used now. +%% | {ok, state_name(), #state{}, timeout()} | +%% ignore %% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or %% gen_fsm:start_link/3,4, this function is called by the new process to %% initialize. %%-------------------------------------------------------------------- -init([Role, Host, Port, Socket, {SSLOpts, _} = Options, +init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), Hashes0 = ssl_handshake:init_hashes(), - try ssl_init(SSLOpts, Role) of + try ssl_init(SSLOpts0, Role) of {ok, Ref, CacheRef, OwnCert, Key, DHParams} -> State = State0#state{tls_handshake_hashes = Hashes0, own_cert = OwnCert, @@ -269,101 +315,96 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options, throw:Error -> {stop, Error} end. - + %%-------------------------------------------------------------------- -%% Function: -%% state_name(Event, State) -> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +%% -spec state_name(event(), #state{}) -> gen_fsm_state_return() %% %% Description:There should be one instance of this function for each %% possible state name. Whenever a gen_fsm receives an event sent %% using gen_fsm:send_event/2, the instance of this function with the %% same name as the current state name StateName is called to handle %% the event. It is also called if a timeout occurs. +%% %%-------------------------------------------------------------------- -hello(socket_control, #state{host = Host, port = Port, role = client, - ssl_options = SslOpts, - transport_cb = Transport, socket = Socket, - connection_states = ConnectionStates} +-spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- +hello(start, #state{host = Host, port = Port, role = client, + ssl_options = SslOpts, + transport_cb = Transport, socket = Socket, + connection_states = ConnectionStates, + renegotiation = {Renegotiation, _}} = State0) -> + Hello = ssl_handshake:client_hello(Host, Port, - ConnectionStates, SslOpts), + ConnectionStates, + SslOpts, Renegotiation), + Version = Hello#client_hello.client_version, Hashes0 = ssl_handshake:init_hashes(), {BinMsg, CS2, Hashes1} = encode_handshake(Hello, Version, ConnectionStates, Hashes0), Transport:send(Socket, BinMsg), - State = State0#state{connection_states = CS2, + State1 = State0#state{connection_states = CS2, negotiated_version = Version, %% Requested version session = #session{session_id = Hello#client_hello.session_id, is_resumable = false}, - tls_handshake_hashes = Hashes1}, - {next_state, hello, next_record(State)}; + tls_handshake_hashes = Hashes1}, + {Record, State} = next_record(State1), + next_state(hello, Record, State); -hello(socket_control, #state{role = server} = State) -> - {next_state, hello, next_record(State)}; +hello(start, #state{role = server} = State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); -hello(#hello_request{}, #state{role = client} = State) -> - {next_state, hello, State}; +hello(#hello_request{}, #state{role = client} = State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression} = Hello, - #state{session = Session0 = #session{session_id = OldId}, + #state{session = #session{session_id = OldId}, connection_states = ConnectionStates0, role = client, negotiated_version = ReqVersion, - host = Host, port = Port, - session_cache = Cache, - session_cache_cb = CacheCb} = State0) -> - - {Version, NewId, ConnectionStates1} = - ssl_handshake:hello(Hello, ConnectionStates0), - - {KeyAlgorithm, _, _, _} = - ssl_cipher:suite_definition(CipherSuite), - - PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - - State = State0#state{key_algorithm = KeyAlgorithm, - negotiated_version = Version, - connection_states = ConnectionStates1, - premaster_secret = PremasterSecret}, - - case ssl_session:is_new(OldId, NewId) of - true -> - Session = Session0#session{session_id = NewId, - cipher_suite = CipherSuite, - compression_method = Compression}, - {next_state, certify, - next_record(State#state{session = Session})}; - false -> - Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}), - case ssl_handshake:master_secret(Version, Session, - ConnectionStates1, client) of - {_, ConnectionStates2} -> - {next_state, abbreviated, - next_record(State#state{ - connection_states = ConnectionStates2, - session = Session})}; - #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State), - {stop, normal, State} - end + renegotiation = {Renegotiation, _}, + ssl_options = SslOptions} = State0) -> + + case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of + {Version, NewId, ConnectionStates} -> + {KeyAlgorithm, _, _} = + ssl_cipher:suite_definition(CipherSuite), + + PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), + + State = State0#state{key_algorithm = KeyAlgorithm, + negotiated_version = Version, + connection_states = ConnectionStates, + premaster_secret = PremasterSecret}, + + case ssl_session:is_new(OldId, NewId) of + true -> + handle_new_session(NewId, CipherSuite, Compression, State); + false -> + handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) + end; + #alert{} = Alert -> + handle_own_alert(Alert, ReqVersion, hello, State0), + {stop, normal, State0} end; hello(Hello = #client_hello{client_version = ClientVersion}, State = #state{connection_states = ConnectionStates0, port = Port, session = Session0, - session_cache = Cache, + renegotiation = {Renegotiation, _}, + session_cache = Cache, session_cache_cb = CacheCb, - ssl_options = SslOpts}) -> + ssl_options = SslOpts, + own_cert = Cert}) -> - case ssl_handshake:hello(Hello, {Port, SslOpts, - Session0, Cache, CacheCb, - ConnectionStates0}) of + case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, + ConnectionStates0, Cert}, Renegotiation) of {Version, {Type, Session}, ConnectionStates} -> do_server_hello(Type, State#state{connection_states = ConnectionStates, @@ -372,50 +413,67 @@ hello(Hello = #client_hello{client_version = ClientVersion}, #alert{} = Alert -> handle_own_alert(Alert, ClientVersion, hello, State), {stop, normal, State} - end. + end; -abbreviated(socket_control, #state{role = server} = State) -> - {next_state, abbreviated, State}; -abbreviated(#hello_request{}, State) -> - {next_state, certify, State}; +hello(Msg, State) -> + handle_unexpected_message(Msg, hello, State). +%%-------------------------------------------------------------------- +-spec abbreviated(#hello_request{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- +abbreviated(#hello_request{}, State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); -abbreviated(Finished = #finished{}, +abbreviated(#finished{verify_data = Data} = Finished, #state{role = server, negotiated_version = Version, tls_handshake_hashes = Hashes, - session = #session{master_secret = MasterSecret}} = - State0) -> + session = #session{master_secret = MasterSecret}, + connection_states = ConnectionStates0} = + State) -> case ssl_handshake:verify_connection(Version, Finished, client, MasterSecret, Hashes) of - verified -> - State = ack_connection(State0), - next_state_connection(State); + verified -> + ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0), + next_state_connection(abbreviated, + ack_connection(State#state{connection_states = ConnectionStates})); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, abbreviated, State), + {stop, normal, State} end; -abbreviated(Finished = #finished{}, +abbreviated(#finished{verify_data = Data} = Finished, #state{role = client, tls_handshake_hashes = Hashes0, session = #session{master_secret = MasterSecret}, - negotiated_version = Version} = State0) -> + negotiated_version = Version, + connection_states = ConnectionStates0} = State) -> case ssl_handshake:verify_connection(Version, Finished, server, MasterSecret, Hashes0) of verified -> - {ConnectionStates, Hashes} = finalize_client_handshake(State0), - State = ack_connection(State0), - next_state_connection(State#state{tls_handshake_hashes = Hashes, - connection_states = - ConnectionStates}); + ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0), + {ConnectionStates, Hashes} = + finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated), + next_state_connection(abbreviated, + ack_connection(State#state{tls_handshake_hashes = Hashes, + connection_states = + ConnectionStates})); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State0), - {stop, normal, State0} - end. + handle_own_alert(Alert, Version, abbreviated, State), + {stop, normal, State} + end; -certify(socket_control, #state{role = server} = State) -> - {next_state, certify, State}; -certify(#hello_request{}, State) -> - {next_state, certify, State}; +abbreviated(Msg, State) -> + handle_unexpected_message(Msg, abbreviated, State). + +%%-------------------------------------------------------------------- +-spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} | + #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- +certify(#hello_request{}, State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); certify(#certificate{asn1_certificates = []}, #state{role = server, negotiated_version = Version, @@ -430,9 +488,9 @@ certify(#certificate{asn1_certificates = []}, #state{role = server, ssl_options = #ssl_options{verify = verify_peer, fail_if_no_peer_cert = false}} = - State) -> - {next_state, certify, - next_record(State#state{client_certificate_requested = false})}; + State0) -> + {Record, State} = next_record(State0#state{client_certificate_requested = false}), + next_state(certify, Record, State); certify(#certificate{} = Cert, #state{negotiated_version = Version, @@ -441,8 +499,7 @@ certify(#certificate{} = Cert, ssl_options = Opts} = State) -> case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth, Opts#ssl_options.verify, - Opts#ssl_options.verify_fun, - Opts#ssl_options.validate_extensions_fun, Role) of + Opts#ssl_options.verify_fun, Role) of {PeerCert, PublicKeyInfo} -> handle_peer_cert(PeerCert, PublicKeyInfo, State#state{client_certificate_requested = false}); @@ -454,28 +511,24 @@ certify(#certificate{} = Cert, certify(#server_key_exchange{} = KeyExchangeMsg, #state{role = client, negotiated_version = Version, key_algorithm = Alg} = State0) - when Alg == dhe_dss; Alg == dhe_rsa ->%%Not imp:Alg == dh_anon;Alg == krb5 -> + when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon -> case handle_server_key(KeyExchangeMsg, State0) of - #state{} = State -> - {next_state, certify, next_record(State)}; + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(certify, Record, State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify_server_keyexchange, State0), {stop, normal, State0} end; -certify(#server_key_exchange{}, - State = #state{role = client, negotiated_version = Version, - key_algorithm = Alg}) - when Alg == rsa; Alg == dh_dss; Alg == dh_rsa -> - Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE), - handle_own_alert(Alert, Version, certify_server_key_exchange, State), - {stop, normal, State}; - -certify(#certificate_request{}, State) -> - NewState = State#state{client_certificate_requested = true}, - {next_state, certify, next_record(NewState)}; +certify(#server_key_exchange{} = Msg, + #state{role = client, key_algorithm = rsa} = State) -> + handle_unexpected_message(Msg, certify_server_keyexchange, State); +certify(#certificate_request{}, State0) -> + {Record, State} = next_record(State0#state{client_certificate_requested = true}), + next_state(certify, Record, State); %% Master secret was determined with help of server-key exchange msg certify(#server_hello_done{}, @@ -515,280 +568,157 @@ certify(#server_hello_done{}, {stop, normal, State0} end; -certify(#client_key_exchange{}, - State = #state{role = server, - client_certificate_requested = true, - ssl_options = #ssl_options{fail_if_no_peer_cert = true}, - negotiated_version = Version}) -> +certify(#client_key_exchange{} = Msg, + #state{role = server, + client_certificate_requested = true, + ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State) -> %% We expect a certificate here - Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE), - handle_own_alert(Alert, Version, - certify_server_waiting_certificate, State), - {stop, normal, State}; - + handle_unexpected_message(Msg, certify_client_key_exchange, State); -certify(#client_key_exchange{exchange_keys - = #encrypted_premaster_secret{premaster_secret - = EncPMS}}, - #state{negotiated_version = Version, - connection_states = ConnectionStates0, - session = Session0, - private_key = Key} = State0) -> - try ssl_handshake:decrypt_premaster_secret(EncPMS, Key) of - PremasterSecret -> - case ssl_handshake:master_secret(Version, PremasterSecret, - ConnectionStates0, server) of - {MasterSecret, ConnectionStates} -> - Session = Session0#session{master_secret = MasterSecret}, - State = State0#state{connection_states = ConnectionStates, - session = Session}, - {next_state, cipher, next_record(State)}; - #alert{} = Alert -> - handle_own_alert(Alert, Version, - certify_client_key_exchange, State0), - {stop, normal, State0} - end +certify(#client_key_exchange{exchange_keys = Keys}, + State = #state{key_algorithm = KeyAlg, negotiated_version = Version}) -> + try + certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State) catch #alert{} = Alert -> - handle_own_alert(Alert, Version, certify_client_key_exchange, - State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify_client_key_exchange, State), + {stop, normal, State} end; -certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{ - dh_public = ClientPublicDhKey}}, - #state{negotiated_version = Version, - diffie_hellman_params = #'DHParameter'{prime = P, - base = G}, - diffie_hellman_keys = {_, ServerDhPrivateKey}, - role = Role, - session = Session, - connection_states = ConnectionStates0} = State0) -> - - PMpint = crypto:mpint(P), - GMpint = crypto:mpint(G), - PremasterSecret = crypto:dh_compute_key(mpint_binary(ClientPublicDhKey), - ServerDhPrivateKey, - [PMpint, GMpint]), - +certify(Msg, State) -> + handle_unexpected_message(Msg, certify, State). + +certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS}, + #state{negotiated_version = Version, + connection_states = ConnectionStates0, + session = Session0, + private_key = Key} = State0) -> + PremasterSecret = ssl_handshake:decrypt_premaster_secret(EncPMS, Key), case ssl_handshake:master_secret(Version, PremasterSecret, - ConnectionStates0, Role) of + ConnectionStates0, server) of {MasterSecret, ConnectionStates} -> - State = State0#state{session = - Session#session{master_secret - = MasterSecret}, - connection_states = ConnectionStates}, - {next_state, cipher, next_record(State)}; + Session = Session0#session{master_secret = MasterSecret}, + State1 = State0#state{connection_states = ConnectionStates, + session = Session}, + {Record, State} = next_record(State1), + next_state(cipher, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, + certify_client_key_exchange, State0), + {stop, normal, State0} + end; + +certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey}, + #state{negotiated_version = Version, + diffie_hellman_params = #'DHParameter'{prime = P, + base = G}, + diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> + + case dh_master_secret(crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of + #state{} = State1 -> + {Record, State} = next_record(State1), + next_state(cipher, Record, State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify_client_key_exchange, State0), {stop, normal, State0} end. -cipher(socket_control, #state{role = server} = State) -> - {next_state, cipher, State}; -cipher(#hello_request{}, State) -> - {next_state, cipher, State}; +%%-------------------------------------------------------------------- +-spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- +cipher(#hello_request{}, State0) -> + {Record, State} = next_record(State0), + next_state(hello, Record, State); cipher(#certificate_verify{signature = Signature}, #state{role = server, public_key_info = PublicKeyInfo, negotiated_version = Version, session = #session{master_secret = MasterSecret}, - key_algorithm = Algorithm, tls_handshake_hashes = Hashes - } = State) -> + } = State0) -> case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - Version, MasterSecret, - Algorithm, Hashes) of + Version, MasterSecret, Hashes) of valid -> - {next_state, cipher, next_record(State)}; + {Record, State} = next_record(State0), + next_state(cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State), - {stop, normal, State} + handle_own_alert(Alert, Version, cipher, State0), + {stop, normal, State0} end; -cipher(#finished{} = Finished, +cipher(#finished{verify_data = Data} = Finished, #state{negotiated_version = Version, host = Host, port = Port, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes} = State0) -> + tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), - MasterSecret, Hashes) of + MasterSecret, Hashes0) of verified -> - State = ack_connection(State0), Session = register_session(Role, Host, Port, Session0), - case Role of - client -> - next_state_connection(State#state{session = Session}); - server -> - {NewConnectionStates, NewHashes} = - finalize_server_handshake(State#state{ - session = Session}), - next_state_connection(State#state{connection_states = - NewConnectionStates, - session = Session, - tls_handshake_hashes = - NewHashes}) - end; + cipher_role(Role, Data, Session, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State0), - {stop, normal, State0} - end. + handle_own_alert(Alert, Version, cipher, State), + {stop, normal, State} + end; -connection(socket_control, #state{role = server} = State) -> - {next_state, connection, State}; -connection(#hello_request{}, State = #state{host = Host, port = Port, - socket = Socket, - ssl_options = SslOpts, - negotiated_version = Version, - transport_cb = Transport, - connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0}) -> +cipher(Msg, State) -> + handle_unexpected_message(Msg, cipher, State). - Hello = ssl_handshake:client_hello(Host, Port, - ConnectionStates0, SslOpts), +%%-------------------------------------------------------------------- +-spec connection(#hello_request{} | #client_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- +connection(#hello_request{}, #state{host = Host, port = Port, + socket = Socket, + ssl_options = SslOpts, + negotiated_version = Version, + transport_cb = Transport, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}, + tls_handshake_hashes = Hashes0} = State0) -> + + Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, + SslOpts, Renegotiation), + {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Hello, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), - {next_state, hello, next_record(State#state{connection_states = - ConnectionStates1, - tls_handshake_hashes = Hashes1})}; + {Record, State} = next_record(State0#state{connection_states = + ConnectionStates1, + tls_handshake_hashes = Hashes1}), + next_state(hello, Record, State); connection(#client_hello{} = Hello, #state{role = server} = State) -> - hello(Hello, State). + hello(Hello, State); +connection(Msg, State) -> + handle_unexpected_message(Msg, connection, State). %%-------------------------------------------------------------------- -%% Function: -%% handle_event(Event, StateName, State) -> {next_state, NextStateName, -%% NextState} | -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +-spec handle_event(term(), state_name(), #state{}) -> term(). +%% As it is not currently used gen_fsm_state_return() makes +%% dialyzer unhappy! +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:send_all_state_event/2, this function is called to handle -%% the event. +%% the event. Not currently used! %%-------------------------------------------------------------------- -handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, - StateName, - State0 = #state{key_algorithm = KeyAlg, - tls_handshake_buffer = Buf0, - negotiated_version = Version}) -> - Handle = - fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> - %% This message should not be included in handshake - %% message hashes. Starts new handshake (renegotiation) - Hs0 = ssl_handshake:init_hashes(), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, - renegotiation = {true, peer}}); - ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> - %% This message should not be included in handshake - %% message hashes. Already in negotiation so it will be ignored! - ?MODULE:SName(Packet, State); - ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> - Hs0 = ssl_handshake:init_hashes(), - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, - renegotiation = {true, peer}}); - ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); - (_, StopState) -> StopState - end, - try - {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version), - Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}}, - lists:foldl(Handle, Start, Packets) - catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State0), - {stop, normal, State0} - end; - -handle_event(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, - StateName, State0) -> - case application_data(Data, State0) of - Stop = {stop,_,_} -> - Stop; - State -> - {next_state, StateName, State} - end; - -handle_event(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} = - _ChangeCipher, - StateName, - State = #state{connection_states = ConnectionStates0}) -> - ?DBG_TERM(_ChangeCipher), - ConnectionStates1 = - ssl_record:activate_pending_connection_state(ConnectionStates0, read), - {next_state, StateName, - next_record(State#state{connection_states = ConnectionStates1})}; - -handle_event(#ssl_tls{type = ?ALERT, fragment = Data}, StateName, State) -> - Alerts = decode_alerts(Data), - ?DBG_TERM(Alerts), - [alert_event(A) || A <- Alerts], - {next_state, StateName, State}; - -handle_event(#alert{level = ?FATAL} = Alert, connection, - #state{from = From, user_application = {_Mon, Pid}, - log_alert = Log, - host = Host, port = Port, session = Session, - role = Role, socket_options = Opts} = State) -> - invalidate_session(Role, Host, Port, Session), - log_alert(Log, connection, Alert), - alert_user(Opts#socket_options.active, Pid, From, Alert, Role), - {stop, normal, State}; -handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, - connection, #state{from = From, - role = Role, - user_application = {_Mon, Pid}, - socket_options = Opts} = State) -> - alert_user(Opts#socket_options.active, Pid, From, Alert, Role), - {stop, normal, State}; - -handle_event(#alert{level = ?FATAL} = Alert, StateName, - #state{from = From, host = Host, port = Port, session = Session, - log_alert = Log, role = Role} = State) -> - invalidate_session(Role, Host, Port, Session), - log_alert(Log, StateName, Alert), - alert_user(From, Alert, Role), - {stop, normal, State}; -handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, - _, #state{from = From, role = Role} = State) -> - alert_user(From, Alert, Role), - {stop, normal, State}; - -handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{log_alert = Log, renegotiation = {true, internal}} = State) -> - log_alert(Log, StateName, Alert), - {stop, normal, State}; - -handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{log_alert = Log, renegotiation = {true, From}} = State) -> - log_alert(Log, StateName, Alert), - gen_fsm:reply(From, {error, renegotiation_rejected}), - {next_state, connection, next_record(State)}; - -handle_event(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName, - #state{log_alert = Log} = State) -> - log_alert(Log, StateName, Alert), - {next_state, StateName, next_record(State)}. +handle_event(_Event, StateName, State) -> + {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_sync_event(Event, From, StateName, -%% State) -> {next_state, NextStateName, NextState} | -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {reply, Reply, NextStateName, NextState}| -%% {reply, Reply, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} | -%% {stop, Reason, Reply, NewState} +-spec handle_sync_event(term(), from(), state_name(), #state{}) -> + gen_fsm_state_return() | + {reply, reply(), state_name(), #state{}} | + {reply, reply(), state_name(), #state{}, timeout()} | + {stop, reason(), reply(), #state{}}. +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle %% the event. @@ -830,27 +760,53 @@ handle_sync_event({application_data, Data}, From, StateName, #state{send_queue = Queue} = State) -> %% In renegotiation priorities handshake, send data when handshake is finished {next_state, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}}; -handle_sync_event(started, From, StateName, State) -> + +handle_sync_event(start, From, hello, State) -> + hello(start, State#state{from = From}); + +%% The two clauses below could happen if a server upgrades a socket in +%% active mode. Note that in this case we are lucky that +%% controlling_process has been evalueated before receiving handshake +%% messages from client. The server should put the socket in passive +%% mode before telling the client that it is willing to upgrade +%% and before calling ssl:ssl_accept/2. These clauses are +%% here to make sure it is the users problem and not owers if +%% they upgrade a active socket. +handle_sync_event(start, _, connection, State) -> + {reply, connected, connection, State}; +handle_sync_event(start, From, StateName, State) -> {next_state, StateName, State#state{from = From}}; -handle_sync_event(close, From, _StateName, State) -> - {stop, normal, ok, State#state{from = From}}; +handle_sync_event(close, _, _StateName, State) -> + {stop, normal, ok, State}; -handle_sync_event({shutdown, How}, From, StateName, - #state{transport_cb = CbModule, +handle_sync_event({shutdown, How0}, _, StateName, + #state{transport_cb = Transport, + negotiated_version = Version, + connection_states = ConnectionStates, socket = Socket} = State) -> - case CbModule:shutdown(Socket, How) of + case How0 of + How when How == write; How == both -> + Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), + {BinMsg, _} = + encode_alert(Alert, Version, ConnectionStates), + Transport:send(Socket, BinMsg); + _ -> + ok + end, + + case Transport:shutdown(Socket, How0) of ok -> {reply, ok, StateName, State}; Error -> - {stop, normal, Error, State#state{from = From}} + {stop, normal, Error, State} end; handle_sync_event({recv, N}, From, connection = StateName, State0) -> passive_receive(State0#state{bytes_to_read = N, from = From}, StateName); %% Doing renegotiate wait with handling request until renegotiate is -%% finished. Will be handled by next_state_connection/1. +%% finished. Will be handled by next_state_connection/2. handle_sync_event({recv, N}, From, StateName, State) -> {next_state, StateName, State#state{bytes_to_read = N, from = From, recv_during_renegotiation = true}}; @@ -888,7 +844,13 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, {reply, ok, StateName, State1}; Buffer =:= <<>>, Opts1#socket_options.active =:= false -> %% Need data, set active once - {reply, ok, StateName, next_record_if_active(State1)}; + {Record, State2} = next_record_if_active(State1), + case next_state(StateName, Record, State2) of + {next_state, StateName, State} -> + {reply, ok, StateName, State}; + {stop, Reason, State} -> + {stop, Reason, State} + end; Buffer =:= <<>> -> %% Active once already set {reply, ok, StateName, State1}; @@ -896,10 +858,15 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, case application_data(<<>>, State1) of Stop = {stop,_,_} -> Stop; - State -> - {reply, ok, StateName, State} + {Record, State2} -> + case next_state(StateName, Record, State2) of + {next_state, StateName, State} -> + {reply, ok, StateName, State}; + {stop, Reason, State} -> + {stop, Reason, State} + end end - end; + end; handle_sync_event(renegotiate, From, connection, State) -> renegotiate(State#state{renegotiation = {true, From}}); @@ -928,31 +895,26 @@ handle_sync_event(peer_certificate, _, StateName, {reply, {ok, Cert}, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} +-spec handle_info(msg(),state_name(), #state{}) -> + {next_state, state_name(), #state{}}| + {next_state, state_name(), #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: This function is called by a gen_fsm when it receives any %% other message than a synchronous or asynchronous event %% (or a system message). %%-------------------------------------------------------------------- %% raw data from TCP, unpack records -handle_info({Protocol, _, Data}, StateName, State = +handle_info({Protocol, _, Data}, StateName, #state{data_tag = Protocol, - negotiated_version = Version, - tls_record_buffer = Buf0, - tls_cipher_texts = CT0}) -> - case ssl_record:get_tls_records(Data, Buf0) of - {Records, Buf1} -> - CT1 = CT0 ++ Records, - {next_state, StateName, - next_record(State#state{tls_record_buffer = Buf1, - tls_cipher_texts = CT1})}; + negotiated_version = Version} = State0) -> + case next_tls_record(Data, State0) of + {Record, State} -> + next_state(StateName, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State), - {stop, normal, State} + handle_own_alert(Alert, Version, StateName, State0), + {stop, normal, State0} end; handle_info({CloseTag, Socket}, _StateName, @@ -973,41 +935,59 @@ handle_info({CloseTag, Socket}, _StateName, ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Role), {stop, normal, State}; +handle_info({ErrorTag, Socket, econnaborted}, StateName, + #state{socket = Socket, from = User, role = Role, + error_tag = ErrorTag} = State) when StateName =/= connection -> + alert_user(User, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), + {stop, normal, State}; + +handle_info({ErrorTag, Socket, Reason}, _, + #state{socket = Socket, from = User, + role = Role, error_tag = ErrorTag} = State) -> + Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]), + error_logger:info_report(Report), + alert_user(User, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role), + {stop, normal, State}; + handle_info({'DOWN', MonitorRef, _, _, _}, _, State = #state{user_application={MonitorRef,_Pid}}) -> {stop, normal, State}; -handle_info(A, StateName, State) -> - io:format("SSL: Bad info (state ~w): ~w\n", [StateName, A]), - {stop, bad_info, State}. +handle_info(Msg, StateName, State) -> + Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]), + error_logger:info_report(Report), + {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, StateName, State) -> void() +-spec terminate(reason(), state_name(), #state{}) -> term(). +%% %% Description:This function is called by a gen_fsm when it is about %% to terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_fsm terminates with %% Reason. The return value is ignored. %%-------------------------------------------------------------------- -terminate(_Reason, connection, #state{negotiated_version = Version, +terminate(Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate}) -> notify_senders(SendQueue), notify_renegotiater(Renegotiate), - {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING,?CLOSE_NOTIFY), - Version, ConnectionStates), + BinAlert = terminate_alert(Reason, Version, ConnectionStates), Transport:send(Socket, BinAlert), + workaround_transport_delivery_problems(Socket, Transport), Transport:close(Socket); terminate(_Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate}) -> notify_senders(SendQueue), notify_renegotiater(Renegotiate), + workaround_transport_delivery_problems(Socket, Transport), Transport:close(Socket). %%-------------------------------------------------------------------- -%% Function: +-spec code_change(term(), state_name(), #state{}, list()) -> {ok, state_name(), #state{}}. +%% %% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- @@ -1017,126 +997,126 @@ code_change(_OldVsn, StateName, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_} = CbInfo, +start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo, Timeout) -> - case ssl_connection_sup:start_child([Role, Host, Port, Socket, - Opts, User, CbInfo]) of - {ok, Pid} -> - CbModule:controlling_process(Socket, Pid), - send_event(Pid, socket_control), - case sync_send_all_state_event(Pid, started, Timeout) of - connected -> - {ok, sslsocket(Pid)}; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} + try + {ok, Pid} = ssl_connection_sup:start_child([Role, Host, Port, Socket, + Opts, User, CbInfo]), + {ok, SslSocket} = socket_control(Socket, Pid, CbModule), + ok = handshake(SslSocket, Timeout), + {ok, SslSocket} + catch + error:{badmatch, {error, _} = Error} -> + Error end. - + ssl_init(SslOpts, Role) -> {ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, SslOpts#ssl_options.password, Role), - DHParams = init_diffie_hellman(SslOpts#ssl_options.dhfile, Role), + DHParams = init_diffie_hellman(SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), {ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}. -init_certificates(#ssl_options{cacertfile = CACertFile, - certfile = CertFile}, Role) -> - - case ssl_manager:connection_init(CACertFile, Role) of - {ok, CertDbRef, CacheRef} -> - init_certificates(CertDbRef, CacheRef, CertFile, Role); - {error, {badmatch, _Error}} -> - Report = io_lib:format("SSL: Error ~p Initializing: ~p ~n", - [_Error, CACertFile]), - error_logger:error_report(Report), - throw(ecacertfile); - {error, _Error} -> - Report = io_lib:format("SSL: Error ~p Initializing: ~p ~n", - [_Error, CACertFile]), - error_logger:error_report(Report), - throw(ecacertfile) - end. -init_certificates(CertDbRef, CacheRef, CertFile, client) -> +init_certificates(#ssl_options{cacerts = CaCerts, + cacertfile = CACertFile, + certfile = CertFile, + cert = Cert}, Role) -> + {ok, CertDbRef, CacheRef} = + try + Certs = case CaCerts of + undefined -> + CACertFile; + _ -> + {der, CaCerts} + end, + {ok, _, _} = ssl_manager:connection_init(Certs, Role) + catch + Error:Reason -> + handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile, + erlang:get_stacktrace()) + end, + init_certificates(Cert, CertDbRef, CacheRef, CertFile, Role). + +init_certificates(undefined, CertDbRef, CacheRef, "", _) -> + {ok, CertDbRef, CacheRef, undefined}; + +init_certificates(undefined, CertDbRef, CacheRef, CertFile, client) -> try [OwnCert] = ssl_certificate:file_to_certificats(CertFile), {ok, CertDbRef, CacheRef, OwnCert} - catch _E:_R -> + catch _Error:_Reason -> {ok, CertDbRef, CacheRef, undefined} end; -init_certificates(CertDbRef, CacheRef, CertFile, server) -> - try +init_certificates(undefined, CertDbRef, CacheRef, CertFile, server) -> + try [OwnCert] = ssl_certificate:file_to_certificats(CertFile), {ok, CertDbRef, CacheRef, OwnCert} - catch - _E:{badmatch, _R={error,_}} -> - Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, CertFile, - erlang:get_stacktrace()]), - error_logger:error_report(Report), - throw(ecertfile); - _E:_R -> - Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, CertFile, - erlang:get_stacktrace()]), - error_logger:error_report(Report), - throw(ecertfile) - end. - -init_private_key(undefined, "", _Password, client) -> - undefined; -init_private_key(undefined, KeyFile, Password, _) -> - try - {ok, List} = ssl_manager:cache_pem_file(KeyFile), - [Der] = [Der || Der = {PKey, _ , _} <- List, - PKey =:= rsa_private_key orelse - PKey =:= dsa_private_key], - {ok, Decoded} = public_key:decode_private_key(Der,Password), - Decoded catch - _E:{badmatch, _R={error,_}} -> - Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, KeyFile, - erlang:get_stacktrace()]), - error_logger:error_report(Report), - throw(ekeyfile); - _E:_R -> - Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, KeyFile, - erlang:get_stacktrace()]), - error_logger:error_report(Report), - throw(ekeyfile) + Error:Reason -> + handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, + erlang:get_stacktrace()) end; -init_private_key(PrivateKey, _, _,_) -> - PrivateKey. +init_certificates(Cert, CertDbRef, CacheRef, _, _) -> + {ok, CertDbRef, CacheRef, Cert}. -init_diffie_hellman(_, client) -> +init_private_key(undefined, "", _Password, _Client) -> undefined; -init_diffie_hellman(undefined, _) -> - ?DEFAULT_DIFFIE_HELLMAN_PARAMS; -init_diffie_hellman(DHParamFile, server) -> - {ok, List} = ssl_manager:cache_pem_file(DHParamFile), - case [Der || Der = {dh_params, _ , _} <- List] of - [Der] -> - {ok, Decoded} = public_key:decode_dhparams(Der), - Decoded; - [] -> - ?DEFAULT_DIFFIE_HELLMAN_PARAMS - end. +init_private_key(undefined, KeyFile, Password, _) -> + try + {ok, List} = ssl_manager:cache_pem_file(KeyFile), + [PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List, + PKey =:= 'RSAPrivateKey' orelse + PKey =:= 'DSAPrivateKey'], + public_key:pem_entry_decode(PemEntry, Password) + catch + Error:Reason -> + handle_file_error(?LINE, Error, Reason, KeyFile, ekeyfile, + erlang:get_stacktrace()) + end; -send_event(FsmPid, Event) -> - gen_fsm:send_event(FsmPid, Event). +init_private_key({rsa, PrivateKey}, _, _,_) -> + public_key:der_decode('RSAPrivateKey', PrivateKey); +init_private_key({dsa, PrivateKey},_,_,_) -> + public_key:der_decode('DSAPrivateKey', PrivateKey). +handle_file_error(Line, Error, {badmatch, Reason}, File, Throw, Stack) -> + file_error(Line, Error, Reason, File, Throw, Stack); +handle_file_error(Line, Error, Reason, File, Throw, Stack) -> + file_error(Line, Error, Reason, File, Throw, Stack). -send_all_state_event(FsmPid, Event) -> - gen_fsm:send_all_state_event(FsmPid, Event). +-spec(file_error/6 :: (_,_,_,_,_,_) -> no_return()). +file_error(Line, Error, Reason, File, Throw, Stack) -> + Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", + [Line, Error, Reason, File, Stack]), + error_logger:error_report(Report), + throw(Throw). + +init_diffie_hellman(Params, _,_) when is_binary(Params)-> + public_key:der_decode('DHParameter', Params); +init_diffie_hellman(_,_, client) -> + undefined; +init_diffie_hellman(_,undefined, _) -> + ?DEFAULT_DIFFIE_HELLMAN_PARAMS; +init_diffie_hellman(_, DHParamFile, server) -> + try + {ok, List} = ssl_manager:cache_pem_file(DHParamFile), + case [Entry || Entry = {'DHParameter', _ , _} <- List] of + [Entry] -> + public_key:pem_entry_decode(Entry); + [] -> + ?DEFAULT_DIFFIE_HELLMAN_PARAMS + end + catch + Error:Reason -> + handle_file_error(?LINE, Error, Reason, + DHParamFile, edhfile, erlang:get_stacktrace()) + end. sync_send_all_state_event(FsmPid, Event) -> - sync_send_all_state_event(FsmPid, Event, ?DEFAULT_TIMEOUT). + sync_send_all_state_event(FsmPid, Event, infinity). sync_send_all_state_event(FsmPid, Event, Timeout) -> try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout) @@ -1146,22 +1126,21 @@ sync_send_all_state_event(FsmPid, Event, Timeout) -> exit:{timeout, _} -> {error, timeout}; exit:{normal, _} -> + {error, closed}; + exit:{shutdown, _} -> {error, closed} end. -%% Events: #alert{} -alert_event(Alert) -> - send_all_state_event(self(), Alert). - %% We do currently not support cipher suites that use fixed DH. %% If we want to implement that we should add a code %% here to extract DH parameters form cert. handle_peer_cert(PeerCert, PublicKeyInfo, #state{session = Session} = State0) -> - State = State0#state{session = + State1 = State0#state{session = Session#session{peer_certificate = PeerCert}, public_key_info = PublicKeyInfo}, - {next_state, certify, next_record(State)}. + {Record, State} = next_record(State1), + next_state(certify, Record, State). certify_client(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, @@ -1186,89 +1165,123 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, negotiated_version = Version, own_cert = OwnCert, socket = Socket, - key_algorithm = KeyAlg, private_key = PrivateKey, session = #session{master_secret = MasterSecret}, tls_handshake_hashes = Hashes0} = State) -> + case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - Version, KeyAlg, - PrivateKey, Hashes0) of - ignore -> %% No key or cert or fixed_diffie_hellman - State; - Verified -> + Version, PrivateKey, Hashes0) of + #certificate_verify{} = Verified -> {BinVerified, ConnectionStates1, Hashes1} = - encode_handshake(Verified, KeyAlg, Version, + encode_handshake(Verified, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinVerified), State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1} + tls_handshake_hashes = Hashes1}; + ignore -> + State; + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify, State) + end; verify_client_cert(#state{client_certificate_requested = false} = State) -> State. do_server_hello(Type, #state{negotiated_version = Version, - session = Session, - connection_states = ConnectionStates0} + session = #session{session_id = SessId} = Session, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}} = State0) when is_atom(Type) -> + ServerHello = - ssl_handshake:server_hello(Session#session.session_id, Version, - ConnectionStates0), - State = server_hello(ServerHello, State0), + ssl_handshake:server_hello(SessId, Version, + ConnectionStates0, Renegotiation), + State1 = server_hello(ServerHello, State0), case Type of new -> - do_server_hello(ServerHello, State); + new_server_hello(ServerHello, State1); resumed -> + ConnectionStates1 = State1#state.connection_states, case ssl_handshake:master_secret(Version, Session, - ConnectionStates0, server) of - {_, ConnectionStates1} -> - State1 = State#state{connection_states=ConnectionStates1, - session = Session}, + ConnectionStates1, server) of + {_, ConnectionStates2} -> + State2 = State1#state{connection_states=ConnectionStates2, + session = Session}, {ConnectionStates, Hashes} = - finalize_server_handshake(State1), - Resumed = State1#state{connection_states = - ConnectionStates, - tls_handshake_hashes = Hashes}, - {next_state, abbreviated, next_record(Resumed)}; + finalize_handshake(State2, abbreviated), + State3 = State2#state{connection_states = + ConnectionStates, + tls_handshake_hashes = Hashes}, + {Record, State} = next_record(State3), + next_state(abbreviated, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State), - {stop, normal, State} + handle_own_alert(Alert, Version, hello, State1), + {stop, normal, State1} end - end; + end. -do_server_hello(#server_hello{cipher_suite = CipherSuite, +new_server_hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression, session_id = SessionId}, #state{session = Session0, negotiated_version = Version} = State0) -> try server_certify_and_key_exchange(State0) of #state{} = State1 -> - State = server_hello_done(State1), + State2 = server_hello_done(State1), Session = Session0#session{session_id = SessionId, cipher_suite = CipherSuite, compression_method = Compression}, - {next_state, certify, State#state{session = Session}} + {Record, State} = next_record(State2#state{session = Session}), + next_state(certify, Record, State) catch #alert{} = Alert -> handle_own_alert(Alert, Version, hello, State0), {stop, normal, State0} end. +handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> + Session = Session0#session{session_id = NewId, + cipher_suite = CipherSuite, + compression_method = Compression}, + {Record, State} = next_record(State0#state{session = Session}), + next_state(certify, Record, State). + +handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, + negotiated_version = Version, + host = Host, port = Port, + session_cache = Cache, + session_cache_cb = CacheCb} = State0) -> + Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), + case ssl_handshake:master_secret(Version, Session, + ConnectionStates0, client) of + {_, ConnectionStates1} -> + {Record, State} = + next_record(State0#state{ + connection_states = ConnectionStates1, + session = Session}), + next_state(abbreviated, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, hello, State0), + {stop, normal, State0} + end. + + client_certify_and_key_exchange(#state{negotiated_version = Version} = State0) -> try do_client_certify_and_key_exchange(State0) of State1 = #state{} -> - {ConnectionStates, Hashes} = finalize_client_handshake(State1), - State = State1#state{connection_states = ConnectionStates, + {ConnectionStates, Hashes} = finalize_handshake(State1, certify), + State2 = State1#state{connection_states = ConnectionStates, %% Reinitialize client_certificate_requested = false, tls_handshake_hashes = Hashes}, - {next_state, cipher, next_record(State)} - + {Record, State} = next_record(State2), + next_state(cipher, Record, State) catch #alert{} = Alert -> - handle_own_alert(Alert, Version, certify_foo, State0), + handle_own_alert(Alert, Version, client_certify_and_key_exchange, State0), {stop, normal, State0} end. @@ -1288,8 +1301,7 @@ server_hello(ServerHello, #state{transport_cb = Transport, connection_states = ConnectionStates0, tls_handshake_hashes = Hashes0} = State) -> CipherSuite = ServerHello#server_hello.cipher_suite, - {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), - %% Version = ServerHello#server_hello.server_version, TODO ska kontrolleras + {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), @@ -1301,25 +1313,26 @@ server_hello_done(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates, - tls_handshake_hashes = Hashes} = State0) -> + tls_handshake_hashes = Hashes} = State) -> HelloDone = ssl_handshake:server_hello_done(), - + {BinHelloDone, NewConnectionStates, NewHashes} = encode_handshake(HelloDone, Version, ConnectionStates, Hashes), Transport:send(Socket, BinHelloDone), - State = State0#state{connection_states = NewConnectionStates, - tls_handshake_hashes = NewHashes}, - next_record(State). - -certify_server(#state{transport_cb = Transport, - socket = Socket, - negotiated_version = Version, - connection_states = ConnectionStates, - tls_handshake_hashes = Hashes, - cert_db_ref = CertDbRef, - own_cert = OwnCert} = State) -> + State#state{connection_states = NewConnectionStates, + tls_handshake_hashes = NewHashes}. + +certify_server(#state{key_algorithm = dh_anon} = State) -> + State; +certify_server(#state{transport_cb = Transport, + socket = Socket, + negotiated_version = Version, + connection_states = ConnectionStates, + tls_handshake_hashes = Hashes, + cert_db_ref = CertDbRef, + own_cert = OwnCert} = State) -> case ssl_handshake:certificate(OwnCert, CertDbRef, server) of CertMsg = #certificate{} -> {BinCertMsg, NewConnectionStates, NewHashes} = @@ -1332,20 +1345,10 @@ certify_server(#state{transport_cb = Transport, throw(Alert) end. -key_exchange(#state{role = server, key_algorithm = Algo} = State) - when Algo == rsa; - Algo == dh_dss; - Algo == dh_rsa -> +key_exchange(#state{role = server, key_algorithm = rsa} = State) -> State; - -%key_exchange(#state{role = server, key_algorithm = rsa_export} = State) -> - %% TODO when the public key in the server certificate is - %% less than or equal to 512 bits in length dont send key_exchange - %% but do it otherwise -% State; - key_exchange(#state{role = server, key_algorithm = Algo, - diffie_hellman_params = Params, + diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, @@ -1354,11 +1357,10 @@ key_exchange(#state{role = server, key_algorithm = Algo, transport_cb = Transport } = State) when Algo == dhe_dss; - Algo == dhe_dss_export; Algo == dhe_rsa; - Algo == dhe_rsa_export -> + Algo == dh_anon -> - Keys = public_key:gen_key(Params), + Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), SecParams = ConnectionState#connection_state.security_parameters, @@ -1375,11 +1377,6 @@ key_exchange(#state{role = server, key_algorithm = Algo, diffie_hellman_keys = Keys, tls_handshake_hashes = Hashes1}; - -%% key_algorithm = dh_anon is not supported. Should be by default disabled -%% if support is implemented and then we need a key_exchange clause for it -%% here. - key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = rsa, @@ -1394,7 +1391,6 @@ key_exchange(#state{role = client, Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates1, tls_handshake_hashes = Hashes1}; - key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = Algorithm, @@ -1403,34 +1399,17 @@ key_exchange(#state{role = client, socket = Socket, transport_cb = Transport, tls_handshake_hashes = Hashes0} = State) when Algorithm == dhe_dss; - Algorithm == dhe_dss_export; Algorithm == dhe_rsa; - Algorithm == dhe_rsa_export -> + Algorithm == dh_anon -> Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; - -key_exchange(#state{role = client, - connection_states = ConnectionStates0, - key_algorithm = Algorithm, - negotiated_version = Version, - client_certificate_requested = ClientCertReq, - own_cert = OwnCert, - diffie_hellman_keys = DhKeys, - socket = Socket, transport_cb = Transport, - tls_handshake_hashes = Hashes0} = State) - when Algorithm == dh_dss; - Algorithm == dh_rsa -> - Msg = dh_key_exchange(OwnCert, DhKeys, ClientCertReq), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), - Transport:send(Socket, BinMsg), - State#state{connection_states = ConnectionStates1, tls_handshake_hashes = Hashes1}. +-spec(rsa_key_exchange/2 :: (_,_) -> no_return()). + rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) when Algorithm == ?rsaEncryption; Algorithm == ?md2WithRSAEncryption; @@ -1442,17 +1421,6 @@ rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) rsa_key_exchange(_, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). -dh_key_exchange(OwnCert, DhKeys, true) -> - case public_key:pkix_is_fixed_dh_cert(OwnCert) of - true -> - ssl_handshake:key_exchange(client, fixed_diffie_hellman); - false -> - {DhPubKey, _} = DhKeys, - ssl_handshake:key_exchange(client, {dh, DhPubKey}) - end; -dh_key_exchange(_, {DhPubKey, _}, false) -> - ssl_handshake:key_exchange(client, {dh, DhPubKey}). - request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, cert_db_ref = CertDbRef, @@ -1471,45 +1439,52 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} = State) -> State. -finalize_client_handshake(#state{connection_states = ConnectionStates0} - = State) -> - ConnectionStates1 = - cipher_protocol(State#state{connection_states = - ConnectionStates0}), - ConnectionStates2 = - ssl_record:activate_pending_connection_state(ConnectionStates1, +finalize_handshake(State, StateName) -> + ConnectionStates0 = cipher_protocol(State), + ConnectionStates = + ssl_record:activate_pending_connection_state(ConnectionStates0, write), - finished(State#state{connection_states = ConnectionStates2}). + finished(State#state{connection_states = ConnectionStates}, StateName). - -finalize_server_handshake(State) -> - ConnectionStates0 = cipher_protocol(State), - ConnectionStates = - ssl_record:activate_pending_connection_state(ConnectionStates0, - write), - finished(State#state{connection_states = ConnectionStates}). - -cipher_protocol(#state{connection_states = ConnectionStates, +cipher_protocol(#state{connection_states = ConnectionStates0, socket = Socket, negotiated_version = Version, transport_cb = Transport}) -> - {BinChangeCipher, NewConnectionStates} = + {BinChangeCipher, ConnectionStates} = encode_change_cipher(#change_cipher_spec{}, - Version, ConnectionStates), + Version, ConnectionStates0), Transport:send(Socket, BinChangeCipher), - NewConnectionStates. + ConnectionStates. finished(#state{role = Role, socket = Socket, negotiated_version = Version, transport_cb = Transport, session = Session, - connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}) -> + connection_states = ConnectionStates0, + tls_handshake_hashes = Hashes0}, StateName) -> MasterSecret = Session#session.master_secret, - Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes), - {BinFinished, NewConnectionStates, NewHashes} = - encode_handshake(Finished, Version, ConnectionStates, Hashes), + Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0), + ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName), + {BinFinished, ConnectionStates, Hashes} = + encode_handshake(Finished, Version, ConnectionStates1, Hashes0), Transport:send(Socket, BinFinished), - {NewConnectionStates, NewHashes}. + {ConnectionStates, Hashes}. + +save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) -> + ssl_record:set_client_verify_data(current_write, Data, ConnectionStates); +save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, cipher) -> + ssl_record:set_server_verify_data(current_both, Data, ConnectionStates); +save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbreviated) -> + ssl_record:set_client_verify_data(current_both, Data, ConnectionStates); +save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) -> + ssl_record:set_server_verify_data(current_write, Data, ConnectionStates). + +handle_server_key(#server_key_exchange{params = + #server_dh_params{dh_p = P, + dh_g = G, + dh_y = ServerPublicDhKey}, + signed_params = <<>>}, + #state{key_algorithm = dh_anon} = State) -> + dh_master_secret(P, G, ServerPublicDhKey, undefined, State); handle_server_key( #server_key_exchange{params = @@ -1517,17 +1492,16 @@ handle_server_key( dh_g = G, dh_y = ServerPublicDhKey}, signed_params = Signed}, - #state{session = Session, negotiated_version = Version, role = Role, - public_key_info = PubKeyInfo, + #state{public_key_info = PubKeyInfo, key_algorithm = KeyAlgo, - connection_states = ConnectionStates0} = State) -> + connection_states = ConnectionStates} = State) -> PLen = size(P), GLen = size(G), YLen = size(ServerPublicDhKey), ConnectionState = - ssl_record:pending_connection_state(ConnectionStates0, read), + ssl_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, @@ -1541,52 +1515,70 @@ handle_server_key( case verify_dh_params(Signed, Hash, PubKeyInfo) of true -> - PMpint = mpint_binary(P), - GMpint = mpint_binary(G), - Keys = {_, ClientDhPrivateKey} = - crypto:dh_generate_key([PMpint,GMpint]), - PremasterSecret = - crypto:dh_compute_key(mpint_binary(ServerPublicDhKey), - ClientDhPrivateKey, [PMpint, GMpint]), - case ssl_handshake:master_secret(Version, PremasterSecret, - ConnectionStates0, Role) of - {MasterSecret, ConnectionStates} -> - State#state{diffie_hellman_keys = Keys, - session = - Session#session{master_secret - = MasterSecret}, - connection_states = ConnectionStates}; - #alert{} = Alert -> - Alert - end; + dh_master_secret(P, G, ServerPublicDhKey, undefined, State); false -> - ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE) + ?ALERT_REC(?FATAL, ?DECRYPT_ERROR) end. -verify_dh_params(Signed, Hash, {?rsaEncryption, PubKey, _PubKeyparams}) -> +verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> case public_key:decrypt_public(Signed, PubKey, [{rsa_pad, rsa_pkcs1_padding}]) of - Hash -> + Hashes -> true; _ -> false + end; +verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify(Hash, none, Signed, {PublicKey, PublicKeyParams}). + +dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> + PMpint = mpint_binary(Prime), + GMpint = mpint_binary(Base), + Keys = {_, PrivateDhKey} = + crypto:dh_generate_key([PMpint,GMpint]), + dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); + +dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, + #state{session = Session, + negotiated_version = Version, role = Role, + connection_states = ConnectionStates0} = State) -> + PremasterSecret = + crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, + [PMpint, GMpint]), + case ssl_handshake:master_secret(Version, PremasterSecret, + ConnectionStates0, Role) of + {MasterSecret, ConnectionStates} -> + State#state{ + session = + Session#session{master_secret = MasterSecret}, + connection_states = ConnectionStates}; + #alert{} = Alert -> + Alert end. +cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), + next_state_connection(cipher, ack_connection(State#state{session = Session, + connection_states = ConnectionStates})); + +cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), + {ConnectionStates, Hashes} = + finalize_handshake(State#state{connection_states = ConnectionStates1, + session = Session}, cipher), + next_state_connection(cipher, ack_connection(State#state{connection_states = + ConnectionStates, + session = Session, + tls_handshake_hashes = + Hashes})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> - ?DBG_TERM(Alert), ssl_record:encode_alert_record(Alert, Version, ConnectionStates). encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> - ?DBG_TERM(#change_cipher_spec{}), ssl_record:encode_change_cipher_spec(Version, ConnectionStates). -encode_handshake(HandshakeRec, Version, ConnectionStates, Hashes) -> - encode_handshake(HandshakeRec, undefined, Version, - ConnectionStates, Hashes). - -encode_handshake(HandshakeRec, SigAlg, Version, ConnectionStates0, Hashes0) -> - ?DBG_TERM(HandshakeRec), - Frag = ssl_handshake:encode_handshake(HandshakeRec, Version, SigAlg), +encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) -> + Frag = ssl_handshake:encode_handshake(HandshakeRec, Version), Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag), {E, ConnectionStates1} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), @@ -1622,14 +1614,14 @@ decode_alerts(<<>>, Acc) -> passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> case Buffer of <<>> -> - State = next_record(State0), - {next_state, StateName, State}; + {Record, State} = next_record(State0), + next_state(StateName, Record, State); _ -> case application_data(<<>>, State0) of Stop = {stop, _, _} -> Stop; - State -> - {next_state, StateName, State} + {Record, State} -> + next_state(StateName, Record, State) end end. @@ -1644,8 +1636,6 @@ application_data(Data, #state{user_application = {_Mon, Pid}, true -> <<Buffer0/binary, Data/binary>> end, case get_data(SOpts, BytesToRead, Buffer1) of - {ok, <<>>, Buffer} -> % no reply, we need more data - next_record(State0#state{user_data_buffer = Buffer}); {ok, ClientData, Buffer} -> % Send data SocketOpt = deliver_app_data(SOpts, ClientData, Pid, From), State = State0#state{user_data_buffer = Buffer, @@ -1654,19 +1644,23 @@ application_data(Data, #state{user_application = {_Mon, Pid}, socket_options = SocketOpt }, if - SocketOpt#socket_options.active =:= false -> - State; %% Passive mode, wait for active once or recv - Buffer =:= <<>> -> %% Active and empty, get more data - next_record(State); - true -> %% We have more data - application_data(<<>>, State) + SocketOpt#socket_options.active =:= false; Buffer =:= <<>> -> + %% Passive mode, wait for active once or recv + %% Active and empty, get more data + next_record_if_active(State); + true -> %% We have more data + application_data(<<>>, State) end; + {more, Buffer} -> % no reply, we need more data + next_record(State0#state{user_data_buffer = Buffer}); {error,_Reason} -> %% Invalid packet in packet mode deliver_packet_error(SOpts, Buffer1, Pid, From), {stop, normal, State0} end. %% Picks ClientData +get_data(_, _, <<>>) -> + {more, <<>>}; get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) when Raw =:= raw; Raw =:= 0 -> %% Raw Mode if @@ -1679,13 +1673,13 @@ get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) {ok, Data, Rest}; true -> %% Passive Mode not enough data - {ok, <<>>, Buffer} + {more, Buffer} end; get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) -> PacketOpts = [{packet_size, Size}], case decode_packet(Type, Buffer, PacketOpts) of {more, _} -> - {ok, <<>>, Buffer}; + {more, Buffer}; Decoded -> Decoded end. @@ -1727,33 +1721,42 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, SO end. -format_reply(#socket_options{active=false, mode=Mode, header=Header}, Data) -> - {ok, format_reply(Mode, Header, Data)}; -format_reply(#socket_options{active=_, mode=Mode, header=Header}, Data) -> - {ssl, sslsocket(), format_reply(Mode, Header, Data)}. +format_reply(#socket_options{active = false, mode = Mode, packet = Packet, + header = Header}, Data) -> + {ok, format_reply(Mode, Packet, Header, Data)}; +format_reply(#socket_options{active = _, mode = Mode, packet = Packet, + header = Header}, Data) -> + {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}. -deliver_packet_error(SO= #socket_options{active=Active}, Data, Pid, From) -> +deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) -> send_or_reply(Active, Pid, From, format_packet_error(SO, Data)). -format_packet_error(#socket_options{active=false, mode=Mode}, Data) -> - {error, {invalid_packet, format_reply(Mode, raw, Data)}}; -format_packet_error(#socket_options{active=_, mode=Mode}, Data) -> - {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, Data)}}. - -format_reply(list, _, Data) -> binary_to_list(Data); -format_reply(binary, 0, Data) -> Data; -format_reply(binary, raw, Data) -> Data; -format_reply(binary, N, Data) -> % Header mode - <<Header:N/binary, Rest/binary>> = Data, - [binary_to_list(Header), Rest]. - -%% tcp_closed -send_or_reply(false, _Pid, undefined, _Data) -> - Report = io_lib:format("SSL(debug): Unexpected Data ~p ~n",[_Data]), - error_logger:error_report(Report), - erlang:error({badarg, _Pid, undefined, _Data}), - ok; -send_or_reply(false, _Pid, From, Data) -> +format_packet_error(#socket_options{active = false, mode = Mode}, Data) -> + {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}}; +format_packet_error(#socket_options{active = _, mode = Mode}, Data) -> + {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}. + +format_reply(binary, _, N, Data) when N > 0 -> % Header mode + header(N, Data); +format_reply(binary, _, _, Data) -> + Data; +format_reply(list, Packet, _, Data) + when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers} -> + Data; +format_reply(list, _,_, Data) -> + binary_to_list(Data). + +header(0, <<>>) -> + <<>>; +header(_, <<>>) -> + []; +header(0, Binary) -> + Binary; +header(N, Binary) -> + <<?BYTE(ByteN), NewBinary/binary>> = Binary, + [ByteN | header(N-1, NewBinary)]. + +send_or_reply(false, _Pid, From, Data) when From =/= undefined -> gen_fsm:reply(From, Data); send_or_reply(_, Pid, _From, Data) -> send_user(Pid, Data). @@ -1766,40 +1769,131 @@ opposite_role(server) -> send_user(Pid, Msg) -> Pid ! Msg. -next_record(#state{tls_cipher_texts = [], socket = Socket} = State) -> +handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet]} = State) -> + FsmReturn = {next_state, StateName, State#state{tls_packets = []}}, + Handle(Packet, FsmReturn); + +handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]} = State0) -> + FsmReturn = {next_state, StateName, State0#state{tls_packets = Packets}}, + case Handle(Packet, FsmReturn) of + {next_state, NextStateName, State} -> + handle_tls_handshake(Handle, NextStateName, State); + {stop, _,_} = Stop -> + Stop + end. + +next_state(_, #alert{} = Alert, #state{negotiated_version = Version} = State) -> + handle_own_alert(Alert, Version, decipher_error, State), + {stop, normal, State}; + +next_state(Next, no_record, State) -> + {next_state, Next, State}; + +next_state(Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) -> + Alerts = decode_alerts(EncAlerts), + handle_alerts(Alerts, {next_state, Next, State}); + +next_state(StateName, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, + State0 = #state{tls_handshake_buffer = Buf0, negotiated_version = Version}) -> + Handle = + fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> + %% This message should not be included in handshake + %% message hashes. Starts new handshake (renegotiation) + Hs0 = ssl_handshake:init_hashes(), + ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, + renegotiation = {true, peer}}); + ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> + %% This message should not be included in handshake + %% message hashes. Already in negotiation so it will be ignored! + ?MODULE:SName(Packet, State); + ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> + Hs0 = ssl_handshake:init_hashes(), + Hs1 = ssl_handshake:update_hashes(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, + renegotiation = {true, peer}}); + ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> + Hs1 = ssl_handshake:update_hashes(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); + (_, StopState) -> StopState + end, + try + {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0), + State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf}, + handle_tls_handshake(Handle, StateName, State) + catch throw:#alert{} = Alert -> + handle_own_alert(Alert, Version, StateName, State0), + {stop, normal, State0} + end; + +next_state(StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) -> + case application_data(Data, State0) of + Stop = {stop,_,_} -> + Stop; + {Record, State} -> + next_state(StateName, Record, State) + end; +next_state(StateName, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} = + _ChangeCipher, + #state{connection_states = ConnectionStates0} = State0) -> + ConnectionStates1 = + ssl_record:activate_pending_connection_state(ConnectionStates0, read), + {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}), + next_state(StateName, Record, State); +next_state(StateName, #ssl_tls{type = _Unknown}, State0) -> + %% Ignore unknown type + {Record, State} = next_record(State0), + next_state(StateName, Record, State). + +next_tls_record(Data, #state{tls_record_buffer = Buf0, + tls_cipher_texts = CT0} = State0) -> + case ssl_record:get_tls_records(Data, Buf0) of + {Records, Buf1} -> + CT1 = CT0 ++ Records, + next_record(State0#state{tls_record_buffer = Buf1, + tls_cipher_texts = CT1}); + #alert{} = Alert -> + Alert + end. + +next_record(#state{tls_packets = [], tls_cipher_texts = [], socket = Socket} = State) -> inet:setopts(Socket, [{active,once}]), - State; -next_record(#state{tls_cipher_texts = [CT | Rest], + {no_record, State}; +next_record(#state{tls_packets = [], tls_cipher_texts = [CT | Rest], connection_states = ConnStates0} = State) -> - {Plain, ConnStates} = ssl_record:decode_cipher_text(CT, ConnStates0), - gen_fsm:send_all_state_event(self(), Plain), - State#state{tls_cipher_texts = Rest, connection_states = ConnStates}. - + case ssl_record:decode_cipher_text(CT, ConnStates0) of + {Plain, ConnStates} -> + {Plain, State#state{tls_cipher_texts = Rest, connection_states = ConnStates}}; + #alert{} = Alert -> + {Alert, State} + end; +next_record(State) -> + {no_record, State}. next_record_if_active(State = #state{socket_options = #socket_options{active = false}}) -> - State; + {no_record ,State}; next_record_if_active(State) -> next_record(State). -next_state_connection(#state{send_queue = Queue0, - negotiated_version = Version, - socket = Socket, - transport_cb = Transport, - connection_states = ConnectionStates0, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt} - } = State) -> +next_state_connection(StateName, #state{send_queue = Queue0, + negotiated_version = Version, + socket = Socket, + transport_cb = Transport, + connection_states = ConnectionStates0, + ssl_options = #ssl_options{renegotiate_at = RenegotiateAt} + } = State) -> %% Send queued up data case queue:out(Queue0) of {{value, {From, Data}}, Queue} -> case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of {Msgs, [], ConnectionStates} -> Result = Transport:send(Socket, Msgs), - gen_fsm:reply(From, Result), - next_state_connection(State#state{connection_states = ConnectionStates, - send_queue = Queue}); + gen_fsm:reply(From, Result), + next_state_connection(StateName, + State#state{connection_states = ConnectionStates, + send_queue = Queue}); %% This is unlikely to happen. User configuration of the %% undocumented test option renegotiation_at can make it more likely. {Msgs, RestData, ConnectionStates} -> @@ -1822,9 +1916,9 @@ next_state_is_connection(State = #socket_options{active = false}}) -> passive_receive(State#state{recv_during_renegotiation = false}, connection); -next_state_is_connection(State) -> - {next_state, connection, next_record_if_active(State)}. - +next_state_is_connection(State0) -> + {Record, State} = next_record_if_active(State0), + next_state(connection, Record, State). register_session(_, _, _, #session{is_resumable = true} = Session) -> Session; %% Already registered @@ -1843,7 +1937,7 @@ invalidate_session(server, _, Port, Session) -> ssl_manager:invalidate_session(Port, Session). initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, - {CbModule, DataTag, CloseTag}) -> + {CbModule, DataTag, CloseTag, ErrorTag}) -> ConnectionStates = ssl_record:init_connection_states(Role), SessionCacheCb = case application:get_env(ssl, session_cb) of @@ -1863,6 +1957,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, transport_cb = CbModule, data_tag = DataTag, close_tag = CloseTag, + error_tag = ErrorTag, role = Role, host = Host, port = Port, @@ -1934,10 +2029,61 @@ set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) -> set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) -> set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]). +handle_alerts([], Result) -> + Result; +handle_alerts(_, {stop, _, _} = Stop) -> + %% If it is a fatal alert immediately close + Stop; +handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> + handle_alerts(Alerts, handle_alert(Alert, StateName, State)). + +handle_alert(#alert{level = ?FATAL} = Alert, StateName, + #state{from = From, host = Host, port = Port, session = Session, + user_application = {_Mon, Pid}, + log_alert = Log, role = Role, socket_options = Opts} = State) -> + invalidate_session(Role, Host, Port, Session), + log_alert(Log, StateName, Alert), + alert_user(StateName, Opts, Pid, From, Alert, Role), + {stop, normal, State}; + +handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, + StateName, #state{from = From, role = Role, + user_application = {_Mon, Pid}, socket_options = Opts} = State) -> + alert_user(StateName, Opts, Pid, From, Alert, Role), + {stop, normal, State}; + +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{log_alert = Log, renegotiation = {true, internal}, from = From, + role = Role} = State) -> + log_alert(Log, StateName, Alert), + alert_user(From, Alert, Role), + {stop, normal, State}; + +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{log_alert = Log, renegotiation = {true, From}} = State0) -> + log_alert(Log, StateName, Alert), + gen_fsm:reply(From, {error, renegotiation_rejected}), + {Record, State} = next_record(State0), + next_state(connection, Record, State); + +handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName, + #state{log_alert = Log} = State0) -> + log_alert(Log, StateName, Alert), + {Record, State} = next_record(State0), + next_state(StateName, Record, State). + +alert_user(connection, Opts, Pid, From, Alert, Role) -> + alert_user(Opts#socket_options.active, Pid, From, Alert, Role); +alert_user(_, _, _, From, Alert, Role) -> + alert_user(From, Alert, Role). + alert_user(From, Alert, Role) -> alert_user(false, no_pid, From, Alert, Role). alert_user(false = Active, Pid, From, Alert, Role) -> + %% If there is an outstanding ssl_accept | recv + %% From will be defined and send_or_reply will + %% send the appropriate error message. ReasonCode = ssl_alert:reason_code(Alert, Role), send_or_reply(Active, Pid, From, {error, ReasonCode}); alert_user(Active, Pid, From, Alert, Role) -> @@ -1950,13 +2096,13 @@ alert_user(Active, Pid, From, Alert, Role) -> {ssl_error, sslsocket(), ReasonCode}) end. -log_alert(true, StateName, Alert) -> +log_alert(true, Info, Alert) -> Txt = ssl_alert:alert_txt(Alert), - error_logger:format("SSL: ~p: ~s\n", [StateName, Txt]); + error_logger:format("SSL: ~p: ~s\n", [Info, Txt]); log_alert(false, _, _) -> ok. -handle_own_alert(Alert, Version, StateName, +handle_own_alert(Alert, Version, Info, #state{transport_cb = Transport, socket = Socket, from = User, @@ -1965,20 +2111,25 @@ handle_own_alert(Alert, Version, StateName, log_alert = Log}) -> try %% Try to tell the other side {BinMsg, _} = - encode_alert(Alert, Version, ConnectionStates), + encode_alert(Alert, Version, ConnectionStates), + linux_workaround_transport_delivery_problems(Alert, Socket), Transport:send(Socket, BinMsg) catch _:_ -> %% Can crash if we are in a uninitialized state ignore end, try %% Try to tell the local user - log_alert(Log, StateName, Alert), + log_alert(Log, Info, Alert), alert_user(User, Alert, Role) catch _:_ -> ok end. -make_premaster_secret({MajVer, MinVer}, Alg) when Alg == rsa; - Alg == dh_dss; - Alg == dh_rsa -> + +handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> + Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), + handle_own_alert(Alert, Version, {Info, Msg}, State), + {stop, normal, State}. + +make_premaster_secret({MajVer, MinVer}, rsa) -> Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>; make_premaster_secret(_, _) -> @@ -1996,9 +2147,12 @@ ack_connection(#state{renegotiation = {true, Initiater}} = State) ack_connection(#state{renegotiation = {true, From}} = State) -> gen_fsm:reply(From, ok), State#state{renegotiation = undefined}; -ack_connection(#state{renegotiation = {false, first}, from = From} = State) -> +ack_connection(#state{renegotiation = {false, first}, + from = From} = State) when From =/= undefined -> gen_fsm:reply(From, connected), - State#state{renegotiation = undefined}. + State#state{renegotiation = undefined}; +ack_connection(State) -> + State. renegotiate(#state{role = client} = State) -> %% Handle same way as if server requested @@ -2009,16 +2163,18 @@ renegotiate(#state{role = server, socket = Socket, transport_cb = Transport, negotiated_version = Version, - connection_states = ConnectionStates0} = State) -> + connection_states = ConnectionStates0} = State0) -> HelloRequest = ssl_handshake:hello_request(), - Frag = ssl_handshake:encode_handshake(HelloRequest, Version, undefined), + Frag = ssl_handshake:encode_handshake(HelloRequest, Version), Hs0 = ssl_handshake:init_hashes(), {BinMsg, ConnectionStates} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), Transport:send(Socket, BinMsg), - {next_state, hello, next_record(State#state{connection_states = - ConnectionStates, - tls_handshake_hashes = Hs0})}. + {Record, State} = next_record(State0#state{connection_states = + ConnectionStates, + tls_handshake_hashes = Hs0}), + next_state(hello, Record, State). + notify_senders(SendQueue) -> lists:foreach(fun({From, _}) -> gen_fsm:reply(From, {error, closed}) @@ -2028,3 +2184,30 @@ notify_renegotiater({true, From}) when not is_atom(From) -> gen_fsm:reply(From, {error, closed}); notify_renegotiater(_) -> ok. + +terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown -> + {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), + Version, ConnectionStates), + BinAlert; +terminate_alert(_, Version, ConnectionStates) -> + {BinAlert, _} = encode_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR), + Version, ConnectionStates), + BinAlert. + +workaround_transport_delivery_problems(Socket, Transport) -> + %% Standard trick to try to make sure all + %% data sent to to tcp port is really sent + %% before tcp port is closed. + inet:setopts(Socket, [{active, false}]), + Transport:shutdown(Socket, write), + Transport:recv(Socket, 0). + +linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) -> + case os:type() of + {unix, linux} -> + inet:setopts(Socket, [{nodelay, true}]); + _ -> + ok + end; +linux_workaround_transport_delivery_problems(_, _) -> + ok. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 9f5ac7106a..c7a1c4965d 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -28,36 +28,34 @@ -include("ssl_cipher.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). --include("ssl_debug.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([master_secret/4, client_hello/4, server_hello/3, hello/2, - hello_request/0, certify/7, certificate/3, - client_certificate_verify/6, - certificate_verify/6, certificate_request/2, - key_exchange/2, server_key_exchange_hash/2, finished/4, - verify_connection/5, - get_tls_handshake/4, - server_hello_done/0, sig_alg/1, - encode_handshake/3, init_hashes/0, - update_hashes/2, decrypt_premaster_secret/2]). +-export([master_secret/4, client_hello/5, server_hello/4, hello/4, + hello_request/0, certify/6, certificate/3, + client_certificate_verify/5, certificate_verify/5, + certificate_request/2, key_exchange/2, server_key_exchange_hash/2, + finished/4, verify_connection/5, get_tls_handshake/2, + decode_client_key/3, server_hello_done/0, + encode_handshake/2, init_hashes/0, update_hashes/2, + decrypt_premaster_secret/2]). + +-type tls_handshake() :: #client_hello{} | #server_hello{} | + #server_hello_done{} | #certificate{} | #certificate_request{} | + #client_key_exchange{} | #finished{} | #certificate_verify{} | + #hello_request{}. %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: client_hello(Host, Port, ConnectionStates, SslOpts) -> -%% #client_hello{} -%% Host -%% Port -%% ConnectionStates = #connection_states{} -%% SslOpts = #ssl_options{} +-spec client_hello(host(), port_num(), #connection_states{}, + #ssl_options{}, boolean()) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, - ciphers = Ciphers} - = SslOpts) -> + ciphers = UserSuites} + = SslOpts, Renegotiation) -> Fun = fun(Version) -> ssl_record:protocol_version(Version) @@ -65,27 +63,26 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)), Pending = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, - + Ciphers = available_suites(UserSuites, Version), + Id = ssl_manager:client_session_id(Host, Port, SslOpts), #client_hello{session_id = Id, client_version = Version, - cipher_suites = Ciphers, + cipher_suites = cipher_suites(Ciphers, Renegotiation), compression_methods = ssl_record:compressions(), - random = SecParams#security_parameters.client_random + random = SecParams#security_parameters.client_random, + renegotiation_info = + renegotiation_info(client, ConnectionStates, Renegotiation) }. %%-------------------------------------------------------------------- -%% Function: server_hello(Host, Port, SessionId, -%% Version, ConnectionStates) -> #server_hello{} -%% SessionId -%% Version -%% ConnectionStates -%% +-spec server_hello(session_id(), tls_version(), #connection_states{}, + boolean()) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- -server_hello(SessionId, Version, ConnectionStates) -> +server_hello(SessionId, Version, ConnectionStates, Renegotiation) -> Pending = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, #server_hello{server_version = Version, @@ -93,11 +90,13 @@ server_hello(SessionId, Version, ConnectionStates) -> compression_method = SecParams#security_parameters.compression_algorithm, random = SecParams#security_parameters.server_random, - session_id = SessionId + session_id = SessionId, + renegotiation_info = + renegotiation_info(server, ConnectionStates, Renegotiation) }. %%-------------------------------------------------------------------- -%% Function: hello_request() -> #hello_request{} +-spec hello_request() -> #hello_request{}. %% %% Description: Creates a hello request message sent by server to %% trigger renegotiation. @@ -106,116 +105,126 @@ hello_request() -> #hello_request{}. %%-------------------------------------------------------------------- -%% Function: hello(Hello, Info) -> -%% {Version, Id, NewConnectionStates} | -%% #alert{} -%% -%% Hello = #client_hello{} | #server_hello{} -%% Info = ConnectionStates | {Port, Session, ConnectionStates} -%% ConnectionStates = #connection_states{} +-spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, + #connection_states{} | {port_num(), #session{}, cache_ref(), + atom(), #connection_states{}, binary()}, + boolean()) -> {tls_version(), session_id(), #connection_states{}}| + {tls_version(), {resumed | new, #session{}}, + #connection_states{}} | #alert{}. %% %% Description: Handles a recieved hello message %%-------------------------------------------------------------------- hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, compression_method = Compression, random = Random, - session_id = SessionId}, ConnectionStates) -> - NewConnectionStates = - hello_pending_connection_states(client, CipherSuite, Random, - Compression, ConnectionStates), - {Version, SessionId, NewConnectionStates}; - -hello(#client_hello{client_version = ClientVersion, random = Random} = Hello, - {Port, #ssl_options{versions = Versions} = SslOpts, - Session0, Cache, CacheCb, ConnectionStates0}) -> + session_id = SessionId, renegotiation_info = Info}, + #ssl_options{secure_renegotiate = SecureRenegotation}, + ConnectionStates0, Renegotiation) -> + + case ssl_record:is_acceptable_version(Version) of + true -> + case handle_renegotiation_info(client, Info, ConnectionStates0, + Renegotiation, SecureRenegotation, []) of + {ok, ConnectionStates1} -> + ConnectionStates = + hello_pending_connection_states(client, CipherSuite, Random, + Compression, ConnectionStates1), + {Version, SessionId, ConnectionStates}; + #alert{} = Alert -> + Alert + end; + false -> + ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) + end; + +hello(#client_hello{client_version = ClientVersion, random = Random, + cipher_suites = CipherSuites, + renegotiation_info = Info} = Hello, + #ssl_options{versions = Versions, + secure_renegotiate = SecureRenegotation} = SslOpts, + {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> Version = select_version(ClientVersion, Versions), case ssl_record:is_acceptable_version(Version) of true -> {Type, #session{cipher_suite = CipherSuite, compression_method = Compression} = Session} = select_session(Hello, Port, Session0, Version, - SslOpts, Cache, CacheCb), + SslOpts, Cache, CacheCb, Cert), case CipherSuite of no_suite -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); _ -> - ConnectionStates = - hello_pending_connection_states(server, - CipherSuite, - Random, - Compression, - ConnectionStates0), - {Version, {Type, Session}, ConnectionStates} + case handle_renegotiation_info(server, Info, ConnectionStates0, + Renegotiation, SecureRenegotation, + CipherSuites) of + {ok, ConnectionStates1} -> + ConnectionStates = + hello_pending_connection_states(server, + CipherSuite, + Random, + Compression, + ConnectionStates1), + {Version, {Type, Session}, ConnectionStates}; + #alert{} = Alert -> + Alert + end end; false -> ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) end. %%-------------------------------------------------------------------- -%% Function: certify(Certs, CertDbRef, MaxPathLen) -> -%% {PeerCert, PublicKeyInfo} | #alert{} -%% -%% Certs = #certificate{} -%% CertDbRef = reference() -%% MaxPathLen = integer() | nolimit +-spec certify(#certificate{}, term(), integer() | nolimit, + verify_peer | verify_none, {fun(), term}, + client | server) -> {der_cert(), public_key_info()} | #alert{}. %% %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- -certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, - MaxPathLen, Verify, VerifyFun, ValidateFun, Role) -> +certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, + MaxPathLen, _Verify, VerifyFunAndState, Role) -> [PeerCert | _] = ASN1Certs, - VerifyBool = verify_bool(Verify), - ValidateExtensionFun = - case ValidateFun of + ValidationFunAndState = + case VerifyFunAndState of undefined -> - fun(Extensions, ValidationState, Verify0, AccError) -> - ssl_certificate:validate_extensions(Extensions, ValidationState, - [], Verify0, AccError, Role) - end; - Fun -> - fun(Extensions, ValidationState, Verify0, AccError) -> - {NewExtensions, NewValidationState, NewAccError} - = ssl_certificate:validate_extensions(Extensions, ValidationState, - [], Verify0, AccError, Role), - Fun(NewExtensions, NewValidationState, Verify0, NewAccError) - end + {fun(OtpCert, ExtensionOrError, SslState) -> + ssl_certificate:validate_extension(OtpCert, + ExtensionOrError, SslState) + end, Role}; + {Fun, UserState0} -> + {fun(OtpCert, ExtensionOrError, {SslState, UserState}) -> + case ssl_certificate:validate_extension(OtpCert, + ExtensionOrError, + SslState) of + {valid, _} -> + apply_user_fun(Fun, OtpCert, + ExtensionOrError, UserState, + SslState); + {fail, Reason} -> + apply_user_fun(Fun, OtpCert, Reason, UserState, + SslState); + {unknown, _} -> + apply_user_fun(Fun, OtpCert, + ExtensionOrError, UserState, SslState) + end + end, {Role, UserState0}} end, - try - %% Allow missing root_cert and check that with VerifyFun - ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef, false) of - {TrustedErlCert, CertPath, VerifyErrors} -> - Result = public_key:pkix_path_validation(TrustedErlCert, - CertPath, - [{max_path_length, - MaxPathLen}, - {verify, VerifyBool}, - {validate_extensions_fun, - ValidateExtensionFun}, - {acc_errors, - VerifyErrors}]), - case Result of - {error, Reason} -> - path_validation_alert(Reason, Verify); - {ok, {PublicKeyInfo,_, []}} -> - {PeerCert, PublicKeyInfo}; - {ok, {PublicKeyInfo,_, AccErrors = [Error | _]}} -> - case VerifyFun(AccErrors) of - true -> - {PeerCert, PublicKeyInfo}; - false -> - path_validation_alert(Error, Verify) - end - end - catch - throw:Alert -> - Alert + + {TrustedErlCert, CertPath} = + ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef), + + case public_key:pkix_path_validation(TrustedErlCert, + CertPath, + [{max_path_length, + MaxPathLen}, + {verify_fun, ValidationFunAndState}]) of + {ok, {PublicKeyInfo,_}} -> + {PeerCert, PublicKeyInfo}; + {error, Reason} -> + path_validation_alert(Reason) end. - + %%-------------------------------------------------------------------- -%% Function: certificate(OwnCert, CertDbRef, Role) -> #certificate{} -%% -%% OwnCert = binary() -%% CertDbRef = term() as returned by ssl_certificate_db:create() +-spec certificate(der_cert(), term(), client | server) -> #certificate{} | #alert{}. %% %% Description: Creates a certificate message. %%-------------------------------------------------------------------- @@ -227,7 +236,7 @@ certificate(OwnCert, CertDbRef, client) -> {error, _} -> %% If no suitable certificate is available, the client %% SHOULD send a certificate message containing no - %% certificates. (chapter 7.4.6. rfc 4346) + %% certificates. (chapter 7.4.6. RFC 4346) [] end, #certificate{asn1_certificates = Chain}; @@ -241,57 +250,62 @@ certificate(OwnCert, CertDbRef, server) -> end. %%-------------------------------------------------------------------- -%% Function: client_certificate_verify(Cert, ConnectionStates) -> -%% #certificate_verify{} | ignore -%% Cert = #'OTPcertificate'{} -%% ConnectionStates = #connection_states{} +-spec client_certificate_verify(undefined | der_cert(), binary(), + tls_version(), private_key(), + {{binary(), binary()},{binary(), binary()}}) -> + #certificate_verify{} | ignore | #alert{}. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- -client_certificate_verify(undefined, _, _, _, _, _) -> +client_certificate_verify(undefined, _, _, _, _) -> ignore; -client_certificate_verify(_, _, _, _, undefined, _) -> +client_certificate_verify(_, _, _, undefined, _) -> ignore; -client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm, +client_certificate_verify(OwnCert, MasterSecret, Version, PrivateKey, {Hashes0, _}) -> case public_key:pkix_is_fixed_dh_cert(OwnCert) of true -> - ignore; + ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE); false -> Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), + alg_oid(PrivateKey), Hashes0), Signed = digitally_signed(Hashes, PrivateKey), #certificate_verify{signature = Signed} end. %%-------------------------------------------------------------------- -%% Function: certificate_verify(Signature, PublicKeyInfo) -> valid | #alert{} -%% -%% Signature = binary() -%% PublicKeyInfo = {Algorithm, PublicKey, PublicKeyParams} +-spec certificate_verify(binary(), public_key_info(), tls_version(), + binary(), {_, {binary(), binary()}}) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- -certificate_verify(Signature, {_, PublicKey, _}, Version, - MasterSecret, Algorithm, {_, Hashes0}) - when Algorithm == rsa; - Algorithm == dh_rsa; - Algorithm == dhe_rsa -> +certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version, + MasterSecret, {_, Hashes0}) -> Hashes = calc_certificate_verify(Version, MasterSecret, Algorithm, Hashes0), - case public_key:decrypt_public(Signature, PublicKey, + case public_key:decrypt_public(Signature, PublicKey, [{rsa_pad, rsa_pkcs1_padding}]) of Hashes -> valid; _ -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) + end; +certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version, + MasterSecret, {_, Hashes0}) -> + Hashes = calc_certificate_verify(Version, MasterSecret, + Algorithm, Hashes0), + case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of + true -> + valid; + false -> + ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end. -%% TODO dsa clause + %%-------------------------------------------------------------------- -%% Function: certificate_request(ConnectionStates, CertDbRef) -> -%% #certificate_request{} +-spec certificate_request(#connection_states{}, certdb_ref()) -> + #certificate_request{}. %% %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- @@ -307,11 +321,12 @@ certificate_request(ConnectionStates, CertDbRef) -> }. %%-------------------------------------------------------------------- -%% Function: key_exchange(Role, Secret, Params) -> -%% #client_key_exchange{} | #server_key_exchange{} -%% -%% Secret - -%% Params - +-spec key_exchange(client | server, + {premaster_secret, binary(), public_key_info()} | + {dh, binary()} | + {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(), + binary(), binary(), private_key()}) -> + #client_key_exchange{} | #server_key_exchange{}. %% %% Description: Creates a keyexchange message. %%-------------------------------------------------------------------- @@ -319,18 +334,14 @@ key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) -> EncPremasterSecret = encrypted_premaster_secret(Secret, PublicKey), #client_key_exchange{exchange_keys = EncPremasterSecret}; -key_exchange(client, fixed_diffie_hellman) -> - #client_key_exchange{exchange_keys = - #client_diffie_hellman_public{ - dh_public = <<>> - }}; + key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> #client_key_exchange{ exchange_keys = #client_diffie_hellman_public{ dh_public = PublicKey} }; -key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _}, +key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, #'DHParameter'{prime = P, base = G}, KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) -> <<?UINT32(_), PBin/binary>> = crypto:mpint(P), @@ -339,31 +350,28 @@ key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _}, GLen = byte_size(GBin), YLen = byte_size(PublicKey), ServerDHParams = #server_dh_params{dh_p = PBin, - dh_g = GBin, dh_y = PublicKey}, - - Hash = - server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary, - ServerRandom/binary, - ?UINT16(PLen), PBin/binary, - ?UINT16(GLen), GBin/binary, - ?UINT16(YLen), PublicKey/binary>>), - Signed = digitally_signed(Hash, PrivateKey), - #server_key_exchange{params = ServerDHParams, - signed_params = Signed}; -key_exchange(_, _) -> - %%TODO : Real imp - #server_key_exchange{}. - -%%-------------------------------------------------------------------- -%% Function: master_secret(Version, Session/PremasterSecret, -%% ConnectionStates, Role) -> -%% {MasterSecret, NewConnectionStates} | #alert{} -%% Version = #protocol_version{} -%% Session = #session{} (session contains master secret) -%% PremasterSecret = binary() -%% ConnectionStates = #connection_states{} -%% Role = client | server -%% + dh_g = GBin, dh_y = PublicKey}, + + case KeyAlgo of + dh_anon -> + #server_key_exchange{params = ServerDHParams, + signed_params = <<>>}; + _ -> + Hash = + server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary, + ServerRandom/binary, + ?UINT16(PLen), PBin/binary, + ?UINT16(GLen), GBin/binary, + ?UINT16(YLen), PublicKey/binary>>), + Signed = digitally_signed(Hash, PrivateKey), + #server_key_exchange{params = ServerDHParams, + signed_params = Signed} + end. + +%%-------------------------------------------------------------------- +-spec master_secret(tls_version(), #session{} | binary(), #connection_states{}, + client | server) -> {binary(), #connection_states{}} | #alert{}. +%% %% Description: Sets or calculates the master secret and calculate keys, %% updating the pending connection states. The Mastersecret and the update %% connection states are returned or an alert if the calculation fails. @@ -400,9 +408,8 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> end. %%-------------------------------------------------------------------- -%% Function: finished(Version, Role, MacSecret, Hashes) -> #finished{} -%% -%% ConnectionStates = #connection_states{} +-spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) -> + #finished{}. %% %% Description: Creates a handshake finished message %%------------------------------------------------------------------- @@ -411,15 +418,8 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes calc_finished(Version, Role, MasterSecret, Hashes)}. %%-------------------------------------------------------------------- -%% Function: verify_connection(Finished, Role, -%% MasterSecret, Hashes) -> verified | #alert{} -%% -%% Finished = #finished{} -%% Role = client | server - the role of the process that sent the finished -%% message. -%% MasterSecret = binary() -%% Hashes = binary() - {md5_hash, sha_hash} -%% +-spec verify_connection(tls_version(), #finished{}, client | server, binary(), + {_, {binary(), binary()}}) -> verified | #alert{}. %% %% Description: Checks the ssl handshake finished message to verify %% the connection. @@ -427,92 +427,153 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes verify_connection(Version, #finished{verify_data = Data}, Role, MasterSecret, {_, {MD5, SHA}}) -> %% use the previous hashes - ?DBG_HEX(crypto:md5_final(MD5)), - ?DBG_HEX(crypto:sha_final(SHA)), case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of Data -> verified; - _E -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) + _ -> + ?ALERT_REC(?FATAL, ?DECRYPT_ERROR) end. - +%%-------------------------------------------------------------------- +-spec server_hello_done() -> #server_hello_done{}. +%% +%% Description: Creates a server hello done message. +%%-------------------------------------------------------------------- server_hello_done() -> #server_hello_done{}. %%-------------------------------------------------------------------- -%% Function: encode_handshake(HandshakeRec) -> BinHandshake -%% HandshakeRec = #client_hello | #server_hello{} | server_hello_done | -%% #certificate{} | #client_key_exchange{} | #finished{} | -%% #client_certify_request{} +-spec encode_handshake(tls_handshake(), tls_version()) -> iolist(). %% -%% encode a handshake packet to binary +%% Description: Encode a handshake packet to binary %%-------------------------------------------------------------------- -encode_handshake(Package, Version, KeyAlg) -> - SigAlg = sig_alg(KeyAlg), - {MsgType, Bin} = enc_hs(Package, Version, SigAlg), +encode_handshake(Package, Version) -> + {MsgType, Bin} = enc_hs(Package, Version), Len = byte_size(Bin), [MsgType, ?uint24(Len), Bin]. %%-------------------------------------------------------------------- -%% Function: get_tls_handshake(Data, Buffer) -> Result -%% Result = {[#handshake{}], [Raw], NewBuffer} -%% Data = Buffer = NewBuffer = Raw = binary() +-spec get_tls_handshake(binary(), binary() | iolist()) -> + {[tls_handshake()], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects -%% and returns it as a list of #handshake, also returns leftover +%% and returns it as a list of handshake messages, also returns leftover %% data. %%-------------------------------------------------------------------- -get_tls_handshake(Data, <<>>, KeyAlg, Version) -> - get_tls_handshake_aux(Data, KeyAlg, Version, []); -get_tls_handshake(Data, Buffer, KeyAlg, Version) -> - get_tls_handshake_aux(list_to_binary([Buffer, Data]), - KeyAlg, Version, []). +get_tls_handshake(Data, <<>>) -> + get_tls_handshake_aux(Data, []); +get_tls_handshake(Data, Buffer) -> + get_tls_handshake_aux(list_to_binary([Buffer, Data]), []). -get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), - Body:Length/binary,Rest/binary>>, KeyAlg, - Version, Acc) -> - Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - H = dec_hs(Type, Body, key_exchange_alg(KeyAlg), Version), - get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]); -get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) -> - {lists:reverse(Acc), Data}. +%%-------------------------------------------------------------------- +-spec decode_client_key(binary(), key_algo(), tls_version()) -> + #encrypted_premaster_secret{} | #client_diffie_hellman_public{}. +%% +%% Description: Decode client_key data and return appropriate type +%%-------------------------------------------------------------------- +decode_client_key(ClientKey, Type, Version) -> + dec_client_key(ClientKey, key_exchange_alg(Type), Version). + +%%-------------------------------------------------------------------- +-spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}. + +%% +%% Description: Calls crypto hash (md5 and sha) init functions to +%% initalize the hash context. +%%-------------------------------------------------------------------- +init_hashes() -> + T = {crypto:md5_init(), crypto:sha_init()}, + {T, T}. + +%%-------------------------------------------------------------------- +-spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) -> + {{binary(), binary()}, {binary(), binary()}}. +%% +%% Description: Calls crypto hash (md5 and sha) update functions to +%% update the hash context with Data. +%%-------------------------------------------------------------------- +update_hashes(Hashes, % special-case SSL2 client hello + <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor), + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>) -> + update_hashes(Hashes, + <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor), + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>); +update_hashes({{MD50, SHA0}, _Prev}, Data) -> + {MD51, SHA1} = {crypto:md5_update(MD50, Data), + crypto:sha_update(SHA0, Data)}, + {{MD51, SHA1}, {MD50, SHA0}}. + +%%-------------------------------------------------------------------- +-spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary(). + +%% +%% Description: Public key decryption using the private key. +%%-------------------------------------------------------------------- +decrypt_premaster_secret(Secret, RSAPrivateKey) -> + try public_key:decrypt_private(Secret, RSAPrivateKey, + [{rsa_pad, rsa_pkcs1_padding}]) + catch + _:_ -> + throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR)) + end. + +%%-------------------------------------------------------------------- +-spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary(). + +%% +%% Description: Calculate server key exchange hash +%%-------------------------------------------------------------------- +server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa; + Algorithm == dhe_rsa -> + MD5 = crypto:md5(Value), + SHA = crypto:sha(Value), + <<MD5/binary, SHA/binary>>; + +server_key_exchange_hash(dhe_dss, Value) -> + crypto:sha(Value). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -verify_bool(verify_peer) -> - true; -verify_bool(verify_none) -> - false. +get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), + Body:Length/binary,Rest/binary>>, Acc) -> + Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, + H = dec_hs(Type, Body), + get_tls_handshake_aux(Rest, [{H,Raw} | Acc]); +get_tls_handshake_aux(Data, Acc) -> + {lists:reverse(Acc), Data}. -path_validation_alert({bad_cert, cert_expired}, _) -> +path_validation_alert({bad_cert, cert_expired}) -> ?ALERT_REC(?FATAL, ?CERTIFICATE_EXPIRED); -path_validation_alert({bad_cert, invalid_issuer}, _) -> +path_validation_alert({bad_cert, invalid_issuer}) -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE); -path_validation_alert({bad_cert, invalid_signature} , _) -> +path_validation_alert({bad_cert, invalid_signature}) -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE); -path_validation_alert({bad_cert, name_not_permitted}, _) -> +path_validation_alert({bad_cert, name_not_permitted}) -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE); -path_validation_alert({bad_cert, unknown_critical_extension}, _) -> +path_validation_alert({bad_cert, unknown_critical_extension}) -> ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE); -path_validation_alert({bad_cert, cert_revoked}, _) -> +path_validation_alert({bad_cert, cert_revoked}) -> ?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED); -path_validation_alert(_, _) -> +path_validation_alert({bad_cert, selfsigned_peer}) -> + ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE); +path_validation_alert({bad_cert, unknown_ca}) -> + ?ALERT_REC(?FATAL, ?UNKNOWN_CA); +path_validation_alert(_) -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). select_session(Hello, Port, Session, Version, - #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb) -> + #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) -> SuggestedSessionId = Hello#client_hello.session_id, SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId, SslOpts), - Suites = case UserSuites of - [] -> - ssl_cipher:suites(Version); - _ -> - UserSuites - end, - + Suites = available_suites(Cert, UserSuites, Version), case ssl_session:is_new(SuggestedSessionId, SessionId) of true -> CipherSuite = @@ -525,7 +586,119 @@ select_session(Hello, Port, Session, Version, false -> {resumed, CacheCb:lookup(Cache, {Port, SessionId})} end. - + +available_suites(UserSuites, Version) -> + case UserSuites of + [] -> + ssl_cipher:suites(Version); + _ -> + UserSuites + end. + +available_suites(ServerCert, UserSuites, Version) -> + ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version)). + +cipher_suites(Suites, false) -> + [?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites]; +cipher_suites(Suites, true) -> + Suites. + +renegotiation_info(client, _, false) -> + #renegotiation_info{renegotiated_connection = undefined}; +renegotiation_info(server, ConnectionStates, false) -> + CS = ssl_record:current_connection_state(ConnectionStates, read), + case CS#connection_state.secure_renegotiation of + true -> + #renegotiation_info{renegotiated_connection = ?byte(0)}; + false -> + #renegotiation_info{renegotiated_connection = undefined} + end; +renegotiation_info(client, ConnectionStates, true) -> + CS = ssl_record:current_connection_state(ConnectionStates, read), + case CS#connection_state.secure_renegotiation of + true -> + Data = CS#connection_state.client_verify_data, + #renegotiation_info{renegotiated_connection = Data}; + false -> + #renegotiation_info{renegotiated_connection = undefined} + end; + +renegotiation_info(server, ConnectionStates, true) -> + CS = ssl_record:current_connection_state(ConnectionStates, read), + case CS#connection_state.secure_renegotiation of + true -> + CData = CS#connection_state.client_verify_data, + SData =CS#connection_state.server_verify_data, + #renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>}; + false -> + #renegotiation_info{renegotiated_connection = undefined} + end. + +handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)}, + ConnectionStates, false, _, _) -> + {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; + +handle_renegotiation_info(server, undefined, ConnectionStates, _, _, CipherSuites) -> + case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of + true -> + {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; + false -> + {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)} + end; + +handle_renegotiation_info(_, undefined, ConnectionStates, false, _, _) -> + {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}; + +handle_renegotiation_info(client, #renegotiation_info{renegotiated_connection = ClientServerVerify}, + ConnectionStates, true, _, _) -> + CS = ssl_record:current_connection_state(ConnectionStates, read), + CData = CS#connection_state.client_verify_data, + SData = CS#connection_state.server_verify_data, + case <<CData/binary, SData/binary>> == ClientServerVerify of + true -> + {ok, ConnectionStates}; + false -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) + end; +handle_renegotiation_info(server, #renegotiation_info{renegotiated_connection = ClientVerify}, + ConnectionStates, true, _, CipherSuites) -> + + case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of + true -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); + false -> + CS = ssl_record:current_connection_state(ConnectionStates, read), + Data = CS#connection_state.client_verify_data, + case Data == ClientVerify of + true -> + {ok, ConnectionStates}; + false -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) + end + end; + +handle_renegotiation_info(client, undefined, ConnectionStates, true, SecureRenegotation, _) -> + handle_renegotiation_info(ConnectionStates, SecureRenegotation); + +handle_renegotiation_info(server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) -> + case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of + true -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); + false -> + handle_renegotiation_info(ConnectionStates, SecureRenegotation) + end. + +handle_renegotiation_info(ConnectionStates, SecureRenegotation) -> + CS = ssl_record:current_connection_state(ConnectionStates, read), + case {SecureRenegotation, CS#connection_state.secure_renegotiation} of + {_, true} -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); + {true, false} -> + ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION); + {false, false} -> + {ok, ConnectionStates} + end. + %% Update pending connection states with parameters exchanged via %% hello messages %% NOTE : Role is the role of the receiver of the hello message @@ -597,15 +770,13 @@ master_secret(Version, MasterSecret, #security_parameters{ hash_size = HashSize, key_material_length = KML, expanded_key_material_length = EKML, - iv_size = IVS, - exportable = Exportable}, + iv_size = IVS}, ConnectionStates, Role) -> {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV} = - setup_keys(Version, Exportable, MasterSecret, ServerRandom, + setup_keys(Version, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS), - ?DBG_HEX(ClientWriteKey), - ?DBG_HEX(ClientIV), + ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates), ConnStates2 = ssl_record:set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, @@ -618,7 +789,7 @@ master_secret(Version, MasterSecret, #security_parameters{ ServerCipherState, Role)}. -dec_hs(?HELLO_REQUEST, <<>>, _, _) -> +dec_hs(?HELLO_REQUEST, <<>>) -> #hello_request{}; %% Client hello v2. @@ -628,85 +799,125 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), ?UINT16(CSLength), ?UINT16(0), ?UINT16(CDLength), CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>, - _, _) -> - ?DBG_HEX(CipherSuites), - ?DBG_HEX(CipherSuites), + ChallengeData:CDLength/binary>>) -> #client_hello{client_version = {Major, Minor}, random = ssl_ssl2:client_random(ChallengeData, CDLength), session_id = 0, cipher_suites = from_3bytes(CipherSuites), - compression_methods = [?NULL] + compression_methods = [?NULL], + renegotiation_info = undefined }; dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, ?UINT16(Cs_length), CipherSuites:Cs_length/binary, ?BYTE(Cm_length), Comp_methods:Cm_length/binary, - _FutureCompatData/binary>>, - _, _) -> + Extensions/binary>>) -> + + RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions), + undefined), #client_hello{ client_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suites = from_2bytes(CipherSuites), - compression_methods = Comp_methods + compression_methods = Comp_methods, + renegotiation_info = RenegotiationInfo }; + dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, - Cipher_suite:2/binary, ?BYTE(Comp_method)>>, _, _) -> + Cipher_suite:2/binary, ?BYTE(Comp_method)>>) -> #server_hello{ server_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suite = Cipher_suite, - compression_method = Comp_method - }; -dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>, _, _) -> + compression_method = Comp_method, + renegotiation_info = undefined}; + +dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, + ?BYTE(SID_length), Session_ID:SID_length/binary, + Cipher_suite:2/binary, ?BYTE(Comp_method), + ?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> + + RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []), + undefined), + #server_hello{ + server_version = {Major,Minor}, + random = Random, + session_id = Session_ID, + cipher_suite = Cipher_suite, + compression_method = Comp_method, + renegotiation_info = RenegotiationInfo}; +dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; -dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary, - ?UINT16(ExpLen), Exp:ExpLen/binary, - ?UINT16(_), Sig/binary>>, - ?KEY_EXCHANGE_RSA, _) -> - #server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod, - rsa_exponent = Exp}, - signed_params = Sig}; + dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, ?UINT16(GLen), G:GLen/binary, ?UINT16(YLen), Y:YLen/binary, - ?UINT16(_), Sig/binary>>, - ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> + ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon + #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, + dh_y = Y}, + signed_params = <<>>}; +dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, + ?UINT16(Len), Sig:Len/binary>>) -> #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, signed_params = Sig}; dec_hs(?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, - ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>, _, _) -> - %% TODO: maybe we should chop up CertAuths into a list? + ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) -> #certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}; -dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) -> +dec_hs(?SERVER_HELLO_DONE, <<>>) -> #server_hello_done{}; -dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>, _, _)-> +dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)-> #certificate_verify{signature = Signature}; -dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) -> - PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS}, - #client_key_exchange{exchange_keys = PreSecret}; -dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>, - ?KEY_EXCHANGE_RSA, _) -> - PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS}, - #client_key_exchange{exchange_keys = PreSecret}; -dec_hs(?CLIENT_KEY_EXCHANGE, <<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> - %% TODO: Should check whether the cert already contains a suitable DH-key (7.4.7.2) - throw(?ALERT_REC(?FATAL, implicit_public_value_encoding)); -dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, - ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> - #client_key_exchange{exchange_keys = - #client_diffie_hellman_public{dh_public = DH_Y}}; -dec_hs(?FINISHED, VerifyData, _, _) -> +dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) -> + #client_key_exchange{exchange_keys = PKEPMS}; +dec_hs(?FINISHED, VerifyData) -> #finished{verify_data = VerifyData}; -dec_hs(_, _, _, _) -> +dec_hs(_, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). +dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) -> + #encrypted_premaster_secret{premaster_secret = PKEPMS}; +dec_client_key(<<?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA, _) -> + #encrypted_premaster_secret{premaster_secret = PKEPMS}; +dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> + throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE)); +dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, + ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> + #client_diffie_hellman_public{dh_public = DH_Y}. + +dec_hello_extensions(<<>>) -> + []; +dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> + dec_hello_extensions(Extensions, []); +dec_hello_extensions(_) -> + []. + +dec_hello_extensions(<<>>, Acc) -> + Acc; +dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) -> + RenegotiateInfo = case Len of + 1 -> % Initial handshake + Info; % should be <<0>> will be matched in handle_renegotiation_info + _ -> + VerifyLen = Len - 1, + <<?BYTE(VerifyLen), VerifyInfo/binary>> = Info, + VerifyInfo + end, + dec_hello_extensions(Rest, [{renegotiation_info, + #renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]); +dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len, Rest/binary>>, Acc) -> + dec_hello_extensions(Rest, Acc); +%% Need this clause? +dec_hello_extensions(_, Acc) -> + Acc. + encrypted_premaster_secret(Secret, RSAPublicKey) -> try PreMasterSecret = public_key:encrypt_public(Secret, RSAPublicKey, @@ -718,14 +929,6 @@ encrypted_premaster_secret(Secret, RSAPublicKey) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)) end. -decrypt_premaster_secret(Secret, RSAPrivateKey) -> - try public_key:decrypt_private(Secret, RSAPrivateKey, - [{rsa_pad, rsa_pkcs1_padding}]) - catch - _:_ -> - throw(?ALERT_REC(?FATAL, ?DECRYPTION_FAILED)) - end. - %% encode/decode stream of certificate data to/from list of certificate data certs_to_list(ASN1Certs) -> certs_to_list(ASN1Certs, []). @@ -741,50 +944,45 @@ certs_from_list(ACList) -> <<?UINT24(CertLen), Cert/binary>> end || Cert <- ACList]). -enc_hs(#hello_request{}, _Version, _) -> +enc_hs(#hello_request{}, _Version) -> {?HELLO_REQUEST, <<>>}; -enc_hs(#client_hello{ - client_version = {Major, Minor}, - random = Random, - session_id = SessionID, - cipher_suites = CipherSuites, - compression_methods = CompMethods}, _Version, _) -> +enc_hs(#client_hello{client_version = {Major, Minor}, + random = Random, + session_id = SessionID, + cipher_suites = CipherSuites, + compression_methods = CompMethods, + renegotiation_info = RenegotiationInfo}, _Version) -> SIDLength = byte_size(SessionID), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), + Extensions = hello_extensions(RenegotiationInfo), + ExtensionsBin = enc_hello_extensions(Extensions), {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SIDLength), SessionID/binary, ?UINT16(CsLength), BinCipherSuites/binary, - ?BYTE(CmLength), BinCompMethods/binary>>}; -enc_hs(#server_hello{ - server_version = {Major, Minor}, - random = Random, - session_id = Session_ID, - cipher_suite = Cipher_suite, - compression_method = Comp_method}, _Version, _) -> + ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>}; + +enc_hs(#server_hello{server_version = {Major, Minor}, + random = Random, + session_id = Session_ID, + cipher_suite = Cipher_suite, + compression_method = Comp_method, + renegotiation_info = RenegotiationInfo}, _Version) -> SID_length = byte_size(Session_ID), + Extensions = hello_extensions(RenegotiationInfo), + ExtensionsBin = enc_hello_extensions(Extensions), {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID/binary, - Cipher_suite/binary, ?BYTE(Comp_method)>>}; -enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version, _) -> + Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; +enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> ASN1Certs = certs_from_list(ASN1CertList), ACLen = erlang:iolist_size(ASN1Certs), {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>}; -enc_hs(#server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod, - rsa_exponent = Exp}, - signed_params = SignedParams}, _Version, _) -> - ModLen = byte_size(Mod), - ExpLen = byte_size(Exp), - SignedLen = byte_size(SignedParams), - {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen),Mod/binary, - ?UINT16(ExpLen), Exp/binary, - ?UINT16(SignedLen), SignedParams/binary>> - }; enc_hs(#server_key_exchange{params = #server_dh_params{ dh_p = P, dh_g = G, dh_y = Y}, - signed_params = SignedParams}, _Version, _) -> + signed_params = SignedParams}, _Version) -> PLen = byte_size(P), GLen = byte_size(G), YLen = byte_size(Y), @@ -796,21 +994,21 @@ enc_hs(#server_key_exchange{params = #server_dh_params{ }; enc_hs(#certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}, - _Version, _) -> + _Version) -> CertTypesLen = byte_size(CertTypes), CertAuthsLen = byte_size(CertAuths), {?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes/binary, ?UINT16(CertAuthsLen), CertAuths/binary>> }; -enc_hs(#server_hello_done{}, _Version, _) -> +enc_hs(#server_hello_done{}, _Version) -> {?SERVER_HELLO_DONE, <<>>}; -enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version, _) -> +enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) -> {?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)}; -enc_hs(#certificate_verify{signature = BinSig}, _, _) -> +enc_hs(#certificate_verify{signature = BinSig}, _) -> EncSig = enc_bin_sig(BinSig), {?CERTIFICATE_VERIFY, EncSig}; -enc_hs(#finished{verify_data = VerifyData}, _Version, _) -> +enc_hs(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS},{3, 0}) -> @@ -826,29 +1024,29 @@ enc_bin_sig(BinSig) -> Size = byte_size(BinSig), <<?UINT16(Size), BinSig/binary>>. -init_hashes() -> - T = {crypto:md5_init(), crypto:sha_init()}, - {T, T}. +%% Renegotiation info, only current extension +hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> + []; +hello_extensions(#renegotiation_info{} = Info) -> + [Info]. + +enc_hello_extensions(Extensions) -> + enc_hello_extensions(Extensions, <<>>). +enc_hello_extensions([], <<>>) -> + <<>>; +enc_hello_extensions([], Acc) -> + Size = byte_size(Acc), + <<?UINT16(Size), Acc/binary>>; + +enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) -> + Len = byte_size(Info), + enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>); + +enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) -> + InfoLen = byte_size(Info), + Len = InfoLen +1, + enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>). -update_hashes(Hashes, % special-case SSL2 client hello - <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>) -> - update_hashes(Hashes, - <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>); -update_hashes({{MD50, SHA0}, _Prev}, Data) -> - ?DBG_HEX(Data), - {MD51, SHA1} = {crypto:md5_update(MD50, Data), - crypto:sha_update(SHA0, Data)}, - ?DBG_HEX(crypto:md5_final(MD51)), - ?DBG_HEX(crypto:sha_final(SHA1)), - {{MD51, SHA1}, {MD50, SHA0}}. from_3bytes(Bin3) -> from_3bytes(Bin3, []). @@ -868,25 +1066,21 @@ from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) -> certificate_types({KeyExchange, _, _, _}) when KeyExchange == rsa; - KeyExchange == dh_dss; - KeyExchange == dh_rsa; KeyExchange == dhe_dss; KeyExchange == dhe_rsa -> <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>; certificate_types(_) -> - %%TODO: Is this a good default, - %% is there a case where we like to request - %% a RSA_FIXED_DH or DSS_FIXED_DH <<?BYTE(?RSA_SIGN)>>. certificate_authorities(CertDbRef) -> Authorities = certificate_authorities_from_db(CertDbRef), Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> OTPSubj = TBSCert#'OTPTBSCertificate'.subject, - Subj = public_key:pkix_transform(OTPSubj, encode), - {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj), - DNEncodedBin = iolist_to_binary(DNEncoded), + DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp), + %%Subj = public_key:pkix_transform(OTPSubj, encode), + %% {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj), + %% DNEncodedBin = iolist_to_binary(DNEncoded), DNEncodedLen = byte_size(DNEncodedBin), <<?UINT16(DNEncodedLen), DNEncodedBin/binary>> end, @@ -896,7 +1090,7 @@ certificate_authorities_from_db(CertDbRef) -> certificate_authorities_from_db(CertDbRef, no_candidate, []). certificate_authorities_from_db(CertDbRef, PrevKey, Acc) -> - case ssl_certificate_db:issuer_candidate(PrevKey) of + case ssl_manager:issuer_candidate(PrevKey) of no_more_candidates -> lists:reverse(Acc); {{CertDbRef, _, _} = Key, Cert} -> @@ -906,13 +1100,12 @@ certificate_authorities_from_db(CertDbRef, PrevKey, Acc) -> certificate_authorities_from_db(CertDbRef, Key, Acc) end. -digitally_signed(Hashes, #'RSAPrivateKey'{} = Key) -> - public_key:encrypt_private(Hashes, Key, +digitally_signed(Hash, #'RSAPrivateKey'{} = Key) -> + public_key:encrypt_private(Hash, Key, [{rsa_pad, rsa_pkcs1_padding}]); -digitally_signed(Hashes, #'DSAPrivateKey'{} = Key) -> - public_key:sign(Hashes, Key). - - +digitally_signed(Hash, #'DSAPrivateKey'{} = Key) -> + public_key:sign(Hash, none, Key). + calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) -> ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom); @@ -920,20 +1113,15 @@ calc_master_secret({3,N},PremasterSecret, ClientRandom, ServerRandom) when N == 1; N == 2 -> ssl_tls1:master_secret(PremasterSecret, ClientRandom, ServerRandom). -setup_keys({3,0}, Exportable, MasterSecret, +setup_keys({3,0}, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) -> - ssl_ssl3:setup_keys(Exportable, MasterSecret, ServerRandom, + ssl_ssl3:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS); -setup_keys({3,1}, _Exportable, MasterSecret, +setup_keys({3,1}, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) -> ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, - KML, IVS); - -setup_keys({3,2}, _Exportable, MasterSecret, - ServerRandom, ClientRandom, HashSize, KML, _EKML, _IVS) -> - ssl_tls1:setup_keys(MasterSecret, ServerRandom, - ClientRandom, HashSize, KML). + KML, IVS). calc_finished({3, 0}, Role, MasterSecret, Hashes) -> ssl_ssl3:finished(Role, MasterSecret, Hashes); @@ -947,36 +1135,6 @@ calc_certificate_verify({3, N}, _, Algorithm, Hashes) when N == 1; N == 2 -> ssl_tls1:certificate_verify(Algorithm, Hashes). -server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa; - Algorithm == dh_rsa; - Algorithm == dhe_rsa -> - MD5Context = crypto:md5_init(), - NewMD5Context = crypto:md5_update(MD5Context, Value), - MD5 = crypto:md5_final(NewMD5Context), - - SHAContext = crypto:sha_init(), - NewSHAContext = crypto:sha_update(SHAContext, Value), - SHA = crypto:sha_final(NewSHAContext), - - <<MD5/binary, SHA/binary>>; - -server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss; - Algorithm == dhe_dss -> - - SHAContext = crypto:sha_init(), - NewSHAContext = crypto:sha_update(SHAContext, Value), - crypto:sha_final(NewSHAContext). - - -sig_alg(dh_anon) -> - ?SIGNATURE_ANONYMOUS; -sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa -> - ?SIGNATURE_RSA; -sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss -> - ?SIGNATURE_DSA; -sig_alg(_) -> - ?NULL. - key_exchange_alg(rsa) -> ?KEY_EXCHANGE_RSA; key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; @@ -984,3 +1142,18 @@ key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; ?KEY_EXCHANGE_DIFFIE_HELLMAN; key_exchange_alg(_) -> ?NULL. + +apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> + case Fun(OtpCert, ExtensionOrError, UserState0) of + {valid, UserState} -> + {valid, {SslState, UserState}}; + {fail, _} = Fail -> + Fail; + {unknown, UserState} -> + {unknown, {SslState, UserState}} + end. + +alg_oid(#'RSAPrivateKey'{}) -> + ?'rsaEncryption'; +alg_oid(#'DSAPrivateKey'{}) -> + ?'id-dsa'. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 889d39f2af..68a7802ef2 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -26,6 +26,13 @@ -ifndef(ssl_handshake). -define(ssl_handshake, true). +-include_lib("public_key/include/public_key.hrl"). + +-type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'. +-type public_key() :: #'RSAPublicKey'{} | integer(). +-type public_key_params() :: #'Dss-Parms'{} | term(). +-type public_key_info() :: {algo_oid(), public_key(), public_key_params()}. + -record(session, { session_id, peer_certificate, @@ -81,7 +88,8 @@ random, session_id, % opaque SessionID<0..32> cipher_suites, % cipher_suites<2..2^16-1> - compression_methods % compression_methods<1..2^8-1> + compression_methods, % compression_methods<1..2^8-1>, + renegotiation_info }). -record(server_hello, { @@ -89,7 +97,8 @@ random, session_id, % opaque SessionID<0..32> cipher_suite, % cipher_suites - compression_method % compression_method + compression_method, % compression_method + renegotiation_info }). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -195,6 +204,15 @@ verify_data %opaque verify_data[12] }). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Renegotiation info RFC 5746 section 3.2 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(RENEGOTIATION_EXT, 16#ff01). + +-record(renegotiation_info,{ + renegotiated_connection + }). + -endif. % -ifdef(ssl_handshake). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 8d19abfe1e..43a85c2d9d 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -23,6 +23,26 @@ -ifndef(ssl_internal). -define(ssl_internal, true). +-include_lib("public_key/include/public_key.hrl"). + +-type reason() :: term(). +-type reply() :: term(). +-type msg() :: term(). +-type from() :: term(). +-type host() :: string() | tuple(). +-type port_num() :: integer(). +-type session_id() :: 0 | binary(). +-type tls_version() :: {integer(), integer()}. +-type tls_atom_version() :: sslv3 | tlsv1. +-type cache_ref() :: term(). +-type certdb_ref() :: term(). +-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. +-type der_cert() :: binary(). +-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. +-type issuer() :: tuple(). +-type serialnumber() :: integer(). +-type cert_key() :: {reference(), integer(), issuer()}. + %% basic binary constructors -define(BOOLEAN(X), X:8/unsigned-big-integer). -define(BYTE(X), X:8/unsigned-big-integer). @@ -61,10 +81,13 @@ validate_extensions_fun, depth, % integer() certfile, % file() + cert, % der_encoded() keyfile, % file() - key, % + key, % der_encoded() password, % + cacerts, % [der_encoded()] cacertfile, % file() + dh, % der_encoded() dhfile, % file() ciphers, % %% Local policy for the server if it want's to reuse the session @@ -75,6 +98,7 @@ %% will be reused if possible. reuse_sessions, % boolean() renegotiate_at, + secure_renegotiate, debug % }). diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 0151426d43..3b02d96562 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -24,10 +24,13 @@ -module(ssl_manager). -behaviour(gen_server). +-include("ssl_internal.hrl"). + %% Internal application API --export([start_link/0, start_link/1, +-export([start_link/1, connection_init/2, cache_pem_file/1, - lookup_trusted_cert/3, client_session_id/3, server_session_id/3, + lookup_trusted_cert/3, issuer_candidate/1, client_session_id/3, + server_session_id/3, register_session/2, register_session/3, invalidate_session/2, invalidate_session/3]). @@ -58,21 +61,25 @@ %% API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}. +%% %% Description: Starts the server %%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec connection_init(string()| {der, list()}, client | server) -> {ok, reference(), cache_ref()}. +%% +%% Description: Do necessary initializations for a new connection. +%%-------------------------------------------------------------------- +connection_init(Trustedcerts, Role) -> + call({connection_init, Trustedcerts, Role}). +%%-------------------------------------------------------------------- +-spec cache_pem_file(string()) -> {ok, term()}. +%% +%% Description: Cach a pem file and %%-------------------------------------------------------------------- -connection_init(TrustedcertsFile, Role) -> - call({connection_init, TrustedcertsFile, Role}). - cache_pem_file(File) -> case ssl_certificate_db:lookup_cached_certs(File) of [{_,Content}] -> @@ -80,41 +87,56 @@ cache_pem_file(File) -> [] -> call({cache_pem, File}) end. - -%%-------------------------------------------------------------------- -%% Function: -%% Description: %%-------------------------------------------------------------------- -lookup_trusted_cert(SerialNumber, Issuer, Ref) -> +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> + undefined | + {ok, {der_cert(), #'OTPCertificate'{}}}. +%% +%% Description: Lookup the trusted cert with Key = {reference(), +%% serialnumber(), issuer()}. +%% -------------------------------------------------------------------- +lookup_trusted_cert(Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec issuer_candidate(cert_key() | no_candidate) -> + {cert_key(), {der_cert(), #'OTPCertificate'{}}} | no_more_candidates. +%% +%% Description: Return next issuer candidate. +%%-------------------------------------------------------------------- +issuer_candidate(PrevCandidateKey) -> + ssl_certificate_db:issuer_candidate(PrevCandidateKey). +%%-------------------------------------------------------------------- +-spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the client. %%-------------------------------------------------------------------- client_session_id(Host, Port, SslOpts) -> call({client_session_id, Host, Port, SslOpts}). - + %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the server. %%-------------------------------------------------------------------- server_session_id(Port, SuggestedSessionId, SslOpts) -> call({server_session_id, Port, SuggestedSessionId, SslOpts}). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec register_session(port_num(), #session{}) -> ok. +-spec register_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session available for reuse. %%-------------------------------------------------------------------- register_session(Host, Port, Session) -> cast({register_session, Host, Port, Session}). register_session(Port, Session) -> cast({register_session, Port, Session}). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec invalidate_session(port_num(), #session{}) -> ok. +-spec invalidate_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session unavilable for reuse. %%-------------------------------------------------------------------- invalidate_session(Host, Port, Session) -> cast({invalidate_session, Host, Port, Session}). @@ -127,34 +149,36 @@ invalidate_session(Port, Session) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} +-spec init(list()) -> {ok, #state{}}. +%% Possible return values not used now. +%% | {ok, #state{}, timeout()} | ignore | {stop, term()}. +%% %% Description: Initiates the server %%-------------------------------------------------------------------- -init(Opts) -> +init([Opts]) -> process_flag(trap_exit, true), - CacheCb = proplists:get_value(session_cache, Opts, ssl_session_cache), + CacheCb = proplists:get_value(session_cb, Opts, ssl_session_cache), SessionLifeTime = proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'), CertDb = ssl_certificate_db:create(), - SessionCache = CacheCb:init(), + SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])), Timer = erlang:send_after(SessionLifeTime * 1000, self(), validate_sessions), {ok, #state{certificate_db = CertDb, session_cache = SessionCache, session_cache_cb = CacheCb, - session_lifetime = SessionLifeTime , + session_lifetime = SessionLifeTime, session_validation_timer = Timer}}. %%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} +-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}. +%% Possible return values not used now. +%% {reply, reply(), #state{}, timeout()} | +%% {noreply, #state{}} | +%% {noreply, #state{}, timeout()} | +%% {stop, reason(), reply(), #state{}} | +%% {stop, reason(), #state{}}. +%% %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call({{connection_init, "", _Role}, Pid}, _From, @@ -163,19 +187,17 @@ handle_call({{connection_init, "", _Role}, Pid}, _From, Result = {ok, make_ref(), Cache}, {reply, Result, State}; -handle_call({{connection_init, TrustedcertsFile, _Role}, Pid}, _From, +handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, #state{certificate_db = Db, session_cache = Cache} = State) -> erlang:monitor(process, Pid), Result = try - {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, TrustedcertsFile, Db), + {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db), {ok, Ref, Cache} catch - _:{badmatch, Error} -> - {error, Error}; - _E:_R -> - {error, {_R,erlang:get_stacktrace()}} + _:Reason -> + {error, Reason} end, {reply, Result, State}; @@ -197,18 +219,16 @@ handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) -> try ssl_certificate_db:cache_pem_file(Pid,File,Db) of Result -> {reply, Result, State} - catch _:{badmatch, Reason} -> - {reply, Reason, State}; - _:Reason -> + catch + _:Reason -> {reply, {error, Reason}, State} - end; - -handle_call(_,_, State) -> - {reply, ok, State}. + end. %%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. +%% Possible return values not used now. +%% | {noreply, #state{}, timeout()} | +%% {stop, reason(), #state{}}. +%% %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast({register_session, Host, Port, Session}, @@ -242,9 +262,11 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}}, {noreply, State}. %%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_info(msg(), #state{}) -> {noreply, #state{}}. +%% Possible return values not used now. +%% |{noreply, #state{}, timeout()} | +%% {stop, reason(), #state{}}. +%% %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info(validate_sessions, #state{session_cache_cb = CacheCb, @@ -277,7 +299,8 @@ handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() +-spec terminate(reason(), #state{}) -> term(). +%% %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. @@ -293,7 +316,8 @@ terminate(_Reason, #state{certificate_db = Db, ok. %%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +-spec code_change(term(), #state{}, list()) -> {ok, #state{}}. +%% %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> @@ -332,10 +356,9 @@ init_session_validator([Cache, CacheCb, LifeTime]) -> CacheCb:foldl(fun session_validation/2, LifeTime, Cache). -session_validation({{Host, Port, _}, Session}, LifeTime) -> +session_validation({{{Host, Port}, _}, Session}, LifeTime) -> validate_session(Host, Port, Session, LifeTime), LifeTime; session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. - diff --git a/lib/ssl/src/ssl_pem.erl b/lib/ssl/src/ssl_pem.erl deleted file mode 100644 index 0a1bf0f32a..0000000000 --- a/lib/ssl/src/ssl_pem.erl +++ /dev/null @@ -1,147 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. 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(ssl_pem). - -%%% Purpose: Reading and writing of PEM type encoded files for SSL. - -%% NB write_file/2 is only preliminary. - -%% PEM encoded files have the following structure: -%% -%% <text> -%% -----BEGIN SOMETHING-----<CR><LF> -%% <Base64 encoding line><CR><LF> -%% <Base64 encoding line><CR><LF> -%% ... -%% -----END SOMETHING-----<CR><LF> -%% <text> -%% -%% A file can contain several BEGIN/END blocks. Text lines between -%% blocks are ignored. - --export([read_file/1, read_file/2, write_file/2]). - -%% Read a PEM file and return each decoding as a binary. - -read_file(File) -> - read_file(File, no_passwd). - -read_file(File, Passwd) -> - {ok, Fd} = file:open(File, [read]), - Result = decode_file(Fd, Passwd), - file:close(Fd), - Result. - -decode_file(Fd, Passwd) -> - decode_file(Fd, [], [], notag, [Passwd]). - -decode_file(Fd, _RLs, Ens, notag, Info) -> - case io:get_line(Fd, "") of - "-----BEGIN CERTIFICATE REQUEST-----" ++ _ -> - decode_file(Fd, [], Ens, cert_req, Info); - "-----BEGIN CERTIFICATE-----" ++ _ -> - decode_file(Fd, [], Ens, cert, Info); - "-----BEGIN RSA PRIVATE KEY-----" ++ _ -> - decode_file(Fd, [], Ens, rsa_private_key, Info); - eof -> - {ok, lists:reverse(Ens)}; - _ -> - decode_file(Fd, [], Ens, notag, Info) - end; -decode_file(Fd, RLs, Ens, Tag, Info0) -> - case io:get_line(Fd, "") of - "Proc-Type: 4,ENCRYPTED"++_ -> - Info = dek_info(Fd, Info0), - decode_file(Fd, RLs, Ens, Tag, Info); - "-----END" ++ _ -> % XXX sloppy - Cs = lists:flatten(lists:reverse(RLs)), - Bin = ssl_base64:join_decode(Cs), - case Info0 of - [Password, Cipher, SaltHex | Info1] -> - Decoded = decode_key(Bin, Password, Cipher, unhex(SaltHex)), - decode_file(Fd, [], [{Tag, Decoded}| Ens], notag, Info1); - _ -> - decode_file(Fd, [], [{Tag, Bin}| Ens], notag, Info0) - end; - eof -> - {ok, lists:reverse(Ens)}; - L -> - decode_file(Fd, [L|RLs], Ens, Tag, Info0) - end. - -dek_info(Fd, Info) -> - Line = io:get_line(Fd, ""), - [_, DekInfo0] = string:tokens(Line, ": "), - DekInfo1 = string:tokens(DekInfo0, ",\n"), - Info ++ DekInfo1. - -unhex(S) -> - unhex(S, []). - -unhex("", Acc) -> - lists:reverse(Acc); -unhex([D1, D2 | Rest], Acc) -> - unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]). - -decode_key(Data, Password, "DES-CBC", Salt) -> - Key = password_to_key(Password, Salt, 8), - IV = Salt, - crypto:des_cbc_decrypt(Key, IV, Data); -decode_key(Data, Password, "DES-EDE3-CBC", Salt) -> - Key = password_to_key(Password, Salt, 24), - IV = Salt, - <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key, - crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data). - -write_file(File, Ds) -> - file:write_file(File, encode_file(Ds)). - -encode_file(Ds) -> - [encode_file_1(D) || D <- Ds]. - -encode_file_1({cert, Bin}) -> - %% PKIX (X.509) - ["-----BEGIN CERTIFICATE-----\n", - ssl_base64:encode_split(Bin), - "-----END CERTIFICATE-----\n\n"]; -encode_file_1({cert_req, Bin}) -> - %% PKCS#10 - ["-----BEGIN CERTIFICATE REQUEST-----\n", - ssl_base64:encode_split(Bin), - "-----END CERTIFICATE REQUEST-----\n\n"]; -encode_file_1({rsa_private_key, Bin}) -> - %% PKCS#? - ["XXX Following key assumed not encrypted\n", - "-----BEGIN RSA PRIVATE KEY-----\n", - ssl_base64:encode_split(Bin), - "-----END RSA PRIVATE KEY-----\n\n"]. - -password_to_key(Data, Salt, KeyLen) -> - <<Key:KeyLen/binary, _/binary>> = - password_to_key(<<>>, Data, Salt, KeyLen, <<>>), - Key. - -password_to_key(_, _, _, Len, Acc) when Len =< 0 -> - Acc; -password_to_key(Prev, Data, Salt, Len, Acc) -> - M = crypto:md5([Prev, Data, Salt]), - password_to_key(M, Data, Salt, Len - byte_size(M), <<Acc/binary, M/binary>>). diff --git a/lib/ssl/src/ssl_pkix.erl b/lib/ssl/src/ssl_pkix.erl deleted file mode 100644 index 8f540f74ad..0000000000 --- a/lib/ssl/src/ssl_pkix.erl +++ /dev/null @@ -1,307 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. 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% -%% - -%%% Purpose : API module for decoding of certificates. - --module(ssl_pkix). - --include("ssl_pkix.hrl"). - --export([decode_cert_file/1, decode_cert_file/2, - decode_cert/1, decode_cert/2, encode_cert/1, encoded_tbs_cert/1, - signature_digest/1, decode_rsa_keyfile/2]). - -%% The public API is dprecated by public_key and -%% the internal application API is no longer used ssl. -%% So this file can be compleatly removed in R14. --deprecated({decode_cert_file, 1, next_major_release}). --deprecated({decode_cert_file, 2, next_major_release}). --deprecated({decode_cert, 1, next_major_release}). --deprecated({decode_cert, 2, next_major_release}). - -%%==================================================================== -%% API -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: decode_cert_file(File, <Opts>) -> {ok, Cert} | {ok, [Cert]} -%% -%% File = string() -%% Opts = [Opt] -%% Opt = pem | ssl | pkix - ssl and pkix are mutual exclusive -%% Cert = term() -%% -%% Description: Decodes certificats found in file <File>. -%% If the options list is empty the certificate is -%% returned as a DER encoded binary, i.e. {ok, Bin} is returned, where -%% Bin> is the provided input. The options pkix and ssl imply that the -%% certificate is returned as a parsed ASN.1 structure in the form of -%% an Erlang term. The ssl option gives a more elaborate return -%% structure, with more explicit information. In particular object -%% identifiers are replaced by atoms. The option subject implies that -%% only the subject's distinguished name part of the certificate is -%% returned. It can only be used together with the option pkix or the -%% option ssl. -%%-------------------------------------------------------------------- -decode_cert_file(File) -> - decode_cert_file(File, []). - -decode_cert_file(File, Opts) -> - case lists:member(pem, Opts) of - true -> - {ok, List} = ssl_pem:read_file(File), - Certs = [Bin || {cert, Bin} <- List], - NewOpts = lists:delete(pem, Opts), - Fun = fun(Cert) -> - {ok, Decoded} = decode_cert(Cert, NewOpts), - Decoded - end, - case lists:map(Fun, Certs) of - [DecodedCert] -> - {ok, DecodedCert}; - DecodedCerts -> - {ok, DecodedCerts} - end; - false -> - {ok, Bin} = file:read_file(File), - decode_cert(Bin, Opts) - end. -%%-------------------------------------------------------------------- -%% Function: decode_cert(Bin, <Opts>) -> {ok, Cert} -%% Bin - binary() -%% Opts = [Opt] -%% Opt = ssl | pkix | subject - ssl and pkix are mutual exclusive -%% Cert = term() -%% -%% Description: If the options list is empty the certificate is -%% returned as a DER encoded binary, i.e. {ok, Bin} is returned, where -%% Bin> is the provided input. The options pkix and ssl imply that the -%% certificate is returned as a parsed ASN.1 structure in the form of -%% an Erlang term. The ssl option gives a more elaborate return -%% structure, with more explicit information. In particular object -%% identifiers are replaced by atoms. The option subject implies that -%% only the subject's distinguished name part of the certificate is -%% returned. It can only be used together with the option pkix or the -%% option ssl. -%%-------------------------------------------------------------------- -decode_cert(Bin) -> - decode_cert(Bin, []). - -decode_cert(Bin, []) when is_binary(Bin) -> - {ok, Bin}; -decode_cert(Bin, Opts) when is_binary(Bin) -> - - {ok, Cert} = 'OTP-PKIX':decode('Certificate', Bin), - - case {lists:member(ssl, Opts), lists:member(pkix, Opts)} of - {true, false} -> - cert_return(transform(Cert, ssl), Opts); - {false, true} -> - cert_return(transform(Cert, pkix), Opts); - _ -> - {error, eoptions} - end. - -encode_cert(#'Certificate'{} = Cert) -> - {ok, List} = 'OTP-PKIX':encode('Certificate', Cert), - list_to_binary(List). - -decode_rsa_keyfile(KeyFile, Password) -> - {ok, List} = ssl_pem:read_file(KeyFile, Password), - [PrivatKey] = [Bin || {rsa_private_key, Bin} <- List], - 'OTP-PKIX':decode('RSAPrivateKey', PrivatKey). - -%%==================================================================== -%% Application internal API -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: encoded_tbs_cert(Cert) -> PKXCert -%% -%% Cert = binary() - Der encoded -%% PKXCert = binary() - Der encoded -%% -%% Description: Extracts the binary TBSCert from the binary Certificate. -%%-------------------------------------------------------------------- -encoded_tbs_cert(Cert) -> - {ok, PKIXCert} = - 'OTP-PKIX':decode_TBSCert_exclusive(Cert), - {'Certificate', - {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = PKIXCert, - EncodedTBSCert. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- - -cert_return(Cert, Opts) -> - case lists:member(subject, Opts) of - true -> - {ok, get_subj(Cert)}; - false -> - {ok, Cert} - end. - - -%% Transfrom from PKIX1-Explicit88 to SSL-PKIX. - -transform(#'Certificate'{signature = Signature, - signatureAlgorithm = SignatureAlgorithm, - tbsCertificate = TbsCertificate} = Cert, Type) -> - Cert#'Certificate'{tbsCertificate = transform(TbsCertificate, Type), - signatureAlgorithm = transform(SignatureAlgorithm, Type), - signature = transform(Signature, Type)}; - -%% -record('TBSCertificate',{ -%% version = asn1_DEFAULT, serialNumber, signature, issuer, validity, subject, -%% subjectPublicKeyInfo, issuerUniqueID = asn1_NOVALUE, -%% subjectUniqueID = asn1_NOVALUE, extensions = asn1_NOVALUE}). - -transform(#'TBSCertificate'{signature = Signature, issuer = Issuer, - subject = Subject, extensions = Extensions, - subjectPublicKeyInfo = SPKInfo} = TBSCert, Type) -> - TBSCert#'TBSCertificate'{signature = transform(Signature, Type), - issuer = transform(Issuer, Type), - subject = transform(Subject, Type), - subjectPublicKeyInfo = transform(SPKInfo, Type), - extensions = transform_extensions(Extensions, Type) - }; - -transform(#'AlgorithmIdentifier'{algorithm = Algorithm, - parameters = Params}, ssl) -> - SignAlgAny = - #'SignatureAlgorithm-Any'{algorithm = Algorithm, parameters = Params}, - {ok, AnyEnc} = 'OTP-PKIX':encode('SignatureAlgorithm-Any', SignAlgAny), - {ok, SignAlgCd} = 'OTP-PKIX':decode('SignatureAlgorithm', - list_to_binary(AnyEnc)), - NAlgo = ssl_pkix_oid:id2atom(SignAlgCd#'SignatureAlgorithm'.algorithm), - SignAlgCd#'SignatureAlgorithm'{algorithm = NAlgo}; - -transform({rdnSequence, Lss}, Type) when is_list(Lss) -> - {rdnSequence, [[transform(L, Type) || L <- Ls] || Ls <- Lss]}; -transform({rdnSequence, Lss}, _) -> - {rdnSequence, Lss}; - -transform(#'AttributeTypeAndValue'{} = ATAV, ssl) -> - {ok, ATAVEnc} = - 'OTP-PKIX':encode('AttributeTypeAndValue', ATAV), - {ok, ATAVDec} = 'OTP-PKIX':decode('SSLAttributeTypeAndValue', - list_to_binary(ATAVEnc)), - AttrType = ATAVDec#'SSLAttributeTypeAndValue'.type, - #'AttributeTypeAndValue'{type = ssl_pkix_oid:id2atom(AttrType), - value = - ATAVDec#'SSLAttributeTypeAndValue'.value}; - -transform(#'AttributeTypeAndValue'{} = Att, pkix) -> - Att; - -%% -record('SubjectPublicKeyInfo',{ -%% algorithm, subjectPublicKey}). -%% -%% -record('SubjectPublicKeyInfo_algorithm',{ -%% algo, parameters = asn1_NOVALUE}). -%% -%% -record('SubjectPublicKeyInfo-Any',{ -%% algorithm, subjectPublicKey}). -%% -%% -record('PublicKeyAlgorithm',{ -%% algorithm, parameters = asn1_NOVALUE}). - -transform(#'SubjectPublicKeyInfo'{subjectPublicKey = SubjectPublicKey, - algorithm = Algorithm}, ssl) -> - %% Transform from SubjectPublicKeyInfo (PKIX1Explicit88) - %% to SubjectPublicKeyInfo-Any (SSL-PKIX). - Algo = Algorithm#'AlgorithmIdentifier'.algorithm, - Parameters = Algorithm#'AlgorithmIdentifier'.parameters, - AlgorithmAny = #'PublicKeyAlgorithm'{algorithm = Algo, - parameters = Parameters}, - {0, Bin} = SubjectPublicKey, - SInfoAny = #'SSLSubjectPublicKeyInfo-Any'{algorithm = AlgorithmAny, - subjectPublicKey = Bin}, - - %% Encode according to SubjectPublicKeyInfo-Any, and decode according - %% to SubjectPublicKeyInfo. - {ok, AnyEnc} = - 'OTP-PKIX':encode('SSLSubjectPublicKeyInfo-Any', SInfoAny), - {ok, SInfoCd} = 'OTP-PKIX':decode('SSLSubjectPublicKeyInfo', - list_to_binary(AnyEnc)), - %% Replace object identifier by atom - AlgorithmCd = SInfoCd#'SSLSubjectPublicKeyInfo'.algorithm, - AlgoCd = AlgorithmCd#'SSLSubjectPublicKeyInfo_algorithm'.algo, - Params = AlgorithmCd#'SSLSubjectPublicKeyInfo_algorithm'.parameters, - Key = SInfoCd#'SSLSubjectPublicKeyInfo'.subjectPublicKey, - NAlgoCd = ssl_pkix_oid:id2atom(AlgoCd), - NAlgorithmCd = - #'SubjectPublicKeyInfo_algorithm'{algorithm = NAlgoCd, - parameters = Params}, - #'SubjectPublicKeyInfo'{algorithm = NAlgorithmCd, - subjectPublicKey = Key - }; -transform(#'SubjectPublicKeyInfo'{} = SInfo, pkix) -> - SInfo; - -transform(#'Extension'{extnID = ExtnID} = Ext, ssl) -> - NewExtID = ssl_pkix_oid:id2atom(ExtnID), - ExtAny = setelement(1, Ext, 'Extension-Any'), - {ok, AnyEnc} = 'OTP-PKIX':encode('Extension-Any', ExtAny), - {ok, ExtCd} = 'OTP-PKIX':decode('SSLExtension', list_to_binary(AnyEnc)), - - ExtValue = transform_extension_value(NewExtID, - ExtCd#'SSLExtension'.extnValue, - ssl), - #'Extension'{extnID = NewExtID, - critical = ExtCd#'SSLExtension'.critical, - extnValue = ExtValue}; - -transform(#'Extension'{extnID = ExtnID, extnValue = ExtnValue} = Ext, pkix) -> - NewExtID = ssl_pkix_oid:id2atom(ExtnID), - ExtValue = transform_extension_value(NewExtID, ExtnValue, pkix), - Ext#'Extension'{extnValue = ExtValue}; - -transform(#'AuthorityKeyIdentifier'{authorityCertIssuer = CertIssuer} = Ext, - Type) -> - Ext#'AuthorityKeyIdentifier'{authorityCertIssuer = - transform(CertIssuer, Type)}; - -transform([{directoryName, Value}], Type) -> - [{directoryName, transform(Value, Type)}]; - -transform(X, _) -> - X. - -transform_extension_value('ce-authorityKeyIdentifier', Value, Type) -> - transform(Value, Type); -transform_extension_value(_, Value, _) -> - Value. - -transform_extensions(Exts, Type) when is_list(Exts) -> - [transform(Ext, Type) || Ext <- Exts]; -transform_extensions(Exts, _) -> - Exts. - -get_subj(Cert) -> - (Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject. - -signature_digest(BinSignature) -> - case (catch 'OTP-PKIX':decode('DigestInfo', BinSignature)) of - {ok, DigestInfo} -> - list_to_binary(DigestInfo#'DigestInfo'.digest); - _ -> - {error, decode_error} - end. diff --git a/lib/ssl/src/ssl_pkix.hrl b/lib/ssl/src/ssl_pkix.hrl deleted file mode 100644 index a8463369f6..0000000000 --- a/lib/ssl/src/ssl_pkix.hrl +++ /dev/null @@ -1,81 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. 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% -%% - -%% - --ifndef(ssl_pkix). --define(ssl_pkix, true). - --include("OTP-PKIX.hrl"). - -%% The following commented out records are currently defined in OTP-PKIX.hrl -%% and are considered a public interface through ssl_pkix.hrl. -%% NOTE do not include OTP-PKIX.hrl it is an generated file -%% and may change but the following records will still be -%% availanble from this file. - -% -record('Certificate', { -% tbsCertificate, -% signatureAlgorithm, -% signature}). - -% -record('TBSCertificate', { -% version = asn1_DEFAULT, -% serialNumber, -% signature, -% issuer, -% validity, -% subject, -% subjectPublicKeyInfo, -% issuerUniqueID = asn1_NOVALUE, -% subjectUniqueID = asn1_NOVALUE, -% extensions = asn1_NOVALUE}). - -% -record('AttributeTypeAndValue', { -% type, -% value}). - -% -record('SubjectPublicKeyInfo', { -% algorithm, -% subjectPublicKey}). - --record('SubjectPublicKeyInfo_algorithm', { - algorithm, - parameters = asn1_NOVALUE}). - -% -record('FieldID', { -% fieldType, -% parameters}). - -% -record('Characteristic-two', { -% m, -% basis, -% parameters}). - -% -record('ExtensionAttribute', { -% extensionAttributeType, -% extensionAttributeValue}). - -% -record('Extension', { -% extnID, -% critical = asn1_DEFAULT, -% extnValue}). - --endif. % -ifdef(ssl_pkix). - diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index da48f049f6..f1c0073965 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -29,7 +29,7 @@ -include("ssl_internal.hrl"). -include("ssl_alert.hrl"). -include("ssl_handshake.hrl"). --include("ssl_debug.hrl"). +-include("ssl_cipher.hrl"). %% Connection state handling -export([init_connection_states/1, @@ -38,7 +38,10 @@ set_mac_secret/4, set_master_secret/2, activate_pending_connection_state/2, - set_pending_cipher_state/4]). + set_pending_cipher_state/4, + set_renegotiation_flag/2, + set_client_verify_data/3, + set_server_verify_data/3]). %% Handling of incoming data -export([get_tls_records/2]). @@ -62,10 +65,9 @@ %%==================================================================== %% Internal application API %%==================================================================== + %%-------------------------------------------------------------------- -%% Function: init_connection_states(Role) -> #connection_states{} -%% Role = client | server -%% Random = binary() +-spec init_connection_states(client | server) -> #connection_states{}. %% %% Description: Creates a connection_states record with appropriate %% values for the initial SSL connection setup. @@ -81,9 +83,8 @@ init_connection_states(Role) -> }. %%-------------------------------------------------------------------- -%% Function: current_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec current_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the current conection state. @@ -96,9 +97,8 @@ current_connection_state(#connection_states{current_write = Current}, Current. %%-------------------------------------------------------------------- -%% Function: pending_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec pending_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the pending conection state. @@ -111,14 +111,11 @@ pending_connection_state(#connection_states{pending_write = Pending}, Pending. %%-------------------------------------------------------------------- -%% Function: update_security_params(Params, States) -> -%% #connection_states{} -%% Params = #security_parameters{} -%% States = #connection_states{} +-spec update_security_params(#security_parameters{}, #security_parameters{}, + #connection_states{}) -> #connection_states{}. %% %% Description: Creates a new instance of the connection_states record -%% where the pending states gets its security parameters -%% updated to <Params>. +%% where the pending states gets its security parameters updated. %%-------------------------------------------------------------------- update_security_params(ReadParams, WriteParams, States = #connection_states{pending_read = Read, @@ -131,14 +128,10 @@ update_security_params(ReadParams, WriteParams, States = WriteParams} }. %%-------------------------------------------------------------------- -%% Function: set_mac_secret(ClientWriteMacSecret, -%% ServerWriteMacSecret, Role, States) -> -%% #connection_states{} -%% MacSecret = binary() -%% States = #connection_states{} -%% Role = server | client +-spec set_mac_secret(binary(), binary(), client | server, + #connection_states{}) -> #connection_states{}. %% -%% update the mac_secret field in pending connection states +%% Description: update the mac_secret field in pending connection states %%-------------------------------------------------------------------- set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) -> set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States); @@ -155,12 +148,9 @@ set_mac_secret(ReadMacSecret, WriteMacSecret, %%-------------------------------------------------------------------- -%% Function: set_master_secret(MasterSecret, States) -> -%% #connection_states{} -%% MacSecret = -%% States = #connection_states{} +-spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}. %% -%% Set master_secret in pending connection states +%% Description: Set master_secret in pending connection states %%-------------------------------------------------------------------- set_master_secret(MasterSecret, States = #connection_states{pending_read = Read, @@ -175,12 +165,94 @@ set_master_secret(MasterSecret, master_secret = MasterSecret}}, States#connection_states{pending_read = Read1, pending_write = Write1}. +%%-------------------------------------------------------------------- +-spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}. +%% +%% Description: Set secure_renegotiation in pending connection states +%%-------------------------------------------------------------------- +set_renegotiation_flag(Flag, #connection_states{ + current_read = CurrentRead0, + current_write = CurrentWrite0, + pending_read = PendingRead0, + pending_write = PendingWrite0} + = ConnectionStates) -> + CurrentRead = CurrentRead0#connection_state{secure_renegotiation = Flag}, + CurrentWrite = CurrentWrite0#connection_state{secure_renegotiation = Flag}, + PendingRead = PendingRead0#connection_state{secure_renegotiation = Flag}, + PendingWrite = PendingWrite0#connection_state{secure_renegotiation = Flag}, + ConnectionStates#connection_states{current_read = CurrentRead, + current_write = CurrentWrite, + pending_read = PendingRead, + pending_write = PendingWrite}. + +%%-------------------------------------------------------------------- +-spec set_client_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. +%% +%% Description: Set verify data in connection states. +%%-------------------------------------------------------------------- +set_client_verify_data(current_read, Data, + #connection_states{current_read = CurrentRead0, + pending_write = PendingWrite0} + = ConnectionStates) -> + CurrentRead = CurrentRead0#connection_state{client_verify_data = Data}, + PendingWrite = PendingWrite0#connection_state{client_verify_data = Data}, + ConnectionStates#connection_states{current_read = CurrentRead, + pending_write = PendingWrite}; +set_client_verify_data(current_write, Data, + #connection_states{pending_read = PendingRead0, + current_write = CurrentWrite0} + = ConnectionStates) -> + PendingRead = PendingRead0#connection_state{client_verify_data = Data}, + CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data}, + ConnectionStates#connection_states{pending_read = PendingRead, + current_write = CurrentWrite}; +set_client_verify_data(current_both, Data, + #connection_states{current_read = CurrentRead0, + current_write = CurrentWrite0} + = ConnectionStates) -> + CurrentRead = CurrentRead0#connection_state{client_verify_data = Data}, + CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data}, + ConnectionStates#connection_states{current_read = CurrentRead, + current_write = CurrentWrite}. +%%-------------------------------------------------------------------- +-spec set_server_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. +%% +%% Description: Set verify data in pending connection states. +%%-------------------------------------------------------------------- +set_server_verify_data(current_write, Data, + #connection_states{pending_read = PendingRead0, + current_write = CurrentWrite0} + = ConnectionStates) -> + PendingRead = PendingRead0#connection_state{server_verify_data = Data}, + CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data}, + ConnectionStates#connection_states{pending_read = PendingRead, + current_write = CurrentWrite}; + +set_server_verify_data(current_read, Data, + #connection_states{current_read = CurrentRead0, + pending_write = PendingWrite0} + = ConnectionStates) -> + CurrentRead = CurrentRead0#connection_state{server_verify_data = Data}, + PendingWrite = PendingWrite0#connection_state{server_verify_data = Data}, + ConnectionStates#connection_states{current_read = CurrentRead, + pending_write = PendingWrite}; + +set_server_verify_data(current_both, Data, + #connection_states{current_read = CurrentRead0, + current_write = CurrentWrite0} + = ConnectionStates) -> + CurrentRead = CurrentRead0#connection_state{server_verify_data = Data}, + CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data}, + ConnectionStates#connection_states{current_read = CurrentRead, + current_write = CurrentWrite}. %%-------------------------------------------------------------------- -%% Function: activate_pending_connection_state(States, Type) -> -%% #connection_states{} -%% States = #connection_states{} -%% Type = read | write +-spec activate_pending_connection_state(#connection_states{}, read | write) -> + #connection_states{}. %% %% Description: Creates a new instance of the connection_states record %% where the pending state of <Type> has been activated. @@ -191,7 +263,9 @@ activate_pending_connection_state(States = NewCurrent = Pending#connection_state{sequence_number = 0}, SecParams = Pending#connection_state.security_parameters, ConnectionEnd = SecParams#security_parameters.connection_end, - NewPending = empty_connection_state(ConnectionEnd), + EmptyPending = empty_connection_state(ConnectionEnd), + SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, + NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, States#connection_states{current_read = NewCurrent, pending_read = NewPending }; @@ -202,17 +276,17 @@ activate_pending_connection_state(States = NewCurrent = Pending#connection_state{sequence_number = 0}, SecParams = Pending#connection_state.security_parameters, ConnectionEnd = SecParams#security_parameters.connection_end, - NewPending = empty_connection_state(ConnectionEnd), + EmptyPending = empty_connection_state(ConnectionEnd), + SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, + NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, States#connection_states{current_write = NewCurrent, pending_write = NewPending }. %%-------------------------------------------------------------------- -%% Function: set_pending_cipher_state(States, ClientState, -%% ServerState, Role) -> -%% #connection_states{} -%% ClientState = ServerState = #cipher_state{} -%% States = #connection_states{} +-spec set_pending_cipher_state(#connection_states{}, #cipher_state{}, + #cipher_state{}, client | server) -> + #connection_states{}. %% %% Description: Set the cipher state in the specified pending connection state. %%-------------------------------------------------------------------- @@ -231,12 +305,10 @@ set_pending_cipher_state(#connection_states{pending_read = Read, pending_write = Write#connection_state{cipher_state = ClientState}}. %%-------------------------------------------------------------------- -%% Function: get_tls_record(Data, Buffer) -> Result -%% Result = {[#tls_compressed{}], NewBuffer} -%% Data = Buffer = NewBuffer = binary() -%% -%% Description: given old buffer and new data from TCP, packs up a records -%% and returns it as a list of #tls_compressed, also returns leftover +-spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}. +%% +%% Description: Given old buffer and new data from TCP, packs up a records +%% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- get_tls_records(Data, <<>>) -> @@ -299,8 +371,8 @@ get_tls_records_aux(Data, Acc) -> {lists:reverse(Acc), Data}. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version) -> #protocol_version{} -%% Version = atom() +-spec protocol_version(tls_atom_version() | tls_version()) -> + tls_version() | tls_atom_version(). %% %% Description: Creates a protocol version record from a version atom %% or vice versa. @@ -311,19 +383,16 @@ protocol_version(tlsv1) -> {3, 1}; protocol_version(sslv3) -> {3, 0}; -protocol_version(sslv2) -> +protocol_version(sslv2) -> %% Backwards compatibility {2, 0}; protocol_version({3, 2}) -> 'tlsv1.1'; protocol_version({3, 1}) -> tlsv1; protocol_version({3, 0}) -> - sslv3; -protocol_version({2, 0}) -> - sslv2. + sslv3. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version1, Version2) -> #protocol_version{} -%% Version1 = Version2 = #protocol_version{} +-spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version(). %% %% Description: Lowes protocol version of two given versions %%-------------------------------------------------------------------- @@ -338,8 +407,7 @@ lowest_protocol_version(Version = {M,_}, lowest_protocol_version(_,Version) -> Version. %%-------------------------------------------------------------------- -%% Function: protocol_version(Versions) -> #protocol_version{} -%% Versions = [#protocol_version{}] +-spec highest_protocol_version([tls_version()]) -> tls_version(). %% %% Description: Highest protocol version present in a list %%-------------------------------------------------------------------- @@ -361,14 +429,13 @@ highest_protocol_version(_, [Version | Rest]) -> highest_protocol_version(Version, Rest). %%-------------------------------------------------------------------- -%% Function: supported_protocol_versions() -> Versions -%% Versions = [#protocol_version{}] -%% +-spec supported_protocol_versions() -> [tls_version()]. +%% %% Description: Protocol versions supported %%-------------------------------------------------------------------- supported_protocol_versions() -> Fun = fun(Version) -> - protocol_version(Version) + protocol_version(Version) end, case application:get_env(ssl, protocol_version) of undefined -> @@ -376,14 +443,20 @@ supported_protocol_versions() -> {ok, []} -> lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS); {ok, Vsns} when is_list(Vsns) -> - lists:map(Fun, Vsns); + Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)), + supported_protocol_versions(Versions); {ok, Vsn} -> - [Fun(Vsn)] + Versions = lists:filter(fun is_acceptable_version/1, [Fun(Vsn)]), + supported_protocol_versions(Versions) end. +supported_protocol_versions([]) -> + ?DEFAULT_SUPPORTED_VERSIONS; +supported_protocol_versions([_|_] = Vsns) -> + Vsns. + %%-------------------------------------------------------------------- -%% Function: is_acceptable_version(Version) -> true | false -%% Version = #protocol_version{} +-spec is_acceptable_version(tls_version()) -> boolean(). %% %% Description: ssl version 2 is not acceptable security risks are too big. %%-------------------------------------------------------------------- @@ -394,7 +467,7 @@ is_acceptable_version(_) -> false. %%-------------------------------------------------------------------- -%% Function: compressions() -> binary() +-spec compressions() -> [binary()]. %% %% Description: return a list of compressions supported (currently none) %%-------------------------------------------------------------------- @@ -402,8 +475,8 @@ compressions() -> [?byte(?NULL)]. %%-------------------------------------------------------------------- -%% Function: decode_cipher_text(CipherText, ConnectionStates0) -> -%% {Plain, ConnectionStates} +-spec decode_cipher_text(#ssl_tls{}, #connection_states{}) -> + {#ssl_tls{}, #connection_states{}}| #alert{}. %% %% Description: Decode cipher text %%-------------------------------------------------------------------- @@ -412,13 +485,77 @@ decode_cipher_text(CipherText, ConnnectionStates0) -> #connection_state{compression_state = CompressionS0, security_parameters = SecParams} = ReadState0, CompressAlg = SecParams#security_parameters.compression_algorithm, - {Compressed, ReadState1} = decipher(CipherText, ReadState0), - {Plain, CompressionS1} = uncompress(CompressAlg, - Compressed, CompressionS0), - ConnnectionStates = ConnnectionStates0#connection_states{ - current_read = ReadState1#connection_state{ - compression_state = CompressionS1}}, - {Plain, ConnnectionStates}. + case decipher(CipherText, ReadState0) of + {Compressed, ReadState1} -> + {Plain, CompressionS1} = uncompress(CompressAlg, + Compressed, CompressionS0), + ConnnectionStates = ConnnectionStates0#connection_states{ + current_read = ReadState1#connection_state{ + compression_state = CompressionS1}}, + {Plain, ConnnectionStates}; + #alert{} = Alert -> + Alert + end. +%%-------------------------------------------------------------------- +-spec encode_data(iolist(), tls_version(), #connection_states{}, integer()) -> + {iolist(), iolist(), #connection_states{}}. +%% +%% Description: Encodes data to send on the ssl-socket. +%%-------------------------------------------------------------------- +encode_data(Frag, Version, ConnectionStates, RenegotiateAt) + when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) -> + case encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates, RenegotiateAt) of + {renegotiate, Data} -> + {[], Data, ConnectionStates}; + {Msg, CS} -> + {Msg, [], CS} + end; + +encode_data(Frag, Version, ConnectionStates, RenegotiateAt) when is_binary(Frag) -> + Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048), + encode_data(Data, Version, ConnectionStates, RenegotiateAt); + +encode_data(Data, Version, ConnectionStates0, RenegotiateAt) when is_list(Data) -> + {ConnectionStates, EncodedMsg, NotEncdedData} = + lists:foldl(fun(B, {CS0, Encoded, Rest}) -> + case encode_plain_text(?APPLICATION_DATA, + Version, B, CS0, RenegotiateAt) of + {renegotiate, NotEnc} -> + {CS0, Encoded, [NotEnc | Rest]}; + {Enc, CS1} -> + {CS1, [Enc | Encoded], Rest} + end + end, {ConnectionStates0, [], []}, Data), + {lists:reverse(EncodedMsg), lists:reverse(NotEncdedData), ConnectionStates}. + +%%-------------------------------------------------------------------- +-spec encode_handshake(iolist(), tls_version(), #connection_states{}) -> + {iolist(), #connection_states{}}. +%% +%% Description: Encodes a handshake message to send on the ssl-socket. +%%-------------------------------------------------------------------- +encode_handshake(Frag, Version, ConnectionStates) -> + encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates). + +%%-------------------------------------------------------------------- +-spec encode_alert_record(#alert{}, tls_version(), #connection_states{}) -> + {iolist(), #connection_states{}}. +%% +%% Description: Encodes an alert message to send on the ssl-socket. +%%-------------------------------------------------------------------- +encode_alert_record(#alert{level = Level, description = Description}, + Version, ConnectionStates) -> + encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>, + ConnectionStates). + +%%-------------------------------------------------------------------- +-spec encode_change_cipher_spec(tls_version(), #connection_states{}) -> + {iolist(), #connection_states{}}. +%% +%% Description: Encodes a change_cipher_spec-message to send on the ssl socket. +%%-------------------------------------------------------------------- +encode_change_cipher_spec(Version, ConnectionStates) -> + encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates). %%-------------------------------------------------------------------- %%% Internal functions @@ -433,12 +570,10 @@ initial_connection_state(ConnectionEnd) -> }. initial_security_params(ConnectionEnd) -> - #security_parameters{connection_end = ConnectionEnd, - bulk_cipher_algorithm = ?NULL, - mac_algorithm = ?NULL, - compression_algorithm = ?NULL, - cipher_type = ?NULL - }. + SecParams = #security_parameters{connection_end = ConnectionEnd, + compression_algorithm = ?NULL}, + ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, + SecParams). empty_connection_state(ConnectionEnd) -> SecParams = empty_security_params(ConnectionEnd), @@ -474,43 +609,6 @@ split_bin(Bin, ChunkSize, Acc) -> lists:reverse(Acc, [Bin]) end. -encode_data(Frag, Version, ConnectionStates, RenegotiateAt) - when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) -> - case encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates, RenegotiateAt) of - {renegotiate, Data} -> - {[], Data, ConnectionStates}; - {Msg, CS} -> - {Msg, [], CS} - end; - -encode_data(Frag, Version, ConnectionStates, RenegotiateAt) when is_binary(Frag) -> - Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048), - encode_data(Data, Version, ConnectionStates, RenegotiateAt); - -encode_data(Data, Version, ConnectionStates0, RenegotiateAt) when is_list(Data) -> - {ConnectionStates, EncodedMsg, NotEncdedData} = - lists:foldl(fun(B, {CS0, Encoded, Rest}) -> - case encode_plain_text(?APPLICATION_DATA, - Version, B, CS0, RenegotiateAt) of - {renegotiate, NotEnc} -> - {CS0, Encoded, [NotEnc | Rest]}; - {Enc, CS1} -> - {CS1, [Enc | Encoded], Rest} - end - end, {ConnectionStates0, [], []}, Data), - {lists:reverse(EncodedMsg), lists:reverse(NotEncdedData), ConnectionStates}. - -encode_handshake(Frag, Version, ConnectionStates) -> - encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates). - -encode_alert_record(#alert{level = Level, description = Description}, - Version, ConnectionStates) -> - encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>, - ConnectionStates). - -encode_change_cipher_spec(Version, ConnectionStates) -> - encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates). - encode_plain_text(Type, Version, Data, ConnectionStates, RenegotiateAt) -> #connection_states{current_write = #connection_state{sequence_number = Num}} = ConnectionStates, @@ -544,29 +642,35 @@ encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) -> cipher(Type, Version, Fragment, CS0) -> Length = erlang:iolist_size(Fragment), - {Hash, CS1=#connection_state{cipher_state = CipherS0, + {MacHash, CS1=#connection_state{cipher_state = CipherS0, security_parameters= #security_parameters{bulk_cipher_algorithm = BCA} }} = hash_and_bump_seqno(CS0, Type, Version, Length, Fragment), - ?DBG_HEX(Fragment), - {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, Hash, Fragment), - ?DBG_HEX(Ciphered), + {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment), CS2 = CS1#connection_state{cipher_state=CipherS1}, {Ciphered, CS2}. decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) -> SP = CS0#connection_state.security_parameters, - BCA = SP#security_parameters.bulk_cipher_algorithm, % eller Cipher? + BCA = SP#security_parameters.bulk_cipher_algorithm, HashSz = SP#security_parameters.hash_size, CipherS0 = CS0#connection_state.cipher_state, - {T, Mac, CipherS1} = ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment), - CS1 = CS0#connection_state{cipher_state = CipherS1}, - TLength = size(T), - {Hash, CS2} = hash_and_bump_seqno(CS1, Type, Version, TLength, Fragment), - ok = check_hash(Hash, Mac), - {TLS#ssl_tls{fragment = T}, CS2}. + case ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment, Version) of + {T, Mac, CipherS1} -> + CS1 = CS0#connection_state{cipher_state = CipherS1}, + TLength = size(T), + {MacHash, CS2} = hash_and_bump_seqno(CS1, Type, Version, TLength, T), + case is_correct_mac(Mac, MacHash) of + true -> + {TLS#ssl_tls{fragment = T}, CS2}; + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; + #alert{} = Alert -> + Alert + end. uncompress(?NULL, Data = #ssl_tls{type = _Type, version = _Version, @@ -587,10 +691,12 @@ hash_and_bump_seqno(#connection_state{sequence_number = SeqNo, Length, Fragment), {Hash, CS0#connection_state{sequence_number = SeqNo+1}}. -check_hash(_, _) -> - ok. %% TODO check this +is_correct_mac(Mac, Mac) -> + true; +is_correct_mac(_M,_H) -> + false. -mac_hash(?NULL, {_,_}, _MacSecret, _SeqNo, _Type, +mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type, _Length, _Fragment) -> <<>>; mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) -> diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index 362b7039d4..5fb0070b91 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -60,7 +60,11 @@ compression_state, cipher_state, mac_secret, - sequence_number + sequence_number, + %% RFC 5746 + secure_renegotiation, + client_verify_data, + server_verify_data }). -define(MAX_SEQENCE_NUMBER, 18446744073709552000). %% math:pow(2, 64) - 1 = 1.8446744073709552e19 diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index bcb10daf69..25e7445180 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2010. 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 @@ -32,11 +32,10 @@ -define(GEN_UNIQUE_ID_MAX_TRIES, 10). +-type seconds() :: integer(). + %%-------------------------------------------------------------------- -%% Function: is_new(ClientSuggestedId, ServerDecidedId) -> true | false -%% -%% ClientSuggestedId = binary() -%% ServerDecidedId = binary() +-spec is_new(session_id(), session_id()) -> boolean(). %% %% Description: Checks if the session id decided by the server is a %% new or resumed sesion id. @@ -45,17 +44,11 @@ is_new(<<>>, _) -> true; is_new(SessionId, SessionId) -> false; -is_new(_, _) -> +is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- -%% Function: id(ClientInfo, Cache, CacheCb) -> SessionId -%% -%% ClientInfo = {HostIP, Port, SslOpts} -%% HostIP = ipadress() -%% Port = integer() -%% CacheCb = atom() -%% SessionId = binary() +-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. @@ -69,14 +62,8 @@ id(ClientInfo, Cache, CacheCb) -> end. %%-------------------------------------------------------------------- -%% Function: id(Port, SuggestedSessionId, ReuseFun, CacheCb, -%% SecondLifeTime) -> SessionId -%% -%% Port = integer() -%% SuggestedSessionId = SessionId = binary() -%% ReuseFun = fun(SessionId, PeerCert, Compression, CipherSuite) -> -%% true | false -%% CacheCb = atom() +-spec id(port_num(), binary(), #ssl_options{}, cache_ref(), + atom(), seconds()) -> binary(). %% %% Description: Should be called by the server side to get an id %% for the server hello message. @@ -95,10 +82,7 @@ id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb) end. %%-------------------------------------------------------------------- -%% Function: valid_session(Session, LifeTime) -> true | false -%% -%% Session = #session{} -%% LifeTime = integer() - seconds +-spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired %%-------------------------------------------------------------------- @@ -129,7 +113,7 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers, List -> hd(List) end. - + %% If we can not generate a not allready in use session ID in %% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The %% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index 4a60892235..823bf7acfa 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2008-2010. 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% %% @@ -22,23 +22,24 @@ -behaviour(ssl_session_cache_api). --export([init/0, terminate/1, lookup/2, update/3, delete/2, foldl/3, - select_session/2]). +-include("ssl_handshake.hrl"). +-include("ssl_internal.hrl"). + +-export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, + select_session/2]). + +-type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}. %%-------------------------------------------------------------------- -%% Function: init() -> Cache -%% -%% Cache - Reference to the cash (opaque) +-spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque) %% %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- -init() -> +init(_) -> ets:new(cache_name(), [set, protected]). %%-------------------------------------------------------------------- -%% Function: terminate(Cache) -> -%% -%% Cache - as returned by create/0 +-spec terminate(cache_ref()) -> any(). %% %% %% Description: Handles cache table at termination of ssl manager. %%-------------------------------------------------------------------- @@ -46,9 +47,7 @@ terminate(Cache) -> ets:delete(Cache). %%-------------------------------------------------------------------- -%% Function: lookup(Cache, Key) -> Session | undefined -%% Cache - as returned by create/0 -%% Session = #session{} +-spec lookup(cache_ref(), key()) -> #session{} | undefined. %% %% Description: Looks up a cach entry. Should be callable from any %% process. @@ -62,9 +61,7 @@ lookup(Cache, Key) -> end. %%-------------------------------------------------------------------- -%% Function: update(Cache, Key, Session) -> _ -%% Cache - as returned by create/0 -%% Session = #session{} +-spec update(cache_ref(), key(), #session{}) -> any(). %% %% Description: Caches a new session or updates a already cached one. %% Will only be called from the ssl_manager process. @@ -73,11 +70,7 @@ update(Cache, Key, Session) -> ets:insert(Cache, {Key, Session}). %%-------------------------------------------------------------------- -%% Function: delete(Cache, HostIP, Port, Id) -> _ -%% Cache - as returned by create/0 -%% HostIP = Host = string() | ipadress() -%% Port = integer() -%% Id = +-spec delete(cache_ref(), key()) -> any(). %% %% Description: Delets a cache entry. %% Will only be called from the ssl_manager process. @@ -86,28 +79,19 @@ delete(Cache, Key) -> ets:delete(Cache, Key). %%-------------------------------------------------------------------- -%% Function: foldl(Fun, Acc0, Cache) -> Acc -%% -%% Fun - fun() -%% Acc0 - term() -%% Cache - cache_ref() -%% +-spec foldl(fun(), term(), cache_ref()) -> term(). %% %% Description: Calls Fun(Elem, AccIn) on successive elements of the %% cache, starting with AccIn == Acc0. Fun/2 must return a new %% accumulator which is passed to the next call. The function returns -%% the final value of the accumulator. Acc0 is returned if the cache is -%% empty. -%% Should be callable from any process +%% the final value of the accumulator. Acc0 is returned if the cache +%% is empty.Should be callable from any process %%-------------------------------------------------------------------- foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). %%-------------------------------------------------------------------- -%% Function: select_session(Cache, PartialKey) -> [Sessions] -%% -%% Cache - as returned by create/0 -%% PartialKey - opaque Key = {PartialKey, SessionId} +-spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}]. %% %% Description: Selects a session that could be reused. Should be callable %% from any process. @@ -119,6 +103,5 @@ select_session(Cache, PartialKey) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - cache_name() -> ssl_otp_session_cache. diff --git a/lib/ssl/src/ssl_session_cache_api.erl b/lib/ssl/src/ssl_session_cache_api.erl index d2e846e9fd..f8416bf327 100644 --- a/lib/ssl/src/ssl_session_cache_api.erl +++ b/lib/ssl/src/ssl_session_cache_api.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2008-2010. 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% %% @@ -25,7 +25,7 @@ behaviour_info(callbacks) -> [ - {init, 0}, + {init, 1}, {terminate, 1}, {lookup, 2}, {update, 3}, diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index df809ce275..c49f9f1e6d 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -25,12 +25,11 @@ -module(ssl_ssl3). -include("ssl_cipher.hrl"). --include("ssl_debug.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). % MD5 and SHA -export([master_secret/3, finished/3, certificate_verify/3, - mac_hash/6, setup_keys/8, + mac_hash/6, setup_keys/7, suites/0]). -compile(inline). @@ -38,10 +37,9 @@ %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PremasterSecret, ClientRandom, ServerRandom) -> - ?DBG_HEX(PremasterSecret), - ?DBG_HEX(ClientRandom), - ?DBG_HEX(ServerRandom), %% draft-ietf-tls-ssl-version3-00 - 6.2.2 %% key_block = %% MD5(master_secret + SHA(`A' + master_secret + @@ -53,9 +51,10 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) -> %% MD5(master_secret + SHA(`CCC' + master_secret + %% ServerHello.random + %% ClientHello.random)) + [...]; - B = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48), - ?DBG_HEX(B), - B. + Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48), + Block. + +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished @@ -75,8 +74,9 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), <<MD5/binary, SHA/binary>>. -certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash}) - when Algorithm == rsa; Algorithm == dh_rsa; Algorithm == dhe_rsa -> +-spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary(). + +certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> %% md5_hash %% MD5(master_secret + pad_2 + %% MD5(handshake_messages + master_secret + pad_1)); @@ -88,13 +88,14 @@ certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash}) SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash), <<MD5/binary, SHA/binary>>; -certificate_verify(Algorithm, MasterSecret, {_, SHAHash}) - when Algorithm == dh_dss; Algorithm == dhe_dss -> +certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) -> %% sha_hash %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); handshake_hash(?SHA, MasterSecret, undefined, SHAHash). +-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). + mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1 %% hash(MAC_write_secret + pad_2 + @@ -104,19 +105,19 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> case Method of ?NULL -> ok; _ -> - ?DBG_HEX(Mac_write_secret), - ?DBG_HEX(hash(Method, Fragment)), ok end, Mac = mac_hash(Method, Mac_write_secret, [<<?UINT64(Seq_num), ?BYTE(Type), ?UINT16(Length)>>, Fragment]), - ?DBG_HEX(Mac), Mac. -setup_keys(Exportable, MasterSecret, ServerRandom, ClientRandom, - HS, KML, _EKML, IVS) - when Exportable == no_export; Exportable == ignore -> +-spec setup_keys(binary(), binary(), binary(), + integer(), integer(), term(), integer()) -> + {binary(), binary(), binary(), + binary(), binary(), binary()}. + +setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom, 2*(HS+KML+IVS)), %% draft-ietf-tls-ssl-version3-00 - 6.2.2 @@ -130,86 +131,26 @@ setup_keys(Exportable, MasterSecret, ServerRandom, ClientRandom, <<ClientWriteMacSecret:HS/binary, ServerWriteMacSecret:HS/binary, ClientWriteKey:KML/binary, ServerWriteKey:KML/binary, ClientIV:IVS/binary, ServerIV:IVS/binary>> = KeyBlock, - ?DBG_HEX(ClientWriteMacSecret), - ?DBG_HEX(ServerWriteMacSecret), - ?DBG_HEX(ClientWriteKey), - ?DBG_HEX(ServerWriteKey), - ?DBG_HEX(ClientIV), - ?DBG_HEX(ServerIV), {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, ClientIV, ServerIV}; - -setup_keys(export, MasterSecret, ServerRandom, ClientRandom, - HS, KML, EKML, IVS) -> - KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom, - 2*(HS+KML)), - %% draft-ietf-tls-ssl-version3-00 - 6.2.2 - %% Exportable encryption algorithms (for which - %% CipherSpec.is_exportable is true) require additional processing as - %% follows to derive their final write keys: - - %% final_client_write_key = MD5(client_write_key + - %% ClientHello.random + - %% ServerHello.random); - %% final_server_write_key = MD5(server_write_key + - %% ServerHello.random + - %% ClientHello.random); + ServerWriteKey, ClientIV, ServerIV}. - %% Exportable encryption algorithms derive their IVs from the random - %% messages: - %% client_write_IV = MD5(ClientHello.random + ServerHello.random); - %% server_write_IV = MD5(ServerHello.random + ClientHello.random); - - <<ClientWriteMacSecret:HS/binary, ServerWriteMacSecret:HS/binary, - ClientWriteKey:KML/binary, ServerWriteKey:KML/binary>> = KeyBlock, - <<ClientIV:IVS/binary, _/binary>> = - hash(?MD5, [ClientRandom, ServerRandom]), - <<ServerIV:IVS/binary, _/binary>> = - hash(?MD5, [ServerRandom, ClientRandom]), - <<FinalClientWriteKey:EKML/binary, _/binary>> = - hash(?MD5, [ClientWriteKey, ClientRandom, ServerRandom]), - <<FinalServerWriteKey:EKML/binary, _/binary>> = - hash(?MD5, [ServerWriteKey, ServerRandom, ClientRandom]), - ?DBG_HEX(ClientWriteMacSecret), - ?DBG_HEX(ServerWriteMacSecret), - ?DBG_HEX(FinalClientWriteKey), - ?DBG_HEX(FinalServerWriteKey), - ?DBG_HEX(ClientIV), - ?DBG_HEX(ServerIV), - {ClientWriteMacSecret, ServerWriteMacSecret, FinalClientWriteKey, - FinalServerWriteKey, ClientIV, ServerIV}. +-spec suites() -> [cipher_suite()]. suites() -> [ - %% TODO: uncomment when supported ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, ?TLS_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - %% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - %% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, - %%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this? - %% ?TLS_RSA_WITH_IDEA_CBC_SHA, Not supported: in later openssl version than OTP requires - + %%?TLS_RSA_WITH_IDEA_CBC_SHA, ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, - %%?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5, - %%?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5, - %%?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, - %%?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA, - %%?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, - %%?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA, - %%?TLS_DHE_DSS_WITH_RC4_128_SHA, - ?TLS_RSA_WITH_DES_CBC_SHA - %% ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, - %% ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, - %% ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, - %%?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, - %%?TLS_RSA_EXPORT_WITH_RC4_40_MD5 ]. %%-------------------------------------------------------------------- @@ -269,8 +210,7 @@ handshake_hash(Method, MasterSecret, Sender, HandshakeHash) -> hash(Method, [MasterSecret, pad_2(Method), InnerHash]). get_sender(client) -> "CLNT"; -get_sender(server) -> "SRVR"; -get_sender(none) -> "". +get_sender(server) -> "SRVR". generate_keyblock(MasterSecret, ServerRandom, ClientRandom, WantedLength) -> gen(MasterSecret, [MasterSecret, ServerRandom, ClientRandom], diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl index bd5a02417a..316ed8a4e9 100644 --- a/lib/ssl/src/ssl_sup.erl +++ b/lib/ssl/src/ssl_sup.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1998-2010. 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% %% @@ -32,16 +32,18 @@ %%%========================================================================= %%% API %%%========================================================================= + +-spec start_link() -> {ok, pid()} | ignore | {error, term()}. + start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). %%%========================================================================= %%% Supervisor callback %%%========================================================================= -%% init([]) -> {ok, {SupFlags, [ChildSpec]}} -%% -init([]) -> - +-spec init([]) -> {ok, {SupFlags :: tuple(), [ChildSpec :: tuple()]}}. + +init([]) -> %% OLD ssl - moved start to ssl.erl only if old %% ssl is acctualy run! %%Child1 = {ssl_server, {ssl_server, start_link, []}, @@ -67,7 +69,7 @@ init([]) -> session_and_cert_manager_child_spec() -> Opts = manager_opts(), Name = ssl_manager, - StartFunc = {ssl_manager, start_link, Opts}, + StartFunc = {ssl_manager, start_link, [Opts]}, Restart = permanent, Shutdown = 4000, Modules = [ssl_manager], @@ -86,11 +88,12 @@ connection_manager_child_spec() -> manager_opts() -> CbOpts = case application:get_env(ssl, session_cb) of - {ok, Cb} when is_atom(Cb) -> - [{session_cb, Cb}]; - _ -> - [] - end, + {ok, Cb} when is_atom(Cb) -> + InitArgs = session_cb_init_args(), + [{session_cb, Cb}, {session_cb_init_args, InitArgs}]; + _ -> + [] + end, case application:get_env(ssl, session_lifetime) of {ok, Time} when is_integer(Time) -> [{session_lifetime, Time}| CbOpts]; @@ -98,3 +101,10 @@ manager_opts() -> CbOpts end. +session_cb_init_args() -> + case application:get_env(ssl, session_cb_init_args) of + {ok, Args} when is_list(Args) -> + Args; + _ -> + [] + end. diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index ce9a135168..3784483e9c 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -27,15 +27,16 @@ -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). --include("ssl_debug.hrl"). -export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7, - setup_keys/5, setup_keys/6, suites/0]). + setup_keys/6, suites/0]). %%==================================================================== %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret, %% "master secret", ClientHello.random + @@ -43,6 +44,8 @@ master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> prf(PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). + finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% RFC 2246 & 4346 - 7.4.9. Finished %% struct { @@ -56,18 +59,20 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = hash_final(?SHA, SHAHash), prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). +-spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary(). -certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; - Algorithm == dh_rsa; - Algorithm == dhe_rsa -> +certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) -> MD5 = hash_final(?MD5, MD5Hash), SHA = hash_final(?SHA, SHAHash), <<MD5/binary, SHA/binary>>; -certificate_verify(Algorithm, {_, SHAHash}) when Algorithm == dh_dss; - Algorithm == dhe_dss -> +certificate_verify(?'id-dsa', {_, SHAHash}) -> hash_final(?SHA, SHAHash). - + +-spec setup_keys(binary(), binary(), binary(), integer(), + integer(), integer()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen, IVSize) -> %% RFC 2246 - 6.3. Key calculation @@ -92,26 +97,30 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV}. -setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) -> - %% RFC 4346 - 6.3. Key calculation - %% key_block = PRF(SecurityParameters.master_secret, - %% "key expansion", - %% SecurityParameters.server_random + - %% SecurityParameters.client_random); - %% Then the key_block is partitioned as follows: - %% client_write_MAC_secret[SecurityParameters.hash_size] - %% server_write_MAC_secret[SecurityParameters.hash_size] - %% client_write_key[SecurityParameters.key_material_length] - %% server_write_key[SecurityParameters.key_material_length] - WantedLength = 2 * (HashSize + KeyMatLen), - KeyBlock = prf(MasterSecret, "key expansion", - [ServerRandom, ClientRandom], WantedLength), - <<ClientWriteMacSecret:HashSize/binary, - ServerWriteMacSecret:HashSize/binary, - ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>> - = KeyBlock, - {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, undefined, undefined}. +%% TLS v1.1 uncomment when supported. +%% setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) -> +%% %% RFC 4346 - 6.3. Key calculation +%% %% key_block = PRF(SecurityParameters.master_secret, +%% %% "key expansion", +%% %% SecurityParameters.server_random + +%% %% SecurityParameters.client_random); +%% %% Then the key_block is partitioned as follows: +%% %% client_write_MAC_secret[SecurityParameters.hash_size] +%% %% server_write_MAC_secret[SecurityParameters.hash_size] +%% %% client_write_key[SecurityParameters.key_material_length] +%% %% server_write_key[SecurityParameters.key_material_length] +%% WantedLength = 2 * (HashSize + KeyMatLen), +%% KeyBlock = prf(MasterSecret, "key expansion", +%% [ServerRandom, ClientRandom], WantedLength), +%% <<ClientWriteMacSecret:HashSize/binary, +%% ServerWriteMacSecret:HashSize/binary, +%% ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>> +%% = KeyBlock, +%% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, +%% ServerWriteKey, undefined, undefined}. + +-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), + integer(), binary()) -> binary(). mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, Length, Fragment) -> @@ -122,48 +131,32 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, case Method of ?NULL -> ok; _ -> - ?DBG_HEX(Mac_write_secret), - ?DBG_HEX(hash(Method, Fragment)), ok end, Mac = hmac_hash(Method, Mac_write_secret, [<<?UINT64(Seq_num), ?BYTE(Type), ?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>, Fragment]), - ?DBG_HEX(Mac), Mac. +-spec suites() -> [cipher_suite()]. + suites() -> [ - %% TODO: uncomment when supported ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - %%?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, ?TLS_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - %% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - %% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, - %%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this? - %% ?TLS_RSA_WITH_IDEA_CBC_SHA, + %%?TLS_RSA_WITH_IDEA_CBC_SHA, ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, - %%?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5, - %%?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5, - %%?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, - %%?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA, - %%?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, - %%?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA, - %%?TLS_DHE_DSS_WITH_RC4_128_SHA, - %%?TLS_DHE_RSA_WITH_DES_CBC_SHA, - %% EDH-DSS-DES-CBC-SHA TODO: ?? + ?TLS_DHE_RSA_WITH_DES_CBC_SHA, ?TLS_RSA_WITH_DES_CBC_SHA - %% ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, - %% ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, - %%?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, - %%?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, - %%?TLS_RSA_EXPORT_WITH_RC4_40_MD5 ]. %%-------------------------------------------------------------------- @@ -245,7 +238,3 @@ hash_final(?MD5, Conntext) -> crypto:md5_final(Conntext); hash_final(?SHA, Conntext) -> crypto:sha_final(Conntext). - - - - diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index bd86120c98..c0a7f8d257 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1999-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 1999-2010. 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% # @@ -40,6 +40,7 @@ MODULES = \ ssl_packet_SUITE \ ssl_payload_SUITE \ ssl_to_openssl_SUITE \ + ssl_session_cache_SUITE \ ssl_test_MACHINE \ old_ssl_active_SUITE \ old_ssl_active_once_SUITE \ @@ -50,7 +51,8 @@ MODULES = \ old_ssl_protocol_SUITE \ old_transport_accept_SUITE \ old_ssl_dist_SUITE \ - make_certs + make_certs\ + erl_make_certs ERL_FILES = $(MODULES:%=%.erl) @@ -58,12 +60,10 @@ ERL_FILES = $(MODULES:%=%.erl) HRL_FILES = ssl_test_MACHINE.hrl HRL_FILES_SRC = \ - ssl_pkix.hrl \ ssl_alert.hrl \ ssl_handshake.hrl -HRL_FILES_INC = \ - OTP-PKIX.hrl +HRL_FILES_INC = HRL_FILES_NEEDED_IN_TEST = \ $(HRL_FILES_SRC:%=../src/%) \ diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl new file mode 100644 index 0000000000..8b01ca3ad4 --- /dev/null +++ b/lib/ssl/test/erl_make_certs.erl @@ -0,0 +1,421 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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% +%% + +%% Create test certificates + +-module(erl_make_certs). +-include_lib("public_key/include/public_key.hrl"). + +-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]). +-compile(export_all). + +%%-------------------------------------------------------------------- +%% @doc Create and return a der encoded certificate +%% Option Default +%% ------------------------------------------------------- +%% digest sha1 +%% validity {date(), date() + week()} +%% version 3 +%% subject [] list of the following content +%% {name, Name} +%% {email, Email} +%% {city, City} +%% {state, State} +%% {org, Org} +%% {org_unit, OrgUnit} +%% {country, Country} +%% {serial, Serial} +%% {title, Title} +%% {dnQualifer, DnQ} +%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) +%% (obs IssuerKey migth be {Key, Password} +%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% +%% +%% (OBS: The generated keys are for testing only) +%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()} +%% @end +%%-------------------------------------------------------------------- + +make_cert(Opts) -> + SubjectPrivateKey = get_key(Opts), + {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts), + Cert = public_key:pkix_sign(TBSCert, IssuerKey), + true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok + {Cert, encode_key(SubjectPrivateKey)}. + +%%-------------------------------------------------------------------- +%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem" +%% @spec (::string(), ::string(), {Cert,Key}) -> ok +%% @end +%%-------------------------------------------------------------------- +write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) -> + ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"), + [{'Certificate', Cert, not_encrypted}]), + ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]). + +%%-------------------------------------------------------------------- +%% @doc Creates a rsa key (OBS: for testing only) +%% the size are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_rsa(Size) when is_integer(Size) -> + Key = gen_rsa2(Size), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% @doc Creates a dsa key (OBS: for testing only) +%% the sizes are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> + Key = gen_dsa2(LSize, NSize), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% @doc Verifies cert signatures +%% @spec (::binary(), ::tuple()) -> ::boolean() +%% @end +%%-------------------------------------------------------------------- +verify_signature(DerEncodedCert, DerKey, _KeyParams) -> + Key = decode_key(DerKey), + case Key of + #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} -> + public_key:pkix_verify(DerEncodedCert, + #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}); + #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> + public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_key(Opts) -> + case proplists:get_value(key, Opts) of + undefined -> make_key(rsa, Opts); + rsa -> make_key(rsa, Opts); + dsa -> make_key(dsa, Opts); + Key -> + Password = proplists:get_value(password, Opts, no_passwd), + decode_key(Key, Password) + end. + +decode_key({Key, Pw}) -> + decode_key(Key, Pw); +decode_key(Key) -> + decode_key(Key, no_passwd). + + +decode_key(#'RSAPublicKey'{} = Key,_) -> + Key; +decode_key(#'RSAPrivateKey'{} = Key,_) -> + Key; +decode_key(#'DSAPrivateKey'{} = Key,_) -> + Key; +decode_key(PemEntry = {_,_,_}, Pw) -> + public_key:pem_entry_decode(PemEntry, Pw); +decode_key(PemBin, Pw) -> + [KeyInfo] = public_key:pem_decode(PemBin), + decode_key(KeyInfo, Pw). + +encode_key(Key = #'RSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), + {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; +encode_key(Key = #'DSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), + {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + +make_tbs(SubjectKey, Opts) -> + Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), + + IssuerProp = proplists:get_value(issuer, Opts, true), + {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey), + + {Algo, Parameters} = sign_algorithm(IssuerKey, Opts), + + SignAlgo = #'SignatureAlgorithm'{algorithm = Algo, + parameters = Parameters}, + Subject = case IssuerProp of + true -> %% Is a Root Ca + Issuer; + _ -> + subject(proplists:get_value(subject, Opts),false) + end, + + {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1, + signature = SignAlgo, + issuer = Issuer, + validity = validity(Opts), + subject = Subject, + subjectPublicKeyInfo = publickey(SubjectKey), + version = Version, + extensions = extensions(Opts) + }, IssuerKey}. + +issuer(true, Opts, SubjectKey) -> + %% Self signed + {subject(proplists:get_value(subject, Opts), true), SubjectKey}; +issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) -> + {issuer_der(Issuer), decode_key(IssuerKey)}; +issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) -> + {ok, [{cert, Cert, _}|_]} = public_key:pem_to_der(File), + {issuer_der(Cert), decode_key(IssuerKey)}. + +issuer_der(Issuer) -> + Decoded = public_key:pkix_decode_cert(Issuer, otp), + #'OTPCertificate'{tbsCertificate=Tbs} = Decoded, + #'OTPTBSCertificate'{subject=Subject} = Tbs, + Subject. + +subject(undefined, IsRootCA) -> + User = if IsRootCA -> "RootCA"; true -> os:getenv("USER") end, + Opts = [{email, User ++ "@erlang.org"}, + {name, User}, + {city, "Stockholm"}, + {country, "SE"}, + {org, "erlang"}, + {org_unit, "testing dep"}], + subject(Opts); +subject(Opts, _) -> + subject(Opts). + +subject(SubjectOpts) when is_list(SubjectOpts) -> + Encode = fun(Opt) -> + {Type,Value} = subject_enc(Opt), + [#'AttributeTypeAndValue'{type=Type, value=Value}] + end, + {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}. + +%% Fill in the blanks +subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}}; +subject_enc({email, Email}) -> {?'id-emailAddress', Email}; +subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}}; +subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}}; +subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}}; +subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}}; +subject_enc({country, Country}) -> {?'id-at-countryName', Country}; +subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial}; +subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}}; +subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ}; +subject_enc(Other) -> Other. + + +extensions(Opts) -> + case proplists:get_value(extensions, Opts, []) of + false -> + asn1_NOVALUE; + Exts -> + lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)]) + end. + +default_extensions(Exts) -> + Def = [{key_usage,undefined}, + {subject_altname, undefined}, + {issuer_altname, undefined}, + {basic_constraints, default}, + {name_constraints, undefined}, + {policy_constraints, undefined}, + {ext_key_usage, undefined}, + {inhibit_any, undefined}, + {auth_key_id, undefined}, + {subject_key_id, undefined}, + {policy_mapping, undefined}], + Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end, + Exts ++ lists:foldl(Filter, Def, Exts). + +extension({_, undefined}) -> []; +extension({basic_constraints, Data}) -> + case Data of + default -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true}, + critical=true}; + false -> + []; + Len when is_integer(Len) -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len}, + critical=true}; + _ -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = Data} + end; +extension({Id, Data, Critical}) -> + #'Extension'{extnID = Id, extnValue = Data, critical = Critical}. + + +publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> + Public = #'RSAPublicKey'{modulus=N, publicExponent=E}, + Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = Public}; +publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', + parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + +validity(Opts) -> + DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), + DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7), + {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}), + Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end, + #'Validity'{notBefore={generalTime, Format(DefFrom)}, + notAfter ={generalTime, Format(DefTo)}}. + +sign_algorithm(#'RSAPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'sha1WithRSAEncryption'; + sha512 -> ?'sha512WithRSAEncryption'; + sha384 -> ?'sha384WithRSAEncryption'; + sha256 -> ?'sha256WithRSAEncryption'; + md5 -> ?'md5WithRSAEncryption'; + md2 -> ?'md2WithRSAEncryption' + end, + {Type, 'NULL'}; +sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> + {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}. + +make_key(rsa, _Opts) -> + %% (OBS: for testing only) + gen_rsa2(64); +make_key(dsa, _Opts) -> + gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RSA key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53, + 47,43,41,37,31,29,23,19,17,13,11,7,5,3]). + +gen_rsa2(Size) -> + P = prime(Size), + Q = prime(Size), + N = P*Q, + Tot = (P - 1) * (Q - 1), + [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES), + {D1,D2} = extended_gcd(E, Tot), + D = erlang:max(D1,D2), + case D < E of + true -> + gen_rsa2(Size); + false -> + {Co1,Co2} = extended_gcd(Q, P), + Co = erlang:max(Co1,Co2), + #'RSAPrivateKey'{version = 'two-prime', + modulus = N, + publicExponent = E, + privateExponent = D, + prime1 = P, + prime2 = Q, + exponent1 = D rem (P-1), + exponent2 = D rem (Q-1), + coefficient = Co + } + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% DSA key generation (OBS: for testing only) +%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm +%% and the fips_186-3.pdf +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_dsa2(LSize, NSize) -> + Q = prime(NSize), %% Choose N-bit prime Q + X0 = prime(LSize), + P0 = prime((LSize div 2) +1), + + %% Choose L-bit prime modulus P such that p–1 is a multiple of q. + case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of + error -> + gen_dsa2(LSize, NSize); + P -> + G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used. + + X = prime(20), %% Choose x by some random method, where 0 < x < q. + Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + + #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + end. + +%% See fips_186-3.pdf +dsa_search(T, P0, Q, Iter) when Iter > 0 -> + P = 2*T*Q*P0 + 1, + case is_prime(crypto:mpint(P), 50) of + true -> P; + false -> dsa_search(T+1, P0, Q, Iter-1) + end; +dsa_search(_,_,_,_) -> + error. + + +%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +prime(ByteSize) -> + Rand = odd_rand(ByteSize), + crypto:erlint(prime_odd(Rand, 0)). + +prime_odd(Rand, N) -> + case is_prime(Rand, 50) of + true -> + Rand; + false -> + NotPrime = crypto:erlint(Rand), + prime_odd(crypto:mpint(NotPrime+2), N+1) + end. + +%% see http://en.wikipedia.org/wiki/Fermat_primality_test +is_prime(_, 0) -> true; +is_prime(Candidate, Test) -> + CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), + case crypto:mod_exp(CoPrime, Candidate, Candidate) of + CoPrime -> is_prime(Candidate, Test-1); + _ -> false + end. + +odd_rand(Size) -> + Min = 1 bsl (Size*8-1), + Max = (1 bsl (Size*8))-1, + odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + +odd_rand(Min,Max) -> + Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max), + BitSkip = (Sz+4)*8-1, + case Rand of + Odd = <<_:BitSkip, 1:1>> -> Odd; + Even = <<_:BitSkip, 0:1>> -> + crypto:mpint(crypto:erlint(Even)+1) + end. + +extended_gcd(A, B) -> + case A rem B of + 0 -> + {0, 1}; + N -> + {X, Y} = extended_gcd(B, N), + {Y, X-Y*(A div B)} + end. + +pem_to_der(File) -> + {ok, PemBin} = file:read_file(File), + public_key:pem_decode(PemBin). + +der_to_pem(File, Entries) -> + PemBin = public_key:pem_encode(Entries), + file:write_file(File, PemBin). diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index 0cdf33c3e2..3c18a905b4 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -90,8 +90,10 @@ enduser(Root, OpenSSLCmd, CA, User) -> KeyFile = filename:join([UsrRoot, "key.pem"]), ReqFile = filename:join([UsrRoot, "req.pem"]), create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile), - CertFile = filename:join([UsrRoot, "cert.pem"]), - sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFile). + CertFileAllUsage = filename:join([UsrRoot, "cert.pem"]), + sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFileAllUsage), + CertFileDigitalSigOnly = filename:join([UsrRoot, "digital_signature_only_cert.pem"]), + sign_req(Root, OpenSSLCmd, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly). collect_certs(Root, CAs, Users) -> Bins = lists:foldr( @@ -255,6 +257,7 @@ ca_cnf(CA) -> "RANDFILE = $dir/private/RAND\n" "\n" "x509_extensions = user_cert\n" + "unique_subject = no\n" "default_days = 3600\n" "default_md = sha1\n" "preserve = no\n" @@ -279,6 +282,15 @@ ca_cnf(CA) -> "issuerAltName = issuer:copy\n" "\n" + "[user_cert_digital_signature_only]\n" + "basicConstraints = CA:false\n" + "keyUsage = digitalSignature\n" + "subjectKeyIdentifier = hash\n" + "authorityKeyIdentifier = keyid,issuer:always\n" + "subjectAltName = email:copy\n" + "issuerAltName = issuer:copy\n" + "\n" + "[ca_cert]\n" "basicConstraints = critical,CA:true\n" "keyUsage = cRLSign, keyCertSign\n" diff --git a/lib/ssl/test/old_ssl_active_SUITE.erl b/lib/ssl/test/old_ssl_active_SUITE.erl index 010596f351..d1cec26827 100644 --- a/lib/ssl/test/old_ssl_active_SUITE.erl +++ b/lib/ssl/test/old_ssl_active_SUITE.erl @@ -87,6 +87,8 @@ config(Config) -> %% operating system, version of OTP, Erts, kernel and stdlib. %% Check if SSL exists. If this case fails, all other cases are skipped + crypto:start(), + application:start(public_key), case ssl:start() of ok -> ssl:stop(); {error, {already_started, _}} -> ssl:stop(); diff --git a/lib/ssl/test/old_ssl_active_once_SUITE.erl b/lib/ssl/test/old_ssl_active_once_SUITE.erl index 6224b17aa7..63eaa730e9 100644 --- a/lib/ssl/test/old_ssl_active_once_SUITE.erl +++ b/lib/ssl/test/old_ssl_active_once_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2002-2010. 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% %% @@ -79,6 +79,8 @@ config(Config) -> io:format("Config: ~p~n", [Config]), %% Check if SSL exists. If this case fails, all other cases are skipped + crypto:start(), + application:start(public_key), case ssl:start() of ok -> ssl:stop(); {error, {already_started, _}} -> ssl:stop(); diff --git a/lib/ssl/test/old_ssl_dist_SUITE.erl b/lib/ssl/test/old_ssl_dist_SUITE.erl index 56209c3530..97090c1409 100644 --- a/lib/ssl/test/old_ssl_dist_SUITE.erl +++ b/lib/ssl/test/old_ssl_dist_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -254,7 +254,8 @@ mk_node_cmdline(ListenPort, Name, Args) -> Prog ++ " " ++ Static ++ " " ++ NameSw ++ " " ++ Name ++ " " - ++ "-pa " ++ Pa ++ " " + ++ "-pa " ++ Pa ++ " " + ++ "-run application start crypto -run application start public_key " ++ "-run " ++ atom_to_list(?MODULE) ++ " cnct2tstsrvr " ++ host_name() ++ " " ++ integer_to_list(ListenPort) ++ " " @@ -524,23 +525,10 @@ add_ssl_opts_config(Config) -> KrnlDir = filename:join([LibDir, "kernel-" ++ KRNL_VSN]), {ok, _} = file:read_file_info(StdlDir), {ok, _} = file:read_file_info(KrnlDir), - SSL_VSN = case lists:keysearch(ssl, 1, Apps) of - {value, {ssl, _, VSN}} -> - VSN; - _ -> - application:start(ssl), - try - {value, - {ssl, - _, - VSN}} = lists:keysearch(ssl, - 1, - application:which_applications()), - VSN - after - application:stop(ssl) - end - end, + SSL_VSN = vsn(ssl), + VSN_CRYPTO = vsn(crypto), + VSN_PKEY = vsn(public_key), + SslDir = filename:join([LibDir, "ssl-" ++ SSL_VSN]), {ok, _} = file:read_file_info(SslDir), %% We are using an installed otp system, create the boot script. @@ -552,6 +540,8 @@ add_ssl_opts_config(Config) -> " {erts, \"~s\"},~n" " [{kernel, \"~s\"},~n" " {stdlib, \"~s\"},~n" + " {crypto, \"~s\"},~n" + " {public_key, \"~s\"},~n" " {ssl, \"~s\"}]}.~n", [case catch erlang:system_info(otp_release) of {'EXIT', _} -> "R11B"; @@ -560,6 +550,8 @@ add_ssl_opts_config(Config) -> erlang:system_info(version), KRNL_VSN, STDL_VSN, + VSN_CRYPTO, + VSN_PKEY, SSL_VSN]), ok = file:close(RelFile), ok = systools:make_script(Script, []), @@ -593,3 +585,17 @@ success(Config) -> {value, {comment, _} = Res} -> Res; _ -> ok end. + +vsn(App) -> + application:start(App), + try + {value, + {ssl, + _, + VSN}} = lists:keysearch(App, + 1, + application:which_applications()), + VSN + after + application:stop(ssl) + end. diff --git a/lib/ssl/test/old_ssl_misc_SUITE.erl b/lib/ssl/test/old_ssl_misc_SUITE.erl index 55d1b71025..2767123a12 100644 --- a/lib/ssl/test/old_ssl_misc_SUITE.erl +++ b/lib/ssl/test/old_ssl_misc_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2003-2010. 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% %% @@ -61,6 +61,8 @@ config(Config) -> io:format("Config: ~p~n", [Config]), %% Check if SSL exists. If this case fails, all other cases are skipped + crypto:start(), + application:start(public_key), case ssl:start() of ok -> ssl:stop(); {error, {already_started, _}} -> ssl:stop(); diff --git a/lib/ssl/test/old_ssl_passive_SUITE.erl b/lib/ssl/test/old_ssl_passive_SUITE.erl index 4cb8c1f0cd..96a7938583 100644 --- a/lib/ssl/test/old_ssl_passive_SUITE.erl +++ b/lib/ssl/test/old_ssl_passive_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1999-2010. 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% %% @@ -78,6 +78,8 @@ config(Config) -> io:format("Config: ~p~n", [Config]), %% Check if SSL exists. If this case fails, all other cases are skipped + crypto:start(), + application:start(public_key), case ssl:start() of ok -> ssl:stop(); {error, {already_started, _}} -> ssl:stop(); diff --git a/lib/ssl/test/old_ssl_peer_cert_SUITE.erl b/lib/ssl/test/old_ssl_peer_cert_SUITE.erl index f0b8db2607..e5b3975d41 100644 --- a/lib/ssl/test/old_ssl_peer_cert_SUITE.erl +++ b/lib/ssl/test/old_ssl_peer_cert_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2003-2010. 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% %% @@ -62,6 +62,8 @@ config(Config) -> io:format("Config: ~p~n", [Config]), %% Check if SSL exists. If this case fails, all other cases are skipped + crypto:start(), + application:start(public_key), case ssl:start() of ok -> ssl:stop(); {error, {already_started, _}} -> ssl:stop(); diff --git a/lib/ssl/test/old_ssl_protocol_SUITE.erl b/lib/ssl/test/old_ssl_protocol_SUITE.erl index 7bde5d6749..efdbf45a3d 100644 --- a/lib/ssl/test/old_ssl_protocol_SUITE.erl +++ b/lib/ssl/test/old_ssl_protocol_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2005-2010. 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% %% @@ -55,6 +55,8 @@ config(Config) -> io:format("Config: ~p~n", [Config]), %% Check if SSL exists. If this case fails, all other cases are skipped + crypto:start(), + application:start(public_key), case ssl:start() of ok -> ssl:stop(); {error, {already_started, _}} -> ssl:stop(); diff --git a/lib/ssl/test/old_ssl_verify_SUITE.erl b/lib/ssl/test/old_ssl_verify_SUITE.erl index 5db964526f..7a8cd1578a 100644 --- a/lib/ssl/test/old_ssl_verify_SUITE.erl +++ b/lib/ssl/test/old_ssl_verify_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1999-2010. 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% %% @@ -60,6 +60,8 @@ config(Config) -> io:format("Config: ~p~n", [Config]), %% Check if SSL exists. If this case fails, all other cases are skipped + crypto:start(), + application:start(public_key), case ssl:start() of ok -> ssl:stop(); {error, {already_started, _}} -> ssl:stop(); diff --git a/lib/ssl/test/old_transport_accept_SUITE.erl b/lib/ssl/test/old_transport_accept_SUITE.erl index 4bb09cee19..71c1d9e181 100644 --- a/lib/ssl/test/old_transport_accept_SUITE.erl +++ b/lib/ssl/test/old_transport_accept_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -224,12 +224,9 @@ tolerant_server_loop(Client, LSock, Msg, N) -> tolerant_server_loop(Client, LSock, Msg, N-1). app() -> - case application:get_application(ssl) of - undefined -> - application:start(ssl); - _ -> - ok - end. + crypto:start(), + application:start(public_key), + ssl:start(). start_node(Kind, Params) -> S = atom_to_list(?MODULE)++"_" ++ atom_to_list(Kind), diff --git a/lib/ssl/test/ssl.cover b/lib/ssl/test/ssl.cover index 138bf96b9d..e8daa363c5 100644 --- a/lib/ssl/test/ssl.cover +++ b/lib/ssl/test/ssl.cover @@ -3,5 +3,17 @@ 'PKIX1Explicit88', 'PKIX1Implicit88', 'PKIXAttributeCertificate', - 'SSL-PKIX']}. + 'SSL-PKIX', + ssl_pem, + ssl_pkix, + ssl_base64, + ssl_broker, + ssl_broker_int, + ssl_broker_sup, + ssl_debug, + ssl_server, + ssl_prim, + inet_ssl_dist, + 'OTP-PKIX' + ]}. diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 7f33efd7e1..8f9554f3ce 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -7,7 +7,7 @@ %% 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/. +%% retrieved online at http://www.erlang.org/.2 %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See @@ -27,19 +27,14 @@ -include("test_server.hrl"). -include("test_server_line.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include("ssl_alert.hrl"). -define('24H_in_sec', 86400). -define(TIMEOUT, 60000). +-define(LONG_TIMEOUT, 600000). -define(EXPIRE, 10). -define(SLEEP, 500). - --behaviour(ssl_session_cache_api). - -%% For the session cache tests --export([init/0, terminate/1, lookup/2, update/3, - delete/2, foldl/3, select_session/2]). - %% Test server callback functions %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config @@ -50,14 +45,21 @@ %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- -init_per_suite(Config) -> +init_per_suite(Config0) -> + Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), crypto:start(), + application:start(public_key), ssl:start(), + + %% make rsa certs using oppenssl Result = - (catch make_certs:all(?config(data_dir, Config), - ?config(priv_dir, Config))), + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), test_server:format("Make certs ~p~n", [Result]), - ssl_test_lib:cert_options(Config). + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config]. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -82,13 +84,6 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %% Description: Initialization before each test case %%-------------------------------------------------------------------- -init_per_testcase(session_cache_process_list, Config) -> - init_customized_session_cache(Config); - -init_per_testcase(session_cache_process_mnesia, Config) -> - mnesia:start(), - init_customized_session_cache(Config); - init_per_testcase(reuse_session_expired, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), @@ -98,19 +93,42 @@ init_per_testcase(reuse_session_expired, Config0) -> ssl:start(), [{watchdog, Dog} | Config]; -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. +init_per_testcase(no_authority_key_identifier, Config) -> + %% Clear cach so that root cert will not + %% be found. + ssl:stop(), + ssl:start(), + Config; -init_customized_session_cache(Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), +init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3; + TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3; + TestCase == ciphers_dsa_signed_certs_ssl3; + TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3 -> ssl:stop(), application:load(ssl), - application:set_env(ssl, session_cb, ?MODULE), + application:set_env(ssl, protocol_version, sslv3), ssl:start(), - [{watchdog, Dog} | Config]. + Config; + +init_per_testcase(protocol_versions, Config) -> + ssl:stop(), + application:load(ssl), + %% For backwards compatibility sslv2 should be filtered out. + application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), + ssl:start(), + Config; + +init_per_testcase(empty_protocol_versions, Config) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, protocol_version, []), + ssl:start(), + Config; + +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config) -> _ @@ -120,16 +138,17 @@ init_customized_session_cache(Config0) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(session_cache_process_list, Config) -> - application:unset_env(ssl, session_cb), - end_per_testcase(default_action, Config); -end_per_testcase(session_cache_process_mnesia, Config) -> - application:unset_env(ssl, session_cb), - mnesia:stop(), - end_per_testcase(default_action, Config); end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_lifetime), end_per_testcase(default_action, Config); +end_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3; + TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3; + TestCase == ciphers_dsa_signed_certs_ssl3; + TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3; + TestCase == protocol_versions; + TestCase == empty_protocol_versions-> + application:unset_env(ssl, protocol_version), + end_per_testcase(default_action, Config); end_per_testcase(_TestCase, Config) -> Dog = ?config(watchdog, Config), case Dog of @@ -151,28 +170,42 @@ all(doc) -> ["Test the basic ssl functionality"]; all(suite) -> - [app, connection_info, controlling_process, controller_dies, - peercert, connect_dist, - peername, sockname, socket_options, misc_ssl_options, versions, cipher_suites, - upgrade, upgrade_with_timeout, tcp_connect, - ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown, - shutdown_write, shutdown_both, shutdown_error, ciphers, - send_close, close_transport_accept, dh_params, - server_verify_peer_passive, + [app, alerts, connection_info, protocol_versions, + empty_protocol_versions, controlling_process, controller_dies, + client_closes_socket, peercert, connect_dist, peername, sockname, + socket_options, misc_ssl_options, versions, cipher_suites, + upgrade, upgrade_with_timeout, tcp_connect, ipv6, ekeyfile, + ecertfile, ecacertfile, eoptions, shutdown, shutdown_write, + shutdown_both, shutdown_error, + ciphers_rsa_signed_certs, ciphers_rsa_signed_certs_ssl3, + ciphers_rsa_signed_certs_openssl_names, + ciphers_rsa_signed_certs_openssl_names_ssl3, + ciphers_dsa_signed_certs, + ciphers_dsa_signed_certs_ssl3, + ciphers_dsa_signed_certs_openssl_names, + ciphers_dsa_signed_certs_openssl_names_ssl3, + anonymous_cipher_suites, + default_reject_anonymous, + send_close, + close_transport_accept, dh_params, server_verify_peer_passive, server_verify_peer_active, server_verify_peer_active_once, - server_verify_none_passive, server_verify_none_active, + server_verify_none_passive, server_verify_none_active, server_verify_none_active_once, server_verify_no_cacerts, server_require_peer_cert_ok, server_require_peer_cert_fail, server_verify_client_once_passive, server_verify_client_once_active, - server_verify_client_once_active_once, - client_verify_none_passive, - client_verify_none_active, client_verify_none_active_once - %%, session_cache_process_list, session_cache_process_mnesia - ,reuse_session, reuse_session_expired, server_does_not_want_to_reuse_session, - client_renegotiate, server_renegotiate, - client_no_wrap_sequence_number, server_no_wrap_sequence_number, - extended_key_usage, validate_extensions_fun + server_verify_client_once_active_once, client_verify_none_passive, + client_verify_none_active, client_verify_none_active_once, + reuse_session, reuse_session_expired, + server_does_not_want_to_reuse_session, client_renegotiate, + server_renegotiate, client_renegotiate_reused_session, + server_renegotiate_reused_session, client_no_wrap_sequence_number, + server_no_wrap_sequence_number, extended_key_usage, + no_authority_key_identifier, + invalid_signature_client, invalid_signature_server, cert_expired, + client_with_cert_cipher_suites_handshake, unknown_server_ca_fail, + der_input, unknown_server_ca_accept_verify_none, unknown_server_ca_accept_verify_peer, + unknown_server_ca_accept_backwardscompatibilty ]. %% Test cases starts here. @@ -183,7 +216,31 @@ app(suite) -> []; app(Config) when is_list(Config) -> ok = test_server:app_test(ssl). - +%%-------------------------------------------------------------------- +alerts(doc) -> + "Test ssl_alert:alert_txt/1"; +alerts(suite) -> + []; +alerts(Config) when is_list(Config) -> + Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC, + ?DECRYPTION_FAILED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE, + ?HANDSHAKE_FAILURE, ?BAD_CERTIFICATE, ?UNSUPPORTED_CERTIFICATE, + ?CERTIFICATE_REVOKED,?CERTIFICATE_EXPIRED, ?CERTIFICATE_UNKNOWN, + ?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR, + ?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION, + ?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED, + ?NO_RENEGOTIATION], + Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) | + [?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]], + lists:foreach(fun(Alert) -> + case ssl_alert:alert_txt(Alert) of + Txt when is_list(Txt) -> + ok; + Other -> + test_server:fail({unexpected, Other}) + end + end, Alerts). +%%-------------------------------------------------------------------- connection_info(doc) -> ["Test the API function ssl:connection_info/1"]; connection_info(suite) -> @@ -212,7 +269,7 @@ connection_info(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha,no_export}}}, + ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha}}}, ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -224,6 +281,49 @@ connection_info_result(Socket) -> %%-------------------------------------------------------------------- +protocol_versions(doc) -> + ["Test to set a list of protocol versions in app environment."]; + +protocol_versions(suite) -> + []; + +protocol_versions(Config) when is_list(Config) -> + basic_test(Config). + +empty_protocol_versions(doc) -> + ["Test to set an empty list of protocol versions in app environment."]; + +empty_protocol_versions(suite) -> + []; + +empty_protocol_versions(Config) when is_list(Config) -> + basic_test(Config). + + +basic_test(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + controlling_process(doc) -> ["Test API function controlling_process/2"]; @@ -281,7 +381,7 @@ controlling_process_result(Socket, Pid, Msg) -> ssl:send(Socket, Msg), no_result_msg. - +%%-------------------------------------------------------------------- controller_dies(doc) -> ["Test that the socket is closed after controlling process dies"]; controller_dies(suite) -> []; @@ -322,6 +422,10 @@ controller_dies(Config) when is_list(Config) -> Connect = fun(Pid) -> {ok, Socket} = ssl:connect(Hostname, Port, [{reuseaddr,true},{ssl_imp,new}]), + %% Make sure server finishes and verification + %% and is in coonection state before + %% killing client + test_server:sleep(?SLEEP), Pid ! {self(), connected, Socket}, receive die_nice -> normal end end, @@ -393,6 +497,36 @@ get_close(Pid, Where) -> end. %%-------------------------------------------------------------------- +client_closes_socket(doc) -> + ["Test what happens when client closes socket before handshake is compleated"]; +client_closes_socket(suite) -> []; +client_closes_socket(Config) when is_list(Config) -> + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}], + + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Connect = fun() -> + {ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect, + [Hostname, Port, TcpOpts]), + %% Make sure that ssl_accept is called before + %% client process ends and closes socket. + test_server:sleep(?SLEEP) + end, + + _Client = spawn_link(Connect), + + ssl_test_lib:check_result(Server, {error,closed}), + + ssl_test_lib:close(Server). + +%%-------------------------------------------------------------------- + peercert(doc) -> [""]; @@ -416,8 +550,8 @@ peercert(Config) when is_list(Config) -> {options, ClientOpts}]), CertFile = proplists:get_value(certfile, ServerOpts), - {ok, [{cert, BinCert, _}]} = public_key:pem_to_der(CertFile), - {ok, ErlCert} = public_key:pkix_decode_cert(BinCert, otp), + [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile), + ErlCert = public_key:pkix_decode_cert(BinCert, otp), ServerMsg = {{error, no_peercert}, {error, no_peercert}}, ClientMsg = {{ok, BinCert}, {ok, ErlCert}}, @@ -562,9 +696,12 @@ cipher_suites(suite) -> []; cipher_suites(Config) when is_list(Config) -> - MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha,no_export}, + MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha}, [_|_] = Suites = ssl:cipher_suites(), - true = lists:member(MandatoryCipherSuite, Suites). + true = lists:member(MandatoryCipherSuite, Suites), + Suites = ssl:cipher_suites(erlang), + [_|_] =ssl:cipher_suites(openssl). + %%-------------------------------------------------------------------- socket_options(doc) -> ["Test API function getopts/2 and setopts/2"]; @@ -599,9 +736,16 @@ socket_options(Config) when is_list(Config) -> {options, ClientOpts}]), ssl_test_lib:check_result(Server, ok, Client, ok), - + ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + ssl_test_lib:close(Client), + + {ok, Listen} = ssl:listen(0, ServerOpts), + {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), + ok = ssl:setopts(Listen, [{mode, binary}]), + {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]), + {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]), + ssl:close(Listen). socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> %% Test get/set emulated opts @@ -610,6 +754,8 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> {ok, NewValues} = ssl:getopts(Socket, NewOptions), %% Test get/set inet opts {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]), + ssl:setopts(Socket, [{nodelay, true}]), + {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), ok. %%-------------------------------------------------------------------- @@ -630,7 +776,7 @@ misc_ssl_options(Config) when is_list(Config) -> {password, []}, {reuse_session, fun(_,_,_,_) -> true end}, {debug, []}, - {cb_info, {gen_tcp, tcp, tcp_closed}}], + {cb_info, {gen_tcp, tcp, tcp_closed, tcp_error}}], Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -796,11 +942,12 @@ upgrade(Config) when is_list(Config) -> TcpOpts = [binary, {reuseaddr, true}], Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - upgrade_result, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), + {from, self()}, + {mfa, {?MODULE, + upgrade_result, []}}, + {tcp_options, + [{active, false} | TcpOpts]}, + {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, {port, Port}, @@ -819,6 +966,7 @@ upgrade(Config) when is_list(Config) -> ssl_test_lib:close(Client). upgrade_result(Socket) -> + ssl:setopts(Socket, [{active, true}]), ok = ssl:send(Socket, "Hello world"), %% Make sure binary is inherited from tcp socket and that we do %% not get the list default! @@ -845,7 +993,8 @@ upgrade_with_timeout(Config) when is_list(Config) -> {timeout, 5000}, {mfa, {?MODULE, upgrade_result, []}}, - {tcp_options, TcpOpts}, + {tcp_options, + [{active, false} | TcpOpts]}, {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, @@ -985,13 +1134,13 @@ ecertfile(Config) when is_list(Config) -> Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, - {options, ServerBadOpts}]), + {options, ServerBadOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, + {port, Port}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), @@ -1081,7 +1230,6 @@ eoptions(Config) when is_list(Config) -> {verify_fun, function}, {fail_if_no_peer_cert, 0}, {verify_client_once, 1}, - {validate_extensions_fun, function}, {depth, four}, {certfile, 'cert.pem'}, {keyfile,'key.pem' }, @@ -1233,20 +1381,142 @@ shutdown_error(Config) when is_list(Config) -> ok = ssl:close(Listen), {error, closed} = ssl:shutdown(Listen, read_write). -%%-------------------------------------------------------------------- -ciphers(doc) -> - [""]; +%%------------------------------------------------------------------- +ciphers_rsa_signed_certs(doc) -> + ["Test all rsa ssl cipher suites in highest support ssl/tls version"]; + +ciphers_rsa_signed_certs(suite) -> + []; + +ciphers_rsa_signed_certs(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Ciphers = ssl_test_lib:rsa_suites(), + test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, rsa). + +ciphers_rsa_signed_certs_ssl3(doc) -> + ["Test all rsa ssl cipher suites in ssl3"]; + +ciphers_rsa_signed_certs_ssl3(suite) -> + []; + +ciphers_rsa_signed_certs_ssl3(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version({3,0}), + + Ciphers = ssl_test_lib:rsa_suites(), + test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, rsa). + +ciphers_rsa_signed_certs_openssl_names(doc) -> + ["Test all rsa ssl cipher suites in highest support ssl/tls version"]; -ciphers(suite) -> +ciphers_rsa_signed_certs_openssl_names(suite) -> []; -ciphers(Config) when is_list(Config) -> +ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:openssl_rsa_suites(), + test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, rsa). + + +ciphers_rsa_signed_certs_openssl_names_ssl3(doc) -> + ["Test all dsa ssl cipher suites in ssl3"]; + +ciphers_rsa_signed_certs_openssl_names_ssl3(suite) -> + []; + +ciphers_rsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) -> + Version = ssl_record:protocol_version({3,0}), + Ciphers = ssl_test_lib:openssl_rsa_suites(), + run_suites(Ciphers, Version, Config, rsa). + + +ciphers_dsa_signed_certs(doc) -> + ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; + +ciphers_dsa_signed_certs(suite) -> + []; + +ciphers_dsa_signed_certs(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Ciphers = ssl_test_lib:dsa_suites(), + test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, dsa). + +ciphers_dsa_signed_certs_ssl3(doc) -> + ["Test all dsa ssl cipher suites in ssl3"]; + +ciphers_dsa_signed_certs_ssl3(suite) -> + []; + +ciphers_dsa_signed_certs_ssl3(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version({3,0}), - Ciphers = ssl:cipher_suites(), + Ciphers = ssl_test_lib:dsa_suites(), + test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, dsa). + + +ciphers_dsa_signed_certs_openssl_names(doc) -> + ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; + +ciphers_dsa_signed_certs_openssl_names(suite) -> + []; + +ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Ciphers = ssl_test_lib:openssl_dsa_suites(), + test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), + run_suites(Ciphers, Version, Config, dsa). + + +ciphers_dsa_signed_certs_openssl_names_ssl3(doc) -> + ["Test all dsa ssl cipher suites in ssl3"]; + +ciphers_dsa_signed_certs_openssl_names_ssl3(suite) -> + []; + +ciphers_dsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) -> + Version = ssl_record:protocol_version({3,0}), + Ciphers = ssl_test_lib:openssl_dsa_suites(), + run_suites(Ciphers, Version, Config, dsa). + +anonymous_cipher_suites(doc)-> + ["Test the anonymous ciphersuites"]; +anonymous_cipher_suites(suite) -> + []; +anonymous_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:anonymous_suites(), + run_suites(Ciphers, Version, Config, anonymous). + +run_suites(Ciphers, Version, Config, Type) -> + {ClientOpts, ServerOpts} = + case Type of + rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; + dsa -> + {?config(client_opts, Config), + ?config(server_dsa_opts, Config)}; + anonymous -> + %% No certs in opts! + {?config(client_opts, Config), + ?config(server_anon, Config)} + end, + Result = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config) end, + cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, Ciphers), case lists:flatten(Result) of [] -> @@ -1255,30 +1525,36 @@ ciphers(Config) when is_list(Config) -> test_server:format("Cipher suite errors: ~p~n", [Error]), test_server:fail(cipher_suite_failed_see_test_case_log) end. - -cipher(CipherSuite, Version, Config) -> + +erlang_cipher_suite(Suite) when is_list(Suite)-> + ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite)); +erlang_cipher_suite(Suite) -> + Suite. + +cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> process_flag(trap_exit, true), test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + ErlangCipherSuite = erlang_cipher_suite(CipherSuite), + + ConnectionInfo = {ok, {Version, ErlangCipherSuite}}, + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, connection_info_result, []}}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, connection_info_result, []}}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, {options, [{ciphers,[CipherSuite]} | ClientOpts]}]), - - ServerMsg = ClientMsg = {ok, {Version, CipherSuite}}, - - Result = ssl_test_lib:wait_for_result(Server, ServerMsg, - Client, ClientMsg), + + Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), receive {'EXIT', Server, normal} -> @@ -1294,10 +1570,36 @@ cipher(CipherSuite, Version, Config) -> ok -> []; Error -> - [{CipherSuite, Error}] + [{ErlangCipherSuite, Error}] end. %%-------------------------------------------------------------------- +default_reject_anonymous(doc)-> + ["Test that by default anonymous cipher suites are rejected "]; +default_reject_anonymous(suite) -> + []; +default_reject_anonymous(Config) when is_list(Config) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + [Cipher | _] = ssl_test_lib:anonymous_suites(), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, + [{ciphers,[Cipher]} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, "insufficient security"}, + Client, {error, "insufficient security"}). + +%%-------------------------------------------------------------------- reuse_session(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -1884,7 +2186,6 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, no_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -1892,13 +2193,10 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, no_result, []}}, {options, [{active, false} | BadClientOpts]}]), ssl_test_lib:check_result(Server, {error, esslaccept}, - Client, {error, esslconnect}), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + Client, {error, esslconnect}). %%-------------------------------------------------------------------- @@ -1980,14 +2278,7 @@ client_verify_none_active_once(Config) when is_list(Config) -> {mfa, {?MODULE, send_recv_result_active_once, []}}, {options, [{active, once} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - %% TODO: send message to test process to make sure - %% verifyfun has beeen run as it has the same behavior as - %% the default fun - VerifyFun = fun([{bad_cert, unknown_ca}]) -> - true; - (_) -> - false - end, + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -1995,8 +2286,7 @@ client_verify_none_active_once(Config) when is_list(Config) -> send_recv_result_active_once, []}}, {options, [{active, once}, - {verify, verify_none}, - {verify_fun, VerifyFun} + {verify, verify_none} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -2076,6 +2366,76 @@ server_renegotiate(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- +client_renegotiate_reused_session(doc) -> + ["Test ssl:renegotiate/1 on client when the ssl session will be reused."]; + +client_renegotiate_reused_session(suite) -> + []; + +client_renegotiate_reused_session(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate_reuse_session, [Data]}}, + {options, [{reuse_sessions, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + process_flag(trap_exit, false), + ok. +%%-------------------------------------------------------------------- +server_renegotiate_reused_session(doc) -> + ["Test ssl:renegotiate/1 on server when the ssl session will be reused."]; + +server_renegotiate_reused_session(suite) -> + []; + +server_renegotiate_reused_session(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + renegotiate_reuse_session, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, [{reuse_sessions, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + ok. + +%%-------------------------------------------------------------------- client_no_wrap_sequence_number(doc) -> ["Test that erlang client will renegotiate session when", "max sequence number celing is about to be reached. Although" @@ -2162,34 +2522,89 @@ extended_key_usage(suite) -> []; extended_key_usage(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), PrivDir = ?config(priv_dir, Config), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - NewCertFile = filename:join(PrivDir, "cert.pem"), - - {ok, [{cert, DerCert, _}]} = public_key:pem_to_der(CertFile), + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = public_key:pem_entry_decode(KeyEntry), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, + NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = + [ServerExtKeyUsageExt | + ServerExtensions]}, + NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, + NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = + [ClientExtKeyUsageExt | + ClientExtensions]}, + NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, [{verify, verify_peer} | NewServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, [{verify, verify_peer} | NewClientOpts]}]), - {ok, [KeyInfo]} = public_key:pem_to_der(KeyFile), + ssl_test_lib:check_result(Server, ok, Client, ok), - {ok, Key} = public_key:decode_private_key(KeyInfo), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). - {ok, OTPCert} = public_key:pkix_decode_cert(DerCert, otp), - - ExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, +%%-------------------------------------------------------------------- +no_authority_key_identifier(doc) -> + ["Test cert that does not have authorityKeyIdentifier extension" + " but are present in trusted certs db."]; +no_authority_key_identifier(suite) -> + []; +no_authority_key_identifier(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = public_key:pem_entry_decode(KeyEntry), + + CertFile = proplists:get_value(certfile, ServerOpts), + NewCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile), + OTPCert = public_key:pkix_decode_cert(DerCert, otp), OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, - Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions, + NewExtensions = delete_authority_key_extension(Extensions, []), + NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = NewExtensions}, - NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = [ExtKeyUsageExt |Extensions]}, + test_server:format("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]), - NewDerCert = public_key:sign(NewOTPTbsCert, Key), - - public_key:der_to_pem(NewCertFile, [{cert, NewDerCert}]), - + NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]), NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2203,51 +2618,453 @@ extended_key_usage(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {mfa, {?MODULE, send_recv_result_active, []}}, - {options, ClientOpts}]), + {options, [{verify, verify_peer} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). +delete_authority_key_extension([], Acc) -> + lists:reverse(Acc); +delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest], + Acc) -> + delete_authority_key_extension(Rest, Acc); +delete_authority_key_extension([Head | Rest], Acc) -> + delete_authority_key_extension(Rest, [Head | Acc]). + %%-------------------------------------------------------------------- -validate_extensions_fun(doc) -> - ["Test that it is possible to specify a validate_extensions_fun"]; -validate_extensions_fun(suite) -> +invalid_signature_server(doc) -> + ["Test server with invalid signature"]; + +invalid_signature_server(suite) -> []; -validate_extensions_fun(Config) when is_list(Config) -> +invalid_signature_server(Config) when is_list(Config) -> ClientOpts = ?config(client_verification_opts, Config), ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "server/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = public_key:pem_entry_decode(KeyEntry), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/invalid_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - Fun = fun(Extensions, State, _, AccError) -> - {Extensions, State, AccError} - end, + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, "bad certificate"}, + Client, {error,"bad certificate"}). + +%%-------------------------------------------------------------------- + +invalid_signature_client(doc) -> + ["Test server with invalid signature"]; + +invalid_signature_client(suite) -> + []; + +invalid_signature_client(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "client/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = public_key:pem_entry_decode(KeyEntry), + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/invalid_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{validate_extensions_fun, Fun}, - {verify, verify_peer} | ServerOpts]}]), + {options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, NewClientOpts}]), + + tcp_delivery_workaround(Server, {error, "bad certificate"}, + Client, {error,"bad certificate"}). + +tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) -> + receive + {Server, ServerMsg} -> + receive + {Client, ClientMsg} -> + ok; + {Client, {error,closed}} -> + test_server:format("client got close"); + Unexpected -> + test_server:fail(Unexpected) + end; + {Client, ClientMsg} -> + receive + {Server, ServerMsg} -> + ok; + Unexpected -> + test_server:fail(Unexpected) + end; + {Client, {error,closed}} -> + receive + {Server, ServerMsg} -> + ok; + Unexpected -> + test_server:fail(Unexpected) + end; + {Server, {error,closed}} -> + receive + {Client, ClientMsg} -> + ok; + {Client, {error,closed}} -> + test_server:format("client got close"), + ok; + Unexpected -> + test_server:fail(Unexpected) + end; + Unexpected -> + test_server:fail(Unexpected) + end. +%%-------------------------------------------------------------------- +cert_expired(doc) -> + ["Test server with invalid signature"]; + +cert_expired(suite) -> + []; + +cert_expired(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = public_key:pem_entry_decode(KeyEntry), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/expired_cert.pem"), + [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + OTPCert = public_key:pkix_decode_cert(DerCert, otp), + OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, + + {Year, Month, Day} = date(), + {Hours, Min, Sec} = time(), + NotBeforeStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-2, + two_digits_str(Month), + two_digits_str(Day), + two_digits_str(Hours), + two_digits_str(Min), + two_digits_str(Sec)])), + NotAfterStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-1, + two_digits_str(Month), + two_digits_str(Day), + two_digits_str(Hours), + two_digits_str(Min), + two_digits_str(Sec)])), + NewValidity = {'Validity', {generalTime, NotBeforeStr}, {generalTime, NotAfterStr}}, + + test_server:format("Validity: ~p ~n NewValidity: ~p ~n", + [OTPTbsCert#'OTPTBSCertificate'.validity, NewValidity]), + + NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{validity = NewValidity}, + NewServerDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, NewServerOpts}]), Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, "certificate expired"}, + Client, {error, "certificate expired"}). - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, +two_digits_str(N) when N < 10 -> + lists:flatten(io_lib:format("0~p", [N])); +two_digits_str(N) -> + lists:flatten(io_lib:format("~p", [N])). + +%%-------------------------------------------------------------------- + +client_with_cert_cipher_suites_handshake(doc) -> + ["Test that client with a certificate without keyEncipherment usage " + " extension can connect to a server with restricted cipher suites "]; + +client_with_cert_cipher_suites_handshake(suite) -> + []; + +client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts_digital_signature_only, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, [{active, true}, + {ciphers, ssl_test_lib:rsa_non_signed_suites()} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options,[{validate_extensions_fun, Fun} | ClientOpts]}]), - + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, [{active, true} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +unknown_server_ca_fail(doc) -> + ["Test that the client fails if the ca is unknown in verify_peer mode"]; +unknown_server_ca_fail(suite) -> + []; +unknown_server_ca_fail(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, [test_to_update_user_state | UserState]}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error,"unknown ca"}, + Client, {error, "unknown ca"}), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +unknown_server_ca_accept_verify_none(doc) -> + ["Test that the client succeds if the ca is unknown in verify_none mode"]; +unknown_server_ca_accept_verify_none(suite) -> + []; +unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, + [{verify, verify_none}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +unknown_server_ca_accept_verify_peer(doc) -> + ["Test that the client succeds if the ca is unknown in verify_peer mode" + " with a verify_fun that accepts the unknown ca error"]; +unknown_server_ca_accept_verify_peer(suite) -> + []; +unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) -> + {valid, UserState}; + (_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +unknown_server_ca_accept_backwardscompatibilty(doc) -> + ["Test that old style verify_funs will work"]; +unknown_server_ca_accept_backwardscompatibilty(suite) -> + []; +unknown_server_ca_accept_backwardscompatibilty(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; + (Other, Acc) -> [Other | Acc] + end, + VerifyFun = + fun(ErrorList) -> + case lists:foldl(AcceptBadCa, [], ErrorList) of + [] -> true; + [_|_] -> false + end + end, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, VerifyFun}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +der_input(doc) -> + ["Test to input certs and key as der"]; + +der_input(suite) -> + []; + +der_input(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + DHParamFile = filename:join(DataDir, "dHParam.pem"), + + SeverVerifyOpts = ?config(server_verification_opts, Config), + {ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} | + SeverVerifyOpts]), + ClientVerifyOpts = ?config(client_verification_opts, Config), + {ClientCert, ClientKey, ClientCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} | + ClientVerifyOpts]), + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, + {dh, DHParams}, + {cert, ServerCert}, {key, ServerKey}, {cacerts, ServerCaCerts}], + ClientOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, + {dh, DHParams}, + {cert, ClientCert}, {key, ClientKey}, {cacerts, ClientCaCerts}], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), ssl_test_lib:close(Client). +der_input_opts(Opts) -> + Certfile = proplists:get_value(certfile, Opts), + CaCertsfile = proplists:get_value(cacertfile, Opts), + Keyfile = proplists:get_value(keyfile, Opts), + Dhfile = proplists:get_value(dhfile, Opts), + [{_, Cert, _}] = ssl_test_lib:pem_to_der(Certfile), + [{_, Key, _}] = ssl_test_lib:pem_to_der(Keyfile), + [{_, DHParams, _}] = ssl_test_lib:pem_to_der(Dhfile), + CaCerts = + lists:map(fun(Entry) -> + {_, CaCert, _} = Entry, + CaCert + end, ssl_test_lib:pem_to_der(CaCertsfile)), + {Cert, {rsa, Key}, CaCerts, DHParams}. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +erlang_ssl_receive(Socket, Data) -> + receive + {ssl, Socket, Data} -> + io:format("Received ~p~n",[Data]), + ok; + Other -> + test_server:fail({unexpected_message, Other}) + after ?SLEEP * 3 -> + test_server:fail({did_not_get, Data}) + end. + send_recv_result(Socket) -> ssl:send(Socket, "Hello world"), {ok,"Hello world"} = ssl:recv(Socket, 11), @@ -2270,6 +3087,7 @@ send_recv_result_active_once(Socket) -> result_ok(_Socket) -> ok. + renegotiate(Socket, Data) -> test_server:format("Renegotiating ~n", []), Result = ssl:renegotiate(Socket), @@ -2278,261 +3096,11 @@ renegotiate(Socket, Data) -> case Result of ok -> ok; - %% It is not an error in erlang ssl - %% if peer rejects renegotiation. - %% Connection will stay up - {error, renegotiation_rejected} -> - ok; Other -> Other end. - -session_cache_process_list(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_list(suite) -> - []; -session_cache_process_list(Config) when is_list(Config) -> - session_cache_process(list,Config). -session_cache_process_mnesia(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_mnesia(suite) -> - []; -session_cache_process_mnesia(Config) when is_list(Config) -> - session_cache_process(mnesia,Config). - -session_cache_process(Type,Config) when is_list(Config) -> - process_flag(trap_exit, true), - setup_session_cb(Type), - - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, session_info_result, []}}, - {options, - [{session_cache_cb, ?MODULE}| - ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! listen, - - %% Make sure session is registered +renegotiate_reuse_session(Socket, Data) -> + %% Make sure session is registerd test_server:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {?MODULE, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - receive - {Client1, SessionInfo} -> - ok; - {Client1, Other} -> - test_server:format("Expected: ~p, Unexpected: ~p~n", - [SessionInfo, Other]), - test_server:fail(session_not_reused) - end, - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, session_info_result, []}}, - {options, - [{reuse_sessions, false} | ServerOpts]}]), - Port1 = ssl_test_lib:inet_port(Server1), - - Client3 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {?MODULE, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - SessionInfo1 = - receive - {Server1, Info1} -> - Info1 - end, - - Server1 ! listen, - - %% Make sure session is registered - test_server:sleep(?SLEEP), - - Client4 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {?MODULE, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client4, SessionInfo1} -> - test_server:fail( - session_reused_when_session_reuse_disabled_by_server); - {Client4, _Other} -> - ok - end, - - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client3), - ssl_test_lib:close(Client4), - process_flag(trap_exit, false). - -setup_session_cb(Type) -> - ssl_test = ets:new(ssl_test,[named_table, set,public]), - ets:insert(ssl_test, {type,Type}). - -session_cb() -> - [{type,Type}] = ets:lookup(ssl_test, type), - Type. - -init() -> - io:format("~p~n",[?LINE]), - case session_cb() of - list -> - spawn(fun() -> session_loop([]) end); - mnesia -> - mnesia:start(), - {atomic,ok} = mnesia:create_table(sess_cache, []) - end. - -terminate(Cache) -> - io:format("~p~n",[?LINE]), - case session_cb() of - list -> - Cache ! terminate; - mnesia -> - {atomic,ok} = mnesia:delete_table(sess_cache, []) - end. - -lookup(Cache, Key) -> - io:format("~p~n",[?LINE]), - case session_cb() of - list -> - Cache ! {self(), lookup, Key}, - receive {Cache, Res} -> Res end; - mnesia -> - case mnesia:transaction(fun() -> - mnesia:read(sess_cache, - Key, read) - end) of - {atomic, [Session]} -> Session; - _ -> undefined - end - end. - -update(Cache, Key, Value) -> - io:format("~p~n",[?LINE]), - case session_cb() of - list -> - Cache ! {update, Key, Value}; - mnesia -> - {atomic, ok} = - mnesia:transaction(fun() -> - mnesia:write(sess_cache, - Key, Value) - end) - end. - -delete(Cache, Key) -> - io:format("~p~n",[?LINE]), - case session_cb() of - list -> - Cache ! {delete, Key}; - mnesia -> - {atomic, ok} = - mnesia:transaction(fun() -> - mnesia:delete(sess_cache, Key) - end) - end. - -foldl(Fun, Acc, Cache) -> - io:format("~p~n",[?LINE]), - case session_cb() of - list -> - Cache ! {self(),foldl,Fun,Acc}, - receive {Cache, Res} -> Res end; - mnesia -> - Foldl = fun() -> - mnesia:foldl(Fun, Acc, sess_cache) - end, - {atomic, Res} = mnesia:transaction(Foldl), - Res - end. - -select_session(Cache, PartialKey) -> - io:format("~p~n",[?LINE]), - case session_cb() of - list -> - Cache ! {self(),select_session, PartialKey}, - receive {Cache, Res} -> Res end; - mnesia -> - Sel = fun() -> - mnesia:select(Cache, - [{{{PartialKey,'$1'}, '$2'}, - [],['$$']}]) - end, - {atomic, Res} = mnesia:transaction(Sel), - Res - end. - -session_loop(Sess) -> - receive - terminate -> - ok; - {Pid, lookup, Key} -> - case lists:keysearch(Key,1,Sess) of - {value, {Key,Value}} -> - Pid ! {self(), Value}; - _ -> - Pid ! {self(), undefined} - end, - session_loop(Sess); - {update, Key, Value} -> - session_loop([{Key,Value}|Sess]); - {delete, Key} -> - session_loop(lists:keydelete(Key,1,Sess)); - {Pid,foldl,Fun,Acc} -> - Res = lists:foldl(Fun, Acc,Sess), - Pid ! {self(), Res}, - session_loop(Sess); - {Pid,select_session,PKey} -> - Sel = fun({{Head, _},Session}, Acc) when Head =:= PKey -> - [Session|Acc]; - (_,Acc) -> - Acc - end, - Pid ! {self(), lists:foldl(Sel, [], Sess)}, - session_loop(Sess) - end. - -erlang_ssl_receive(Socket, Data) -> - receive - {ssl, Socket, Data} -> - io:format("Received ~p~n",[Data]), - ok; - Other -> - test_server:fail({unexpected_message, Other}) - after ?SLEEP * 3 -> - test_server:fail({did_not_get, Data}) - end. - + renegotiate(Socket, Data). diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 1bcb9a657b..88d2d99ef8 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -42,7 +42,6 @@ -define(MANY, 1000). -define(SOME, 50). - %% Test server callback functions %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config @@ -55,6 +54,7 @@ %%-------------------------------------------------------------------- init_per_suite(Config) -> crypto:start(), + application:start(public_key), ssl:start(), Result = (catch make_certs:all(?config(data_dir, Config), @@ -144,9 +144,26 @@ all(suite) -> packet_wait_passive, packet_wait_active, packet_baddata_passive, packet_baddata_active, packet_size_passive, packet_size_active, - packet_erl_decode, + packet_cdr_decode, + packet_cdr_decode_list, packet_http_decode, - packet_http_bin_decode_multi + packet_http_decode_list, + packet_http_bin_decode_multi, + packet_http_error_passive, + packet_line_decode, + packet_line_decode_list, + packet_asn1_decode, + packet_asn1_decode_list, + packet_tpkt_decode, + packet_tpkt_decode_list, + %packet_fcgi_decode, + packet_sunrm_decode, + packet_sunrm_decode_list, + header_decode_one_byte, + header_decode_two_bytes, + header_decode_two_bytes_one_sent, + header_decode_two_bytes_two_sent + ]. %% Test cases starts here. @@ -503,7 +520,8 @@ packet_raw_active_once_many_small(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, active_once_raw, [Data, ?MANY]}}, + {mfa, {?MODULE, active_once_raw, + [Data, ?MANY]}}, {options, [{active, once}, {packet, raw} | ClientOpts]}]), @@ -535,7 +553,8 @@ packet_raw_active_once_some_big(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, active_once_raw, [Data, ?SOME]}}, + {mfa, {?MODULE, active_once_raw, + [Data, ?SOME]}}, {options, [{active, once}, {packet, raw} | ClientOpts]}]), @@ -1191,7 +1210,8 @@ packet_send_to_large(Config) when is_list(Config) -> {mfa, {?MODULE, active_packet, [Data, 1]}}, {options, [{active, true} | ClientOpts]}]), - ssl_test_lib:check_result(Server, {error, {badarg, {packet_to_large, 300, 255}}}), + ssl_test_lib:check_result(Server, {error, {badarg, + {packet_to_large, 300, 255}}}), ssl_test_lib:close(Server), ssl_test_lib:close(Client). @@ -1216,7 +1236,8 @@ packet_wait_active(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_incomplete ,[Data, ?SOME]}}, + {mfa, {?MODULE, send_incomplete, + [Data, ?SOME]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, @@ -1251,7 +1272,8 @@ packet_wait_passive(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_incomplete ,[Data, ?SOME]}}, + {mfa, {?MODULE, send_incomplete, + [Data, ?SOME]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, @@ -1293,7 +1315,8 @@ packet_baddata_active(Config) when is_list(Config) -> {packet, cdr} | ClientOpts]}]), receive - {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; + {Client, {other, {ssl_error, _Socket, + {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> test_server:fail({unexpected, Unexpected}) end, @@ -1338,8 +1361,11 @@ packet_baddata_passive(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- + packet_size_active(doc) -> - ["Test that if a packet of size larger than packet_size arrives error msg is sent and socket is closed"]; + ["Test that if a packet of size larger than + packet_size arrives error msg is sent and socket is closed"]; + packet_size_active(suite) -> []; @@ -1363,7 +1389,8 @@ packet_size_active(Config) when is_list(Config) -> {packet, 4}, {packet_size, 10} | ClientOpts]}]), receive - {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; + {Client, {other, {ssl_error, _Socket, + {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> test_server:fail({unexpected, Unexpected}) end, @@ -1371,10 +1398,11 @@ packet_size_active(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- + packet_size_passive(doc) -> - ["Test that if a packet of size larger than packet_size arrives error msg is sent and socket is closed"]; -packet_size_passive(suite) -> - []; + ["Test that if a packet of size larger + than packet_size arrives error msg is sent and socket is closed"]; +packet_size_passive(suite) -> []; packet_size_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1391,7 +1419,8 @@ packet_size_passive(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, passive_recv_packet, [Data, 1]}}, + {mfa, {?MODULE, passive_recv_packet, + [Data, 1]}}, {options, [{active, false}, {packet, 4}, {packet_size, 30} | ClientOpts]}]), @@ -1405,14 +1434,11 @@ packet_size_passive(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_erl_decode(doc) -> - ["Test that packets of sent to erlang:decode_packet works, i.e. currently" - "asn1 | cdr | sunrm | fcgi | tpkt | line | http | http_bin" - ]; -packet_erl_decode(suite) -> +packet_cdr_decode(doc) -> + ["Test setting the packet option {packet, cdr}, {mode, binary}"]; +packet_cdr_decode(suite) -> []; - -packet_erl_decode(Config) when is_list(Config) -> +packet_cdr_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1423,54 +1449,64 @@ packet_erl_decode(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_packet_decode ,[Data]}}, - {options, [{active, true}, binary, {packet, cdr}|ServerOpts]}]), + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, cdr}|ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_packet_decode, [Data]}}, - {options, [{active, true}, binary | ClientOpts]}]), + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, cdr}, + binary | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +packet_cdr_decode_list(doc) -> + ["Test setting the packet option {packet, cdr} {mode, list}"]; +packet_cdr_decode_list(suite) -> + []; +packet_cdr_decode_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), -server_packet_decode(Socket, CDR) -> - receive - {ssl, Socket, CDR} -> ok; - Other1 -> exit({?LINE, Other1}) - end, - ok = ssl:send(Socket, CDR), - receive - {ssl, Socket, CDR} -> ok; - Other2 -> exit({?LINE, Other2}) - end, - ok = ssl:send(Socket, CDR), - ok. + %% A valid cdr packet + Data = [71,73,79,80,1,2,2,1,0,0,0,41,0,0,0,0,0,0,0,0,0,0,0,1,78, + 69,79,0,0,0,0,2,0,10,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,4,49], -client_packet_decode(Socket, CDR) -> - <<P1:10/binary, P2/binary>> = CDR, - ok = ssl:send(Socket, P1), - ok = ssl:send(Socket, P2), - receive - {ssl, Socket, CDR} -> ok; - Other1 -> exit({?LINE, Other1}) - end, - ssl:setopts(Socket, [{packet, cdr}]), - ok = ssl:send(Socket, CDR), - receive - {ssl, Socket, CDR} -> ok; - Other2 -> exit({?LINE, Other2}) - end, - ok. + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, list, + {packet, cdr}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, cdr}, + list | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- packet_http_decode(doc) -> - ["Test setting the packet option {packet, http}"]; + ["Test setting the packet option {packet, http} {mode, binary} " + "(Body will be binary http strings are lists)"]; packet_http_decode(suite) -> []; @@ -1489,16 +1525,19 @@ packet_http_decode(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_http_decode, [Response]}}, - {options, [{active, true}, binary, {packet, http} | - ServerOpts]}]), + {mfa, {?MODULE, server_http_decode, + [Response]}}, + {options, [{active, true},binary, + {packet, http} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_http_decode, [Request]}}, - {options, [{active, true}, binary, {packet, http} | + {mfa, {?MODULE, client_http_decode, + [Request]}}, + {options, [{active, true}, binary, + {packet, http} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1550,6 +1589,66 @@ client_http_decode(Socket, HttpRequest) -> ok. %%-------------------------------------------------------------------- +packet_http_decode_list(doc) -> + ["Test setting the packet option {packet, http}, {mode, list}" + "(Body will be litst too)"]; +packet_http_decode_list(suite) -> + []; +packet_http_decode_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Request = "GET / HTTP/1.1\r\n" + "host: www.example.com\r\n" + "user-agent: HttpTester\r\n" + "\r\n", + Response = "HTTP/1.1 200 OK\r\n" + "\r\n" + "Hello!", + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_http_decode, + [Response]}}, + {options, [{active, true}, binary, + {packet, http} | + ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_http_decode_list, + [Request]}}, + {options, [{active, true}, list, + {packet, http} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +client_http_decode_list(Socket, HttpRequest) -> + ok = ssl:send(Socket, HttpRequest), + receive + {ssl, Socket, {http_response, {1,1}, 200, "OK"}} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, http_eoh} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:setopts(Socket, [{packet, 0}]), + receive + {ssl, Socket, "Hello!"} -> ok; + Other3 -> exit({?LINE, Other3}) + end, + ok. + +%%-------------------------------------------------------------------- packet_http_bin_decode_multi(doc) -> ["Test setting the packet option {packet, http_bin} with multiple requests"]; packet_http_bin_decode_multi(suite) -> @@ -1571,16 +1670,20 @@ packet_http_bin_decode_multi(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_http_bin_decode, [Response, NumMsgs]}}, - {options, [{active, true}, binary, {packet, http_bin} | + {mfa, {?MODULE, server_http_bin_decode, + [Response, NumMsgs]}}, + {options, [{active, true}, binary, + {packet, http_bin} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_http_bin_decode, [Request, NumMsgs]}}, - {options, [{active, true}, binary, {packet, http_bin} | + {mfa, {?MODULE, client_http_bin_decode, + [Request, NumMsgs]}}, + {options, [{active, true}, binary, + {packet, http_bin} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1637,23 +1740,551 @@ client_http_bin_decode(_, _, _) -> ok. %%-------------------------------------------------------------------- +packet_http_error_passive(doc) -> + ["Test setting the packet option {packet, http}, {active, false}" + " with a incorrect http header." ]; +packet_http_error_passive(suite) -> + []; +packet_http_error_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Request = "GET / HTTP/1.1\r\n" + "host: www.example.com\r\n" + "user-agent HttpTester\r\n" + "\r\n", + Response = "HTTP/1.1 200 OK\r\n" + "\r\n" + "Hello!", + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_http_decode_error, + [Response]}}, + {options, [{active, false}, binary, + {packet, http} | + ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_http_decode_list, + [Request]}}, + {options, [{active, true}, list, + {packet, http} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +server_http_decode_error(Socket, HttpResponse) -> + assert_packet_opt(Socket, http), + + {ok, {http_request, 'GET', _, {1,1}}} = ssl:recv(Socket, 0), + + assert_packet_opt(Socket, http), + + {ok, {http_header, _, 'Host', _, "www.example.com"}} = ssl:recv(Socket, 0), + assert_packet_opt(Socket, http), + + {ok, {http_error, _}} = ssl:recv(Socket, 0), + + assert_packet_opt(Socket, http), + + {ok, http_eoh} = ssl:recv(Socket, 0), + + assert_packet_opt(Socket, http), + ok = ssl:send(Socket, HttpResponse), + ok. + + +%%-------------------------------------------------------------------- +packet_line_decode(doc) -> + ["Test setting the packet option {packet, line}, {mode, binary}"]; +packet_line_decode(suite) -> + []; +packet_line_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = list_to_binary(lists:flatten(io_lib:format("Line ends here.~n" + "Now it is a new line.~n", + []))), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_line_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, line}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_line_packet_decode, + [Data]}}, + {options, [{active, true}, + {packet, line}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +packet_line_decode_list(doc) -> + ["Test setting the packet option {packet, line}, {mode, list}"]; +packet_line_decode_list(suite) -> + []; +packet_line_decode_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = lists:flatten(io_lib:format("Line ends here.~n" + "Now it is a new line.~n", [])), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + server_line_packet_decode, + [Data]}}, + {options, [{active, true}, list, + {packet, line}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + client_line_packet_decode, + [Data]}}, + {options, [{active, true}, + {packet, line}, + list | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +packet_asn1_decode(doc) -> + ["Test setting the packet option {packet, asn1}"]; +packet_asn1_decode(suite) -> + []; +packet_asn1_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + File = proplists:get_value(certfile, ServerOpts), + + %% A valid asn1 BER packet (DER is stricter BER) + [{'Certificate', Data, _}] = ssl_test_lib:pem_to_der(File), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, asn1}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, asn1}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +packet_asn1_decode_list(doc) -> + ["Test setting the packet option {packet, asn1}"]; +packet_asn1_decode_list(suite) -> + []; +packet_asn1_decode_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + File = proplists:get_value(certfile, ServerOpts), + + %% A valid asn1 BER packet (DER is stricter BER) + [{'Certificate', BinData, _}] = ssl_test_lib:pem_to_der(File), + + Data = binary_to_list(BinData), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, list, + {packet, asn1}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, asn1}, + list | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +packet_tpkt_decode(doc) -> + ["Test setting the packet option {packet, tpkt}"]; +packet_tpkt_decode(suite) -> + []; +packet_tpkt_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = list_to_binary(add_tpkt_header("TPKT data")), + + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, tpkt}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, tpkt}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +packet_tpkt_decode_list(doc) -> + ["Test setting the packet option {packet, tpkt}"]; +packet_tpkt_decode_list(suite) -> + []; +packet_tpkt_decode_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = binary_to_list(list_to_binary(add_tpkt_header("TPKT data"))), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, list, + {packet, tpkt}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, tpkt}, + list | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +%% packet_fcgi_decode(doc) -> +%% ["Test setting the packet option {packet, fcgi}"]; +%% packet_fcgi_decode(suite) -> +%% []; +%% packet_fcgi_decode(Config) when is_list(Config) -> +%% ClientOpts = ?config(client_opts, Config), +%% ServerOpts = ?config(server_opts, Config), +%% {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + +%% Data = ... + +%% Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, +%% {from, self()}, +%% {mfa, {?MODULE, server_packet_decode, +%% [Data0, Data1]}}, +%% {options, [{active, true}, binary, +%% {packet, fcgi}|ServerOpts]}]), + +%% Port = ssl_test_lib:inet_port(Server), +%% Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, +%% {host, Hostname}, +%% {from, self()}, +%% {mfa, {?MODULE, client_packet_decode, +%% [Data0, Data1]}}, +%% {options, [{active, true}, {packet, fcgi}, +%% binary | ClientOpts]}]), + +%% ssl_test_lib:check_result(Server, ok, Client, ok), + +%% ssl_test_lib:close(Server), +%% ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +packet_sunrm_decode(doc) -> + ["Test setting the packet option {packet, sunrm}"]; +packet_sunrm_decode(suite) -> + []; +packet_sunrm_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:32, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, binary, + {packet, sunrm}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, sunrm}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +packet_sunrm_decode_list(doc) -> + ["Test setting the packet option {packet, sunrm}"]; +packet_sunrm_decode_list(suite) -> + []; +packet_sunrm_decode_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = binary_to_list(list_to_binary([<<11:32>>, "Hello world"])), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_packet_decode, + [Data]}}, + {options, [{active, true}, list, + {packet, sunrm}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_packet_decode, + [Data]}}, + {options, [{active, true}, {packet, sunrm}, + list | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- + +header_decode_one_byte(doc) -> + ["Test setting the packet option {header, 1}"]; +header_decode_one_byte(suite) -> + []; +header_decode_one_byte(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, [11 | <<"Hello world">>]]}}, + {options, [{active, true}, binary, + {header,1}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, [11 | <<"Hello world">> ]]}}, + {options, [{active, true}, {header, 1}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +header_decode_two_bytes(doc) -> + ["Test setting the packet option {header, 2}"]; +header_decode_two_bytes(suite) -> + []; +header_decode_two_bytes(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, true}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, true}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_two_sent(doc) -> + ["Test setting the packet option {header, 2} and sending on byte"]; +header_decode_two_bytes_two_sent(suite) -> + []; +header_decode_two_bytes_two_sent(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"He">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, [$H, $e | <<>> ]]}}, + {options, [{active, true}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, [$H, $e | <<>> ]]}}, + {options, [{active, true}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_one_sent(doc) -> + ["Test setting the packet option {header, 2} and sending on byte"]; +header_decode_two_bytes_one_sent(suite) -> + []; +header_decode_two_bytes_one_sent(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"H">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode, + [Data, "H"]}}, + {options, [{active, true}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode, + [Data, "H"]}}, + {options, [{active, true}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- %% Internal functions -send_raw(_,_, 0) -> +send_raw(Socket,_, 0) -> + ssl:send(Socket, <<>>), no_result_msg; send_raw(Socket, Data, N) -> ssl:send(Socket, Data), send_raw(Socket, Data, N-1). -passive_raw(_, _, 0) -> +passive_raw(Socket, _, 0) -> + {error, timeout} = ssl:recv(Socket, 0, 500), ok; passive_raw(Socket, Data, N) -> Length = length(Data), {ok, Data} = ssl:recv(Socket, Length), passive_raw(Socket, Data, N-1). -passive_recv_packet(_, _, 0) -> - ok; +passive_recv_packet(Socket, _, 0) -> + case ssl:recv(Socket, 0) of + {ok, []} -> + {error, timeout} = ssl:recv(Socket, 0, 500), + ok; + Other -> + {other, Other, ssl:session_info(Socket), 0} + end; passive_recv_packet(Socket, Data, N) -> case ssl:recv(Socket, 0) of {ok, Data} -> @@ -1662,7 +2293,8 @@ passive_recv_packet(Socket, Data, N) -> {other, Other, ssl:session_info(Socket), N} end. -send(_,_, 0) -> +send(Socket,_, 0) -> + ssl:send(Socket, <<>>), no_result_msg; send(Socket, Data, N) -> case ssl:send(Socket, [Data]) of @@ -1676,6 +2308,7 @@ send_incomplete(Socket, Data, N) -> send_incomplete(Socket, Data, N, <<>>). send_incomplete(Socket, _Data, 0, Prev) -> ssl:send(Socket, Prev), + ssl:send(Socket, [?uint32(0)]), no_result_msg; send_incomplete(Socket, Data, N, Prev) -> Length = size(Data), @@ -1704,8 +2337,13 @@ active_once_raw(Socket, Data, N, Acc) -> end end. -active_once_packet(_,_, 0) -> - ok; +active_once_packet(Socket,_, 0) -> + receive + {ssl, Socket, []} -> + ok; + {ssl, Socket, Other} -> + {other, Other, ssl:session_info(Socket), 0} + end; active_once_packet(Socket, Data, N) -> receive {ssl, Socket, Data} -> @@ -1717,7 +2355,7 @@ active_once_packet(Socket, Data, N) -> active_raw(Socket, Data, N) -> active_raw(Socket, Data, N, []). -active_raw(_, _, 0, _) -> +active_raw(_Socket, _, 0, _) -> ok; active_raw(Socket, Data, N, Acc) -> receive @@ -1732,8 +2370,13 @@ active_raw(Socket, Data, N, Acc) -> end end. -active_packet(_, _, 0) -> - ok; +active_packet(Socket, _, 0) -> + receive + {ssl, Socket, []} -> + ok; + Other -> + {other, Other, ssl:session_info(Socket), 0} + end; active_packet(Socket, Data, N) -> receive {ssl, Socket, Data} -> @@ -1744,3 +2387,105 @@ active_packet(Socket, Data, N) -> assert_packet_opt(Socket, Type) -> {ok, [{packet, Type}]} = ssl:getopts(Socket, [packet]). + +server_packet_decode(Socket, Packet) -> + receive + {ssl, Socket, Packet} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Packet} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:send(Socket, Packet). + +client_packet_decode(Socket, Packet) when is_binary(Packet)-> + <<P1:10/binary, P2/binary>> = Packet, + client_packet_decode(Socket, P1, P2, Packet); +client_packet_decode(Socket, [Head | Tail] = Packet) -> + client_packet_decode(Socket, [Head], Tail, Packet). + +client_packet_decode(Socket, P1, P2, Packet) -> + test_server:format("Packet: ~p ~n", [Packet]), + ok = ssl:send(Socket, P1), + ok = ssl:send(Socket, P2), + receive + {ssl, Socket, Packet} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Packet} -> ok; + Other2 -> exit({?LINE, Other2}) + end. + +server_header_decode(Socket, Packet, Result) -> + receive + {ssl, Socket, Result} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Result} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:send(Socket, Packet). + +client_header_decode(Socket, Packet, Result) -> + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Result} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + ok = ssl:send(Socket, Packet), + receive + {ssl, Socket, Result} -> ok; + Other2 -> exit({?LINE, Other2}) + end. + +server_line_packet_decode(Socket, Packet) when is_binary(Packet) -> + [L1, L2] = string:tokens(binary_to_list(Packet), "\n"), + server_line_packet_decode(Socket, list_to_binary(L1 ++ "\n"), list_to_binary(L2 ++ "\n"), Packet); +server_line_packet_decode(Socket, Packet) -> + [L1, L2] = string:tokens(Packet, "\n"), + server_line_packet_decode(Socket, L1 ++ "\n", L2 ++ "\n", Packet). + +server_line_packet_decode(Socket, L1, L2, Packet) -> + receive + {ssl, Socket, L1} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, L2} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:send(Socket, Packet). + +client_line_packet_decode(Socket, Packet) when is_binary(Packet)-> + <<P1:10/binary, P2/binary>> = Packet, + [L1, L2] = string:tokens(binary_to_list(Packet), "\n"), + client_line_packet_decode(Socket, P1, P2, list_to_binary(L1 ++ "\n"), list_to_binary(L2 ++ "\n")); +client_line_packet_decode(Socket, [Head | Tail] = Packet) -> + [L1, L2] = string:tokens(Packet, "\n"), + client_line_packet_decode(Socket, [Head], Tail, L1 ++ "\n", L2 ++ "\n"). + +client_line_packet_decode(Socket, P1, P2, L1, L2) -> + ok = ssl:send(Socket, P1), + ok = ssl:send(Socket, P2), + receive + {ssl, Socket, L1} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, L2} -> ok; + Other2 -> exit({?LINE, Other2}) + end. + +add_tpkt_header(Data) when is_binary(Data) -> + L = size(Data) + 4, + [3, 0, ((L) bsr 8) band 16#ff, (L) band 16#ff ,Data]; +add_tpkt_header(IOList) when is_list(IOList) -> + Binary = list_to_binary(IOList), + L = size(Binary) + 4, + [3, 0, ((L) bsr 8) band 16#ff, (L) band 16#ff , Binary]. diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index a0aa92bdf2..d80df0bfbd 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2008-2010. 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% %% @@ -38,6 +38,7 @@ %%-------------------------------------------------------------------- init_per_suite(Config) -> crypto:start(), + application:start(public_key), ssl:start(), make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)), ssl_test_lib:cert_options(Config). diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl new file mode 100644 index 0000000000..0f39759d97 --- /dev/null +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -0,0 +1,306 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2010. 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/.2 +%% +%% 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(ssl_session_cache_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). + +-define(SLEEP, 500). +-define(TIMEOUT, 60000). +-define(LONG_TIMEOUT, 600000). +-behaviour(ssl_session_cache_api). + +%% For the session cache tests +-export([init/1, terminate/1, lookup/2, update/3, + delete/2, foldl/3, select_session/2]). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config0) -> + Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), + crypto:start(), + application:start(public_key), + ssl:start(), + + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + test_server:format("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ssl:stop(), + crypto:stop(). + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(session_cache_process_list, Config) -> + init_customized_session_cache(list, Config); + +init_per_testcase(session_cache_process_mnesia, Config) -> + mnesia:start(), + init_customized_session_cache(mnesia, Config); + +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +init_customized_session_cache(Type, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_cb, ?MODULE), + application:set_env(ssl, session_cb_init_args, [Type]), + ssl:start(), + [{watchdog, Dog} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(session_cache_process_list, Config) -> + application:unset_env(ssl, session_cb), + end_per_testcase(default_action, Config); +end_per_testcase(session_cache_process_mnesia, Config) -> + application:unset_env(ssl, session_cb), + application:unset_env(ssl, session_cb_init_args), + mnesia:kill(), + ssl:stop(), + ssl:start(), + end_per_testcase(default_action, Config); +end_per_testcase(_TestCase, Config) -> + Dog = ?config(watchdog, Config), + case Dog of + undefined -> + ok; + _ -> + test_server:timetrap_cancel(Dog) + end. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all(doc) -> + ["Test session cach API"]; + +all(suite) -> + [ + session_cache_process_list, session_cache_process_mnesia + ]. + +session_cache_process_list(doc) -> + ["Test reuse of sessions (short handshake)"]; + +session_cache_process_list(suite) -> + []; +session_cache_process_list(Config) when is_list(Config) -> + session_cache_process(list,Config). +%%-------------------------------------------------------------------- +session_cache_process_mnesia(doc) -> + ["Test reuse of sessions (short handshake)"]; + +session_cache_process_mnesia(suite) -> + []; +session_cache_process_mnesia(Config) when is_list(Config) -> + session_cache_process(mnesia,Config). + + +%%-------------------------------------------------------------------- +%%% Session cache API callbacks +%%-------------------------------------------------------------------- + +init([Type]) -> + ets:new(ssl_test, [named_table, public, set]), + ets:insert(ssl_test, {type, Type}), + case Type of + list -> + spawn(fun() -> session_loop([]) end); + mnesia -> + mnesia:start(), + {atomic,ok} = mnesia:create_table(sess_cache, []), + sess_cache + end. + +session_cb() -> + [{type, Type}] = ets:lookup(ssl_test, type), + Type. + +terminate(Cache) -> + case session_cb() of + list -> + Cache ! terminate; + mnesia -> + catch {atomic,ok} = + mnesia:delete_table(sess_cache) + end. + +lookup(Cache, Key) -> + case session_cb() of + list -> + Cache ! {self(), lookup, Key}, + receive {Cache, Res} -> Res end; + mnesia -> + case mnesia:transaction(fun() -> + mnesia:read(sess_cache, + Key, read) + end) of + {atomic, [{sess_cache, Key, Value}]} -> + Value; + _ -> + undefined + end + end. + +update(Cache, Key, Value) -> + case session_cb() of + list -> + Cache ! {update, Key, Value}; + mnesia -> + {atomic, ok} = + mnesia:transaction(fun() -> + mnesia:write(sess_cache, + {sess_cache, Key, Value}, write) + end) + end. + +delete(Cache, Key) -> + case session_cb() of + list -> + Cache ! {delete, Key}; + mnesia -> + {atomic, ok} = + mnesia:transaction(fun() -> + mnesia:delete(sess_cache, Key) + end) + end. + +foldl(Fun, Acc, Cache) -> + case session_cb() of + list -> + Cache ! {self(),foldl,Fun,Acc}, + receive {Cache, Res} -> Res end; + mnesia -> + Foldl = fun() -> + mnesia:foldl(Fun, Acc, sess_cache) + end, + {atomic, Res} = mnesia:transaction(Foldl), + Res + end. + +select_session(Cache, PartialKey) -> + case session_cb() of + list -> + Cache ! {self(),select_session, PartialKey}, + receive + {Cache, Res} -> + Res + end; + mnesia -> + Sel = fun() -> + mnesia:select(Cache, + [{{sess_cache,{PartialKey,'$1'}, '$2'}, + [],['$$']}]) + end, + {atomic, Res} = mnesia:transaction(Sel), + Res + end. + +session_loop(Sess) -> + receive + terminate -> + ok; + {Pid, lookup, Key} -> + case lists:keysearch(Key,1,Sess) of + {value, {Key,Value}} -> + Pid ! {self(), Value}; + _ -> + Pid ! {self(), undefined} + end, + session_loop(Sess); + {update, Key, Value} -> + NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)], + session_loop(NewSess); + {delete, Key} -> + session_loop(lists:keydelete(Key,1,Sess)); + {Pid,foldl,Fun,Acc} -> + Res = lists:foldl(Fun, Acc,Sess), + Pid ! {self(), Res}, + session_loop(Sess); + {Pid,select_session,PKey} -> + Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 -> + [[Id, Session]|Acc]; + (_,Acc) -> + Acc + end, + Sessions = lists:foldl(Sel, [], Sess), + Pid ! {self(), Sessions}, + session_loop(Sess) + end. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +session_cache_process(_Type,Config) when is_list(Config) -> + ssl_basic_SUITE:reuse_session(Config). diff --git a/lib/ssl/test/ssl_test_MACHINE.erl b/lib/ssl/test/ssl_test_MACHINE.erl index e75f7079ed..e0ffa15d80 100644 --- a/lib/ssl/test/ssl_test_MACHINE.erl +++ b/lib/ssl/test/ssl_test_MACHINE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2003-2010. 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% %% @@ -60,10 +60,12 @@ many_conns_1() -> %% mk_ssl_cert_opts(_Config) -> Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), - COpts = [{cacertfile, filename:join([Dir, "client", "cacerts.pem"])}, + COpts = [{ssl_imp, old}, + {cacertfile, filename:join([Dir, "client", "cacerts.pem"])}, {certfile, filename:join([Dir, "client", "cert.pem"])}, {keyfile, filename:join([Dir, "client", "key.pem"])}], - SOpts = [{cacertfile, filename:join([Dir, "server", "cacerts.pem"])}, + SOpts = [{ssl_imp, old}, + {cacertfile, filename:join([Dir, "server", "cacerts.pem"])}, {certfile, filename:join([Dir, "server", "cert.pem"])}, {keyfile, filename:join([Dir, "server", "key.pem"])}], {ok, {COpts, SOpts}}. @@ -225,11 +227,13 @@ start_ssl(Nodes, Config) -> ok. do_start(Env) -> + application:start(crypto), + application:start(public_key), application:load(ssl), lists:foreach( fun({Par, Val}) -> application:set_env(ssl, Par, Val) end, Env), - application:start(ssl), - application:start(crypto). + application:start(ssl). + %% %% start_node(Name) -> {ok, Node} @@ -542,7 +546,7 @@ get_active(St) -> listen(St, LPort) -> case St#st.protomod of ssl -> - ssl:listen(LPort, St#st.sockopts ++ St#st.sslopts); + ssl:listen(LPort, [{ssl_imp, old} | St#st.sockopts ++ St#st.sslopts]); gen_tcp -> gen_tcp:listen(LPort, St#st.sockopts) end. @@ -584,7 +588,8 @@ connect(St, Host, Port) -> case St#st.protomod of ssl -> - case ssl:connect(Host, Port, St#st.sockopts ++ St#st.sslopts, + case ssl:connect(Host, Port, + [{ssl_imp, old} | St#st.sockopts ++ St#st.sslopts], St#st.timeout) of {ok, Sock} -> {ok, LPort} = ssl:sockname(Sock), diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 00c5350ad0..e1e8214ed6 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -268,6 +268,8 @@ cert_options(Config) -> "client", "cacerts.pem"]), ClientCertFile = filename:join([?config(priv_dir, Config), "client", "cert.pem"]), + ClientCertFileDigitalSignatureOnly = filename:join([?config(priv_dir, Config), + "client", "digital_signature_only_cert.pem"]), ServerCaCertFile = filename:join([?config(priv_dir, Config), "server", "cacerts.pem"]), ServerCertFile = filename:join([?config(priv_dir, Config), @@ -292,8 +294,13 @@ cert_options(Config) -> {certfile, ClientCertFile}, {keyfile, ClientKeyFile}, {ssl_imp, new}]}, + {client_verification_opts_digital_signature_only, [{cacertfile, ClientCaCertFile}, + {certfile, ClientCertFileDigitalSignatureOnly}, + {keyfile, ClientKeyFile}, + {ssl_imp, new}]}, {server_opts, [{ssl_imp, new},{reuseaddr, true}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, {server_verification_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -318,6 +325,39 @@ cert_options(Config) -> | Config]. +make_dsa_cert(Config) -> + + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_dsa_cert_files("server", Config), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_dsa_cert_files("client", Config), + [{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_dsa_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, + {client_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + | Config]. + + + +make_dsa_cert_files(RoleStr, Config) -> + CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, dsa}]), + {Cert, CertKey} = erl_make_certs:make_cert([{key, dsa}, {issuer, CaInfo}]), + CaCertFile = filename:join([?config(priv_dir, Config), + RoleStr, "dsa_cacerts.pem"]), + CertFile = filename:join([?config(priv_dir, Config), + RoleStr, "dsa_cert.pem"]), + KeyFile = filename:join([?config(priv_dir, Config), + RoleStr, "dsa_key.pem"]), + + der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]), + der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]), + der_to_pem(KeyFile, [CertKey]), + {CaCertFile, CertFile, KeyFile}. + start_upgrade_server(Args) -> Result = spawn_link(?MODULE, run_upgrade_server, [Args]), receive @@ -394,6 +434,41 @@ run_upgrade_client(Opts) -> ok = rpc:call(Node, ssl, close, [SslSocket]) end. +start_upgrade_server_error(Args) -> + Result = spawn_link(?MODULE, run_upgrade_server_error, [Args]), + receive + {listen, up} -> + Result + end. + +run_upgrade_server_error(Opts) -> + Node = proplists:get_value(node, Opts), + Port = proplists:get_value(port, Opts), + TimeOut = proplists:get_value(timeout, Opts, infinity), + TcpOptions = proplists:get_value(tcp_options, Opts), + SslOptions = proplists:get_value(ssl_options, Opts), + Pid = proplists:get_value(from, Opts), + + test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), + test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]), + {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), + Error = case TimeOut of + infinity -> + test_server:format("ssl:ssl_accept(~p, ~p)~n", + [AcceptSocket, SslOptions]), + rpc:call(Node, ssl, ssl_accept, + [AcceptSocket, SslOptions]); + _ -> + test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n", + [AcceptSocket, SslOptions, TimeOut]), + rpc:call(Node, ssl, ssl_accept, + [AcceptSocket, SslOptions, TimeOut]) + end, + Pid ! {self(), Error}. + start_server_error(Args) -> Result = spawn_link(?MODULE, run_server_error, [Args]), receive @@ -494,3 +569,82 @@ send_selected_port(Pid, 0, Socket) -> Pid ! {self(), {port, NewPort}}; send_selected_port(_,_,_) -> ok. + +rsa_suites() -> + lists:filter(fun({dhe_dss, _, _}) -> + false; + (_) -> + true + end, + ssl:cipher_suites()). + +rsa_non_signed_suites() -> + lists:filter(fun({rsa, _, _}) -> + true; + (_) -> + false + end, + ssl:cipher_suites()). + +dsa_suites() -> + lists:filter(fun({dhe_dss, _, _}) -> + true; + (_) -> + false + end, + ssl:cipher_suites()). + + +openssl_rsa_suites() -> + Ciphers = ssl:cipher_suites(openssl), + lists:filter(fun(Str) -> + case re:run(Str,"DSS",[]) of + nomatch -> + true; + _ -> + false + end + end, Ciphers). + +openssl_dsa_suites() -> + Ciphers = ssl:cipher_suites(openssl), + lists:filter(fun(Str) -> + case re:run(Str,"DSS",[]) of + nomatch -> + false; + _ -> + true + end + end, Ciphers). + +anonymous_suites() -> + [{dh_anon, rc4_128, md5}, + {dh_anon, des_cbc, sha}, + {dh_anon, '3des_ede_cbc', sha}, + {dh_anon, aes_128_cbc, sha}, + {dh_anon, aes_256_cbc, sha}]. + +pem_to_der(File) -> + {ok, PemBin} = file:read_file(File), + public_key:pem_decode(PemBin). + +der_to_pem(File, Entries) -> + PemBin = public_key:pem_encode(Entries), + file:write_file(File, PemBin). + +cipher_result(Socket, Result) -> + Result = ssl:connection_info(Socket), + test_server:format("Successfull connect: ~p~n", [Result]), + %% Importante to send two packets here + %% to properly test "cipher state" handling + ssl:send(Socket, "Hello\n"), + receive + {ssl, Socket, "Hello\n"} -> + ssl:send(Socket, " world\n"), + receive + {ssl, Socket, " world\n"} -> + ok + end; + Other -> + {unexpected, Other} + end. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index cbf0447bf0..afedeaf099 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -25,14 +25,14 @@ -compile(export_all). -include("test_server.hrl"). --include("test_server_line.hrl"). --include("ssl_pkix.hrl"). -define(TIMEOUT, 120000). +-define(LONG_TIMEOUT, 600000). -define(SLEEP, 1000). -define(OPENSSL_RENEGOTIATE, "r\n"). -define(OPENSSL_QUIT, "Q\n"). -define(OPENSSL_GARBAGE, "P\n"). +-define(EXPIRE, 10). %% Test server callback functions %%-------------------------------------------------------------------- @@ -44,18 +44,22 @@ %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- -init_per_suite(Config) -> +init_per_suite(Config0) -> + Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), case os:find_executable("openssl") of false -> {skip, "Openssl not found"}; _ -> crypto:start(), + application:start(public_key), ssl:start(), Result = - (catch make_certs:all(?config(data_dir, Config), - ?config(priv_dir, Config))), + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), test_server:format("Make certs ~p~n", [Result]), - ssl_test_lib:cert_options(Config) + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config] end. %%-------------------------------------------------------------------- @@ -81,11 +85,29 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %% Description: Initialization before each test case %%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> +init_per_testcase(expired_session, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_lifetime, ?EXPIRE), + ssl:start(), + [{watchdog, Dog} | Config]; + +init_per_testcase(TestCase, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), Dog = ssl_test_lib:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. + special_init(TestCase, [{watchdog, Dog} | Config]). + +special_init(TestCase, Config) + when TestCase == erlang_client_openssl_server_renegotiate; + TestCase == erlang_client_openssl_server_no_wrap_sequence_number; + TestCase == erlang_server_openssl_client_no_wrap_sequence_number -> + check_sane_openssl_renegotaite(Config); +special_init(_, Config) -> + Config. + %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config) -> _ %% Case - atom() @@ -94,14 +116,20 @@ init_per_testcase(_TestCase, Config0) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> +end_per_testcase(reuse_session_expired, Config) -> + application:unset_env(ssl, session_lifetime), + end_per_testcase(default_action, Config); + +end_per_testcase(default_action, Config) -> Dog = ?config(watchdog, Config), case Dog of undefined -> ok; _ -> test_server:timetrap_cancel(Dog) - end. + end; +end_per_testcase(_, Config) -> + end_per_testcase(default_action, Config). %%-------------------------------------------------------------------- %% Function: all(Clause) -> TestCases @@ -117,6 +145,10 @@ all(doc) -> all(suite) -> [erlang_client_openssl_server, erlang_server_openssl_client, + tls1_erlang_client_openssl_server_dsa_cert, + tls1_erlang_server_openssl_client_dsa_cert, + ssl3_erlang_client_openssl_server_dsa_cert, + ssl3_erlang_server_openssl_client_dsa_cert, erlang_server_openssl_client_reuse_session, erlang_client_openssl_server_renegotiate, erlang_client_openssl_server_no_wrap_sequence_number, @@ -132,8 +164,11 @@ all(suite) -> tls1_erlang_client_openssl_server_client_cert, tls1_erlang_server_openssl_client_client_cert, tls1_erlang_server_erlang_client_client_cert, - ciphers, - erlang_client_bad_openssl_server + ciphers_rsa_signed_certs, + ciphers_dsa_signed_certs, + erlang_client_bad_openssl_server, + expired_session, + ssl2_erlang_server_openssl_client ]. %% Test cases starts here. @@ -220,6 +255,185 @@ erlang_server_openssl_client(Config) when is_list(Config) -> %%-------------------------------------------------------------------- +tls1_erlang_client_openssl_server_dsa_cert(doc) -> + ["Test erlang server with openssl client"]; +tls1_erlang_client_openssl_server_dsa_cert(suite) -> + []; +tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_dsa_opts, Config), + ServerOpts = ?config(server_dsa_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile + ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1 -msg", + + test_server:format("openssl cmd: ~p~n", [Cmd]), + + OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + erlang_ssl_receive, [Data]}}, + {options, ClientOpts}]), + + port_command(OpensslPort, Data), + + ssl_test_lib:check_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- + +tls1_erlang_server_openssl_client_dsa_cert(doc) -> + ["Test erlang server with openssl client"]; +tls1_erlang_server_openssl_client_dsa_cert(suite) -> + []; +tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_dsa_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + CaCertFile = proplists:get_value(cacertfile, ClientOpts), + CertFile = proplists:get_value(certfile, ClientOpts), + KeyFile = proplists:get_value(keyfile, ClientOpts), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile + ++ " -key " ++ KeyFile ++ " -tls1 -msg", + + test_server:format("openssl cmd: ~p~n", [Cmd]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + port_command(OpenSslPort, Data), + + ssl_test_lib:check_result(Server, ok), + + ssl_test_lib:close(Server), + + close_port(OpenSslPort), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- + +ssl3_erlang_client_openssl_server_dsa_cert(doc) -> + ["Test erlang server with openssl client"]; +ssl3_erlang_client_openssl_server_dsa_cert(suite) -> + []; +ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_dsa_opts, Config), + ServerOpts = ?config(server_dsa_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile + ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3 -msg", + + test_server:format("openssl cmd: ~p~n", [Cmd]), + + OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + erlang_ssl_receive, [Data]}}, + {options, ClientOpts}]), + + port_command(OpensslPort, Data), + + ssl_test_lib:check_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- + +ssl3_erlang_server_openssl_client_dsa_cert(doc) -> + ["Test erlang server with openssl client"]; +ssl3_erlang_server_openssl_client_dsa_cert(suite) -> + []; +ssl3_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_dsa_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + CaCertFile = proplists:get_value(cacertfile, ClientOpts), + CertFile = proplists:get_value(certfile, ClientOpts), + KeyFile = proplists:get_value(keyfile, ClientOpts), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile + ++ " -key " ++ KeyFile ++ " -ssl3 -msg", + + test_server:format("openssl cmd: ~p~n", [Cmd]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + port_command(OpenSslPort, Data), + + ssl_test_lib:check_result(Server, ok), + + ssl_test_lib:close(Server), + + close_port(OpenSslPort), + process_flag(trap_exit, false), + ok. + + +%%-------------------------------------------------------------------- + erlang_server_openssl_client_reuse_session(doc) -> ["Test erlang server with openssl client that reconnects with the" "same session id, to test reusing of sessions."]; @@ -297,12 +511,8 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> test_server:sleep(?SLEEP), port_command(OpensslPort, OpenSslData), - %%ssl_test_lib:check_result(Client, ok), - %% Currently allow test case to not fail - %% if server requires secure renegotiation from RFC-5746 - %% This should be removed as soon as we have implemented it. - ssl_test_lib:check_result_ignore_renegotiation_reject(Client, ok), - + ssl_test_lib:check_result(Client, ok), + %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), @@ -350,11 +560,7 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config {options, [{reuse_sessions, false}, {renegotiate_at, N} | ClientOpts]}]), - %%ssl_test_lib:check_result(Client, ok), - %% Currently allow test case to not fail - %% if server requires secure renegotiation from RFC-5746 - %% This should be removed as soon as we have implemented it. - ssl_test_lib:check_result_ignore_renegotiation_reject(Client, ok), + ssl_test_lib:check_result(Client, ok), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), @@ -862,19 +1068,46 @@ tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- -ciphers(doc) -> - [""]; +ciphers_rsa_signed_certs(doc) -> + ["Test cipher suites that uses rsa certs"]; + +ciphers_rsa_signed_certs(suite) -> + []; + +ciphers_rsa_signed_certs(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Ciphers = ssl_test_lib:rsa_suites(), + run_suites(Ciphers, Version, Config, rsa). + + +ciphers_dsa_signed_certs(doc) -> + ["Test cipher suites that uses dsa certs"]; -ciphers(suite) -> +ciphers_dsa_signed_certs(suite) -> []; -ciphers(Config) when is_list(Config) -> +ciphers_dsa_signed_certs(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - Ciphers = ssl:cipher_suites(), + Ciphers = ssl_test_lib:dsa_suites(), + run_suites(Ciphers, Version, Config, dsa). + +run_suites(Ciphers, Version, Config, Type) -> + {ClientOpts, ServerOpts} = + case Type of + rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; + dsa -> + {?config(client_opts, Config), + ?config(server_dsa_opts, Config)} + end, + Result = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config) end, + cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, Ciphers), case lists:flatten(Result) of [] -> @@ -883,12 +1116,10 @@ ciphers(Config) when is_list(Config) -> test_server:format("Cipher suite errors: ~p~n", [Error]), test_server:fail(cipher_suite_failed_see_test_case_log) end. - -cipher(CipherSuite, Version, Config) -> + +cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> process_flag(trap_exit, true), test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), Port = ssl_test_lib:inet_port(node()), @@ -896,25 +1127,39 @@ cipher(CipherSuite, Version, Config) -> KeyFile = proplists:get_value(keyfile, ServerOpts), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - + " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", + test_server:format("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), wait_for_openssl_server(), + ConnectionInfo = {ok, {Version, CipherSuite}}, + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, connection_info_result, []}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - ClientMsg = {ok, {Version, CipherSuite}}, - - Result = ssl_test_lib:wait_for_result(Client, ClientMsg), + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), + + port_command(OpenSslPort, "Hello\n"), + + receive + {Port, {data, _}} when is_port(Port) -> + ok + after 500 -> + test_server:format("Time out on openssl port, check that" + " the messages Hello and world are received" + " during close of port" , []), + ok + end, + + port_command(OpenSslPort, " world\n"), + + Result = ssl_test_lib:wait_for_result(Client, ok), close_port(OpenSslPort), %% Clean close down! @@ -958,7 +1203,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> wait_for_openssl_server(), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, server_sent_garbage, []}}, @@ -970,15 +1215,120 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> test_server:sleep(?SLEEP), - Client ! server_sent_garbage, + Client0 ! server_sent_garbage, + + ssl_test_lib:check_result(Client0, true), + + ssl_test_lib:close(Client0), + + %% Make sure openssl does not hang and leave zombie process + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result_msg, []}}, + {options, + [{versions, [tlsv1]} | ClientOpts]}]), + + ssl_test_lib:close(Client1), + + %% Clean close down! + close_port(OpensslPort), + process_flag(trap_exit, false), + ok. - ssl_test_lib:check_result(Client, true), +%%-------------------------------------------------------------------- + +expired_session(doc) -> + ["Test our ssl client handling of expired sessions. Will make" + "better code coverage of the ssl_manager module"]; + +expired_session(suite) -> + []; + +expired_session(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", + + test_server:format("openssl cmd: ~p~n", [Cmd]), + + OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + + ssl_test_lib:close(Client0), + + %% Make sure session is registered + test_server:sleep(?SLEEP), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + + ssl_test_lib:close(Client1), + %% Make sure session is unregistered due to expiration + test_server:sleep((?EXPIRE+1) * 1000), + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), - ssl_test_lib:close(Client), - %% Clean close down! close_port(OpensslPort), + ssl_test_lib:close(Client2), + process_flag(trap_exit, false). + +%%-------------------------------------------------------------------- +ssl2_erlang_server_openssl_client(doc) -> + ["Test that ssl v2 clients are rejected"]; +ssl2_erlang_server_openssl_client(suite) -> + []; +ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ?config(server_opts, Config), + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + " -host localhost -ssl2 -msg", + + test_server:format("openssl cmd: ~p~n", [Cmd]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + port_command(OpenSslPort, Data), + + ssl_test_lib:check_result(Server, {error,"protocol version"}), + + ssl_test_lib:close(Server), + + close_port(OpenSslPort), process_flag(trap_exit, false), ok. + %%-------------------------------------------------------------------- erlang_ssl_receive(Socket, Data) -> @@ -1018,8 +1368,7 @@ delayed_send(Socket, [ErlData, OpenSslData]) -> erlang_ssl_receive(Socket, OpenSslData). close_port(Port) -> - port_command(Port, ?OPENSSL_QUIT), - %%catch port_command(Port, "quit\n"), + catch port_command(Port, ?OPENSSL_QUIT), close_loop(Port, 500, false). close_loop(Port, Time, SentClose) -> @@ -1055,6 +1404,7 @@ server_sent_garbage(Socket) -> receive server_sent_garbage -> {error, closed} == ssl:send(Socket, "data") + end. wait_for_openssl_server() -> @@ -1068,3 +1418,12 @@ wait_for_openssl_server() -> test_server:sleep(?SLEEP) end. +check_sane_openssl_renegotaite(Config) -> + case os:cmd("openssl version") of + "OpenSSL 0.9.8" ++ _ -> + {skip, "Known renegotiation bug in OppenSSL"}; + "OpenSSL 0.9.7" ++ _ -> + {skip, "Known renegotiation bug in OppenSSL"}; + _ -> + Config + end. diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index a8966d46d7..ee692adb3b 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1,33 +1,2 @@ -SSL_VSN = 3.11 -TICKETS = OTP-8517 \ - OTP-7046 \ - OTP-8557 \ - OTP-8560 \ - OTP-8545 \ - OTP-8554 - -#TICKETS_3.10.9 = OTP-8510 - -#TICKETS_3.10.8 = OTP-8372 OTP-8441 OTP-8459 -#TICKETS_3.10.7 = OTP-8260 OTP-8218 OTP-8250 - -#TICKETS_3.10.6 = OTP-8275 - -#TICKETS_3.10.5 = OTP-8224 OTP-8244 - -#TICKETS_3.10.4 = OTP-8137 - -#TICKETS_3.10.3 = OTP-8011 -#TICKETS_3.10.2 = OTP-7963 - -# TICKETS_3.10.1 = OTP-7878 \ -# OTP-7656 \ -# OTP-7870 \ -# OTP-7871 - -# TICKETS_3.10 = OTP-7258 \ -# OTP-6894 \ -# OTP-7037 \ -# OTP-7039 \ -# OTP-7150 +SSL_VSN = 4.1.1 |